pytorch-word2vec的实例实现
word2vec的實(shí)例實(shí)現(xiàn)
實(shí)現(xiàn)詞嵌入word2vec中的跳字模型和近似訓(xùn)練中的負(fù)采樣以及二次采樣(subsampling),在語(yǔ)料庫(kù)上訓(xùn)練詞嵌入模型的實(shí)現(xiàn)。
首先導(dǎo)入實(shí)驗(yàn)所需的包或模塊。
import collections import math import random import sys import time import os import numpy as np import torch from torch import nn import torch.utils.data as Data import osprint(torch.__version__)處理數(shù)據(jù)集
PTB(Penn Tree Bank)是一個(gè)常用的小型語(yǔ)料庫(kù) 。它采樣自《華爾街日?qǐng)?bào)》的文章,包括訓(xùn)練集、驗(yàn)證集和測(cè)試集。我們將在PTB訓(xùn)練集上訓(xùn)練詞嵌入模型。該數(shù)據(jù)集的每一行作為一個(gè)句子。句子中的每個(gè)詞由空格隔開(kāi)。
assert 'ptb.train.txt' in os.listdir("../ref/data/ptb")with open('../ref/data/ptb/ptb.train.txt', 'r') as f:lines = f.readlines()# st是sentence的縮寫raw_dataset = [st.split() for st in lines]'# sentences: %d' % len(raw_dataset) # 輸出 '# sentences: 42068'輸出:
'# sentences: 42068'對(duì)于數(shù)據(jù)集的前5個(gè)句子,打印每個(gè)句子的詞數(shù)和前5個(gè)詞。這個(gè)數(shù)據(jù)集中句尾符為"",生僻詞全用"“表示,數(shù)字則被替換成了"N”。
for st in raw_dataset[:5]:print('# tokens:', len(st), st[:5]) # tokens: 24 ['aer', 'banknote', 'berlitz', 'calloway', 'centrust'] # tokens: 15 ['pierre', '<unk>', 'N', 'years', 'old'] # tokens: 11 ['mr.', '<unk>', 'is', 'chairman', 'of'] # tokens: 23 ['rudolph', '<unk>', 'N', 'years', 'old'] # tokens: 34 ['a', 'form', 'of', 'asbestos', 'once']建立詞語(yǔ)索引
只保留在數(shù)據(jù)集中至少出現(xiàn)5次的詞。
# tk是token的縮寫 counter = collections.Counter([tk for st in raw_dataset for tk in st]) counter = dict(filter(lambda x: x[1] >= 5, counter.items()))然后將詞映射到整數(shù)索引。
idx_to_token = [tk for tk, _ in counter.items()] token_to_idx = {tk: idx for idx, tk in enumerate(idx_to_token)} dataset = [[token_to_idx[tk] for tk in st if tk in token_to_idx]for st in raw_dataset] num_tokens = sum([len(st) for st in dataset]) '# tokens: %d' % num_tokens # 輸出 '# tokens: 887100'二次采樣
文本數(shù)據(jù)中一般會(huì)出現(xiàn)一些高頻詞,如英文中的“the”“a”和“in”。通常來(lái)說(shuō),在一個(gè)背景窗口中,一個(gè)詞(如“chip”)和較低頻詞(如“microprocessor”)同時(shí)出現(xiàn)比和較高頻詞(如“the”)同時(shí)出現(xiàn)對(duì)訓(xùn)練詞嵌入模型更有益。因此,訓(xùn)練詞嵌入模型時(shí)可以對(duì)詞進(jìn)行二次采樣 [2]。 具體來(lái)說(shuō),數(shù)據(jù)集中每個(gè)被索引詞wiw_iwi?將有一定概率被丟棄,該丟棄概率為
P(wi)=max?(1?tf(wi),0)P(w_i) = \max\left(1 - \sqrt{\frac{t}{f(w_i)}}, 0\right)P(wi?)=max(1?f(wi?)t??,0)
其中 f(wi)f(w_i)f(wi?) 是數(shù)據(jù)集中詞wiw_iwi?的個(gè)數(shù)與總詞數(shù)之比,常數(shù)ttt是一個(gè)超參數(shù)(實(shí)驗(yàn)中設(shè)為10?410^{-4}10?4)。可見(jiàn),只有當(dāng)f(wi)>tf(w_i) > tf(wi?)>t時(shí),我們才有可能在二次采樣中丟棄詞wiw_iwi?,并且越高頻的詞被丟棄的概率越大。
def discard(idx):return random.uniform(0, 1) < 1 - math.sqrt(1e-4 / counter[idx_to_token[idx]] * num_tokens) #概率丟棄subsampled_dataset = [[tk for tk in st if not discard(tk)] for st in dataset] '# tokens: %d' % sum([len(st) for st in subsampled_dataset]) # '# tokens: 375875'二次采樣后去掉了一半左右的詞。下面比較一個(gè)詞在二次采樣前后出現(xiàn)在數(shù)據(jù)集中的次數(shù)。可見(jiàn)高頻詞“the”的采樣率不足1/20。
def compare_counts(token):return '# %s: before=%d, after=%d' % (token, sum([st.count(token_to_idx[token]) for st in dataset]), sum([st.count(token_to_idx[token]) for st in subsampled_dataset]))compare_counts('the') # '# the: before=50770, after=2013'低頻詞“join”則完整地保留了下來(lái)。
compare_counts('join') # '# join: before=45, after=45'提取中心詞和背景詞
我們將與中心詞距離不超過(guò)背景窗口大小的詞作為它的背景詞。下面定義函數(shù)提取出所有中心詞和它們的背景詞。它每次在整數(shù)1和max_window_size(最大背景窗口)之間隨機(jī)均勻采樣一個(gè)整數(shù)作為背景窗口大小。
def get_centers_and_contexts(dataset, max_window_size):centers, contexts = [], []for st in dataset:if len(st) < 2: # 每個(gè)句子至少要有2個(gè)詞才可能組成一對(duì)“中心詞-背景詞”continuecenters += stfor center_i in range(len(st)):window_size = random.randint(1, max_window_size)indices = list(range(max(0, center_i - window_size),min(len(st), center_i + 1 + window_size)))indices.remove(center_i) # 將中心詞排除在背景詞之外contexts.append([st[idx] for idx in indices])return centers, contexts創(chuàng)建一個(gè)人工數(shù)據(jù)集,其中含有詞數(shù)分別為7和3的兩個(gè)句子。設(shè)最大背景窗口為2,打印所有中心詞和它們的背景詞。
tiny_dataset = [list(range(7)), list(range(7, 10))] print('dataset', tiny_dataset) for center, context in zip(*get_centers_and_contexts(tiny_dataset, 2)):print('center', center, 'has contexts', context)輸出:
dataset [[0, 1, 2, 3, 4, 5, 6], [7, 8, 9]] center 0 has contexts [1] center 1 has contexts [0, 2] center 2 has contexts [0, 1, 3, 4] center 3 has contexts [2, 4] center 4 has contexts [3, 5] center 5 has contexts [3, 4, 6] center 6 has contexts [5] center 7 has contexts [8, 9] center 8 has contexts [7, 9] center 9 has contexts [8]設(shè)最大背景窗口大小為5。提取數(shù)據(jù)集中所有的中心詞及其背景詞。
all_centers, all_contexts = get_centers_and_contexts(subsampled_dataset, 5)使用負(fù)采樣來(lái)進(jìn)行近似訓(xùn)練。對(duì)于一對(duì)中心詞和背景詞,我們隨機(jī)采樣KKK個(gè)噪聲詞(實(shí)驗(yàn)中設(shè)K=5K=5K=5)。根據(jù)word2vec論文的建議,噪聲詞采樣概率P(w)P(w)P(w)設(shè)為www詞頻與總詞頻之比的0.75次方
def get_negatives(all_contexts, sampling_weights, K):all_negatives, neg_candidates, i = [], [], 0population = list(range(len(sampling_weights)))for contexts in all_contexts:negatives = []while len(negatives) < len(contexts) * K: #K negative for one backgroundif i == len(neg_candidates):# 根據(jù)每個(gè)詞的權(quán)重(sampling_weights)隨機(jī)生成k個(gè)詞的索引作為噪聲詞。# 為了高效計(jì)算,可以將k設(shè)得稍大一點(diǎn)i, neg_candidates = 0, random.choices(population, sampling_weights, k=int(1e5)) #參數(shù)weights設(shè)置相對(duì)權(quán)重,它的值是一個(gè)列表,設(shè)置之后,每一個(gè)成員被抽取到的概率就被確定了。neg, i = neg_candidates[i], i + 1# 噪聲詞不能是背景詞if neg not in set(contexts):negatives.append(neg) #select negative from 10**5 candidatesall_negatives.append(negatives) return all_negativessampling_weights = [counter[w]**0.75 for w in idx_to_token] all_negatives = get_negatives(all_contexts, sampling_weights, 5)這里展示一下K與背景詞數(shù)量、干擾詞數(shù)量的關(guān)系:
K = 5 print(" {} * len of ({}) = len of({})".format(K, all_contexts[0], all_negatives[0]))讀取數(shù)據(jù)
從數(shù)據(jù)集中提取所有中心詞all_centers,以及每個(gè)中心詞對(duì)應(yīng)的背景詞all_contexts和噪聲詞all_negatives。我們先定義一個(gè)Dataset類。
class MyDataset(torch.utils.data.Dataset):def __init__(self, centers, contexts, negatives):assert len(centers) == len(contexts) == len(negatives)#trigger for exceptionself.centers = centersself.contexts = contextsself.negatives = negativesdef __getitem__(self, index):return (self.centers[index], self.contexts[index], self.negatives[index])def __len__(self):return len(self.centers)通過(guò)隨機(jī)小批量來(lái)讀取它們。在一個(gè)小批量數(shù)據(jù)中,第iii個(gè)樣本包括一個(gè)中心詞以及它所對(duì)應(yīng)的nin_ini?個(gè)背景詞和mim_imi?個(gè)噪聲詞。
- 由于每個(gè)樣本的背景窗口大小可能不一樣,其中背景詞與噪聲詞個(gè)數(shù)之和ni+min_i+m_ini?+mi?也會(huì)不同。
- 在構(gòu)造小批量時(shí),我們將每個(gè)樣本的背景詞和噪聲詞連結(jié)在一起,并添加填充項(xiàng)0直至連結(jié)后的長(zhǎng)度相同,即長(zhǎng)度均為max?ini+mi\max_i n_i+m_imaxi?ni?+mi?(max_len變量)。
- 為了避免填充項(xiàng)對(duì)損失函數(shù)計(jì)算的影響,我們構(gòu)造了掩碼變量masks,其每一個(gè)元素分別與連結(jié)后的背景詞和噪聲詞contexts_negatives中的元素一一對(duì)應(yīng)。
- 當(dāng)contexts_negatives變量中的某個(gè)元素為填充項(xiàng)時(shí),相同位置的掩碼變量masks中的元素取0,否則取1。
- 為了區(qū)分正類和負(fù)類,我們還需要將contexts_negatives變量中的背景詞和噪聲詞區(qū)分開(kāi)來(lái)。
- 依據(jù)掩碼變量的構(gòu)造思路,我們只需創(chuàng)建與contexts_negatives變量形狀相同的標(biāo)簽變量labels,并將與背景詞(正類)對(duì)應(yīng)的元素設(shè)1,其余清0。
下面我們實(shí)現(xiàn)這個(gè)小批量讀取函數(shù)batchify。它的小批量輸入data是一個(gè)長(zhǎng)度為批量大小的列表,其中每個(gè)元素分別包含中心詞center、背景詞context和噪聲詞negative。該函數(shù)返回的小批量數(shù)據(jù)符合我們需要的格式,例如,包含了掩碼變量。
def batchify(data):"""用作DataLoader的參數(shù)collate_fn: 輸入是個(gè)長(zhǎng)為batchsize的list, list中的每個(gè)元素都是Dataset類調(diào)用__getitem__得到的結(jié)果"""max_len = max(len(c) + len(n) for _, c, n in data)centers, contexts_negatives, masks, labels = [], [], [], []for center, context, negative in data:cur_len = len(context) + len(negative)centers += [center]contexts_negatives += [context + negative + [0] * (max_len - cur_len)]masks += [[1] * cur_len + [0] * (max_len - cur_len)]labels += [[1] * len(context) + [0] * (max_len - len(context))]return (torch.tensor(centers).view(-1, 1), torch.tensor(contexts_negatives),torch.tensor(masks), torch.tensor(labels))用剛剛定義的batchify函數(shù)指定DataLoader實(shí)例中小批量的讀取方式,然后打印讀取的第一個(gè)批量中各個(gè)變量的形狀。
batch_size = 512 num_workers = 0 if sys.platform.startswith('win32') else 4dataset = MyDataset(all_centers, all_contexts, all_negatives) data_iter = Data.DataLoader(dataset, batch_size, shuffle=True, #set a dataset forcollate_fn=batchify, num_workers=num_workers) for batch in data_iter:for name, data in zip(['centers', 'contexts_negatives', 'masks','labels'], batch):print(name, 'shape:', data.shape)break輸出:
centers shape: torch.Size([512, 1]) contexts_negatives shape: torch.Size([512, 60]) masks shape: torch.Size([512, 60]) labels shape: torch.Size([512, 60])跳字模型
通過(guò)使用嵌入層和小批量乘法來(lái)實(shí)現(xiàn)跳字模型。它們也常常用于實(shí)現(xiàn)其他自然語(yǔ)言處理的應(yīng)用。
嵌入層
獲取詞嵌入的層稱為嵌入層,在PyTorch中可以通過(guò)創(chuàng)建nn.Embedding實(shí)例得到。嵌入層的權(quán)重是一個(gè)矩陣,其行數(shù)為詞典大小(num_embeddings),列數(shù)為每個(gè)詞向量的維度(embedding_dim)。我們?cè)O(shè)詞典大小為20,詞向量的維度為4。
embed = nn.Embedding(num_embeddings=20, embedding_dim=4) embed.weight輸出:
Parameter containing: tensor([[ 1.4661, -0.0863, -0.7256, -0.4591],[-0.1848, 0.1527, -0.9891, 1.3739],[ 1.6892, -0.8399, -0.1476, -0.6153],[ 1.6853, -0.0610, 1.8559, 0.6242],[-1.5009, 0.2730, -0.3688, 0.5599],[-1.3175, 0.9324, 0.0477, 0.6728],[ 0.0316, 0.9806, -0.6857, 1.2622],[-1.4576, -0.9973, -0.1076, 0.0197],[-1.4366, -0.8724, -0.0563, 1.2543],[-1.5040, 0.4426, -0.6406, 0.0243],[-1.4785, 1.2112, -0.2328, 1.2140],[-2.2098, -0.3141, 0.3318, -1.0206],[ 1.2869, 0.9700, 2.3408, 0.2634],[-0.7229, -0.1446, 0.5023, 1.4011],[ 0.5258, 0.7519, 0.5948, 1.2724],[-0.4999, -1.9596, -1.9181, 1.3039],[-0.5907, 0.0956, -1.3246, -0.1742],[-1.0380, -0.1586, -0.8281, 0.1813],[ 0.7077, 0.4264, 1.7595, 1.1582],[-0.5711, 0.0796, 0.7719, -0.5790]], requires_grad=True)嵌入層的輸入為詞的索引。輸入一個(gè)詞的索引iii,嵌入層返回權(quán)重矩陣的第iii行作為它的詞向量。下面我們將形狀為(2, 3)的索引輸入進(jìn)嵌入層,由于詞向量的維度為4,我們得到形狀為(2, 3, 4)的詞向量。
x = torch.tensor([[1, 2, 3], [4, 5, 6]], dtype=torch.long) embed(x)輸出:
tensor([[[-0.1848, 0.1527, -0.9891, 1.3739],[ 1.6892, -0.8399, -0.1476, -0.6153],[ 1.6853, -0.0610, 1.8559, 0.6242]],[[-1.5009, 0.2730, -0.3688, 0.5599],[-1.3175, 0.9324, 0.0477, 0.6728],[ 0.0316, 0.9806, -0.6857, 1.2622]]], grad_fn=<EmbeddingBackward>)小批量乘法
可以使用小批量乘法運(yùn)算bmm對(duì)兩個(gè)小批量中的矩陣一一做乘法。
- 假設(shè)第一個(gè)小批量中包含nnn個(gè)形狀為a×ba\times ba×b的矩陣X1,…,Xn\boldsymbol{X}_1, \ldots, \boldsymbol{X}_nX1?,…,Xn?
- 第二個(gè)小批量中包含nnn個(gè)形狀為b×cb\times cb×c的矩陣Y1,…,Yn\boldsymbol{Y}_1, \ldots, \boldsymbol{Y}_nY1?,…,Yn?。
- 這兩個(gè)小批量的矩陣乘法輸出為nnn個(gè)形狀為a×ca\times ca×c的矩陣X1Y1,…,XnYn\boldsymbol{X}_1\boldsymbol{Y}_1, \ldots, \boldsymbol{X}_n\boldsymbol{Y}_nX1?Y1?,…,Xn?Yn?。
因此,給定兩個(gè)形狀分別為(nnn, aaa, bbb)和(nnn, bbb, ccc)的Tensor,小批量乘法輸出的形狀為(nnn, aaa, ccc)。
X = torch.ones((2, 1, 4)) Y = torch.ones((2, 4, 6)) torch.bmm(X, Y).shape輸出:
X = torch.ones((2, 1, 4)) Y = torch.ones((2, 4, 6)) torch.bmm(X, Y).shape跳字模型前向計(jì)算
在前向計(jì)算中,跳字模型的輸入包含中心詞索引center以及連結(jié)的背景詞與噪聲詞索引contexts_and_negatives。其中center變量的形狀為(批量大小, 1),而contexts_and_negatives變量的形狀為(批量大小, max_len)。這兩個(gè)變量先通過(guò)詞嵌入層分別由詞索引變換為詞向量,再通過(guò)小批量乘法得到形狀為(批量大小, 1, max_len)的輸出。輸出中的每個(gè)元素是中心詞向量與背景詞向量或噪聲詞向量的內(nèi)積。
def skip_gram(center, contexts_and_negatives, embed_v, embed_u):v = embed_v(center)#(batch_size, 1)u = embed_u(contexts_and_negatives)#(batch_size, max_len)pred = torch.bmm(v, u.permute(0, 2, 1))return pred簡(jiǎn)單說(shuō)一下Tensor.permute的作用:
permute就是更改Tensor的維度,下面的例子將Tensor的二、三維度做了交換
輸出
tensor([[[ 0.4602, 0.1497, -0.4955, -0.6957, 0.0581],[-0.5020, -0.6797, -0.6404, 2.0036, 0.5790],[-0.0533, -0.4460, -0.2509, 0.1712, -1.3488]],[[-0.3313, 0.7201, 0.2478, -1.6327, -0.4580],[ 0.5060, -0.1724, 2.7267, 0.1494, -0.3988],[-1.8063, -0.5025, -0.3524, 0.4211, 1.5029]]]) tensor([[[ 0.4602, -0.5020, -0.0533],[ 0.1497, -0.6797, -0.4460],[-0.4955, -0.6404, -0.2509],[-0.6957, 2.0036, 0.1712],[ 0.0581, 0.5790, -1.3488]],[[-0.3313, 0.5060, -1.8063],[ 0.7201, -0.1724, -0.5025],[ 0.2478, 2.7267, -0.3524],[-1.6327, 0.1494, 0.4211],[-0.4580, -0.3988, 1.5029]]])訓(xùn)練模型
定義二元交叉熵?fù)p失函數(shù)
根據(jù)負(fù)采樣中損失函數(shù)的定義,我們可以使用二元交叉熵?fù)p失函數(shù),
?log?P(w(t+j)∣w(t))=?log?P(D=1∣w(t),w(t+j))?∑k=1,wk~P(w)Klog?P(D=0∣w(t),wk)=?log?σ(uit+j?vit)?∑k=1,wk~P(w)Klog?(1?σ(uhk?vit))=?log?σ(uit+j?vit)?∑k=1,wk~P(w)Klog?σ(?uhk?vit).\begin{aligned} -\log P(w^{(t+j)} \mid w^{(t)}) =& -\log P(D=1\mid w^{(t)}, w^{(t+j)}) - \sum_{k=1,\ w_k \sim P(w)}^K \log P(D=0\mid w^{(t)}, w_k)\ \\ =&- \log \sigma\left(\boldsymbol{u}_{i_{t+j}}^\top \boldsymbol{v}_{i_t}\right) - \sum{k=1,\ w_k \sim P(w)}^K \log\left(1-\sigma\left(\boldsymbol{u}_{h_k}^\top \boldsymbol{v}_{i_t}\right)\right)\\\ =&- \log \sigma\left(\boldsymbol{u}_{i_{t+j}}^\top \boldsymbol{v}_{i_t}\right) - \sum{k=1,\ w_k \sim P(w)}^K \log\sigma\left(-\boldsymbol{u}_{h_k}^\top \boldsymbol{v}_{i_t}\right). \end{aligned} ?logP(w(t+j)∣w(t))==?=??logP(D=1∣w(t),w(t+j))?k=1,?wk?~P(w)∑K?logP(D=0∣w(t),wk?)??logσ(uit+j???vit??)?∑k=1,?wk?~P(w)Klog(1?σ(uhk???vit??))?logσ(uit+j???vit??)?∑k=1,?wk?~P(w)Klogσ(?uhk???vit??).?
下面定義SigmoidBinaryCrossEntropyLoss。
class SigmoidBinaryCrossEntropyLoss(nn.Module):def __init__(self): # none mean sumsuper(SigmoidBinaryCrossEntropyLoss, self).__init__()def forward(self, inputs, targets, mask=None):"""input – Tensor shape: (batch_size, len)target – Tensor of the same shape as input"""inputs, targets, mask = inputs.float(), targets.float(), mask.float()res = nn.functional.binary_cross_entropy_with_logits(inputs, targets, reduction="none", weight=mask)return res.mean(dim=1)loss = SigmoidBinaryCrossEntropyLoss()我們可以通過(guò)掩碼變量指定小批量中參與損失函數(shù)計(jì)算的部分預(yù)測(cè)值和標(biāo)簽:
- 當(dāng)掩碼為1時(shí),相應(yīng)位置的預(yù)測(cè)值和標(biāo)簽將參與損失函數(shù)的計(jì)算;
- 當(dāng)掩碼為0時(shí),相應(yīng)位置的預(yù)測(cè)值和標(biāo)簽則不參與損失函數(shù)的計(jì)算。
我們之前提到,掩碼變量可用于避免填充項(xiàng)對(duì)損失函數(shù)計(jì)算的影響。
pred = torch.tensor([[1.5, 0.3, -1, 2], [1.1, -0.6, 2.2, 0.4]]) # 標(biāo)簽變量label中的1和0分別代表背景詞和噪聲詞 label = torch.tensor([[1, 0, 0, 0], [1, 1, 0, 0]]) mask = torch.tensor([[1, 1, 1, 1], [1, 1, 1, 0]]) # 掩碼變量 loss(pred, label, mask) * mask.shape[1] / mask.float().sum(dim=1)輸出:
tensor([0.8740, 1.2100])下面將從零開(kāi)始實(shí)現(xiàn)二元交叉熵?fù)p失函數(shù)的計(jì)算,并根據(jù)掩碼變量mask計(jì)算掩碼為1的預(yù)測(cè)值和標(biāo)簽的損失。
def sigmd(x):return - math.log(1 / (1 + math.exp(-x)))print('%.4f' % ((sigmd(1.5) + sigmd(-0.3) + sigmd(1) + sigmd(-2)) / 4)) # 注意1-sigmoid(x) = sigmoid(-x) print('%.4f' % ((sigmd(1.1) + sigmd(-0.6) + sigmd(-2.2)) / 3))輸出:
0.8740 1.2100發(fā)現(xiàn)結(jié)果和使用pytorch內(nèi)置函數(shù)是一樣的
初始化模型參數(shù)
分別構(gòu)造中心詞和背景詞的嵌入層,并將超參數(shù)詞向量維度embed_size設(shè)置成100。
embed_size = 100 net = nn.Sequential(nn.Embedding(num_embeddings=len(idx_to_token), embedding_dim=embed_size),nn.Embedding(num_embeddings=len(idx_to_token), embedding_dim=embed_size) )定義訓(xùn)練函數(shù)
由于填充項(xiàng)的存在,與之前的訓(xùn)練函數(shù)相比,損失函數(shù)的計(jì)算稍有不同
def train(net, lr, num_epochs):device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')print("train on", device)net = net.to(device)optimizer = torch.optim.Adam(net.parameters(), lr=lr)for epoch in range(num_epochs):start, l_sum, n = time.time(), 0.0, 0for batch in data_iter:center, context_negative, mask, label = [d.to(device) for d in batch]pred = skip_gram(center, context_negative, net[0], net[1])# 使用掩碼變量mask來(lái)避免填充項(xiàng)對(duì)損失函數(shù)計(jì)算的影響l = (loss(pred.view(label.shape), label, mask) *mask.shape[1] / mask.float().sum(dim=1)).mean() # 一個(gè)batch的平均lossoptimizer.zero_grad()l.backward()optimizer.step()l_sum += l.cpu().item()n += 1print('epoch %d, loss %.2f, time %.2fs'% (epoch + 1, l_sum / n, time.time() - start)) train(net, 0.01, 10)輸出:
train on cuda epoch 1, loss 1.97, time 7.79s epoch 2, loss 0.63, time 7.28s epoch 3, loss 0.45, time 7.43s epoch 4, loss 0.40, time 7.62s epoch 5, loss 0.37, time 7.46s epoch 6, loss 0.35, time 7.39s epoch 7, loss 0.34, time 7.64s epoch 8, loss 0.33, time 7.60s epoch 9, loss 0.32, time 7.34s epoch 10, loss 0.32, time 7.38s應(yīng)用詞嵌入模型
根據(jù)兩個(gè)詞向量的余弦相似度表示詞與詞之間在語(yǔ)義上的相似度
def get_similar_tokens(query_token, k, embed):W = embed.weight.datax = W[token_to_idx[query_token]]# 添加的1e-9是為了數(shù)值穩(wěn)定性cos = torch.matmul(W, x) / (torch.sum(W * W, dim=1) * torch.sum(x * x) + 1e-9).sqrt() #詞向量之間的關(guān)聯(lián)性_, topk = torch.topk(cos, k=k+1)topk = topk.cpu().numpy()for i in topk[1:]: # 除去輸入詞print('cosine sim=%.3f: %s' % (cos[i], (idx_to_token[i])))get_similar_tokens('news', 10, net[0])輸出:
cosine sim=0.424: hoffman cosine sim=0.398: daughter cosine sim=0.396: tokyo cosine sim=0.394: attributed cosine sim=0.376: slew cosine sim=0.373: steps cosine sim=0.370: pleased cosine sim=0.369: daily cosine sim=0.362: reviewing cosine sim=0.361: wednesday輸出了與”news“最相關(guān)的10個(gè)詞,按照相關(guān)度排序
總結(jié)
以上是生活随笔為你收集整理的pytorch-word2vec的实例实现的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 双路服务器单路运行,单路还是双路?看需求
- 下一篇: 从单亲家庭内向小男生到哈佛耶鲁全奖,百万