Word2Vec
作者:互联网
词嵌入
1.为什么使用词嵌入?
- one-hot向量(长度为词库大小,去重排序,一个one-hot仅在单词序号处取1,其余均为0)可以表示词,但是各个单词的one-hot乘积均为0,也就是看不出关联.
- 所以可以用特征化的嵌入向量来表示单词(矩阵列不是序号,而是n个特征,所需空间远少于列长为词库大小的one-hot),然后根据t-SNE算法把n维数据嵌入到而且平面,可以直观看到一些词的相近程度
- $$嵌入向量=嵌入矩阵*onehot向量$$
2.词嵌入的类比推理
1.观察向量man、woman、king和queen的嵌入向量,可以发现
$$
man-woman≈king-queen
$$
则可以知道单词之间的类比;同时根据向量差也可以看出,man和woman、king和queen在哪些特征上有差别(例如gender)
2.两个单词之间的相似度可以用余弦相似度衡量
3.学习词嵌入
学习词嵌入 -> 建立一个语言模型:
预测下一个单词 初始化一个参数矩阵,乘以各单词的one-hot向量,得到嵌入向量,把这些嵌入向量作为输入x放进神经网络,假设嵌入向量长度为n,句子已知单词数为m,则softmax分类器得到的向量维度是m×n,相当于把几个嵌入向量叠加在一起(实际中会设置一个固定的历史窗口,例如每次只看前/后k个词(也可同时看前和后),即窗口大小=k,,softmax层输入向量维度=k×n)
需要在m*n个可能的输出中预测这个单词(每个隐藏层包括softmax层都有自己的参数W和b)
4.Word2Vec:Skip-Gram(跳字模型)
采用Skip-Gram模型的Word2Vec算法: 基本步骤与3.学习词嵌入
相同。不同之处在于,每次给定一个上下文词,然后选择目标词,但是每次并不只选前k个词,而是在一定词距内随机选择一个词
(从目标字词推测原始语句)
5.分级&负采样
由于softmax层向量维度过大,因此实践中往往会采用分级或负采样两种方法降低计算成本
分级
每次并不会直接精确到属于n个词中的哪一个词,而是采用二分类法,不断划分直至最后。分级方法的计算成本与log2(词汇表大小)成正比。
Tips:为了进一步降低成本,常用词往往会放到离树顶更近的位置。
负采样
对于每一个上下文词,在已选择正确的目标词,即有正类样本的基础上,会特意生成一系列负类样本。相当于一个样本一次迭代进行了多次训练。
#
# code by Tae Hwan Jung @graykode
import numpy as np
import torch
import torch.nn as nn
import torch.optim as optim
import matplotlib.pyplot as plt
def random_batch():
random_inputs = []
random_labels = []
random_index = np.random.choice(range(len(skip_grams)), batch_size, replace=False)
for i in random_index:
random_inputs.append(np.eye(voc_size)[skip_grams[i][0]]) # target
random_labels.append(skip_grams[i][1]) # context word
return random_inputs, random_labels
# Model
class Word2Vec(nn.Module):
def __init__(self):
super(Word2Vec, self).__init__()
# W and WT is not Traspose relationship
self.W = nn.Linear(voc_size, embedding_size, bias=False) # voc_size > embedding_size Weight
self.WT = nn.Linear(embedding_size, voc_size, bias=False) # embedding_size > voc_size Weight
def forward(self, X):
# X : [batch_size, voc_size]
hidden_layer = self.W(X) # hidden_layer : [batch_size, embedding_size]
output_layer = self.WT(hidden_layer) # output_layer : [batch_size, voc_size]
return output_layer
if __name__ == '__main__':
batch_size = 2 # mini-batch size
embedding_size = 2 # embedding size
sentences = ["apple banana fruit", "banana orange fruit", "orange banana fruit",
"dog cat animal", "cat monkey animal", "monkey dog animal"]
word_sequence = " ".join(sentences).split()
word_list = " ".join(sentences).split()
word_list = list(set(word_list))
word_dict = {w: i for i, w in enumerate(word_list)}
voc_size = len(word_list)
# Make skip gram of one size window
skip_grams = []
for i in range(1, len(word_sequence) - 1):
target = word_dict[word_sequence[i]]
context = [word_dict[word_sequence[i - 1]], word_dict[word_sequence[i + 1]]]
for w in context:
skip_grams.append([target, w])
model = Word2Vec()
criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters(), lr=0.001)
# Training
for epoch in range(5000):
input_batch, target_batch = random_batch()
input_batch = torch.Tensor(input_batch)
target_batch = torch.LongTensor(target_batch)
optimizer.zero_grad()
output = model(input_batch)
# output : [batch_size, voc_size], target_batch : [batch_size] (LongTensor, not one-hot)
loss = criterion(output, target_batch)
if (epoch + 1) % 1000 == 0:
print('Epoch:', '%04d' % (epoch + 1), 'cost =', '{:.6f}'.format(loss))
loss.backward()
optimizer.step()
for i, label in enumerate(word_list):
W, WT = model.parameters()
x, y = W[0][i].item(), W[1][i].item()
plt.scatter(x, y)
plt.annotate(label, xy=(x, y), xytext=(5, 2), textcoords='offset points', ha='right', va='bottom')
plt.show()
标签:嵌入,word,random,batch,Word2Vec,向量,size 来源: https://www.cnblogs.com/chengjunkai/p/16656849.html