NLP入门教程系列
第三章 word2vec的简单介绍
前言
word2vec是Google研究团队里的Tomas Mikolov等人于2013年的《Distributed Representations ofWords and Phrases and their Compositionality》以及后续的《Efficient Estimation of Word Representations in Vector Space》两篇文章中提出的一种高效训练词向量的模型,基本出发点是上下文相似的两个词,它们的词向量也应该相似
word2vec 一词最初用来指程序或者工具,但是随着该词的流行,在某些语境下,也指神经网络的模型。正确地说,CBOW 模型和 skip-gram 模型是 word2vec 中使用的两个神经网络。
一、CBOW模型的推理
CBOW(continuous bag-of-words)模型是根据上下文预测目标词的神经网络(“目标词”是指中间
的单词,它周围的单词是“上下文”)。通过训练这个 CBOW 模型,使其能尽可能地进行正确的预测,我们可以获得单词的分布式表示。CBOW可以是非常简单的网络,例如只有一个输入层、中间层以及输出层。
中间层的神经元数量比输入层少这一点很重要。中间层需要将预测单词所需的信息压缩保存,从而产生密集的向量表示
该模型是一个多分类的网络结构,因此,对进行学习只是使用softmax以及交叉熵损失函数。
二、CBOW的简单实现
现在就简单实现一下CBOW
1.矩阵相乘
首先,定义一个基础的MatMul,该类主要是用来做矩阵乘积
class MatMul:
def __init__(self, W):
self.params = [W]
self.grads = [np.zeros_like(W)]
self.x = None
def forward(self, x):
W, = self.params
out = np.dot(x, W)
self.x = x
return out
def backward(self, dout):
W, = self.params
dx = np.dot(dout, W.T)
dW = np.dot(self.x.T, dout)
self.grads[0][...] = dW
return dx
2.交叉损失熵与softmax
接下来要实现softmax,并且整合交叉熵
class SoftmaxWithLoss:
def __init__(self):
self.params, self.grads = [], []
self.y = None # softmax的输出
self.t = None # 监督标签
def forward(self, x, t):
self.t = t
self.y = softmax(x)
# 在监督标签为one-hot向量的情况下,转换为正确解标签的索引
if self.t.size == self.y.size:
self.t = self.t.argmax(axis=1)
loss = cross_entropy_error(self.y, self.t)
return loss
def backward(self, dout=1):
batch_size = self.t.shape[0]
dx = self.y.copy()
dx[np.arange(batch_size), self.t] -= 1
dx *= dout
dx = dx / batch_size
return dx
3.CBOW类
现在就可以具体实现CBOW了
import numpy as np
class SimpleCBOW:
def __init__(self, vocab_size, hidden_size):
V, H = vocab_size, hidden_size
# 初始化权重
W_in = 0.01 * np.random.randn(V, H).astype('f')
W_out = 0.01 * np.random.randn(H, V).astype('f')
# 生成层
self.in_layer0 = MatMul(W_in)
self.in_layer1 = MatMul(W_in)
self.out_layer = MatMul(W_out)
self.loss_layer = SoftmaxWithLoss()
# 将所有的权重和梯度整理到列表中
layers = [self.in_layer0, self.in_layer1, self.out_layer]
self.params, self.grads = [], []
for layer in layers:
self.params += layer.params
self.grads += layer.grads
# 将单词的分布式表示设置为成员变量
self.word_vecs = W_in
def forward(self, contexts, target):
h0 = self.in_layer0.forward(contexts[:, 0])
h1 = self.in_layer1.forward(contexts[:, 1])
h = (h0 + h1) * 0.5
score = self.out_layer.forward(h)
loss = self.loss_layer.forward(score, target)
return loss
def backward(self, dout=1):
ds = self.loss_layer.backward(dout)
da = self.out_layer.backward(ds)
da *= 0.5
self.in_layer1.backward(da)
self.in_layer0.backward(da)
return None
这里,初始化方法的参数包括词汇个数 vocab_size 和中间层的神经元个数 hidden_size。然后生成两个输入侧的 MatMul 层、一个输出侧的 MatMul 层,以及一个 Softmax with Loss 层。这里,用来处理输入侧上下文的 MatMul 层的数量与上下文的单词数量相同(本例中是两个)。另外,我们使用相同的权重来初始化 MatMul 层。最后,将该神经网络中使用的权重参数和梯度分别保存在列表类型的成员变量 params 和 grads 中。
这里仅仅是模型的简单代码示例仅供参考,如果想运行还需要其他一些操作
总结
以上就是关于word2vec的简单介绍。CBOW的主要功能就是在给定某上下文的时候,输出目标此的概率(正好与skip-gram的功能相反)