【数据竞赛】“达观杯”文本智能处理挑战赛3

一、语言模型

在统计自然语言处理中,语言模型指的是计算一个句子的概率模型。

传统的语言模型

  • 词的表示是原始的、面向字符串的。
  • 向量角度:更高维、更稀疏的向量。若词汇表大小为 NNN,每个字符串形式的词语字典序为 iii,则其被表示为一个 NNN 维向量,该向量的第 iii 维为 1,其他维都为 0。==》维度灾难

神经概率语言模型

  • 词的表示是向量形式、面向语义的。
  • 向量角度:低维(可以*控制维度,一般是100左右)
  • 可以计算两个词的相似度

二、word2vec

是一种 Distributed Representation 方法, 其存在大量的非零分量,Distributed Representation 思想是:通过训练将某种语言中的每一个词映射成一个固定长度的短向量(相对于one-hot而言)。其本身其实是神经概率模型的副产品,是为了通过神经网络学习某个语言模型而产生的中间结果。

  • 某个语言模型:CBOW 和 Skip-gram。
  • 学习过程中降低复杂度近似方法:Hierarchical Softmax或Negative Sampling。
    ==》两个模型乘以两种方法,一共有四种实现。

1、模型

word2vec的两种模型:CBOW模型和Skip-gram模型。
【数据竞赛】“达观杯”文本智能处理挑战赛3

两个模型都包含三层:输入层、投影层和输出层。

  • CBOW:已知当前词 wtw_twt​ 的上下文 wt2,wt1,wt+1,wt+2w_{t-2},w_{t-1},w_{t+1},w_{t+2}wt−2​,wt−1​,wt+1​,wt+2​的前提下预测当前词 wtw_twt​.
  • Skip-gram:已知当前词 wtw_twt​ 的前提下预测其上下文 wt2,wt1,wt+1,wt+2w_{t-2},w_{t-1},w_{t+1},w_{t+2}wt−2​,wt−1​,wt+1​,wt+2​。

2、基于 Hierarchical Softmax 的 CBOW 模型

CBOW(Continuous Bag-of-Words Model),是一种根据上下文的词语预测当前词语的出现概率的语言模型。

其学习目标是最大化对数似然函数:
L=wClogp(wContext(w))L=\sum_{w\in C}\log p(w|Context(w))L=w∈C∑​logp(w∣Context(w))
其中,www 表示语料库 CCC 中任意一个词。

对于 CBOW 模型

  • 输入层:上下文词语的词向量(训练模型,词向量是模型中的参数,会不断更新)
  • 投影层:对输入层进行求和,即向量加法。
  • 输出层:输出最可能的 www。可看为多分类问题(语料库 C|C|∣C∣ 个词向量)

对于神经网络模型多分类,常使用 SoftmaxSoftmaxSoftmax 回归:
hθ(x(i))=[p(y(i)=1x(i);θ)p(y(i)=2x(i);θ)...p(y(i)=kx(i);θ)]=1j=1keθjTx(i)[eθ1Tx(i)eθ2Tx(i)...eθkTx(i)]h_{\theta}(x^{(i)})= \left[ \begin{matrix} p(y^{(i)}=1|x^{(i)};\theta) \\ p(y^{(i)}=2|x^{(i)};\theta) \\ ... \\ p(y^{(i)}=k|x^{(i)};\theta) \end{matrix} \right] =\frac{1}{\sum_{j=1}^ke^{\theta_j^Tx^{(i)}}} \left[ \begin{matrix} e^{\theta_1^Tx^{(i)}} \\ e^{\theta_2^Tx^{(i)}} \\ ... \\ e^{\theta_k^Tx^{(i)}} \end{matrix} \right]hθ​(x(i))=⎣⎢⎢⎡​p(y(i)=1∣x(i);θ)p(y(i)=2∣x(i);θ)...p(y(i)=k∣x(i);θ)​⎦⎥⎥⎤​=∑j=1k​eθjT​x(i)1​⎣⎢⎢⎢⎡​eθ1T​x(i)eθ2T​x(i)...eθkT​x(i)​⎦⎥⎥⎥⎤​
==》缺点:需要对语料库中每个词语(类)都计算一遍输出概率并进行归一化。

Hierarchical Softmax,是输出层的树形结构。它是利用二叉树结构对Softmax进行优化。
【数据竞赛】“达观杯”文本智能处理挑战赛3

  • 非叶子节点(黄色节点)相当于神经元,进行二分类决策并输出1或0,分别代表向下左转或向下右转;
  • 叶子节点代表语料库中的一个词语,于是每个词语都可以被 01 唯一地编码,并且其编码序列对应一个事件序列.
    ==》则可计算条件概率 p(wContext(w))p(w|Context(w))p(w∣Context(w))

符号约定:

  • pwp^wpw:从根节点出发到达 www 对应叶子结点的路径;
  • lwl^wlw:路径中包含结点的个数;
  • p1w,p2w,...,plwwp^w_1,p^w_2,...,p^w_{l^w}p1w​,p2w​,...,plww​:路径 pwp^wpw 中各个节点;
  • d1w,d2w,...,dlww(0,1)d^w_1,d^w_2,...,d^w_{l^w}\in{(0,1)}d1w​,d2w​,...,dlww​∈(0,1):词 www 的编码,djwd^w_jdjw​ 表示路径 pwp^wpw 第 jjj 个节点对应的编码(根节点无编码)
  • θ1w,θ2w,...,θlwwRm\theta^w_1,\theta^w_2,...,\theta^w_{l^w}\in{\mathbb{R}^m}θ1w​,θ2w​,...,θlww​∈Rm:路径 pwp^wpw 中非叶节点对应的参数向量

www 的条件概率:
p(wContext(w))=j=2lwp(djwxw,θj1w)p(w|Context(w))=\prod_{j=2}^{l^w}p(d^w_j|{x}_w,\theta_{j-1}^w)p(w∣Context(w))=j=2∏lw​p(djw​∣xw​,θj−1w​)
表示根节点到叶子节点 xw{x}_wxw​,经过 lw1l^w-1lw−1 个节点,编码从下标2开始(根节点无编码),对应的参数向量下标从1开始(根节点为1)。

其中,每一项是一个 Logistic Regression:
p(djwxw,θj1w)={σ(xwTθj1w),djw=01σ(xwTθj1w),djw=1p(d_j^w|{x}_w,\theta_{j-1}^w)= \begin{cases} \sigma(x_w^T\theta_{j-1}^w), & \text{$d_j^w=0$} \\[2ex] 1-\sigma(x_w^T\theta_{j-1}^w), & \text{$d_j^w=1$} \end{cases}p(djw​∣xw​,θj−1w​)=⎩⎨⎧​σ(xwT​θj−1w​),1−σ(xwT​θj−1w​),​djw​=0djw​=1​
考虑到 ddd 只有0和1两种取值,则用指数形式表示为:
p(djwxw,θj1w)=[σ(xwTθj1w)]1djw.[1σ(xwTθj1w)]djw p(d_j^w|{x}_w,\theta_{j-1}^w)=[\sigma(x_w^T\theta_{j-1}^w)]^{1-d_j^w}.[1-\sigma(x_w^T\theta_{j-1}^w)]^{d_j^w}p(djw​∣xw​,θj−1w​)=[σ(xwT​θj−1w​)]1−djw​.[1−σ(xwT​θj−1w​)]djw​

对目标函数对对数似然并将 p(wContext(w))p(w|Context(w))p(w∣Context(w)) 带入目标函数:
L=wClogp(wContext(w))=wClogj=2lw{[σ(xwTθj1w)]1djw.[1σ(xwTθj1w)]djw}=wCj=2lw(1djw).log[σ(xwTθj1w)]+djw.log[1σ(xwTθj1w)] \begin{aligned} L & = \sum_{w\in C}\log p(w|Context(w))\\ & =\sum_{w\in C}log\prod_{j=2}^{l^w}\{[\sigma(x_w^T\theta_{j-1}^w)]^{1-d_j^w}.[1-\sigma(x_w^T\theta_{j-1}^w)]^{d_j^w} \}\\ &=\sum_{w\in C}\sum_{j=2}^{l^w}{({1-d_j^w}).\log[\sigma(x_w^T\theta_{j-1}^w)]}+d_j^w.\log[1-\sigma(x_w^T\theta_{j-1}^w)] \end{aligned}L​=w∈C∑​logp(w∣Context(w))=w∈C∑​logj=2∏lw​{[σ(xwT​θj−1w​)]1−djw​.[1−σ(xwT​θj−1w​)]djw​}=w∈C∑​j=2∑lw​(1−djw​).log[σ(xwT​θj−1w​)]+djw​.log[1−σ(xwT​θj−1w​)]​

每一项不妨简写为:
L(w,j)=(1djw).log[σ(xwTθj1w)]+djw.log[1σ(xwTθj1w)]L(w,j)={({1-d_j^w}).\log[\sigma(x_w^T\theta_{j-1}^w)]}+d_j^w.\log[1-\sigma(x_w^T\theta_{j-1}^w)]L(w,j)=(1−djw​).log[σ(xwT​θj−1w​)]+djw​.log[1−σ(xwT​θj−1w​)]

如何最大化对数似然函数:最大化每一项即可(近似)。
如何最大化每一项:随机梯度上升法。首先求函数对每个变量的偏导数;然后,对于每个样本,带入偏导数表达式可得函数在该维度的梯度,进行参数更新。

对函数 L(w,j)L(w, j)L(w,j) 有两个参数:每个节点的参数向量 θj1w\theta_{j-1}^wθj−1w​ 和输出层的输入 xwx_wxw​,分别求偏导:
L(w,j)θj1w=θj1w{(1djw).log[σ(xwTθj1w)]+djw.log[1σ(xwTθj1w)]}\frac{\partial L(w,j)}{\partial \theta_{j-1}^w}=\frac{\partial}{\partial \theta_{j-1}^w}\{{({1-d_j^w}).\log[\sigma(x_w^T\theta_{j-1}^w)]}+d_j^w.\log[1-\sigma(x_w^T\theta_{j-1}^w)]\}∂θj−1w​∂L(w,j)​=∂θj−1w​∂​{(1−djw​).log[σ(xwT​θj−1w​)]+djw​.log[1−σ(xwT​θj−1w​)]}
sigmoid\because sigmoid∵sigmoid 函数的导数为:σ(x)=σ(x)[1σ(x)]\sigma'(x)=\sigma(x)[1-\sigma(x)]σ′(x)=σ(x)[1−σ(x)]
于是代入上式得到:
L(w,j)θj1w=(1djw)[1σ(xwTθj1w)]xwdjwσ(xwTθj1w)xw=[1djwσ(xwTθj1w)]xw\begin{aligned} \frac{\partial L(w,j)}{\partial \theta_{j-1}^w}&= {({1-d_j^w})[1-\sigma(x_w^T\theta_{j-1}^w)]x_w}-d_j^w\sigma(x_w^T\theta_{j-1}^w)x_w \\ &=[1-{d_j^w}-\sigma(x_w^T\theta_{j-1}^w)]x_w \end{aligned}∂θj−1w​∂L(w,j)​​=(1−djw​)[1−σ(xwT​θj−1w​)]xw​−djw​σ(xwT​θj−1w​)xw​=[1−djw​−σ(xwT​θj−1w​)]xw​​

于是 θj1w\theta_{j-1}^wθj−1w​ 的更新表达式如下:
θj1w:=θj1w+η[1djwσ(xwTθj1w)]xw\theta_{j-1}^w:=\theta_{j-1}^w+\eta[1-{d_j^w}-\sigma(x_w^T\theta_{j-1}^w)]x_wθj−1w​:=θj−1w​+η[1−djw​−σ(xwT​θj−1w​)]xw​
其中,η\etaη是学习率,学习率越大训练速度越快,但目标函数容易在局部区域来回抖动。

xwx_wxw​ 求偏导,注意到在 L(w,j)=(1djw).log[σ(xwTθj1w)]+djw.log[1σ(xwTθj1w)]L(w,j)={({1-d_j^w}).\log[\sigma(x_w^T\theta_{j-1}^w)]}+d_j^w.\log[1-\sigma(x_w^T\theta_{j-1}^w)]L(w,j)=(1−djw​).log[σ(xwT​θj−1w​)]+djw​.log[1−σ(xwT​θj−1w​)] 中 xwx_wxw​ 和 θj1w\theta_{j-1}^{w}θj−1w​ 是对称的,所以可以将 θj1w\theta_{j-1}^{w}θj−1w​ 换成 xwx_wxw​,得到关于 xwx_wxw​ 的偏导数:
L(w,j)xw==[1djwσ(xwTθj1w)]θj1w\begin{aligned} \frac{\partial L(w,j)}{\partial x_w}&= &=[1-{d_j^w}-\sigma(x_w^T\theta_{j-1}^w)]\theta_{j-1}^w \end{aligned}∂xw​∂L(w,j)​​=​=[1−djw​−σ(xwT​θj−1w​)]θj−1w​​

xwx_wxw​ 是上下文的词向量的和,而非上下文单个词的词向量。如何将这个更新量应用到单个单词的词向量上?
==》直接将 xwx_wxw​ 的更新量整个应用到每个单词的词向量上:
v(w~):=v(w~)+ηj=2lwL(w,j)xw,w~Context(w)v(\tilde w):=v(\tilde w)+\eta\sum_{j=2}^{l^w}\frac{\partial L(w,j)}{\partial{x_w}},\tilde{w}\in Context(w)v(w~):=v(w~)+ηj=2∑lw​∂xw​∂L(w,j)​,w~∈Context(w)
其中,v(w~)v(\tilde w)v(w~) 表示上下文某一个单词的词向量。
想法:可以取平均之后更新到每个词向量上,

伪代码:

  1. e=0e = 0e=0

  2. xw=uContext(w)v(u)x_w=\sum_{u\in Context(w)}v(u)xw​=∑u∈Context(w)​v(u)

  3. For j=2:lwj = 2: l^wj=2:lw DO
    {
    \quad 3.1     q=σ(xwTθj1w)\;\; q=\sigma(x_w^T\theta_{j-1}^w)q=σ(xwT​θj−1w​)
    \quad 3.2     g=η(1djwq)\;\; g=\eta(1-d_j^w-q)g=η(1−djw​−q)
    \quad 3.3     e:=e+gθj1w\;\; e:=e+g\theta_{j-1}^we:=e+gθj−1w​
    \quad 3.4     θj1w:=θj1w+gxw\;\; \theta_{j-1}^w:=\theta_{j-1}^w+gx_wθj−1w​:=θj−1w​+gxw​ }

  4. FOR uContext(w)u\in Context(w)u∈Context(w) DO
    {
    v(u):=v(u)+e\quad v(u):=v(u)+ev(u):=v(u)+e
    }

3、基于Hierarchical Softmax的Skip-gram模型

Skip-gram只是逆转了CBOW的因果关系而已,即已知当前词语,预测上下文。其网络结构如图所示:
【数据竞赛】“达观杯”文本智能处理挑战赛3
与CBOW模型的区别:

  • 输入层不再是多个词向量,而是一个词向量;
  • 投影层直接将输入层的词向量传递给输出层。

记:uuu:表示 www 的上下文中的一个词语。

语言模型的概率函数可以写作:
p(Context(w)w)=wContext(w)p(uw)p(Context(w)|w)=\prod_{w\in Context(w)}p(u|w)p(Context(w)∣w)=w∈Context(w)∏​p(u∣w)
这里是一个词袋模型,所以 uuu 是无序的(相互独立的)

在Hierarchical Softmax思想下,每个 uuu 都可以编码为一条01路径:
p(uw)=j=2lup(djuv(w),θj1u)p(u|w)=\prod_{j=2}^{l^u}p(d_j^u|v(w),\theta_{j-1}^u)p(u∣w)=j=2∏lu​p(dju​∣v(w),θj−1u​)
类似的,每项可以简写如下:
p(djuv(w),θj1u)=[σ(v(w)Tθj1u)]1dju[1σ(v(w)Tθj1u)]dju p(d_j^u|v(w),\theta_{j-1}^u)=[\sigma(v(w)^T\theta_{j-1}^u)]^{1-d_j^u}\cdot[1-\sigma(v(w)^T\theta_{j-1}^{u})]^{d_j^u} p(dju​∣v(w),θj−1u​)=[σ(v(w)Tθj−1u​)]1−dju​⋅[1−σ(v(w)Tθj−1u​)]dju​

将它们写到一块,得到目标函数:
L=wCloguContext(w)j=2lu{[σ(v(w)Tθj1u)]1dju[1σ(v(w)Tθj1u)]dju}=wCuContext(w)j=2lu{(1dju)log[σ(v(w)Tθj1u)]+djulog[1σ(v(w)Tθj1u)]}\begin{aligned} L&=\sum_{w\in C}\log\prod_{u\in Context(w)}\prod_{j=2}^{l^u}\{[\sigma(v(w)^T\theta_{j-1}^u)]^{1-d_j^u}\cdot[1-\sigma(v(w)^T\theta_{j-1}^{u})]^{d_j^u}\} \\ &=\sum_{w\in C}\sum_{u\in Context(w)}\sum_{j=2}^{l^u}\{(1-d_j^u)\cdot\log[\sigma(v(w)^T\theta_{j-1}^u)]+d_j^u\cdot\log[1-\sigma(v(w)^T\theta_{j-1}^u)]\} \end{aligned}L​=w∈C∑​logu∈Context(w)∏​j=2∏lu​{[σ(v(w)Tθj−1u​)]1−dju​⋅[1−σ(v(w)Tθj−1u​)]dju​}=w∈C∑​u∈Context(w)∑​j=2∑lu​{(1−dju​)⋅log[σ(v(w)Tθj−1u​)]+dju​⋅log[1−σ(v(w)Tθj−1u​)]}​
虽然上式对比CBOW多了一个uuu,但给定训练实例(一个词www和它的上下文{u}\{u\}{u}),uuu也是固定的。所以上式其实依然只有两个变量 xwx_wxw​ 和 θj1w\theta_{j-1}^wθj−1w​,对其求偏导数:
L(w,u,j)xw=[1djuσ(v(w)Tθj1u)]v(w)\begin{aligned} \frac{\partial L(w,u,j)}{\partial x_w}&= [1-{d_j^u}-\sigma(v(w)^T\theta_{j-1}^u)]v(w) \end{aligned}∂xw​∂L(w,u,j)​​=[1−dju​−σ(v(w)Tθj−1u​)]v(w)​
省略求导过程,可以得到 θj1w\theta_{j-1}^wθj−1w​ 的更新表达式:
θj1u:=θj1u+η[1djuσ(v(w)Tθj1u)]v(w)\theta_{j-1}^u:=\theta_{j-1}^u+\eta[1-{d_j^u}-\sigma(v(w)^T\theta_{j-1}^u)]v(w)θj−1u​:=θj−1u​+η[1−dju​−σ(v(w)Tθj−1u​)]v(w)
利用对称性可得 xwx_wxw​ 的偏导数:
L(w,u,j)v(w)=[1djuσ(v(w)Tθj1u)]θj1u\begin{aligned} \frac{\partial L(w,u,j)}{\partial v(w)}&= [1-{d_j^u}-\sigma(v(w)^T\theta_{j-1}^u)]\theta_{j-1}^u \end{aligned}∂v(w)∂L(w,u,j)​​=[1−dju​−σ(v(w)Tθj−1u​)]θj−1u​​
于是得到 xwx_wxw​ 的更新表达式:
v(w):=v(w)+ηuContext(w)j=2luL(w,u,j)v(w)v(w):=v(w)+\eta\sum_{u\in Context(w)}\sum_{j=2}^{l^u}\frac{\partial L(w,u,j)}{\partial v(w)}v(w):=v(w)+ηu∈Context(w)∑​j=2∑lu​∂v(w)∂L(w,u,j)​

4、Negative Sampling

对于Negative Sampling,负例是随机挑选出来的。据说Negative Sampling能提高速度、改进模型质量。

三、用gensim实现word2vec

1、API参数

在gensim中,word2vec 相关的API都在包gensim.models.word2vec中。
和算法有关的参数都在类gensim.models.word2vec.Word2Vec中。

2、参数

  • sentences: 我们要分析的语料,可以是一个列表,或者从文件中遍历读出。后面我们会有从文件读出的例子。

  • size: 词向量的维度,默认值是100。这个维度的取值一般与我们的语料的大小相关,如果是不大的语料,比如小于100M的文本语料,则使用默认值一般就可以了。如果是超大的语料,建议增大维度。

  • window:即词向量上下文最大距离,这个参数在我们的算法原理篇中标记为c,window越大,则和某一词较远的词也会产生上下文关系。默认值为5。在实际使用中,可以根据实际的需求来动态调整这个window的大小。如果是小语料则这个值可以设的更小。对于一般的语料这个值推荐在[5,10]之间。

  • sg: 即我们的word2vec两个模型的选择了。如果是0, 则是CBOW模型,是1则是Skip-Gram模型,默认是0即CBOW模型。

  • hs: 即我们的word2vec两个解法的选择了,如果是0, 则是Negative Sampling,是1的话并且负采样个数negative大于0, 则是Hierarchical Softmax。默认是0即Negative Sampling。

  • negative:即使用Negative Sampling时负采样的个数,默认是5。推荐在[3,10]之间。这个参数在我们的算法原理篇中标记为neg。

  • cbow_mean: 仅用于CBOW在做投影的时候,为0,则算法中的xw为上下文的词向量之和,为1则为上下文的词向量的平均值。在我们的原理篇中,是按照词向量的平均值来描述的。个人比较喜欢用平均值来表示xw,默认值也是1,不推荐修改默认值。

  • min_count:需要计算词向量的最小词频。这个值可以去掉一些很生僻的低频词,默认是5。如果是小语料,可以调低这个值。

  • iter: 随机梯度下降法中迭代的最大次数,默认是5。对于大语料,可以增大这个值。

  • alpha: 在随机梯度下降法中迭代的初始步长。算法原理篇中标记为η,默认是0.025。

  • min_alpha: 由于算法支持在迭代的过程中逐渐减小步长,min_alpha给出了最小的迭代步长值。随机梯度下降中每轮的迭代步长可以由iteralphamin_alpha一起得出。这部分由于不是word2vec算法的核心内容,因此在原理篇我们没有提到。对于大语料,需要对alpha, min_alpha,iter一起调参,来选择合适的三个值。

import pandas as pd
import gensim
import time
import pickle
import numpy as np
import csv,sys
vector_size = 100

maxInt = sys.maxsize
decrement = True
while decrement:
    # decrease the maxInt value by factor 10
    # as long as the OverflowError occurs.
    decrement = False
    try:
        csv.field_size_limit(maxInt)
    except OverflowError:
        maxInt = int(maxInt/10)
        decrement = True

#=======================================================================================================================
# 0 辅助函数
#=======================================================================================================================

def sentence2list(sentence):
    return sentence.strip().split()

start_time = time.time()

#=======================================================================================================================
# 1 准备训练数据
#=======================================================================================================================

print("准备数据................ ")
df_train = pd.read_csv('train_set.csv',nrows=5000,engine='python')
df_test = pd.read_csv('test_set.csv',nrows=5000,engine='python')
sentences_train = list(df_train.loc[:, 'word_seg'].apply(sentence2list))
sentences_test = list(df_test.loc[:, 'word_seg'].apply(sentence2list))
sentences = sentences_train + sentences_test
print("准备数据完成! ")

#=======================================================================================================================
# 2 训练
#=======================================================================================================================
print("开始训练................ ")
model = gensim.models.Word2Vec(sentences=sentences, size=vector_size, window=5, min_count=5, workers=8, sg=0, iter=5)
print("训练完成! ")

#=======================================================================================================================
# 3 提取词汇表及vectors,并保存
#=======================================================================================================================
print(" 保存训练结果........... ")
wv = model.wv
vocab_list = wv.index2word
word_idx_dict = {}
for idx, word in enumerate(vocab_list):
    word_idx_dict[word] = idx
    
vectors_arr = wv.vectors
vectors_arr = np.concatenate((np.zeros(vector_size)[np.newaxis, :], vectors_arr), axis=0)#第0位置的vector为'unk'的vector

f_wordidx = open('word_seg_word_idx_dict.pkl', 'wb')
f_vectors = open('word_seg_vectors_arr.pkl', 'wb')
pickle.dump(word_idx_dict, f_wordidx)
pickle.dump(vectors_arr, f_vectors)
f_wordidx.close()
f_vectors.close()
print("训练结果已保存到该目录下! ")

end_time = time.time()
print("耗时:{}s ".format(end_time - start_time))

参考:

上一篇:Adidas Forum Mid sko udsalg anerkendte en person


下一篇:比特币滑铁卢即将再现