这次复习cs224n主要是先熟悉python和pytorch,方便之后进行论文复现等工作,同时也回顾一下模型和数学公式推导,找找感觉。
解答:理解词向量(23分)
我们先快速回顾一下word2vec算法,它的核心思想是“一个词的含义取决于它周围的词”。具体来说,我们有一个中心词(center word) c,和这个词 c 周围上下文构成的窗口,这个窗口内的除了 c 之外的词叫做外围词(outside words)。比如下图中,中心词是“banking”,窗口大小为2,所以上下文窗口是:“turning”、”into“、”crises“和”as“。
Skip-gram模型(word2vec的一种实现)目的是习得概率分布 。这样一来,就能计算给定的一个词 o 和词 c 的概率
(意为,在已知词 c 出现的情况下,词 o 出现的概率), c 是中心词,o 是外围词。
在word2vec中,这个条件概率分布是通过计算向量点积(dot-products),再应用naive-softmax函数得到的:
这里, 向量代表外围词,
向量代表中心词。为了包含这些向量,我们有两个矩阵
和
。
的列是外围词,
的列是中心词,这两矩阵都有所有词
的表示 。
对于词 c 和词 o,损失函数为对数几率:
可以从交叉熵的角度看这个损失函数。真实值为 ,是一个独热向量,预测值
是由公式(1)计算得到。具体来说,
如果是第k个单词,那么它的第k维为1,其余维都是0,而
的第k维表示这是第k个词的概率大小。
问题(a) (3分)
证明公式(2)给出的naive-softmax的损失函数,和 与
的交叉熵损失函数是一样的,均如下所示(答案控制在一行)
答:
因为除了 之外的词都不在窗口内,所以只有词
对损失函数有贡献
问题(b) (5分)
计算损失函数 对中心词
的偏导数,用
,
和
来表示。
答:
为了方便表述,对于该外围词 我们设:
,
,
,
,下面对
求导
∴ (这里的log蕴含意思是ln)
∵ ,
∵
∴
则对 的导数为:
,引入矩阵得:
问题(c) (5分)
计算损失函数 对上下文窗口内的词
的偏导数,考虑两种情况,即 w 是外围词
,和 w 不是
,用
,
和
来表示。
答:
在问题(b)基础上,对 求导。
当 时:
则 ,
故
当 时,由问题(b)得:
综合上述两种情况,所以有:
∴
问题(d) (3分)
sigmoid函数如公式(4)所示
请计算出它对于 的导数,
是一个向量
答:
问题(e) (4分)
现在我们考虑负采样的损失函数。假设有K个负样本,表示为 ,它们对应的向量为
,外围词
,则外围词
在中心词是
时产生的损失函数如公式(5)所示。
根据该损失函数,重新计算问题(b)、问题(c)的偏导数,用 、
、
来表示。
完成计算后,简要解释为什么这个损失函数比naive-softmax效率更高。
注意:你可以用问题(d)的答案来帮助你计算导数
答:
(略,详见代码)
提示:
-
词库从
变成了这K+1个词
-
在求内层导数的时候用了sigmoid函数
问题(f) (3分)
假设中心词是 ,上下文窗口是
,
是窗口大小,回顾skip-gram的word2vec实现,在该窗口下的总损失函数是:
这里, 是外围词
在中心词
下产生的损失,损失函数可以是naive-softmax或者是neg-sample(负采样),这取决于具体实现。
计算:
(i) 损失函数对 的偏导数
(ii) 损失函数对 的偏导数
(iii) 损失函数对 (
)的偏导数
答:
(略,详见代码)
提示:把上下文窗口所有词的损失加起来即可
代码:实现word2vec(20分)
点击 此处 下载代码,python版本 >= 3.5,需要安装numpy,你利用conda来配置环境:
conda env create -f env.yml
conda activate a2
复制代码
写完代码后,运行:
conda deactivate
复制代码
问题(a) (12分)
首先,实现 word2vec.py 里的 sigmoid函数,要支持向量输入。接着实现同一个文件里的 softmax 、负采样损失和导数。然后实现skip-gram的损失函数和导数。全部做完之后,运行python word2vec.py来检查是否正确。
答:
sigmoid
没什么好讲的,numpy会自己广播,最终得到向量输出
s = 1 / (1 + np.exp(-x))
复制代码
naiveSoftmaxLossAndGradient
这个模型其实就是一个三层的前馈神经网络(详解),只需要注意维度即可,注释里已经标记出了维度。
需要注意的是,单词表示是在行,而不是列。
# forward
a, W, target = centerWordVec, outsideVectors, outsideWordIdx
a = a.reshape((a.shape[0], 1))
# assume N words, V dimentions, so
# a.shape == (V, 1) W.shape == (N, V)
z = np.dot(W, a) # (N, 1)
preds = softmax(z.reshape(-1)).reshape(-1, 1) # (N, 1)
loss = -np.log(preds[target])
# backprop
delta = preds.copy() # (N, 1)
delta[target] -= 1.0
gradCenterVec = np.dot(W.T, delta) # (V, 1)
gradOutsideVecs = np.dot(delta, a.T) # (N, V)
gradCenterVec = gradCenterVec.flatten() # (V, )
复制代码
negSamplingLossAndGradient
与native-softmax不同的是:
- 只选取K个非外围词(负样本,可能有重复词),外加1个正确的外围词,共K+1个输出
- 最后一层使用sigmoid输出,而不是softmax
注意,反向传播得到的是这K+1个词的梯度,所以需要挨个更新到 梯度矩阵 中去
### Please use your implementation of sigmoid in here.
# indices might have same index
# extract W
W = np.zeros((len(indices), outsideVectors.shape[1]))
for i in range(len(indices)):
W[i] = outsideVectors[indices[i]]
# forward
a = centerWordVec
a = a.reshape((a.shape[0], 1))
z = np.dot(W, a) # (K+1, 1)
preds = sigmoid(z)
# backprop
y = np.zeros((preds.shape[0], 1))
y[0] = 1 # index 0 is target
loss = -(y*np.log(preds) + (1 - y)*np.log(1 - preds)).sum()
delta = preds - y
gradCenterVec = np.dot(W.T, delta) # (V, 1)
gradW = np.dot(delta, a.T) # (K+1, V)
gradCenterVec = gradCenterVec.flatten()
# apply gradW into gradOutsideVecs
gradOutsideVecs = np.zeros_like(outsideVectors)
for i in range(len(indices)):
oi = indices[i]
gradOutsideVecs[oi] += gradW[i]
复制代码
skipgram
遍历所有的外围词,求和损失函数
ci = word2Ind[currentCenterWord]
vc = centerWordVectors[ci]
for o in outsideWords:
oi = word2Ind[o]
loss_, gradVc, gradUo = word2vecLossAndGradient(vc, oi, outsideVectors, dataset)
gradCenterVecs[ci] += gradVc
gradOutsideVectors += gradUo
loss += loss_
复制代码
问题(b) (4分)
完成sgd.py文件的SGD优化器,运行python sgd.py来检查是否正确。
答:
sgd
调用函数得到损失值和梯度,更新即可
loss, grad = f(x)
x = x - step*grad
复制代码
问题(c) (4分)
至此所有的代码都写完了,接下来是下载数据集,这里我们使用Stanform Sentiment Treebank(SST)数据集,它可以用在简单的语义分析任务中去。通过运行 sh get_datasets.sh 可以获得该数据集,下载完成后运行 python run.py 即可。
注意:训练的时间取决于代码是否高效(即便是高效的实现,也要跑接近一个小时)
经过40,000次迭代后,最终结果会保存到 word_vectors.png 里。
答:
- 注意看,male->famale 和 king -> queen 这两条向量是平行的
- (women, famale),(enjoyable,annoying) 这些词距离很近
参考
[1] CS224n: Natural Language Processing with Deep Learning, 2019-03-14. web.stanford.edu/class/cs224….
[2] CS224n Assignment 1, 2019-03-14. www.hankcs.com/nlp/cs224n-…