从神经网络语言模型(NNLM)到Word2Vec:自然语言处理中的词向量学习
自然语言处理NLP
语言模型
语言(人说的话)+模型(完成某个任务)
任务:
- 概率评估任务:在两句话中,判断哪句话出现的概率大(哪句话在自然语言中更合理)
- 生成任务:预测词语,我明天要
____
统计语言模型
用统计的方法解决上述的两个任务
核心思想
给定一个词序列,计算该序列出现的概率
比如句子:”判断这个词的词性”,分词得到”判断”,”这个”,”词”,”的”,”词性”
这句话是有顺序的(是一个序列),怎么理解(?)
就是自然语言我们所说的语序
条件概率链式法则
用于解决第一个任务
$$
统计语言模型的核心是计算一个句子 P(w_1,w_2,…,w_n) 的联合概率,即一句话出现的概率(w_i就是单个词)\
P(w_1,w_2,\cdots,w_n) = P(w_1)\cdot P(w_2|w_1)\cdots P(w_n|w_1,w_2,\cdots,w_{n-1}) = \prod_{i}P(w_i | w_1,w_2,\cdots,w_{i-1})\
$$
(?)理解:求出每个词wi的概率,然后连乘,就是这个句子的概率;
为什么是条件概率(?)
因为句子是一个序列,计算当前词的概率需要在前面的词已经确定的前提下(每个词是基于上下文的)
词库/词典
解决第二个任务:生成任务 我明天要____
$$
令要生成的词为w_{next},其概率为\
(1) P(w_{next} | “我”,”明天”,”要”)
$$
思想
- 建立一个词典V,把所有词都装入V
- 把V中每个词都进行(1)式计算
缺点
- 计算太复杂了
- 词典很大,要匹配很多次计算
- 句子很复杂,分词后非常长,计算也很复杂
前面没有给出这个情境下的条件概率怎么计算
$$
P(w_{next} | “我”,”明天”,”要”) = \frac{count(w_{next},”我”,”明天”,”要”)}{count(“我”,”明天”,”要”)} \
count(): 表示某个词或词序列在语料库中出现的次数\
eg:\
我 \ 明天\ 要\ 去\ 学校\
我\ 明天\ 要\ 去\ 玩\
我 \ 明天\ 要 \ 睡觉\
我 \ 明天\ 要 \ 上班\
我 \ 明天 \ 要\ 吃饭\
count(“我”,”明天”,”要”)=5 \ \ \ -我\ 明天\ 要\ 这三个词同时出现的次数
$$
这count()怎么算的(?)
在语料库中词序列出现次数,比如count(“我”,”明天”,”要”)要把"我","明天","要"当成序列,再找出现次数,而不是分开每个词寻找
n-gram模型/n元统计语言模型
n-gram模型用于预测一个词在给定前n-1个词的情况下出现的概率
引入
比如对于这样一个生成任务:
判断这个词的___
$$
w_next = {“词性”,”火星”}\
最原始的式子:P(w_{next}|”判断”,”这个”,”词”,”的”)\
但是也可以这样:P(w_{next}|这个”,”词”,”的”)\
还可以这样:P(w_{next}|”词”,”的”)\
三个式子都能达到选出最佳生成词语的作用,但是很显然最后一个式子要更加高效
$$
定义
n-gram是指连续的n个词构成的序列。n元模型(n-gram model)是基于前 n−1 个词来预测第 n 个词的模型。- 常见的有:
- unigram(n=1):不考虑上下文,只统计词频。
- bigram(n=2):基于前一个词预测下一个词。
- trigram(n=3):基于前两个词预测下一个词。
- 4-gram、5-gram 等:基于更多上下文。
最大似然估计MLE
$$
P(w_i | w_{i-n+1},\cdots,w_{i-1}) = \frac{count(w_{i-n+1},\cdots,w_{i-1},w_i)}{count(w_{i-n+1},\cdots,w_{i-1})}
$$
缺点:
- 有现实意义的文本,通常会出现数据稀疏的情况;例如训练时未出现的单词,在测试时出现
- 这样就会导致MLE的分子/分母为0,造成计算上的问题,我们需要引入一些平滑策略来避免0的出现
平滑策略
拉普拉斯平滑/加一平滑
$$
P(w_i | w_{i-n+1},\cdots,w_{i-1}) = \frac{count(w_{i-n+1},\cdots,w_{i-1},w_i)+1}{count(w_{i-n+1},\cdots,w_{i-1})+|V|}\
|V|:词库大小
$$
加k平滑
$$
P(w_i | w_{i-n+1},\cdots,w_{i-1}) = \frac{count(w_{i-n+1},\cdots,w_{i-1},w_i)+k}{count(w_{i-n+1},\cdots,w_{i-1})+k|V|}\
k:一个小于1的数,可以使用交叉验证进行选择\
|V|:词库大小
$$
Kneser-Ney 平滑
核心思想:一个词是否可以作为多个上下文的结尾
$$
P_{KN}(w_i|context) = \frac{\max(count(w_i,context),D,0)}{count(context)}+\lambda(context)P_{cont}(w_i)\
D:折扣因子\
\lambda:归一化系数\
\lambda=\frac{D\cdot N_{1+}(context)}{C(context)}\
N_{1+}(Context):表示有多少个不同的 w_i 使得 C(w_i,context)>0\
P_{cont}(w_i):词w_i作为上下文结尾的概率\
P_{cont}(w_i) = \frac{C_{prefix}(w_i)}{\sum_{w’}C_{prefix}(w’)}\
C_{prefix}(w_i):有多少个不同的词可以跟在w_i前面组成bigram(n=2,根据前一个词预测下一个词)\
\sum_{w’}C_{prefix}(w’):所有词的C_{prefix}总和\
$$
例子:
$$
我 \ 明天\ 要\ 去\ 玩\
我\ 明天\ 要\ 去\ 学校\
\
生成bigram列表\
{(我,明天),(明天,要),(要,去),(去,玩)}\
{(我,明天),(明天,要),(要,去),(去,学校)}\
\sum_{w’}C_{prefix}(w’) = 8\
C_{prefix}(“明天”)=2 \
P_{cont}(“明天”)=\frac{2}{8}=\frac{1}{4}
$$
问题(?)
公式的理解,对prefix一开始理解不太透彻,prefix就是:有多少个不同的词可以跟在w_i前面组成bigram(n=2,根据前一个词预测下一个词)
神经网络语言模型NNLM
这里提到NNLM是为了接下来引入Word2Vec
余弦相似度
$$
cos(\theta) = \frac{A\cdot B}{||A||B||} = \frac{\sum_{i=1}^{n}A_iB_i}{\sqrt{\sum_{i=1}^{n}A_i^2}\sqrt{\sum_{i=1}^{n}B_i^2}}
$$
one-hot编码表示词的缺点

- 维度灾难:假设有m=10000个词
- 每个词的向量长度都是m,整体大小太大,存储开销很大
- 不能表示出词与词之间的关系
- 例如Man和Woman关系比较近,Man和Apple关系比较远,但是其实任取2个向量的内积都是0,也就是余弦相似度为0
流程
输入层/Embedding层
先随机生成矩阵Q,后面对Q进行训练
$$
w_i \cdot Q = c_i \
词矩阵C = [c_1,c_2,\cdots,c_n]\
w_{1\times V}:词的one-hot编码的向量\
Q_{V\times M}:嵌入矩阵,类似于权重矩阵W(可学习)\
V:词典大小\
M:新的词向量大小
$$
隐藏层/第一层感知机
$$
h = Tanh(CW+b_1)
$$
输出层/第二层感知机
$$
O_{1\times V} = SoftMax(Uh+b_2) = Softmax(U(Tanh(CW+b_1))+b_2)
$$
词向量 && 词嵌入-Word Embedding
能对one-hot编码的向量起到压缩的作用
(把一个维数为所有词的数量的高维空间嵌入到一个维数低得多的连续向量空间中,每个单词或词组被映射为实数域上的向量)
$$
w_{1\times V}\cdot Q_{V\times M} = c_{1\times M}\
c_i就称为词向量
$$
词向量:用一个向量表示一个单词;所以one-hot编码也是词向量的一种形式
解决了任务:
- 概率评估任务:将词转换成词向量了,那么就可以输入到神经网络中,是二分类或者多分类问题
- 生成任务:NNLM中的softmax给出的概率分布

特点
- 能够体现出词与词之间的关系
- 比如用Man-Woman或者Apple-Orange,都能得到一个向量
- 能够找到相似词
- 例如Man-Woman = King-? (?=Queen)
Word2Vec
也可以理解成一种神经网络语言模型
与NNLM的区别
- NNLM:重点是预测下一个词
- Word2Vec:重点是得到
Q矩阵
重要假设:文本中离得越近的词语相似度越高

skip-gram(跳字模型)
使用中心词预测上下文词

例子
假设文本序列有5个词[“The”, “man”, “loves”, “his”, “son”],中心词wt=”love”,背景窗口大小skip-window=2
那么上下文词(背景词)为”The”, “man”, “his”, “son”
skip-gram做的是:通过中心词wc,生成距离它不超过m=skip-window的背景词,用公式表示就是:
$$
\begin{align}
p = P(context | w_c) = P(w_{c-m},w_{c-m+1},\cdots,w_{c+m-1},w_{c+m}| w_c) = P(w_{c1},w_{c2},\cdots,w_{c2m}|w_c)\
如果在给定中心词的情况下,背景词是互相独立的,那么上面的公式可以写成:\
P(context |w_t) = P(w_{c1}|w_c)P(w_{c2}|w_c)\cdots P(w_{c2m-1}|w_t)P(w_{c2m}|w_t)\
w_{cj}:第j个上下文词\
w_{ncj}:第j个非上下文词
\end{align}
$$
思想
使用一个词预测另一个词,尽量使这两个词的词向量接近
skip-gram在迭代过程中,调整词向量:
- 中心词(目标词)的词向量与上下文词(背景词)的词向量尽可能接近
- 中心词词向量与非上下文词词向量尽可能远离

在skip-gram中,衡量两个词向量相似程度,采用点积的方式
点积衡量了两个向量在同一方向上的强度,点积越大,两个向量越相似,他们对应的词语的语义就越接近
结构

流程
和CBOW的流程其实很相似
one-hot编码,将中心词转换成one-hot编码形式输入第一层嵌入层,用于计算词向量
word embedding;计算中心词的词向量v
$$
v_i = w_i\cdot W \
得到形状为1\times d的中心词词向量v_i
$$skip-gram模型训练,计算上下文词的词向量u
$$
\begin{align}
u_i = v_i\cdot W’ \
W’是d\times V的矩阵,每一列对应单词作为上下文词的词向量u_i
\end{align}
$$softmax计算概率分布
$$
\begin{align}
P(w_c |w_t) = \frac{exp(u_c^T\cdot v_t)}{\sum_{i=1}^{V}exp(u_i^T\cdot v_t)} \
v_t:当前中心词词向量\
u_c:当前中心词的上下文词的词向量
\end{align}
$$极大化概率(求损失函数)
$$
\begin{align}
L = -logP(w_c|w_t) = -(log(w_{t-m}|w_t)+log(w_{t-m+1}|w_t)+\cdots+log(w_{t+m-1}|w_t)+log(w_{t+m}|w_t))
\end{align}
$$反向传播更新参数矩阵
优化
在模型中出现的单词对或短语当成一个词
比如有些短语/单词对的意思和一个一个单词拼写出的意思完全不一样,将他们作为一个词
二次采样
在一个很大的语料库中,有一些出现频率很高的词(比如冠词a,the,中文的的,地,得,了之类的)
但是这些词,提供的信息不多,会导致我们很多训练是没用的
因此我们引入二次采样的思路:每个单词都有一定概率被丢弃,这个概率为:
$$
\begin{align}
P(w_i) = 1-\sqrt{\frac{t}{f(w_i)}} \
t:一个选定的阈值,通常是1e-5\
f(w_i):单词w_i出现的频率(次数与总单词数的比值)\
\end{align}
$$
为什么t取1e-5(?)
这是在 Word2Vec 原始论文和 Google 的实现中推荐的默认值
负采样
在 Word2Vec的两个算法中,每接收一个训练样本,就需要调整隐藏层和输出层的权重参数 W,W',
来使神经网络预测的更加准确;也就是说,每个训练样本都将会调整所有神经网络中的参数
负采样的==思路==是:每次让样本仅仅更新一小部分的权重参数,从而降低梯度下降过程中的计算量
如何去理解?
在skip-gram中,我们的目标是学习中心词和上下文词之间的语义关系,可以分为两种词:
- 正词/正样本(positive word):真正出现在当前词上下文的词(即给定的中心词$w_t$及其上下文词$w_c$)
- 负词/负样本(negative word):随机从词汇表中采样出的词,不是当前词的上下文词,即与当前词没用上下文关系
那么为什么这样就可以更新小部分的权重矩阵了呢(?)
原本我们需要更新所有词对应的权重,现在我们只要更新正词以及采样的负词对应的权重
这里的权重指的是输出嵌入层参数W'
负样本的选择
选择的经验公式为:
$$
P(w_i) = \frac{f(w_i)^{3/4}}{\sum_{j=1}^{V}f(w_j)^{3/4}}
$$
3/4是纯基于经验的选择
正样本概率
$$
\begin{align}
给定中心词w和上下文词q\
\hat{p_q} = \sigma(x_w^T\cdot \theta^q) \
x_w \in R^{1\times N}:中心词w的低维密集向量(词嵌入后的词向量) \
\theta^q \in R^{N\times 1}:输出参数矩阵W’ \in R^{N\times V} 中q对应的上下文向量\
对于正样本,我们希望\hat{p_q}越大越好
\end{align}
$$
为什么不再使用Softmax而是使用sigmoid了(?)
- 引入负采样后,问题可以被看成是一个二分类:是否是正样本?
- 而且
Softmax分母需要计算所有词的exp(),sigmoid不用
负样本概率
$$
\begin{align}
选择负样本词u\
\hat{p_u} = \sigma(w_w^T\cdot \theta^u) \
我们希望\hat{p_q}越小越好
\end{align}
$$
总样本
$$
\begin{align}
给定(w,context(w)),可构造|(w,context(w))|个正样本\
对于每个正样本(w,q),q \in context(w),采样一组负样本NEG(q)进行对比学习\
则构造出|NEG(q)|个负样本,负样本为(w,u),u \in NEG(q) \
每个中心词w的损失函数设计为: \
L_w = \prod_{q \in context(w)}(\hat{p_q} \prod_{u \in NEG(q)}(1-\hat{p_u}))\
对于语料库C:\
L_C = \prod_{w \in C}L_w\
为了计算方便,进行取对数操作:\
L_c = \sum_{w \in C}logL_w \
=\sum_{w \in C}\sum_{q \in context(w)}log(\hat{p_q}\prod_{u \in NEG(q)}(1-\hat{p_u}))\
=\sum_{w \in C}\sum_{q \in context(w)}(log\hat{p_q}+\sum_{u \in NEG(q)}log(1-\hat{p_u}))\
\end{align}
$$
CBOW(连续词袋模型)
使用上下文词预测中心词

前向传播
embeddings层
embeddings层其实是输入层和投影层的结合,负责接受输入的词索引,并映射成词向量后输出
$$
\begin{align}
输入的one-hot编码向量为w_{1\times V}\
embeddings层是一个V\times M的矩阵Q,其中:\
V:词典中词语个数\
M:词向量维度\
Q就是我们最终希望得到的嵌入矩阵\
\end{align}
$$
一个词的上下文会包含多个词语,这些词语会被同时输入embeddings层,每个词语都会转换成一个词向量
$$
\begin{align}
w_1Q = c_1 \
w_2Q = c_2\
\vdots\
w_kQ = c_k\
C = [c_1,c_2,\cdots,c_k]\
C是序列对应的词向量矩阵,每个元素c_i(每一行)对应一个词的word\ embedding\
输入embeddings层k个上下文词,得到k个对应的词向量\
需要对多个上下文词进行统一表示:\
h = \frac{1}{k}\sum_{i=1}^{k}c_i
\end{align}
$$
embeddings层的输出结果就是:将语义信息平均的向量h
embeddings层的本质就是一个查找表(look up table),因为其每一行都对应着一个词向量(如图所示)

优点
- embeddings层是可训练的,也就是说每一行对应的词向量可以更新,使最终词与词之间的语义关系能更好的表示
- 可以不进行矩阵运算,仅进行查表操作,效率高很多
线性层/输出层
特点:不设置激活函数
$$
\begin{align}
前面的embeddings层输入进来的向量h \in R^{1\times M} \
线性层的输出的结果是一个词向量v \in R^{1\times V} \
所以线性层的权重矩阵W \in R^{M\times V} \
v_{1\times V} =h\cdot W\
v = [u_1,u_2,\cdots,u_V] \
第j个词的得分u_j = W_j^Th\
W_j:线性层权重矩阵的第i行\
线性层输出U = [u_1,u_2,\cdots,u_m]
\end{align}
$$
为什么CBOW中不设置激活函数Tanh或者ReLU呢(?)
LLNM中设置激活函数(第一层感知机)是为了让预测下一个词变得更准确- 但是
CBOW中,我们只是想得到嵌入矩阵Q,然后进行正常的反向传播更新Q和W就可以- 并且embeddings层输出时,是进行了求平均向量这步线性操作,如果使用了非线性的激活函数,会破坏词向量之间的语义关系
Softmax层
将线性层的输出向量转换为概率分布
$$
P(y_j | context) = \frac{exp(u_j)}{\sum_{i=1}^{V}exp(u_i)}
$$
损失函数
$$
\begin{align}
L = -logP(w_t | w_c) = -logP_{true} \
w_t:中心词\
w_c:所有上下文词\
P_{true}:Softmax输出的对应目标词位置的概率
\end{align}
$$
使用mini-batch进行训练:
$$
\begin{align}
L = -\frac{1}{B}\sum_{i=1}^{B}logP(w_t^{(i)}|w_c^{(i)}) \
B:Batch_size;一个Batch的样本数\
^{(i)}:第i个样本
\end{align}
$$
反向传播
$$
\begin{align}
先回顾一下前向传播;\
embeddings层输出:h = \frac{1}{V}\sum_{i=1}^{V}w_i\cdot Q \
线性层:u_j = W_j^Th\
U = [u_1,u_2,\cdots,u_m]\
Softmax层:p_j = P(w_j|context) = \frac{exp(u_j)}{\sum_{i=1}^{V}exp(u_i)}\
\end{align}
$$
对于损失函数:
$$
\begin{align}
E = -logP(w_t | w_c)\
=-log(\frac{exp(u_{t})}{\sum_{i=1}^{V}exp(u_i)})\
= -log(\frac{exp(W^T_{t}\cdot h)}{\sum_{i=1}^{V}exp(W_i^T\cdot h)})\
=-(W_t^T\cdot h - log\sum_{i=1}^{V}exp(W_i^T\cdot h))\
=-W_t^T\cdot h + log\sum_{i=1}^{V}exp(W_i^T\cdot h)\
=-\frac{W_t^T\cdot \sum_{i=1}^{V}w_i\cdot Q}{V}+log\sum_{i=1}^{V}\frac{exp(W_i^T\cdot \sum_{i=1}^{V}w_i\cdot Q)}{V}
\end{align}
$$
计算梯度
$$
\begin{align}
首先是对线性层的权重矩阵W求梯度:\
对当前的中心词w_t\
\nabla_{W_t} = \frac{\partial E}{\partial W_t}=\frac{\partial}{\partial W_t}(-W_t^T\cdot h + log\sum_{i=1}^{V}exp(W_i^T\cdot h))\ = -h +\frac{\frac{\partial}{\partial W_t}exp(W_t^T\cdot h)}{\sum_{i=1}^{V}exp(W_i^T\cdot h)} = -h+\frac{exp(W_t^T\cdot h)\cdot h}{\sum_{i=1}^{V}exp(W_i^T\cdot h)} = -h+p_t\cdot h = (p_t-1)\cdot h\
对其他上下文词w_c\
\nabla_{W_c} = \frac{\partial E}{\partial W_c}=\frac{\partial}{\partial W_c}(-W_t^T\cdot h + log\sum_{i=1}^{V}exp(W_i^T\cdot h))\
=p_c\cdot h\
可以得到一个统一形式:\
\nabla_{W} = (p_i-y_i)\cdot h\ \ (y_i=1,当i=t)
\end{align}
$$
$$
\begin{align}
对embeddings层的嵌入矩阵Q求梯度:\
\nabla_Q = \frac{\partial E}{\partial U}\frac{\partial U}{\partial h}\frac{\partial h}{\partial Q}\
=(p_i-y_i)\cdot W_i\cdot \frac{\partial}{\partial Q}(\frac{1}{V}\sum_{i=1}^{V}w_i\cdot Q )\
=\frac{1}{V}((p_i-y_i)\cdot W_i\cdot \sum_{i=1}^{V}w_i) \
因为w_i是one-hot编码得到的二进制向量 \
\sum_{i=1}^{V}w_i相当于是一个长度为V的全1向量\
所以得到最终的梯度:\
\nabla_Q = \frac{1}{V}(p_i-y_i)\cdot W_i
\end{align}
$$
层次Softmax(Hierarchical Softmax)
前置知识
哈夫曼树
哈夫曼树是保证所有叶子节点的带权路径长度最小的一种树
比如对于这样一颗二叉树

首先找出两个最小的叶子节点,组成一颗新的树
找出下一个最小的叶子节点,与新树组成树

以此类推,最终组成哈夫曼树

优化点
类似于
CBOW的embeddings层,从输入层到隐藏层的映射,不是像神经网络那样线性变换+激活函数的操作,而是对词向量求平均原本我们计算Softmax:
$$
\begin{align}
P(w_c |w_t) = \frac{exp(u_c^T\cdot v_t)}{\sum_{i=1}^{V}exp(u_i^T\cdot v_t)} \
v_t:当前中心词词向量\
u_c:当前中心词的上下文词的词向量
\end{align}
$$
需要遍历整个词汇表,对于大词汇表来说,这个做法的效率太灾难了
因此,层次Softmax通过将词汇表构造成树形结构,从而减少了计算量,特别是在计算时只需要经过树的部分路径,而不是整个词汇表
工作原理

将词汇表V中的所有词汇根据词频$f(w_i))$表示成一颗哈夫曼树,词频越大,路径越短,编码信息更少;其中叶子节点表示对应的词$w_i$,非叶子节点表示决策路径(二分类决策),每个叶子节点都有唯一的从根节点到该节点的路径path
$$
$$
比如对于叶子节点$w_2$,从根节点出发到$w_2$的路径为:
$$
\begin{align}
n(w_2,1),n(w_2,2),n(w_2,3),w_2\
n(w_i,j):词w_i的path的第j个节点
\end{align}
$$
叶子节点词的概率表示
在Hierarchical Softmax中,我们的目标是计算根节点到叶子节点的路径的概率,并最大化路径上每个节点的概率;根节点到叶子节点有很多中间节点$n_k$,每个中间节点的决策都是二分类问题
那么就有正类(+)与负类(-)的区分:在这里规定向左为负(-)
使用sigmoid判断正负类:
$$
\begin{align}
P(+) = \sigma(v_n^T{‘} \cdot h) \
v_n^T{‘}:中间节点词向量\
h:隐藏层输出的中心词的上下文向量
\end{align}
$$
还是计算上面根节点到$w_2$路径的概率,我们应最大化下式:
$$
\begin{align}
P(w_2) = \prod_{i=1}^{3}P(n(w_2,i)) = P(n(w_2,1),-)\cdot P(n(w_2,2),-)\cdot P(n(w_2,3),+) \
=\sigma(-v_{n(w_2,1)}^T{‘} \cdot h)\cdot\sigma(-v_{n(w_2,2)}^T{‘} \cdot h)\cdot\sigma(v_{n(w_2,3)}^T{‘} \cdot h)
\end{align}
$$
损失函数:
$$
\begin{align}
E = -logP(w_2) = -\sum_{i=1}^{3}logP(n(w_2,i)) = -\sum_{i=1}^{3}log\sigma(sign_i \cdot v_{n(w_2,i)}^T{‘} \cdot h)\
sign_i:表示向左(-1)向右(+1)
\end{align}
$$
通过梯度下降更新$v_n^T{‘} 和 h$
案例
设备
1 | device = torch.device("cuda" if torch.cuda.is_available() else "cpu") |
数据
1 | # 句子数据 |
构造词汇表V以及索引与词之间映射关系的字典
1 | # 分词 |
构造负采样的概率
1 | # 计算负采样概率 |
存在问题?
当前负采样概率和不为1,后续选择负样本时会出错
解决:进行归一化
1 | # 归一化 |
修正后的构造负采样的代码为:
1 | # 计算负采样概率 |
生成带负采样的训练数据
1 | # 窗口大小 |
转换为tensor
1 | # 将负采样训练样本转换为torch.tensor |
构建数据集以及分批加载
1 | # 使用TensorDataset将三个张量合成一个数据集对象,每个样本是一个三元组(centres, contexts, labels) |
模型
1 | class NegSkipGram(nn.Module): |
模型训练
1 | # 一批训练多少个样本 |
可视化:使用sklearn.manifold.TSNE进行降维,以及plt进行绘图
1 | # 中心词嵌入层参数矩阵的numpy形式 shape=(vocab_size,embedding_dim) |
问题
为什么嵌入层返回的形状跟之前公式里学习的都不太一样(?)
因为在torch中,我们使用了批量处理的方法,一次处理一个batch而不是一个词;
在嵌入矩阵中,每输入一个词,就会输出一个形状为[,embedding_dim]的向量,那么对于batch_size个词,就会输出形状为[batch_size,embedding_dim]的矩阵
为什么计算余弦相似度时要指定维度(?)
因为torch返回的词向量的shape是[batch_size,embedding_dim],对embedding_dim这一维度求余弦相似度,才能正确计算这一个batch中每个词的余弦相似度评分




