引言
语言表示是将自然语言表示为计算机或者模型能够处理的数据特征,是解决例如情感分析、命名实体识别、机器翻译、文本生成等这些高级任务的基础。本文作为NLP基础知识的入门,梳理了相关文本表征的模型与方法。
- 在语音处理技术中,语音信息被解析成为用音频频谱序列向量所构成的matrix,喂入神经网络或者统计学习模型进行处理;
- 在图像处理技术中,图像信息被解析成由众多像素点构成的matrix,喂入神经网络或者统计学习模型进行处理;
- 在自然语言处理技术中,每一个词、每一个句子、每一个篇章又该被解析成什么样的,模型能够处理的特征呢?
相比于图像和语音,自然语言的特征表示显得更为复杂,因为图像和语音是比较自然的低级信息表示形式,可以通过观察或者距离度量直接判别出两幅图片或者两段语音之间的相似性。而语言是人类经过几百万年的进化后所形成的特有的、高级且抽象的信息表达工具,以字符为单位、以文本为载体。两个词只要字符表达不同,就难以刻画它们之间的联系,比如:“麦克”和“话筒”这样的同义词,从字面上也难以看出这两者意思相同(语义鸿沟现象),而判断两个词是否相似时,还需要更多的背景知识才能做出准确回答。
One-Hot Encoding
独热编码即 One-Hot 编码,又称一位有效编码,其方法是使用N位状态寄存器来对N个状态进行编码,每个状态都由他独立的寄存器位,并且在任意时候,其中只有一位有效,将自然语言中的每个词当成一个类别。
缺点:
- 无法区分不同词语的权重;
- 向量维度会随着句子中词的数量增大而增大(造成词典过于庞大以及稀疏问题);
- 任意两个词之间都是孤立的,根本无法表示出在语义层面上词语词之间的相关信息,而这一点是致命的(缺乏语义层面的表示);
Distributed Representation
Distributed representation最早由Hinton 在 1986 年提出。其基本思想是通过训练将每个词映射成K维实数向量(K 一般为模型中的超参数),通过词之间的距离(比如cosine相似度、欧氏距离等)来判断它们之间的语义相似度。
问题1:无法区分不同词语的权重
Bag-of-words
词袋模型是个在自然语言处理和信息检索下被简化的表达模型。此模型下,一段文本(比如一个句子或是一个文档)可以用一个装着这些词的袋子来表示,这种表示方式不考虑文法以及词的顺序。词袋模型主要解决的是不同词语的权重信息,相比于one-hot,通过将词语出现频次加入到编码过程中。
举例(来自wiki pedia):
以下是两个简单的文件:
(1) John likes to watch movies. Mary likes movies too.
(2) John also likes to watch football games.
构建出下列词语清单:
[
"John",
"likes",
"to",
"watch",
"movies",
"also",
"football",
"games",
"Mary",
"too"
]
此处有10个不同的词,使用清单的索引表示长度为10的向量:
(1) [1, 2, 1, 1, 2, 0, 0, 0, 1, 1] (2) [1, 1, 1, 1, 0, 1, 1, 1, 0, 0]
每个向量的索引内容对应到清单中词出现的次数。
此向量表示法不会保存原始句子中词的顺序。
单靠频率并无法区分出句子中的关键词,如:“John likes to play football, Mary likes too”。关键词应该是“football”,而“likes”的词频高于“football”。
TF-IDF
TF-IDF是一种统计方法,用以评估一个词对于一个文件集或一个语料库中的重要程度。词的重要性随着它在文件中出现的次数成正比增加,但同时会随着它在语料库中出现的频率成反比下降。主要思想是:如果某个单词在一篇文章中出现的频率TF高,并且在其他文章中很少出现,则认为此词或者短语具有很好的类别区分能力,适合用来分类。
- TF-IDF原理
TF-IDF由两部分组成:TF(Term Frequency, 词频)、IDF(Inverse Document Frequency, 逆向文件频率);
TF: 统计词在当前文档中的出现频率,是归一化的结果,归一化的目的是为了防止TF偏向于长文件。
t
f
i
j
=
n
i
,
j
Σ
k
n
k
,
j
tf_{ij}=\frac{n_{i,j}}{\Sigma_{k}n_{k,j}}
tfij=Σknk,jni,j
其中, n i , j n_{i,j} ni,j为文件 j j j中词 i i i出现的次数,分母是文件 j j j所有词出现的次数总和;
IDF:某一词的IDF,可以由总文件数目除以包含该词语的文件的数目,再将得到的商取对数得到。如果包含该词的文档越少, IDF越大,则说明词条具有很好的类别区分能力。
i
d
f
i
=
l
o
g
∣
D
∣
1
+
∣
{
j
:
t
i
∈
d
i
}
∣
idf_{i}=log \frac{|D|}{1+|\{j:t_{i}\in d_{i}\}|}
idfi=log1+∣{j:ti∈di}∣∣D∣
其中,
∣
D
∣
|D|
∣D∣是语料库中的文件总数,
∣
{
j
:
t
i
∈
d
j
}
∣
|\{j:t_{i}\in d_{j} \}|
∣{j:ti∈dj}∣表示在本语料库
d
d
d中包含文档
j
j
j中的词语
t
i
t_{i}
ti的文件数目,分母加1是为了避免分母为0;
TF-IDF求解:计算每一个词的TF-IDF值,
T
F
−
I
D
F
=
T
D
∗
I
D
F
TF-IDF=TD \ast IDF
TF−IDF=TD∗IDF
总结:某一特定文件内的高词语频率,以及该词语在整个文件集合中的低文件频率,可以产生出高权重的 T F − I D F TF-IDF TF−IDF。因此, T F − I D F TF-IDF TF−IDF倾向于过滤掉常见的词语,保留重要的词语。
- T F − I D F TF-IDF TF−IDF在NLP中常见的应用领域
T F − I D F TF-IDF TF−IDF算法简单也易于实现,通过加入“停用词库”和“用户自定义词库”在部分应用场景下也能获得较为不错的关键词抽取效果;
常见的应用领域有:文本摘要、关键词提取、文本相似度计算、搜索引擎等;
- TF-IDF存在的问题
TF-IDF本质上是通过IDF 对TF的值加权,取TF-IDF取值最大的前k个词作为关键词;TF-IDF存在缺陷,如下:
- 没有考虑关键词的位置因素对文本的区分度,词条出现在文档的不同位置时,对区分度的贡献大小也会有差别:如文本的标题、开头、结尾等部分需要被赋予更高的权重;
- 按照传统TF-IDF,往往一些生僻词的IDF(逆文档频率)会比较高、因此这些生僻词常会被误认为是文档关键词;
- 无法区分语义相似的关键词,如计算机和电脑,麦克风和话筒;
- 无法解决一词多义问题,如小龙女“我也想过过过儿过过的生活”(调侃一下,汉语的博大精深);
- 对于文档中出现次数较少的重要人名、地名等关键信息提取效果不佳;
- 对于已分类的文本集,该类中的文本关键词IDF被弱化,使得该关键词的提取召回率降低;
- 要实现理想的关键词抽取效果,需要依赖完备的停用词库和用户自定义词库;
TF-IDF的缺陷来自于算法设计的初衷,由于模型过于简单,其必然会忽略很多问题,简单的更换算子难以取得比较好的效果;目前,个人觉得要用TF-IDF还是应该在完善“停用词库”和“用户自定义词库”上面,通过这两个词库完成辅助分词,效果会好一些,但是还是解决不了其中多项缺陷问题。
TextRank
下面来总结另外一种关键词抽取模型,TextRank,看看这个算法能不能解决TF-IDF难以解决的问题:说到TextRank就不得不提大名鼎鼎的PageRank,这也是Google能够在搜索领域光芒万丈的核心方法之一:
PageRank:
PageRank,即网页排名,是一种由搜索引擎根据网页之间相互的超链接而获得计算排名的技术,以Google公司创办人拉里·佩奇(Larry Page)的姓来命名。Google用它来体现网页的相关性和重要性,在搜索引擎优化操作中是经常被用来评估网页优化的成效因素之一。作为一个基于图的网页重要度排序的算法,每个网页可以看作是一个图中的结点,如果网页A能够跳转到网页B,那么则有一条A->B的有向边。这样,我们就可以构造出一个有向图。
核心思想:
- 如果一个网页被很多其他网页链接到的话说明这个网页比较重要,PageRank值会相对较高;
- 如果一个PageRank值很高的网页链接到一个其他的网页,那么被链接到的网页PageRank值会相应地提高
计算:
- 假设一个由4个网页组成的群体:A,B,C和D。如果所有页面都只链接至A,那么A的PR(PageRank)值将是B,C及D的Pagerank总和:
P R ( A ) = P R ( B ) + P R ( C ) + P R ( D ) PR(A)=PR(B)+PR(C)+PR(D) PR(A)=PR(B)+PR(C)+PR(D) - 假设B链接到A和C,C只链接到A,并且D链接到全部其他的3个页面,此时计算计算需要加权:
P R ( A ) = 1 2 P R ( B ) + 1 1 P R ( C ) + 1 3 P R ( D ) PR(A)=\frac{1}{2}PR(B)+\frac{1}{1}PR(C)+\frac{1}{3}PR(D) PR(A)=21PR(B)+11PR(C)+31PR(D)
因此,计算通式为:
P R ( A ) = ( 1 − d ) + d ( 1 C ( T 1 ) P R ( T 1 ) + 1 C ( T 2 ) P R ( T 2 ) + ⋯ + 1 C ( T n ) P R ( T n ) ) PR(A)=(1-d)+d(\frac{1}{C(T_{1})}PR(T_{1})+\frac{1}{C(T_{2})}PR(T_{2})+\cdots+\frac{1}{C(T_{n})}PR(T_{n})) PR(A)=(1−d)+d(C(T1)1PR(T1)+C(T2)1PR(T2)+⋯+C(Tn)1PR(Tn))
其中: P R ( A ) PR(A) PR(A)为页面 A A A的 P a g e R a n k PageRank PageRank值; P R ( T i ) PR(T_{i}) PR(Ti)是页面 T i T_{i} Ti的页面值; C ( T i ) C(T_{i}) C(Ti)是页面T_{i} 的出度,也就是 T i T_{i} Ti 指向其他页面的边的个数;
d d d是阻尼系数,指在任意时刻,用户到达某页面后并继续向后浏览的概率;
计算举例:假设每个页面的PR初始值为1, d d d为 0.5 0.5 0.5。
P
R
(
A
)
=
(
1
−
0.5
)
+
0.5
∗
P
R
(
C
)
=
0.5
+
0.5
∗
1
=
1
P
R
(
B
)
=
(
1
−
0.5
)
+
0.5
∗
(
1
2
P
R
(
A
)
)
=
0.5
+
0.5
∗
0.5
=
0.75
P
R
(
C
)
=
(
1
−
0.5
)
+
0.5
∗
(
1
2
P
R
(
A
)
+
P
R
(
B
)
)
=
1.125
PR(A)=(1-0.5)+0.5*PR(C)=0.5+0.5*1=1 \\ PR(B)=(1-0.5)+0.5*(\frac{1}{2}PR(A))=0.5+0.5*0.5=0.75 \\ PR(C)=(1-0.5)+0.5*(\frac{1}{2}PR(A)+PR(B))=1.125
PR(A)=(1−0.5)+0.5∗PR(C)=0.5+0.5∗1=1PR(B)=(1−0.5)+0.5∗(21PR(A))=0.5+0.5∗0.5=0.75PR(C)=(1−0.5)+0.5∗(21PR(A)+PR(B))=1.125
算法的结束条件:
- 当前迭代结果与上一轮迭代结果小于设定阈值;
- 达到最大循环次数。
如何用矩阵的形式完成PageRank的计算呢?
- 用 S ( v i ) S(v_{i}) S(vi)定义页面 v i v_{i} vi的PageRank数值;
- 用 ε \varepsilon ε来表示所有其他可以链接到 v i v_{i} vi这个页面的集合;
- 用 ( j , i ) (j,i) (j,i)定义集合中的其中一个(由页面 v j v_{j} vj链接到页面 v i v_{i} vi);
可以给一个页面的PageRank值做这样的定义:
S
(
v
i
)
=
Σ
(
j
,
i
)
∈
ε
S
(
v
j
)
o
u
t
j
S(v_{i})=\Sigma_{(j,i)\in \varepsilon} \frac{S(v_{j})}{out_{j}}
S(vi)=Σ(j,i)∈εoutjS(vj)
因此,大量网页的PageRank计算,可以转为矩阵运算:
S
(
v
)
:
=
A
T
S
(
v
)
S(v):=A^TS(v)
S(v):=ATS(v)
其中:
S
(
v
)
=
(
S
(
v
1
)
⋮
S
(
v
n
)
)
S(v)= \left(\begin {matrix} S(v_{1})\\ \vdots \\S(v_{n}) \end {matrix} \right)
S(v)=⎝⎜⎛S(v1)⋮S(vn)⎠⎟⎞是一个
n
×
1
n\times1
n×1维的PageRank向量;
A是所有页面的集合矩阵,是
n
×
n
n \times n
n×n方阵,
A
=
(
A
11
⋯
A
1
n
⋮
⋱
⋮
A
n
1
⋯
A
n
n
)
A=\left( \begin {matrix} A_{11}& \cdots &A_{1n} \\ \vdots& \ddots & \vdots \\ A_{n1}&\cdots&A_{nn} \end {matrix} \right)
A=⎝⎜⎛A11⋮An1⋯⋱⋯A1n⋮Ann⎠⎟⎞,并且
A
j
i
=
{
1
O
u
t
(
v
j
)
,
i
f
(
j
,
i
)
∈
ε
0
,
i
f
(
j
,
i
)
∉
ε
A_{ji}= \left \{ \begin {matrix} \frac{1}{Out(v_{j})},&if(j,i) \in \varepsilon \\ 0,&if(j,i) \notin \varepsilon \end {matrix} \right .
Aji={Out(vj)1,0,if(j,i)∈εif(j,i)∈/ε
问题:该如何计算
S
(
v
i
)
S(v_{i})
S(vi)呢?
因为
S
(
v
i
)
S(v_{i})
S(vi)是在其他网站PageRank值的基础上计算而来的,要计算当前的PageRank值就需要之前与之相连的网站的PageRank值,而其他网站的PageRank值又与这些网站相连的其他PageRank产生……如果从时序上来看,有点像Markov Chain问题,每一个当前状态都是由前一个转态通过状态转移矩阵变换而来,而前一个状态又由再前一个状态的状态转移矩阵变换而来…
PageRank算法采用迭代训练的方式来解决这个问题:
随机给定
S
(
v
)
S(v)
S(v)的一个初值
S
(
v
)
0
S(v)^0
S(v)0,再通过下面的公式完成迭代求解:
S
(
v
)
k
=
A
T
S
(
v
)
k
−
1
S(v)^k=A^TS(v)^{k-1}
S(v)k=ATS(v)k−1
迭代的终止条件:
- 两代之间误差小于阈值: ∥ S ( v ) k − S ( v ) k − 1 ∥ < θ \Vert S(v)^k-S(v)^{k-1} \Vert<\theta ∥S(v)k−S(v)k−1∥<θ;
- 达到预设的迭代次数
于是,计算PageRank值的过程就变成了一个 Markov 过程,A是这个过程的状态转移矩阵,那么PageRank算法的证明也就转为证明 Markov 过程的收敛性,因此转移矩阵A需要满足:
- stochastic matrix:则行至少存在一个非零值,即必须存在一个外链接(没有外链接的网页被称为dangling pages);
- 不可约(irreducible):即矩阵A所对应的有向图 G必须是强连通的,对于任意两个节点 ( j , i ) ∈ ε (j,i)\in \varepsilon (j,i)∈ε存在一条从 j j j到 i i i的路径;
- 非周期性(aperiodic):即每个节点存在自回路;
所以,PageRank将矩阵A乘以阻尼因子d模拟,用户达到当前网页并继续向后浏览的概率,通过实践验证取值d=0.85。再对stochastic matrix问题,通过加上一个非零项
(
1
−
d
)
E
n
(1-d)\frac{E}{n}
(1−d)nE来平滑零项,转换后的公式为:
S
(
v
)
=
(
1
−
d
)
E
n
+
d
A
T
S
(
v
)
S
(
v
i
)
=
(
1
−
d
)
+
d
Σ
(
j
,
i
)
∈
ε
S
(
v
j
)
O
u
t
j
S(v)=(1-d)\frac{E}{n}+dA^TS(v)\\ S(v_{i})=(1-d)+d\Sigma_{(j,i)\in \varepsilon} \frac{S(v_{j})}{Out_{j}}
S(v)=(1−d)nE+dATS(v)S(vi)=(1−d)+dΣ(j,i)∈εOutjS(vj)
TextRank
TextRank与PageRank类似,可以基于以下思想:
- 如果一个单词出现在很多单词后面的话,那么说明这个单词比较重要;
- 一个TextRank值很高的单词后面跟着的一个单词,那么这个单词的TextRank值会相应地因此而提
改写PageRank公式来获得TextRank:
S
(
v
i
)
=
(
1
−
d
)
+
d
Σ
(
j
,
i
)
∈
ε
w
j
i
Σ
v
k
∈
o
u
t
(
v
j
)
w
j
k
S
(
V
j
)
S(v_{i})=(1-d)+d\Sigma_{(j,i)\in \varepsilon}\frac{w_{ji}}{\Sigma_{v_{k}\in out(v_{j})}w_{jk}}S(V_{j})
S(vi)=(1−d)+dΣ(j,i)∈εΣvk∈out(vj)wjkwjiS(Vj)
TextRank中一个单词
i
i
i 的权重取决于与在
i
i
i前面的各个点
j
j
j组成的
(
j
,
i
)
(j,i)
(j,i)这条边的权重,以及
j
j
j这个点到其他其他边的权重之和。
问题2:词典庞大及稀疏问题
独热表示( one-hot representation)仅仅将词符号化,不包含任何语义信息。那,如何将语义信息融入到词表示中呢?1954 年Harris提出的分布假说( distributional hypothesis)为这一设想提供了理论基础:上下文相似的词,其语义也相似。 1957 年Firth对分布假说进行了进一步阐述和明确:词的语义由其上下文决定( a word is characterized by thecompany it keeps)。到目前为止,基于分布假说的词表示方法,根据建模的不同,主要可以分为三类:
- 基于矩阵的分布表示;
- 基于聚类的分布表示;
- 基于神经网络的分布表示;
尽管这些不同的分布表示方法使用了不同的技术手段获取词表示,但由于这些方法均基于分布假说,它们的核心思想也都由两部分组成:
- 选择一种方式描述上下文;
- 选择一种模型刻画某个词(“目标词”)与其上下文之间的关系。
Co-occurrence Matrix和SVD
Co-occurrence Matrix
基于矩阵的分布表示通常又称为分布语义模型,在这种表示下,矩阵中的一行,就成为了对应词的表示,这种表示描述了该词的上下文的分布。由于分布假说认为上下文相似的词,其语义也相似,因此在这种表示下,两个词的语义相似度可以直接转化为两个向量的空间距离。
共现矩阵:共现矩阵的每个元素表示一个词与另一个词在整篇文档中相邻出现的次数,主要用于发现主题,解决词向量相近关系的表示; 将共现矩阵行(列)作为词向量;
例如:语料库如下:
• I like deep learning.
• I like NLP.
• I enjoy flying.
则共现矩阵表示如下:(使用对称的窗函数(左右window length都为1) ),例如:“I like”出现在第1,2句话中,一共出现2次,所以=2。 对称的窗口指的是,“like I”也是2次将共现矩阵行(列)作为词向量表示后,可以知道like,enjoy都是在I附近且统计数目大约相等,他们意思相近。
共现矩阵缺陷:面临稀疏性问题、向量维数随着词典大小线性增长。
SVD
既然基于Co-occurrence Matrix得到的离散词向量存在着高维和稀疏性问题,一个自然而然的解决思路就是对原始词向量进行降维,从而得到稠密的词向量。
奇异值分解是解决矩阵稀疏的有效方法,为了讲清楚SVD,将从特征值、正交矩阵、特征值分解以及奇异值分解者几部分出发:
特征值
A
v
=
λ
v
Av= \lambda v
Av=λv 说明
λ
\lambda
λ是A的一个特征值,v是对应的特征向量。一个矩阵的一组特征向量是一组正交向量。
A
=
Q
Σ
Q
−
1
A=Q \Sigma Q^{-1}
A=QΣQ−1,其中Q是矩阵A的特征向量所组成的矩阵,
Σ
\Sigma
Σ是一个对角阵。每个对角阵上的元素就是一个特征值,和矩阵Q相乘其实就是一次线性变换。
注意点:
- 对角矩阵对角线上的特征值大于1时,特征值越大拉伸幅度就越大,特征值小于1时,特征值越小,压缩得就越小;
- 在 Σ \Sigma Σ为对角阵的情况下,拉伸和压缩都沿着特征向量的方向,而对于非对称的非对角阵,会有非特征向量方向上的拉伸和压缩;
想要描述好一个变换,那就描述好这个变换主要的变化方向就好,而对角矩阵 Σ \Sigma Σ里面的特征值是由大到小排序的,特征值对应的特征向量就是描述矩阵的主要变换方向;
当矩阵是高维的时候,通过特征值分解得到的前N个特征向量就是矩阵最主要的N个变换方向,可以利用这N个主要的变幻方向来近似原始A矩阵(前提是A矩阵需要为方阵,这也是特征值分解EVD的主要思想)。
正交矩阵(酉矩阵)
一个正交矩阵对应的变换叫做正交变换,这个变换的特点是不改变向量的尺寸和向量间的夹角。正交矩阵U的行(列)之间都是单位正交向量,只对向量做旋转变换,而不拉伸或者压缩该向量。
特征值分解(EVD)
假设对对称矩阵处理。对称矩阵有一个性质就是总能相似对角化,对称矩阵不同的特征值对应的特征向量两两正交。一个矩阵能相似对角化说明其特征子空间就是其列向量,若不能则说明其特征子空间为列空间的子空间。
假设A是
m
×
m
m\times m
m×m的满秩对称矩阵A,有m个不同的特征值,设特征值为
λ
i
\lambda_{i}
λi,对应的单位特征向量为
x
i
x_{i}
xi,则:
有
A
U
=
U
Λ
AU=U\Lambda
AU=UΛ,
U
=
[
x
1
,
x
2
,
.
.
.
,
x
m
]
U=[x_{1},x_{2},...,x_{m}]
U=[x1,x2,...,xm],
对称矩阵的特征向量两两正交。所以U为正交阵,正交阵的逆矩阵等于其转置。
奇异值分解(SVD)
特征值分解的要求是待分解矩阵必须为方阵,而对于任意形状的其他矩阵,需要采用奇异值分解。SVD后的矩阵表示形式为: A = U Σ V T A=U\Sigma V^T A=UΣVT, A m × n = U m × m Σ m × n V n × n T A_{m\times n}=U_{m\times m}\Sigma_{m\times n}V^T_{n\times n} Am×n=Um×mΣm×nVn×nT
假设A是一个mxn矩阵,那么得到的U是一个mxm方阵(里面的向量是正交的,称为左奇异向量),
Σ
\Sigma
Σ是mxn矩阵(除了对角线元素为奇异值以外,其他地方都是0),V是一个nxn矩阵,里面的向量是正交的,称为右奇异向量。
那么矩阵的特征值和方阵的特征值是如何对应的呢?
将
(
A
T
×
A
)
v
i
=
λ
i
v
i
(A^T\times A)v_{i}=\lambda_{i}v_{i}
(AT×A)vi=λivi,这里的
v
i
v_{i}
vi即为右奇异向量,此外得到:
σ
i
=
λ
i
,
u
i
=
1
σ
i
A
v
i
\sigma_{i}=\sqrt{\lambda_{i}},u_{i}=\frac{1}{\sigma_{i}}Av_{i}
σi=λi,ui=σi1Avi。这里的
σ
i
\sigma_{i}
σi是奇异值,ui是左奇异向量。奇异值跟特征值相似在矩阵
Σ
\Sigma
Σ中也按从大到小的顺序进行排列,而且
σ
i
\sigma_{i}
σi减小的特别快。在很多情况下,前10%甚至1%的奇异值之和就占全部奇异值的99%以上。因此,用前
r
r
r个较大奇异值来近似所描述的矩阵,通常r是一个远小于m和n的数值,近似有:
A
m
×
n
≈
U
m
×
r
Σ
r
×
r
V
r
×
n
T
A_{m\times n}\approx U_{m \times r}\Sigma_{r \times r}V^T_{r \times n}
Am×n≈Um×rΣr×rVr×nT
小结:
SVD得到了word的稠密(dense)矩阵。
优点:使得语义相似的词在向量空间中相近,甚至可以在一定程度上反映word间的线性关系;
缺点:
- 由于很多词并未出现,导致矩阵极其稀疏,因此需要对词频做额外处理,以达到较好的矩阵分解效果;
- 矩阵非常大,维度太高;
- 需要手动去掉停用词(stop words),不然这些频繁出现的无意义词会影响效果。
主题模型:pLSA、LDA
参考书籍:《百面机器学习》
基于词袋模型或N-gram模型的文本表示模型有一个明显的缺陷,就是无法识别出两个不同的词或词组具有相同的主题。因此,需要一种技术能够将具有相同主题的词或词组映射到同一维度上去,于是产生了主题模型。主题模型是一种特殊的概率图模型。想象一下我们如何判定两个不同的词具有相同的主题呢?这两个词可能有更高的概率同时出现在同一篇文档中;换句话说,给定某一主题,这两个词的产生概率都是比较高的,而另一些不太相关的词汇产生的概率则是较低的。假设有K个主题,我们就把任意文章表示成一个K维的主题向量,其中向量的每一维代表一个主题,权重代表这篇文章属于这个特定主题的概率。主题模型所解决的事情,就是从文本库中发现有代表性的主题(得到每个主题上面词的分布),并且计算出每篇文章对应着哪些主题。常用的主题模型有pLSA(Probabilistic Latent Semantic Analysis)和LDA(Latent Dirichlet Allocation)。
pLSA
pLSA是用一个生成模型来建模文章的生成过程。假设有K个主题,M篇文章;对语料库中的任意文章d,假设该文章有N个词,则对于其中的每一个词,首先选择一个主题z,然后在当前主题的基础上生成一个词w。pLSA图模型如下所示。
生成主题z和词w的过程遵照一个确定的概率分布。设在文章d中选择主题z的概率为p(z|d),在选定主题的条件下生成词w的概率为p(w|z),则给定文章d,生成词w的概率可以写成:
在这里做一个简化,假设给定主题z的条件下,生成词w的概率是与特定的文章d无关,则公式可以简化为:
那么,整个语料库中的文本生成概率可以用似然函数表示为:
其中p(dm,wn)是在第m篇文章dm中, 出现单词wn的概率, 与上文中的p(w|d)的含义是相同的,只是换了一种符号表达;c(dm,wn)是在第m篇文章dm中,单词wn出现的次数;
于是, Log似然函数可以写成:
在上面的公式中,定义在文章上的主题分布p(zk|dm)和定义在主题上的词分布p(wn|zk)是待估计的参数。我们需要找到最优的参数,使得整个语料库的Log似然函数最大化。 由于参数中包含的zk是隐含变量(即无法直接观测到的变量),因此无法用最大似然估计直接求解, 可以利用**最大期望算法(EM)**来解决。
LDA
LDA可以看作是pLSA的贝叶斯版本,其文本生成过程与pLSA基本相同,不同的是为主题分布和词分布分别加了两个狄利克雷(Dirichlet)先验。为什么要加入狄利克雷先验呢?这就要从频率学派和贝叶斯学派的区别说起。pLSA采用的是频率派思想,将每篇文章对应的主题分布p(zk|dm)和每个主题对应的词分布p(wn|zk)看成确定的未知常数,并可以求解出来;而LDA采用的是贝叶斯学派的思想,认为待估计的参数(主题分布和词分布)不再是一个固定的常数,而是服从一定分布的随机变量。这个分布符合一定的先验概率分布(即狄利克雷分布),并且在观察到样本信息之后,可以对先验分布进行修正,从而得到后验分布。LDA之所以选择狄利克雷分布作为先验分布,是因为它为多项式分布的共轭先验概率分布,后验概率依然服从狄利克雷分布,这样做可以为计算带来便利。下图是LDA的图模型, 其中α, β分别为两个狄利克雷分布的超参数, 为人工设定。
语料库的生成过程为:对文本库中的每一篇文档di,采用以下操作:
- 从超参数为α的狄利克雷分布中抽样生成文档di的主题分布θi;
- 对文档di中的每一个词进行以下3个操作。
- 从代表主题的多项式分布θi中抽样生成它所对应的主题 z i j z_{ij} zij;
- 从超参数为β的狄利克雷分布中抽样生成主题 z i j z_{ij} zij对应的词分布 Ψ z i j \Psi_{z_{ij}} Ψzij;
- 从代表词的多项式分布 Ψ z i j \Psi_{z_{ij}} Ψzij中抽样生成词 w i j w_{ij} wij;
要求解出主题分布 θ i θ_{i} θi以及词分布 Ψ z i j \Psi_{z_{ij}} Ψzij的期望,可以用吉布斯采样(Gibbs Sampling)的方式实现。首先随机给定每个单词的主题,然后在其他变量固定的情况下,根据转移概率抽样生成每个单词的新主题。对于每个单词来说,转移概率可以理解为:给定文章中的所有单词以及除自身以外其他所有单词的主题,在此条件下该单词对应为各个新主题的概率。最后,经过反复迭代,可以根据收敛后的采样结果计算主题分布和词分布的期望。
问题:如何确定LDA模型中的主题个数?
在LDA中,主题的个数K是一个预先指定的超参数。对于模型超参数的选择,实践中的做法一般是将全部数据集分成训练集、验证集和测试集3部分,然后利用验证集对超参数进行选择。例如,在确定LDA的主题个数时,我们可以随机选取60%的文档组成训练集,另外20%的文档组成验证集,剩下20%的文档组成测试集。在训练时,尝试多组超参数的取值,并在验证集上检验哪一组超参数所对应的模型取得了最好的效果。最终,在验证集上效果最好的一组超参数和其对应的模型将被选定, 并在测试集上进行测试。
为了衡量LDA模型在验证集和测试集上的效果,需要寻找一个合适的评估指标。一个常用的评估指标是困惑度(perplexity)。在文档集合D上,模型的困惑度被定义为:
其中M为文档的总数,wd为文档d中单词所组成的词袋向量,p(wd)为模型所预测的文档d的生成概率,Nd为文档d中单词的总数。
一开始,随着主题个数的增多,模型在训练集和验证集的困惑度呈下降趋势,但是当主题数目足够大的时候,会出现过拟合,导致困惑度指标在训练集上
继续下降但在验证集上反而增长。这时,可以取验证集的困惑度极小值点所对应的主题个数作为超参数。在实践中,困惑度的极小值点可能出现在主题数目非常大的时候,然而实际应用并不能承受如此大的主题数目,这时就需要在实际应用中合理的主题数目范围内进行选择,比如选择合理范围内困惑度的下降明显变慢(拐点)的时候。
另外一种方法,在LDA基础之上融入分层狄利克雷过程(Hierarchical Dirichlet Process,HDP),构成一种非参数主题模型HDP-LDA。非参数主题模型的好处是不需要预先指定主题的个数,模型可以随着文档数目的变化而自动对主题个数进行调整;它的缺点是在LDA基础上融入HDP之后使得整个概率图模型更加复杂,训练速度也更加缓慢,因此在实际应用中还是经常采用第一种方法确定合适的主题数目。
问题3:无法表达语义信息问题
词向量
自然语言是一套用来表达含义的复杂系统。在这套系统中,词是表义的基本单元。顾名思义,词向量是用来表示词的向量,也可被认为是词的特征向量或表征。把词映射为实数域向量的技术也叫词嵌入(word embedding)。近年来,词嵌入已逐渐成为自然语言处理的基础知识。词向量(word embedding)又称词嵌入,是自然语言处理NLP中一组语言建模和特征学习的统称,将词汇表的字或词从每个一维的高维空间映射到较低维连续空间,以便计算机进行处理及建模。
Hilton 1986年提出Distributed Representation,通过矩阵乘法或神经网络降维,将每个词映射为低维的密集词向量dense vector,把语义分散存储到向量的各个维度中。Bengio et al. 在2003年首先提出了词向量的概念,提出NNLM模型(Neural network language model,NNLM),当时是将其与语言模型的参数一并训练得到的。神经网络将词汇表中的词作为输入,输出一个低维的向量表示,然后使用BP优化参数。生成词向量的神经网络模型分为两种:一种的目的是训练可以表示语义关系的词向量,能被用于后续任务中,如word2vec;另一种是将词向量作为副产品产生,根据特定任务需要训练得到词向量,如fastText。
注意:虽然词向量是神经网络的输入,但并非第一层输入。第一层是词的one-hot编码,乘以一个权重矩阵后得到才是词向量化表示,而权重在模型训练阶段是可以更新的。从记忆的角度来看,神经网络的连接权值更新是长时记忆,因此词向量的学习可以认为是一种长时学习。
NNLM和RNNLM
nnlm采用神经网络来对语言进行建模,nnlm一个很大的优点就是将历史映射到一个低维的空间而并不像普通n-gram,这就降低了模型的参数,并且使相似的历史进行聚类,映射后的低维向量也被称为词向量,并且从结果来看nnlm的效果非常不错,但仍然有缺点:
- 隐层到输出层的计算复杂度大,参数过多;
- nnlm是一类典型的前馈神经网络,它的历史长度预先必须设置并固定,与循环神经网络(rnnlm)来比不能捕获更长的历史信息。
rnnlm与nnlm主要的不同就在对历史的捕捉上面,nnlm的历史长度也只有数个词,而rnnlm的历史是前面所有的词,这样使得rnnlm可以捕获更长的历史信息。
NNLM
2003年Bengio提出了神经网络语言模型(NNLM),论文题为《A Neural Probabilistic Language Model》,架构图如下:
观察上图,假设有一组词序列:
w
1
,
w
2
,
⋯
,
w
t
w_{1}, w_{2}, \cdots,w_{t}
w1,w2,⋯,wt,其中
w
i
∈
V
w_{i}\in V
wi∈V,
V
V
V是所有单词的集合,即词典。我们的输入是一个词序列,而我们的输出是一个概率值,表示根据context预测出下一个词是
i
i
i的概率。用数学来表示,我们最终是要训练一个模型:
其中有:
- w t w_{t} wt表示这个词序列中的第t个单词, w t − n + 1 w_{t-n+1} wt−n+1表示输入长度为n的词序列中的第一个单词;
- w 1 t − 1 w^{t-1}_{1} w1t−1表示第1个单词到第t-1个单词组成的子序列;
NNLM本质就是:直接从语言模型出发,将模型最优化的过程转换为求词向量表示的过程。
最优的方向就是目标函数:用“我爱自然语言处理“为例,窗长度为n-1:
这个是n=3的通俗表达。
这个过程与上面提到的其实是一样的, 其实就是求: p ( w n ∣ w 1 , ⋯ , w n − 1 ) p(w_{n}|w_{1},\cdots,w_{n-1}) p(wn∣w1,⋯,wn−1)。
该模型满足两个约束条件,其中|V|表示词表大小:
他的使用场景是:假设有一分文本,可以通过前N−1个词预测第N个词?
模型的前向传播
该模型结构分为三层,分别是输入层,一层隐层,输出层。
输入层:从One-hot到distribution representation
对于输入层,先将输入词序列
w
1
,
w
2
,
⋯
,
w
n
w_{1},w_{2},\cdots,w_{n}
w1,w2,⋯,wn映射到词表中,如词
w
i
w_{i}
wi是词表中第i个元素,编码为one-hot embedding;然后再将这个元素映射到实向量C(i)中,其中
C
(
i
)
∈
R
m
C(i)\in \R^m
C(i)∈Rm,表示的是词表中第i个词的distributed representation。C实际上就是一个|V|xm的自由参数矩阵,|V|表示词表的大小,m表示每个词的维度。
其中,词的映射过程用图表示如下:
总结一下这一层做的事:主要是把一句话用one-hot向量表示,通过一个权重矩阵,得到表示这一句话的词向量。
隐层:
这一部分主要做的就是将上一层的输出作为输入,进行全连接,然后一般会有个tanh,来处理这些数据。
输出层:
计算条件概率分布,通过一个函数 g(g是前馈或递归神经网络)将输入的词向量序列
(
C
(
w
t
−
n
+
1
)
,
⋯
,
C
(
w
t
−
1
)
)
(C(w_{t-n+1}),\cdots, C(w_{t-1}))
(C(wt−n+1),⋯,C(wt−1))转化为一个概率分布
y
∈
R
∣
V
∣
y\in R^{|V|}
y∈R∣V∣,
y
y
y中第i位表示词序列中第t个词是Vi的概率,即:
输出层同时受到隐层输出 a 和 输入层输出 x 的影响,公式如下:
x是输入
(
C
(
w
t
−
n
+
1
)
,
⋯
,
C
(
w
t
−
1
)
)
(C(w_{t-n+1}),\cdots, C(w_{t-1}))
(C(wt−n+1),⋯,C(wt−1)),
P
(
w
t
∣
w
t
−
1
,
⋯
,
w
t
−
n
+
1
)
P(w_{t}|w_{t-1},\cdots, w_{t-n+1})
P(wt∣wt−1,⋯,wt−n+1)是输出。需要更新的参数包括:
θ
=
(
b
,
d
,
W
,
U
,
H
,
C
)
\theta=(b,d,W,U,H,C)
θ=(b,d,W,U,H,C)。
W
∈
R
∣
V
∣
×
(
n
−
1
)
m
W\in R^{|V|\times(n-1)m}
W∈R∣V∣×(n−1)m是可选参数,如果输入层与输出层没有直接相连(如图中绿色虚线所示),则可令
W
=
0
W=0
W=0。
H
∈
R
h
×
(
n
−
1
)
m
H\in R^{h\times(n-1)m}
H∈Rh×(n−1)m是输入层到隐含层的权重矩阵,其中h表示隐含层神经元的数目。
U
∈
R
∣
V
∣
×
h
U\in R^{|V|\times h}
U∈R∣V∣×h\是隐含层到输出层的权重矩阵,
d
∈
R
h
d\in R^h
d∈Rh和
b
∈
R
∣
V
∣
b\in R^{|V|}
b∈R∣V∣分别是隐含层和输出层的偏置参数。
模型的训练
-
损失函数为:
其中θ为模型的所有参数,R(θ)为正则化项。 -
使用梯度下降算法更新参数的过程如下:
其中, ϵ \epsilon ϵ为步长。
RNNLM
RNNLM相对于Bengio提出的Feedforward NNLM的特点在于历史记忆的表达:在Feedforward NNLM中,历史仍然像n元模型一样是之前出现的若干单词,而在RNNLM中,历史的从数据集的训练中学习得到的。RNN的隐藏层不仅表现了前 n−1 个单词的历史,而是之前所有的历史,因此理论上讲RNNLM可以克服长上下文单词之间的关联。
其中,输入值w的维数与词汇集中词汇的数量相同 dim(w)=|V|大约在10K-200K量级,非常稀疏;隐层的规模要小很多,大约有50-1000个神经元左右;U、V、W三个矩阵的形状按照线性变化而定。如果不使用W矩阵,也就是将RNN展开,那么神经网络的规模将会相当的大。
RNN训练中的计算:
其中对输出层采用softmax函数进行激活,以确保输出的概率满足归一化与大于零的条件:
模型训练采用随机梯度算法(SGD)。最初用比较小的随机数给 U 、V、W三个矩阵赋值,Mikolov提到在实验中使用平均数0、方差0.1的正态分布进行赋值。每训练一个单词,将U、V、W三个矩阵的参数更新一次。
误差反向传播
误差估计方面采用交叉熵估计输出层中的误差向量
e
o
(
t
)
e_{o}(t)
eo(t)的梯度:
其中,
l
l
l是正确预测的样本序数。由于采用one-hot表示,所以在做向量内积时,只需要将最终的正确分量累加即可。
因为计算输出层概率的时候采用了softmax激活函数,所以交叉熵中的对数部分会使softmax的上下指数分离出来。如果词向量 w(t)预测得很好,那么它的结果 w(t+1)与真正的目标向量 d(t),这两个one-hot编码的向量将会吻合。并且,因为one-hot编码与交叉熵分离softmax的分子分母,所以误差梯度有:
对于上式,如果固定变量 s(t),则可以计算 V矩阵的更新值:
可见,
s
(
t
)
⋅
e
o
(
t
)
T
s(t)\cdot e_{o}(t)^T
s(t)⋅eo(t)T实际上是数值 CE(y)∈R对矩阵变量V的Jacobian矩阵:
j遍历了隐藏层,k遍历了输出层,
s
j
(
t
)
s_{j}(t)
sj(t)是隐藏层中的第j个神经元的输出,
e
o
k
(
t
)
e_{ok}(t)
eok(t)是输出层第 k个神经元的误差梯度。学习率Learning Rate 是标量 α∈R,也可以看做一个微小增量,通过Jacobian矩阵变换到正确的梯度增量方向。
接着与上一个步骤刚好相反,这次固定矩阵V,计算交叉熵对隐藏层的Jacobian矩阵:
计算隐藏层的误差梯度
e
h
(
t
)
e_{h}(t)
eh(t)
总之,隐藏层的梯度:
再更新U矩阵的值,推导与V的情况一样,只是固定的变量多了一些,有 w(t) , W , s(t−1):
矩阵W也一样:
实用的训练建议
训练有多种选择。在处理训练的例子时,可以每次处理都把神经网络展开,但这样会带来很大的复杂度 T×|W|,其中T是展开的次数,也就是回溯的次数,|W|是每次训练的词汇数量。如果采用mini-batch的方式来处理,比如说每训练10-20个例子之后更新一次参数值,那么计算复杂度就可以消除T的影响了。
使用双精度的实数以及一些正则化方法(regularization)会有助于提高数值的稳定性,以免因为舍入等等发生一些莫名其妙的计算问题。然而Mikolov说,在他们的实验中没有实用正则化,也没有实用L2惩罚。。。还有RNN的训练在极少数的某些情况下在BPTT训练中会指数爆炸,Mikolov在实验中采用的方法是限制误差梯度的最大值 |e|∈(−15,15),这样的限制对于巨大的数据集而言是健康的。
Word2vec
word2vec是Google 在2013 年年中开源的一款将词表征为实数值向量的高效工具,采用的模型有CBOW(Continuous Bag-Of-Words,即连续的词袋模型)和Skip-Gram两种。word2vec 一般被外界认为是一个 Deep Learning(深度学习)的模型,究其原因,可能和word2vec的作者Tomas Mikolov 的Deep Learning背景以及word2vec是一种神经网络模型相关,但我们谨慎认为该模型层次较浅,严格来说还不能算是深层模型。当然如果word2vec上层再套一层与具体应用相关的输出层,比如Softmax,此时更像是一个深层模型。word2vec 通过训练,可以把对文本内容的处理简化为 K 维向量空间中的向量运算,而向量空间上的相似度可以用来表示文本语义上的相似度。
word2vec和NNLM对比有什么区别?(word2vec vs NNLM)
1)其本质都可以看作是语言模型;
2)词向量只不过NNLM一个产物,word2vec虽然其本质也是语言模型,但是其专注于词向量本身,因此做了许多优化来提高计算效率:
与NNLM相比,词向量直接sum,不再拼接,并舍弃隐层;word2vec的主要目的是生成词向量而不是语言模型,在CBOW中,投射层将词向量直接相加而不是拼接起来,并舍弃了隐层,这些牺牲都是为了减少计算量,使训练加速。
Skip-gram
跳字模型假设基于某个词来生成它在文本序列周围的词。举个例子,假设文本序列是“the”“man”“loves”“his”“son”。以“loves”作为中心词,设背景窗口大小为2。如下图所示,跳字模型所关心的是,给定中心词“loves”,生成与它距离不超过2个词的背景词“the”“man”“his”“son”的条件概率,即:
P(the",
man",his",
son"∣``loves")
假设给定中心词的情况下,背景词的生成是相互独立的,那么上式可以改写成:
P(the"∣
loves")⋅P(man"∣
loves")⋅P(his"∣
loves")⋅P(son"∣
loves").
在跳字模型中,每个词根据作用的区别(背景词还是中心词)被表示成两个d维向量,用来计算条件概率。假设这个词在词典中索引为i,当它为中心词时向量表示为
v
i
∈
R
d
v_{i}\in \R^d
vi∈Rd,而为背景词时向量表示为
u
i
∈
R
d
u_{i}\in \R^d
ui∈Rd。设中心词w_{c}在词典中索引为c,背景词w_{o}在词典中索引为o,给定中心词生成背景词的条件概率可以通过对向量内积做softmax运算而得到:
其中,词典索引集
V
=
{
0
,
1
,
⋯
,
∣
V
∣
−
1
}
V=\{0,1,\cdots,|V|-1\}
V={0,1,⋯,∣V∣−1}。假设给定一个长度为T的文本序列,设时间步t的词为
w
(
t
)
w^{(t)}
w(t)。假设给定中心词的情况下背景词的生成相互独立,当背景窗口大小为m时,跳字模型的似然函数即给定任一中心词生成所有背景词的概率(最大化目标函数):
这里小于1和大于T的时间步可以忽略。
跳字模型的参数是每个词所对应的中心词向量和背景词向量。训练中我们通过最大化似然函数来学习模型参数,即最大似然估计。这等价于最小化以下损失函数:
如果使用随机梯度下降,那么在每一次迭代里我们随机采样一个较短的子序列来计算有关该子序列的损失,然后计算梯度来更新模型参数。梯度计算的关键是条件概率的对数有关中心词向量和背景词向量的梯度。根据定义,首先看到:
通过微分,我们可以得到上式中
v
c
v_{c}
vc的梯度:
它的计算需要词典中所有词以 w c w_{c} wc为中心词的条件概率。有关其他词向量的梯度同理可得。
训练结束后,对于词典中的任一索引为i的词,我们均得到该词作为中心词和背景词的两组词向量 v i v_{i} vi和 u i u_{i} ui。在自然语言处理应用中,一般使用跳字模型的中心词向量作为词的表征向量。
CBOW
连续词袋模型与跳字模型类似。与跳字模型最大的不同在于,连续词袋模型假设基于某中心词在文本序列前后的背景词来生成该中心词。在同样的文本序列“the”“man”“loves”“his”“son”里,以“loves”作为中心词,且背景窗口大小为2时,连续词袋模型关心的是,给定背景词“the”“man”“his”“son”生成中心词“loves”的条件概率,也就是:
因为连续词袋模型的背景词有多个,我们将这些背景词向量取平均,然后使用和跳字模型一样的方法来计算条件概率。设
v
i
∈
R
d
v_{i}\in \R^{d}
vi∈Rd和
u
i
∈
R
d
u_{i}\in \R^{d}
ui∈Rd分别表示词典中索引为i的词作为背景词和中心词的向量(注意符号的含义与跳字模型中的相反)。设中心词
w
c
w_{c}
wc在词典中索引为c,背景词
w
o
1
,
w
o
2
,
⋯
,
w
o
2
m
w_{o_{1}},w_{o_{2}},\cdots, w_{o_{2m}}
wo1,wo2,⋯,wo2m在词典中索引为
o
1
,
⋯
,
o
2
m
o_{1},\cdots, o_{2m}
o1,⋯,o2m,那么给定背景词生成中心词的条件概率:
为了让符号更加简单,记
W
o
=
{
w
o
1
,
⋯
,
w
o
2
m
}
W_{o}=\{ w_{o_{1}}, \cdots, w_{o_{2m}}\}
Wo={wo1,⋯,wo2m}且
v
o
‾
=
(
v
o
1
+
⋯
+
v
o
2
m
)
/
(
2
m
)
\overline{v_{o}}=(v_{o_{1}}+\cdots+v_{o_{2m}})/(2m)
vo=(vo1+⋯+vo2m)/(2m),那么上式可以简写成:
训练连续词袋模型同训练跳字模型基本一致。连续词袋模型的最大似然估计等价于最小化损失函数:
注意到:
通过微分,我们可以计算出上式中条件概率的对数有关任一背景词向量
v
o
i
(
i
=
1
,
⋯
,
2
m
)
v_{o_{i}}(i=1,\cdots, 2m)
voi(i=1,⋯,2m)的梯度:
有关其他词向量的梯度同理可得。同跳字模型不一样的一点在于,我们一般使用连续词袋模型的背景词向量作为词的表征向量。
近似训练
考虑到sofmax归一化需要遍历整个词汇表,对于含几十万或上百万词的较大词典,每次的梯度计算开销可能过大。为了降低该计算复杂度,采用hierarchical softmax 和negative sampling进行优化,hierarchical softmax 实质上生成一颗带权路径最小的哈夫曼树,让高频词搜索路劲变小;negative sampling更为直接,实质上对每一个样本中每一个词都进行负例采样;由于跳字模型和连续词袋模型类似,下面仅以跳字模型为例介绍这两种方法
Negative Sampling
负采样修改了原来的目标函数。给定中心词w_{c}的一个背景窗口,我们把背景词w_{o}出现在该背景窗口看作一个事件,并将该事件的概率计算为:
其中的σ函数与sigmoid激活函数的定义相同:
先考虑最大化文本序列中所有该事件的联合概率来训练词向量。具体来说,给定一个长度为T的文本序列,设时间步t的词为 w t w^{t} wt且背景窗口大小为m,考虑最大化联合概率:
然而,以上模型中包含的事件仅考虑了正类样本。这导致当所有词向量相等且值为无穷大时,以上的联合概率才被最大化为1。很明显,这样的词向量毫无意义。负采样通过采样并添加负类样本使目标函数更有意义。设背景词
w
o
w_{o}
wo出现在中心词
w
c
w_{c}
wc的一个背景窗口为事件P,根据分布P(w)采样K个未出现在该背景窗口中的词,即噪声词。设噪声词
w
k
w_{k}
wk(k=1,…,K)不出现在中心词
w
c
w_{c}
wc的该背景窗口为事件
N
k
N_{k}
Nk。假设同时含有正类样本和负类样本的事件P,
N
1
,
⋯
,
N
k
N_{1},\cdots,N_{k}
N1,⋯,Nk相互独立,负采样将以上需要最大化的仅考虑正类样本的联合概率改写为:
其中条件概率被近似表示为:
设文本序列中时间步t的词w^{(t)}在词典中的索引为i_{t},噪声词w_{k}在词典中的索引为h_{k}。有关以上条件概率的对数损失为:
现在,训练中每一步的梯度计算开销不再与词典大小相关,而与K线性相关。当K取较小的常数时,负采样在每一步的梯度计算开销较小。
Hierarchical Softmax
层次softmax是另一种近似训练法。它使用了二叉树这一数据结构,树的每个叶结点代表词典V中的每个词。假设L(w)为从二叉树的根结点到词w的叶结点的路径(包括根结点和叶结点)上的结点数。设n(w,j)为该路径上第j个结点,并设该结点的背景词向量为u_{n(w,j)}。以下图为例,L(w3)=4。层次softmax将跳字模型中的条件概率近似表示为:
其中,leftChild(n)是结点n的左子结点:如果判断x为真,[[x]]=1;反之[[x]]=−1。计算上图中给定词w_{c}生成词w_{3}的条件概率。我们需要将w_{c}的词向量v_{c}和根结点到w_{3}路径上的非叶结点向量一一求内积。由于在二叉树中由根结点到叶结点w_{3}的路径上需要向左、向右再向左地遍历(图中加粗的路径),得到:
由于σ(x)+σ(−x)=1,给定中心词w_{c}生成词典V中任一词的条件概率之和为1这一条件也将满足:
此外,由于L(wo)−1的数量级为O(log2|V|),当词典V很大时,层次softmax在训练中每一步的梯度计算开销相较未使用近似训练时大幅降低。
小结
- 负采样通过考虑同时含有正类样本和负类样本的相互独立事件来构造损失函数。其训练中每一步的梯度计算开销与采样的噪声词的个数线性相关。
- 层次softmax使用了二叉树,并根据根结点到叶结点的路径来构造损失函数。其训练中每一步的梯度计算开销与词典大小的对数相关。
GloVe和FastText介绍参见《动手学深度学习》部分
GloVe(Global Vector)
2014年Standford
glove和word2vec、 LSA对比有什么区别?(word2vec vs glove vs LSA)
1)glove vs LSA
- LSA(Latent Semantic Analysis)可以基于co-occurance matrix构建词向量,实质上是基于全局语料采用SVD进行矩阵分解,然而SVD计算复杂度高;
- glove可看作是对LSA一种优化的高效矩阵分解算法,采用Adagrad对最小平方损失进行优化;
2)word2vec vs glove
- word2vec是局部语料库训练的,其特征提取是基于滑窗的;而glove的滑窗是为了构建co-occurance matrix,是基于全局语料的,可见glove需要事先统计共现概率;因此,word2vec可以进行在线学习,glove则需要统计固定语料信息。
- word2vec是无监督学习,同样由于不需要人工标注;glove通常被认为是无监督学习,但实际上glove还是有label的,即共现次数。
- word2vec损失函数实质上是带权重的交叉熵,权重固定;glove的损失函数是最小平方损失函数,权重可以做映射变换。
- 总体来看,glove可以被看作是更换了目标函数和权重函数的全局word2vec。
FastText
2017年Facebook
word2vec和FastText的区别:
1)都可以无监督学习词向量, fastText训练词向量时会考虑subword;
2) fastText还可以进行有监督学习进行文本分类,其主要特点:
- 结构与CBOW类似,但学习目标是人工标注的分类结果;
- 采用hierarchical softmax对输出的分类标签建立哈夫曼树,样本中标签多的类别被分配短的搜寻路径;
- 引入N-gram,考虑词序特征;
- 引入subword来处理长词,处理未登陆词问题;
小结
Word2Vec等方法的局限性对于帮助我们了解NLP研究的未来趋势也很重要。他们为所有未来的研究设定了一个基准。那么,他们在哪些方面做得不够呢?
- 每个词只能嵌入一个词,即每个词只能存储一个向量。所以" bank “只有一个意思"我把钱存进了银行"和"河岸上有一条漂亮的长凳”;
- 它们很难在大型数据集上训练;
- 你无法调整它们。为了使他们适合你的领域,你需要从零开始训练他们;
- 它们不是真正的深度神经网络。他们被训练在一个有一个隐藏层的神经网络上;
HMM(暂略)
(看第一期的资料)
CRF(暂略)
基于深度学习的模型
RNN、LSTM、GRU
链接(来源题库):
如何从RNN起步,一步一步通俗理解LSTM https://www.julyedu.com/question/big/kp_id/26/ques_id/1851
LSTM的结构推导为什么比RNN好 https://www.julyedu.com/question/big/kp_id/26/ques_id/1003
梯度爆炸和弥散问题 https://www.julyedu.com/question/big/kp_id/26/ques_id/1049
LSTM中的激活函数问题 https://www.julyedu.com/question/big/kp_id/26/ques_id/1048
GRU是什么?GRU对LSTM做了哪些改动?https://www.julyedu.com/question/big/kp_id/26/ques_id/2114
RNN模型的几种经典结构 https://www.julyedu.com/question/big/kp_id/26/ques_id/1717
预训练模型
seq2seq(encoder-decoder)
编码器-解码器(encoder-decoder)和seq2seq提出的论文:
[1] Cho, K., Van Merriënboer, B., Gulcehre, C., Bahdanau, D., Bougares, F., Schwenk, H., & Bengio, Y. (2014). Learning phrase representations using RNN encoder-decoder for statistical machine translation. arXiv preprint arXiv:1406.1078.[用的是GRU]
[2] Sutskever, I., Vinyals, O., & Le, Q. V. (2014). Sequence to sequence learning with neural networks. In Advances in neural information processing systems (pp. 3104-3112).[Google用的是:LSTM]
语言模型:输入不定长序列,输出也定长序列;
解决问题:输入不定长序列,输出是也不定长序列,如翻译系统;
当输入和输出都是不定长序列时,我们可以使用编码器—解码器(encoder-decoder)[1] 或者seq2seq模型 [2]。这两个模型本质上都用到了两个循环神经网络,分别叫做编码器和解码器。编码器用来分析输入序列,解码器用来生成输出序列。
下图描述了使用编码器—解码器将上述英语句子翻译成法语句子的一种方法。在训练数据集中,我们可以在每个句子后附上特殊符号“”(end of sequence)以表示序列的终止。编码器每个时间步的输入依次为英语句子中的单词、标点和特殊符号“”。图中使用了编码器在最终时间步的隐藏状态作为输入句子的表征或编码信息。解码器在各个时间步中使用输入句子的编码信息和上个时间步的输出以及隐藏状态作为输入。 我们希望解码器在各个时间步能正确依次输出翻译后的法语单词、标点和特殊符号“”。 需要注意的是,解码器在最初时间步的输入用到了一个表示序列开始的特殊符号“”(beginning of sequence)。
编码器
编码器的作用是把一个不定长的输入序列变换成一个定长的背景变量C(context),并在该背景变量中编码输入序列信息。常用的编码器是循环神经网络。让我们考虑批量大小为1的时序数据样本。假设输入序列是x1,…,xT,例如xi是输入句子中的第i个词。在时间步t,循环神经网络将输入xt的特征向量xt和上个时间步的隐藏状态h****t−1变换为当前时间步的隐藏状态ht。我们可以用函数 f 表达循环神经网络隐藏层的变换:
接下来,编码器通过自定义函数q将各个时间步的隐藏状态变换为背景变量:
例如,当选择q(h1,…,hT)=hT时,背景变量是输入序列最终时间步的隐藏状态hT。当然,q可以使RNN模型(包含RNN、BI-RNN、LSTM、GRU等);
Bi-RNN
以上描述的编码器是一个单向的循环神经网络,每个时间步的隐藏状态只取决于该时间步及之前的输入子序列。我们也可以使用双向循环神经网络构造编码器。在这种情况下,编码器每个时间步的隐藏状态同时取决于该时间步之前和之后的子序列(包括当前时间步的输入),并编码了整个序列的信息。
解码器
编码器输出的背景变量C编码了整个输入序列x1,…,xT的信息。给定训练样本中的输出序列y1,y2,…,yT′,对每个时间步t′('符号与输入序列或编码器的时间步t有区别),解码器输出yt′的条件概率将基于之前的输出序列y1,…,y(t′−1)和背景变量C,即P(yt′∣y1,…,y(t′−1),C)。
为此,可以使用另一个循环神经网络作为解码器。 在输出序列的时间步t′,解码器将上一时间步的输出y(t′−1)以及背景变量C作为输入,并将它们与上一时间步的隐藏状态***s***(t′−1)变换为当前时间步的隐藏状态***s***t′。因此,可以用函数g表达解码器隐藏层的变换:
有了解码器的隐藏状态后,我们可以使用自定义的输出层和softmax运算来计算P(yt′∣y1,…,y(t′−1),C),例如,基于当前时间步的解码器隐藏状态**st′、上一时间步的输出y(t′−1)以及背景变量C来计算当前时间步输出yt′的概率分布。
训练模型
根据最大似然估计,我们可以最大化输出序列基于输入序列的条件概率:
并得到该输出序列的损失:
在模型训练中,所有输出序列损失的均值通常作为需要最小化的损失函数。在encoder-decoder图中所描述的模型预测中,我们需要将解码器在上一个时间步的输出作为当前时间步的输入。与此不同,在训练中我们也可以将标签序列(训练集的真实输出序列)在上一个时间步的标签作为解码器在当前时间步的输入。这叫作强制教学(teacher forcing)。
小结
- 编码器-解码器(seq2seq)可以输入并输出不定长的序列。
- 编码器—解码器使用了两个循环神经网络。
- 在编码器—解码器的训练中,可以采用强制教学。
attention
在encoder-decoder模型中,decoder在各个时间步依赖相同的背景变量来获取输入序列信息。当encoder为循环神经网络时,背景变量来自它最终时间步的隐藏状态。
现在,再次思考那一节提到的翻译例子:输入为英语序列“They”“are”“watching”“.”,输出为法语序列“Ils”“regardent”“.”。不难想到,解码器在生成输出序列中的每一个词时可能只需利用输入序列某一部分的信息。例如,在输出序列的时间步1,解码器可以主要依赖“They”“are”的信息来生成“Ils”,在时间步2则主要使用来自“watching”的编码信息生成“regardent”,最后在时间步3则直接映射句号“.”。这看上去就像是在解码器的每一时间步对输入序列中不同时间步的表征或编码信息分配不同的注意力一样。这也是注意力机制的由来。
仍然以循环神经网络为例,注意力机制通过对编码器所有时间步的隐藏状态做加权平均来得到背景变量。解码器在每一时间步调整这些权重,即注意力权重,从而能够在不同时间步分别关注输入序列中的不同部分并编码进相应时间步的背景变量。本节我们将讨论注意力机制是怎么工作的。
在seq2seq里我们区分了输入序列或编码器的索引t 与输出序列或解码器的索引t′。该节中,解码器在时间步t′的隐藏状态st′=g(y(t′−1),c,s(t′−1)),其中y(t′−1)是上一时间步t′−1的输出y(t′−1)的表征,且任一时间步t′使用相同的背景变量C。但在注意力机制中,解码器的每一时间步将使用可变的背景变量。记Ct′是解码器在时间步t′的背景变量,那么解码器在该时间步的隐藏状态可以改写为:
这里的关键是如何计算背景变量Ct′和如何利用它来更新隐藏状态***s***t′。下面将分别描述这两个关键点。
计算背景向量
先描述第一个关键点,即计算背景变量。下图描绘了注意力机制如何为解码器在时间步2计算背景变量。首先,函数a根据解码器在时间步1的隐藏状态和编码器在各个时间步的隐藏状态计算softmax运算的输入。softmax运算输出概率分布并对编码器各个时间步的隐藏状态做加权平均,从而得到背景变量。
具体来说,令编码器在时间步t的隐藏状态为ht,且总时间步数为T。那么解码器在时间步t’的背景变量为所有编码器隐藏状态的加权平均:
其中给定t′时,权重α(t′t)在t=1,…,T的值是一个概率分布。为了得到概率分布,使用softmax运算:
现在,需要定义如何计算上式中softmax运算的输入
e
t
′
t
e_{t^{'}t}
et′t。由于
e
t
′
t
e_{t^{'}t}
et′t同时取决于解码器的时间步t′和编码器的时间步t,我们不妨以解码器在时间步t′−1的隐藏状态 s(t′−1)与编码器在时间步t的隐藏状态
h
t
\boldsymbol{h}_{t}
ht,并通过函数
a
a
a计算
e
t
′
t
e_{t^{'}t}
et′t:
e
t
′
t
=
a
(
s
t
′
−
1
,
h
t
)
e_{t^{'}t} =a(\boldsymbol{s}_{t^{'}-1}, \boldsymbol{h}_{t})
et′t=a(st′−1,ht)
这里函数a有多种选择,如果两个输入向量长度相同,一个简单的选择是计算它们的内积
a
(
s
,
h
)
=
s
T
h
a(\boldsymbol{s}, \boldsymbol{h})=\boldsymbol{s}^{T}\boldsymbol{h}
a(s,h)=sTh。而最早提出注意力机制的论文则将输入连结后通过含单隐藏层的多层感知机变换:
其中,v、Ws、Wh都是可以学习的模型参数。
矢量化计算
还可以对注意力机制采用更高效的矢量化计算。广义上,注意力机制的输入包括查询项以及一一对应的键项和值项,其中值项是需要加权平均的一组项。在加权平均中,值项的权重来自查询项以及与该值项对应的键项的计算。
在上面的例子中,查询项为解码器的隐藏状态,键项和值项均为编码器的隐藏状态。 让我们考虑一个常见的简单情形,即编码器和解码器的隐藏单元个数均为h,且函数 a ( s , h ) = s T h a(\boldsymbol{s}, \boldsymbol{h})=\boldsymbol{s}^{T}\boldsymbol{h} a(s,h)=sTh。假设我们希望根据解码器单个隐藏状态 s t ′ − 1 ∈ R h s_{t^{'}-1}\in \R^{h} st′−1∈Rh和编码器所有隐藏状态 h t ∈ R h , t = 1 , ⋯ , T \boldsymbol{h}_{t} \in \R^{h}, t=1,\cdots, T ht∈Rh,t=1,⋯,T来计算背景向量 c t ′ , c t ′ ∈ R h \boldsymbol{c}_{t^{'}}, \boldsymbol{c}_{t^{'}}\in \R^{h} ct′,ct′∈Rh。 我们可以将查询项矩阵 Q ∈ R 1 × h Q\in \R^{1\times h} Q∈R1×h设为 s t ′ − 1 T \boldsymbol{s}^{T}_{t^{'}-1} st′−1T,并令键项矩阵 K ∈ R T × h K \in \R^{T\times h} K∈RT×h和值项矩阵 V ∈ R T × h V\in \R^{T\times h} V∈RT×h相同且第t行均为 h t T \boldsymbol{h}^{T}_{t} htT。此时,我们只需要通过矢量化计算: s o f t m a x ( Q K T ) V softmax(\boldsymbol{Q}\boldsymbol{K}^{T})\boldsymbol{V} softmax(QKT)V即可算出转置后的背景向量 c t ′ T \boldsymbol{c}^{T}_{t^{'}} ct′T,当查询项矩阵 Q \boldsymbol{Q} Q的行数为n时,上式将得到n行的输出矩阵。输出矩阵与查询项矩阵在相同行上一一对应。
更新隐藏状态
现在描述第二个关键点,即更新隐藏状态。以门控循环单元为例,在解码器中我们可以对GRU中的门控循环单元的设计稍作修改,从而变换上一时间步t′−1的输出
y
t
′
−
1
y_{t^{'}−1}
yt′−1、隐藏状态
s
t
′
−
1
\boldsymbol{s}_{t^{'}-1}
st′−1和当前时间步t′的含注意力机制的背景变量
c
t
′
\boldsymbol{c}_{t^{'}}
ct′。解码器在时间步t′的隐藏状态为:
其中的重置门、更新门和候选隐藏状态分别为:
其中含下标的
W
和
b
\boldsymbol{W}和\boldsymbol{b}
W和b分别为门控循环单元的权重参数和偏差参数。
发展
本质上,注意力机制能够为表征中较有价值的部分分配较多的计算资源。这个有趣的想法自提出后得到了快速发展,特别是启发了依靠注意力机制来编码输入序列并解码出输出序列的变换器(Transformer)模型的设计。变换器抛弃了卷积神经网络和循环神经网络的架构。它在计算效率上比基于循环神经网络的编码器—解码器模型通常更具明显优势。含注意力机制的变换器的编码结构在后来的BERT预训练模型中得以应用并令后者大放异彩:微调后的模型在多达11项自然语言处理任务中取得了当时最先进的结果。不久后,同样是基于变换器设计的GPT-2模型于新收集的语料数据集预训练后,在7个未参与训练的语言模型数据集上均取得了当时最先进的结果。除了自然语言处理领域,注意力机制还被广泛用于图像分类、自动图像描述、唇语解读以及语音识别。
小结
- 可以在解码器的每个时间步使用不同的背景变量,并对输入序列中不同时间步编码的信息分配不同的注意力。
- 广义上,注意力机制的输入包括查询项以及一一对应的键项和值项。
- 注意力机制可以采用更为高效的矢量化计算。
Self-Attention和Transformer
Transformer是"seq2seq model with self-attention";
上图中左边是RNN(Bi-RNN)的经典结构,RNN比较适合用于sequence序列的场景,其输出b1-b4是基于输入序列a1-a4产生的,即在已知输入sequence的基础上生产新的sequence。但是RNN的问题在于:因为时序依赖的关系,RNN “Hard to parallel”。因此,有学者提出用CNN取代RNN,因为CNN “can parallel”,filter可以同时计算。如右图中的小三角形代表着输入信息的感受野,通过堆叠不同层的感受野,CNN也能够获得表征b1-b4序列的能力。但是CNN的问题就在于“感受野受限”,要想获得更高的层的感知能力,就需要进一步增加一层感受野(即卷积核),如右图中最上方的蓝色小三角形。CNN的处理sequence的缺点就是需要堆叠很多层的filter才能处理长时依赖的sequence,如果在第一层就想解决sequence的长时依赖问题,那么CNN显得无能为力。因此,就有了self-attention,self-attention的设计目的就是想要取代RNN所要处理的事情。【论文:Attention is all you need】
self-attention具有Bi-RNN的功能,即在完全感知输入sequence的情况下生产输出sequence。但是,神奇的是self-attention中的输出b1-b4能够被同时计算(即并行化)。因此,可以采用self-attention取代RNN。
Self-Attention
输入
x1,…,x4是sequence的输入,经过embedding矩阵W,形成文本表征a1,…,a4。
self-attention layer
每一个输入a1,…,a4都会分别乘以三个矩阵Wq、Wk、Wv,形成三个向量q、k和v。
拿每一个query q去对每个key k做attention
d是向量q和k的维数,除以根号d的原因是为了消除向量维数的不同给结果带来的影响。
接下来,用softmax函数来归一化,向量相似度。
对每一个输入向量中加入attention权重,形成输出sequence中的第一个元素b1。同理,计算b2,b3,…。基于此,self-attention能够赋予输入sequence中的每一个元素不同的权重,获得全局或者局部感知的能力。Self-attention的作用跟RNN一致,只不过self-attention能够被平行计算。
矩阵运算的形式表达
总结:
Multi-head Self-attention
好处:每个head可以关注不一样的信息,比如有些head只关注local信息,有些head只关注global信息。而head的具体数量也是一个有待调整的超参数。
input sequence存在的问题:
未考虑不同词之间的位置信息no position information in self-attention,比如:Jack hits Tom和Tom hits Jack。位置的不同代表着动作发出者与承受者的差别。针对这一问题,在原始的论文中加入了一个神奇的vector: e i e^{i} ei。Original paper:each position has a unique positional vector e i e^{i} ei(not learned from data)。Note that:dimension需要相同才行。
如上图所示的直接将
e
i
e^{i}
ei和
a
i
a^{i}
ai相加的话,会不会存在干扰原始信息的问题?
另外一种做法呢,就是在
x
i
x^{i}
xi上加上一个one-hot 向量
p
i
p^{i}
pi表征输入信息的位置信息,那么embedding矩阵将由两部分组成
W
I
W^{I}
WI、
W
p
W^{p}
Wp:
W
p
W^{p}
Wp的设定,可以通过手动设置,可视化的图如下所示:
source of image: http://jalammar.github.io/illustrated-transformer/
Seq2Seq with Attention
用self-attention layer替换encoder和decoder中的RNN部分。
https://ai.googleblog.com/2017/08/transformer-novel-neural-network.html
Transformer
左边:encoder
右边:decoder
一般Layer Norm会搭配RNN使用,而self-attention很像RNN,应该就是选择layer norm的原因。
结论: 只要能用Seq2Seq的场景都能用Transformer代替。
ELMo、BERT、GPT
Putting words into computers
在有ELMo之前,one-hot encoding、基于矩阵的、基于分类的、基于聚类的问题,以及word embedding(word2vec)。
一词多义问题(Contextualized word embedding)
ELMo
Embeddings from Language Model (ELMo):RNN-based language models(trained from lots of sentences)
word2vec是一种静态词向量模型,ELMo是一种动态的词向量模型;
ELMo通过学习大量的句子去预测下一个token的词会是什么?
contextual决定了embedding的不同
同时考虑了让前文和后文的信息
深度RNN,训练语言模型,那每一层都有embedding(如h1,h2),该选择哪一个embedding呢?EMLo的原始论文中的解释是 “全都要”。
α
1
\alpha_{1}
α1和
α
2
\alpha_{2}
α2是要根据处理任务的不同,将其视为整体参数的一部分学习出来;
BERT
BERT(Bidirectional Encoder Representations from Transformers)是Transformer的encoder;Learned from a large amount of text without annotation;
BERT:给定一个进去,吐出来一个embedding,它的内部网络结构跟transformer的Encoder部分是一样的。
如何训练BERT呢?
- Approach 1: Masked LM (15%的词汇MASK,由BERT去填空)
目标:如果两个词汇填在同一个地方没有违和感,那就表明语义上相似。那它们就有类似的embedding。
- Approach 2: Next sentence prediction
给BERT两个句子,让BERT去预测两个句子是接在一起的,还是不是接在一起的。在这个方法里面需要两个特殊的token:[SEP]和[CLS]。
[SEP]:the boundary of two sentences
[CLS]:the position that outputs **classification **results(做分类),接二分类;
其中的Linear Binary Classifier和BERT架构是一起被训练的。
Approach1和Approach2同时训练。
BERT怎么用呢?
ERNIE
认为BERT的Mask的时候一次遮盖一个词比较合理,而非一个字。
GPT
Generative Pre_Training(GPT):是Transformer的decoder部分;
由于GPT2的模型及其巨大,因此可以做到在无需训练资料的情况下,完成NLP任务,可以做到Zero-Shot Learning(没有人教着去完成某个任务,但是模型在训练过程中,学会了该任务);
GPT2根据输入提供的一些信息,自动完成文章的写作任务:
GPT2 Demo的网站:https://talktotransformer.com
RoBERTa(暂略)
XLNet(暂略)
ALBERT
T5
ELECTRA
Future
2019年深度学习、NLP最新十大发展趋势(来源:新智元)
- Knowledge Distillation
- Model compression
- Application
文本表示有哪些方法
- 基于one-hot、tf-idf、text-rank等的bag-of-words词袋模型;
- 主题模型:LSA(SVD)、pLSA、LDA;
- 基于词向量的固定表征:Word2vec、FastText、GloVe
- 基于词向量的动态表征:ELMo、BERT、GPT、XLNet
参考资料
- https://blog.csdn.net/qq_41664845/article/details/82869596
- https://blog.csdn.net/asialee_bird/article/details/81486700
- https://blog.csdn.net/leadai/article/details/81230557
- 词向量,LDA,word2vec三者的关系是什么? https://www.zhihu.com/question/40309730/answer/86453469
- word2vec 相比之前的 Word Embedding 方法好在什么地方? https://www.zhihu.com/question/53011711
- Deep Learning in NLP (一)词向量和语言模型 http://licstar.net/archives/328
- 搞懂 NLP 中的词向量,看这一篇就足够 https://www.infoq.cn/article/PFvZxgGDm27453BbS24W
- 词向量总结笔记(简洁版)http://www.shuang0420.com/2016/06/21/%E8%AF%8D%E5%90%91%E9%87%8F%E6%80%BB%E7%BB%93%E7%AC%94%E8%AE%B0%EF%BC%88%E7%AE%80%E6%B4%81%E7%89%88%EF%BC%89/
- 基于神经网络的词和文档语义向量表示方法研究(来斯惟)
- [NLP] 秒懂词向量Word2vec的本质 https://zhuanlan.zhihu.com/p/26306795
- 理解GloVe模型 https://blog.csdn.net/coderTC/article/details/73864097
- Mikolov RNNLM模型 http://yangminz.coding.me/blog/post/MinkolovRNNLM/MinkolovRNNLM_thesis.html
- Word2Vec-知其然知其所以然 https://www.zybuluo.com/Dounm/note/591752
- Aston Zhang, 李沐,动手学深度学习
- 李宏毅,深度学习2019