0x03 Negative Sampling

使用Negative Sampling的目的

摒弃Hierarchical Softmax结构, 原因:

  • 我们的训练样本里的中心词ww是一个很生僻的词, 就要在树结构中探索较深

为了简化模型, 使用Negative Sampling来代替Hierarchical Softmax模型结构

Negative Sampling原理

对于中心词ww, 总计2c2c个上下文词, 即为Context(w)\text{Context}(w). 由于是语料中存在的上下文关系, 令wwContext(w)\text{Context}(w)为一个正样本. 然后在字典中采样, 得到neg\text{neg}个和ww不同的词作为中心词, 这在语料中是不存在的(或者说极大概率是不存在的), 因此将采样到的每个中心词wi, i=1,2,,negw_i,\ i=1,2,\cdots,\text{neg}Context(w)\text{Context}(w)作为一个负样本.

最后的输出层就演变成了若干个并行的二元逻辑回归层, 其数量与字典中词的数量相同. 每个词对应一个二元逻辑回归层, 有着自己独立的参数.

通过对这一个正样本和neg\text{neg}个负样本的二元逻辑回归, 更新这neg+1\text{neg}+1个词的逻辑回归参数和每个词的词向量.

Negative Sampling负采样方法

Negative Sampling需要采样neg\text{neg}个负样本. 如果词汇表为VV, 其大小为V|V|, 将长度为1的线段分成V|V|份, 每份对应词汇表里的一个词, 每个词对应的线段长度是不一样的, 高频词对应的线段长, 低频词对应线段短. 最简单的计算每个词对应的线段的长度的方法为:

len(w)=count(w)uvocabcount(u)len(w) = \frac{count(w)}{\sum\limits_{u \in vocab} count(u)}

在word2vec论文中使用的函数为:

len(w)=count(w)3/4uvocabcount(u)3/4len(w) = \frac{count(w)^{3/4}}{\sum\limits_{u \in vocab} count(u)^{3/4}}

论文中采用的采样方法具体为: 将长度为1的线段等分MM份, MM远大于V|V|, 这样每个词对应的线段都被划分成若干个更小的线段, 而MM份中的每一份都会落在某个词对应的线段上. 采样时, 只需要采样出这MM个位置中的neg\text{neg}个位置, 对应的线段所属的词就是我们要采样出的负样本单词. 论文中M=108M=10^8.

示例图如下:

Negative Sampling的梯度计算

将正例的中心词定义为w0w_0, 因此就有样本(Context(w0),wi), i=0,1,2,,neg(\text{Context}(w_0), w_i),\ i=0,1,2,\cdots,\text{neg}. 最大似然法, 最大化下式:

i=0negP(context(w0),wi)=σ(xw0Tθw0)i=1neg(1σ(xw0Tθwi))\prod_{i=0}^{neg}P(context(w_0), w_i) = \sigma(x_{w_0}^T\theta^{w_0})\prod_{i=1}^{neg}(1- \sigma(x_{w_0}^T\theta^{w_i}))

对应的对数似然函数为:

L=i=0negyilog(σ(xw0Tθwi))+(1yi)log(1σ(xw0Tθwi))L = \sum\limits_{i=0}^{neg}y_i log(\sigma(x_{w_0}^T\theta^{w_i})) + (1-y_i) log(1- \sigma(x_{w_0}^T\theta^{w_i}))

采用梯度上升法, 每次使用一个样本进行参数更新. 这里我们需要更新的参数为正样本对应的词向量xw0x_{w_0}, 以及输出层中对应的二元逻辑回归神经元θwi,i=0,1,..neg\theta^{w_i}, i=0,1,..neg.

首先是θwi\theta^{w_i}的梯度:

同样求出xw0x_{w_0}的梯度:

Lxw0=i=0neg(yiσ(xw0Tθwi))θwi\frac{\partial L}{\partial x^{w_0} } = \sum\limits_{i=0}^{neg}(y_i -\sigma(x_{w_0}^T\theta^{w_i}))\theta^{w_i}

基于Negative Sampling的CBOW模型

  • 输入: 训练语料样本, 单侧窗口长度cc, 负采样的个数neg\text{neg}

  • 输出: 每个词的词向量xwx_w, 每个词对应的逻辑回归参数θ\theta

  • 过程

    • 随机初始化所有的模型参数θ\theta, 所有词向量xx

    • 对于每个训练正样本(context(w0),w0)(context(w_0), w_0), 负采样出neg\text{neg}个负样本中心词wi,i=1,2,...negw_i, i=1,2,...neg

    • 对于训练集中的每一个样本(context(w0),w0,w1,...wneg)(context(w_0), w_0,w_1,...w_{neg}), 进行梯度上升:

      • 中间变量e=0e=0

      • xw0=i=12cxix_{w_0}=\sum\limits_{i=1}^{2c}x_i

      • for i=0 to neg\text{for } i =0 \text{ to } \text{neg}, 计算:

        • f=σ(xw0Tθwi)f = \sigma(x_{w_0}^T\theta^{w_i})

        • g=(yif)ηg = (y_i-f)\eta

        • e=e+gθwie = e + g\theta^{w_i}

        • θwi=θwi+gxw0\theta^{w_i}= \theta^{w_i} + gx_{w_0}

      • 对于Context(w0)\text{Context}(w_0)中包含的每一个词对应的词向量xk, k=1,2,,2cx_k,\ k=1,2,\cdots,2c进行相同的更新:

        xk=xk+ex_k = x_k + e

    • 梯度收敛则停止迭代, 否则继续循环上一步

基于Negative Sampling的Skip-Gram模型

  • 输入: 训练语料样本, 单侧窗口长度cc, 负采样的个数neg\text{neg}

  • 输出: 每个词的词向量xwx_w, 每个词对应的逻辑回归参数θ\theta

  • 过程

    • 随机初始化所有的模型参数θ\theta, 所有词向量xx

    • 对于每个训练正样本(context(w0),w0)(context(w_0), w_0), 负采样出neg\text{neg}个负样本中心词wi,i=1,2,...negw_i, i=1,2,...neg

    • 对于训练集中的每一个样本(context(w0),w0,w1,...wneg)(context(w_0), w_0,w_1,...w_{neg}), 进行梯度上升:

      • for i=1 to 2c\text{for } i =1 \text{ to } 2c, 即对上下文中的每个词:

        • 中间变量e=0e=0

        • for j=0 to neg\text{for } j =0 \text{ to } \text{neg}, 计算:

          • f=σ(xw0iTθwj)f = \sigma(x_{w_{0i}}^T\theta^{w_j})

          • g=(yjf)ηg = (y_j-f)\eta

          • e=e+gθwje = e + g\theta^{w_j}

          • θwj=θwj+gxw0i\theta^{w_j}= \theta^{w_j} + gx_{w_{0i}}

        • 对于Context(w0)\text{Context}(w_0)中包含的每一个词对应的词向量xk, k=1,2,,2cx_k,\ k=1,2,\cdots,2c进行相同的更新:

          xk=xk+ex_k = x_k + e

    • 梯度收敛则停止迭代, 否则继续循环上一步

最后更新于

这有帮助吗?