Skip to content

Commit

Permalink
add nce file
Browse files Browse the repository at this point in the history
  • Loading branch information
pakchoi committed May 22, 2017
1 parent 0b66e8e commit e92d8a3
Showing 1 changed file with 67 additions and 40 deletions.
107 changes: 67 additions & 40 deletions nce_cost/README.md
Original file line number Diff line number Diff line change
@@ -1,71 +1,98 @@
# NCE加速词向量训练
# 噪声对比估计加速词向量训练
## 背景介绍
神经概率语言模型 (NPLM) 尽管优异的性能,但是其使用率仍然远远低于n-gram传统模型的使用,这是由于其众所周知的漫长训练时间。训练 NPLM 计算开销很大,因为softmax需要全局的计算,必须考虑词汇中的所有单词。
NCE(Noise-contrastive estimation)是一种快速简便的训练 NPLM 的算法,一种新的连续分布估计方法。这里我们使用了ptb数据来训练神经语言模型,并表明它减少了训练时间超过一个数量级, 而不影响所产生的模型的质量。该算法比重要性采样更有效而且更稳定, 因为它需要的噪音样本要少得多\[[1](#参考文献)\]
语言模型是自然语言处理领域的基础问题,其在词性标注、句法分析、机器翻译、信息检索等任务中起到了重要作用。

## 实验数据
本文采用Penn Treebank (PTB)数据集([Tomas Mikolov预处理版本](http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz)),这是一个用来训练语言模型的数据集,给出前4个词让语言模型预测第5个词。PaddlePaddle提供[paddle.dataset.imikolov](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/dataset/imikolov.py)接口来方便调用数据,其中实现了数据自动下载,字典生成,滑动窗口等功能。
神经概率语言模型(Neural Probabilistic Language Model, NPLM)尽管有优异的精度表现,但是相对基于统计的 n-gram 传统模型,训练时间还是太漫长了[[4](#参考文献)]。原因请见[下一章节](#NCE Layer)。

NCE(Noise-contrastive estimation)[[2](#参考文献)],是一种快速简便的离散分布估计方法,这里以训练 NPLM 为例。这里我们使用了 ptb 数据来训练神经语言模型。

## NCE Layer
NCE Layer引用自论文\[[1](#参考文献)\],NCE是指Noise-contrastive estimation,目的是用来提高训练速度并改善所得词向量的质量。与h-sigmoid[[2](#参考文献)\]相比,NCE不再使用复杂的Huffman树来构造目标函数,而是采用相对简单的随机负采样,这样能够大幅度提升计算性能
在这里将 NCE 用于训练神经语言模型,主要目的是用来提高训练速度。训练 NPLM 计算开销很大,是因为 softmax 函数计算时需要计算每个类别的指数项,必须考虑字典中的所有单词,这是相当耗时的,因为对于语言模型字典往往非常大[[4](#参考文献)]。与 hierarchical-sigmoid \[[3](#参考文献)\] 相比,NCE 不再使用复杂的 Huffman 树来构造目标函数,而是采用相对简单的随机负采样,以大幅提升计算效率

NCE原理是通过构造一个逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词label作为正样例,同时采样出K个其他词label作为负样例,从而只需要计算样本在这K+1个label上的概率。整体目标函数的目的就是增大正样本的概率同时降低负样本的概率。相比原始的softmax分类需要计算每个类别的分数,然后归一化得到概率,这个计算过程是十分耗时的。

## 网络结构
**模型的总体结构:**
假设已知具体的上下文 h,并且知道这个分布为 ${ P }^{ h }(w)$ ,我们将训练样例作为正样例,从一个噪音分布 ${ P }_{ n }(w)$ 抽样产生负样例。我们可以任意选择合适的噪音分布,默认为无偏的均匀分布。这里我们同时假设噪音样例 k 倍于数据样例,则训练数据被抽中的概率为[[2](#参考文献)]

$$
{ P }^{ h }(D=1|w,\theta )=\frac { { P }_{ \theta }^{ h }(w) }{ { P }^{ h }_{ \theta }(w)+k{ P }_{ n }(w) } =\sigma (\Delta { s }_{ \theta }^{ }(w,h))
$$

其中 $\Delta { s }_{ \theta }^{ }(w,h)={ s }_{ \theta }^{ }(w,h)-\log { (k{ P }_{ n }^{ }(w)) }$ ,${ s }_{ \theta }(w,h)$ 表示选择在生成 $w$ 字并处于上下文 $h$ 时的特征向量,整体目标函数的目的就是增大正样本的概率同时降低负样本的概率。目标函数如下[[2](#参考文献)]

$$
{ J }_{ }^{ h }(\theta )={ E }_{ { P }_{ d }^{ h } }^{ }\left[ \log { { P }_{ }^{ h }(D=1|w,\theta ) } \right] +k{ E }_{ { P }_{ n }^{ } }^{ }\left[ \log { { P }_{ }^{ h } } (D=0|w,\theta ) \right] \\ \qquad ={ E }_{ { P }_{ d }^{ h } }^{ }\left[ \log { \sigma (\Delta { s }_{ \theta }^{ }(w,h)) } \right] +k{ E }_{ { P }_{ n }^{ } }^{ }\left[ \log { (1-\sigma (\Delta { s }_{ \theta }^{ }(w,h))) } \right]
$$

NCE 原理是通过构造一个逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 k 个其他词 label 作为负样例,从而只需要计算样本在这 k+1 个 label 上的概率。相比原始的 softmax 分类需要计算每个类别的分数,然后归一化得到概率,softmax 这个计算过程是十分耗时的。

## 实验数据
本文采用 Penn Treebank (PTB)数据集([Tomas Mikolov预处理版本](http://www.fit.vutbr.cz/~imikolov/rnnlm/simple-examples.tgz)),这是一个可以用来训练语言模型的数据集。PaddlePaddle 提供 [paddle.dataset.imikolov](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/v2/dataset/imikolov.py) 接口来方便调用数据,其中实现了数据自动下载,字典生成,滑动窗口等功能。数据接口给出的是前4个词让语言模型预测第5个词。

本文通过训练N-gram语言模型来获得词向量,具体地使用前4个词来预测当前词。网络输入为词在字典中的id,然后查询词向量词表获取词向量,接着拼接4个词的词向量,然后接入一个全连接隐层,最后是NCE Layer层。详细网络结构见图1:
## 网络结构
本文在训练 n-gram 语言模型时,即使用前4个词来预测当前词。网络输入为词在字典中的 id,然后查询词向量词表获取词向量,接着拼接4个词的词向量,然后接入一个全连接隐层,最后是 NCE 层。详细网络结构见图1:

<p align="center">
<img src="images/network_conf.png" width = "80%" align="center"/><br/>
图1. 网络配置结构
</p>
**可以看到,模型主要分为如下几个部分:**
可以看到,模型主要分为如下几个部分:

- **输入层**ptb的样本由原始的英文单词组成,将每个英文单词转换为字典中的id表示。
1. **输入层**输入样本由原始的英文单词组成,将每个英文单词转换为字典中的id表示。

- **词向量层**使用定义好的embedding矩阵,将原先的id表示转换为向量表示。这种将英文单词转换为词向量的方法,比传统的one-hot表示更能体现词语的语义内容,关于词向量的更多信息请参考PaddleBook中的[词向量](https://github.com/PaddlePaddle/book/tree/develop/04.word2vec)一节。
2. **词向量层**使用 trainable 的 embedding 矩阵,将原先的 id 表示转换为向量表示。这种将英文单词转换为词向量的方法,比传统的 one-hot 表示更能体现词语的语义内容,关于词向量的更多信息请参考 PaddleBook 中的[词向量](https://github.com/PaddlePaddle/book/tree/develop/04.word2vec)一节。

- **词向量拼接层**将词向量进行并联,就是将向量沿feature边依次拼接在一起形成一个矩阵
3. **词向量拼接层**将词向量进行串联,将向量首尾相接形成一个长向量

- **全连接隐层**将上一层获得的词向量矩阵输入一层隐层的神经网络,
4. **全连接隐层**将上一层获得的长向量输入一层隐层的神经网络,输出特征向量。

- **NCE层**:推断时,输出层的神经元数量和样本的类别数一致,在这里就是整个字典的大小,最后使用softmax对每个类别的概率做一个归一化操作,因此第$i$个神经元的输出就可以认为是样本属于第$i$类的预测概率。训练时,我们需要构造一个二分类分类器,万幸的是paddle已经帮助我们实现了这一切
5. **NCE层**:推断时,输出层的神经元数量和样本的类别数一致,在这里就是整个字典的大小,最后使用 softmax 对每个类别的概率做归一化操作,因此第$i$个神经元的输出就可以认为是样本属于第$i$类的预测概率。训练时,我们需要构造二分类分类器

## 训练阶段
训练直接运行``` python train.py ```。程序第一次运行会检测用户缓存文件夹中是否包含imikolov数据集,如果未包含,则自动下载。运行过程中,每1000个iteration会打印模型训练信息,主要包含训练损失,每个pass计算一次测试损失,并会保存一次模型。
训练直接运行``` python train.py ```。程序第一次运行会检测用户缓存文件夹中是否包含 ptb 数据集,如果未包含,则自动下载。运行过程中,每1000个 iteration 会打印模型训练信息,主要包含训练损失,每个 pass 计算一次测试损失,并同时会保存一次最新的模型。

在 PaddlePaddle 中也有已经实现好的 NCE layer,有一些参数需要自行根据实际场景进行设计:
代码实现如下:

```python
cost = paddle.layer.nce(
input=hidden_layer,
label=next_word,
num_classes=dict_size,
param_attr=paddle.attr.Param(name='nce_w'),
bias_attr=paddle.attr.Param(name='nce_b'),
act=paddle.activation.Sigmoid(),
num_neg_samples=25,
neg_distribution=None)
```

| 参数名 | 参数作用 | 介绍 |
|:-------------: |:---------------:| :-------------:|
| param\_attr / bias\_attr | 用来设置参数名字 | 可以方便后面预测阶段好来实现网络的参数共享,具体内容下一个章节里会陈述。|
| num\_neg\_samples | 参数负责控制对负样例的采样个数。 | 可以控制正负样本比例 |
| neg\_distribution | 可以控制生成负样例标签的分布,默认是一个均匀分布。 | 可以自行控制负样本采样时各个类别的权重 |
| act | 表示使用何种激活函数。 | 根据 NCE 的原理,这里应该使用 sigmoid 函数。 |

在PaddlePaddle中也有已经实现好的nce layer,这里有一些参数需要自行根据实际场景进行设计,例如param\_attr和bias\_attr这两个参数,这是用来设置参数名字,这是为了后面预测阶段好来实现网络的参数共享,具体内容下一个章节里会称述。num\_neg\_samples参数负责控制对负样例的采样个数,同时也是相对正样例的采样倍数。neg\_distribution可以控制生成负样例标签的分布,默认是一个均匀分布。act参数表示激活函数,根据NCE的原理,这里应该使用sigmoid函数。

## 预测阶段
预测直接运行``` python infer.py ```程序会首先load最新模型,然后按照batch方式进行预测,并打印预测结果。预测阶段最重要的就是共享NCE layer中的逻辑回归训练得到的参数,因为PaddlePaddle里的NCE层并不支持直接用在预测时进行输出,所以必须自己重新写一个推断层,推断层的参数为训练时的参数,所以需要参数共享。
预测直接运行``` python infer.py ```程序会首先加载最新模型,然后按照 batch 大小依次进行预测,并打印预测结果。预测阶段需要共享 NCE layer 中的逻辑回归训练得到的参数,因为训练和预测计算逻辑不一样,所以需要重新写推断层,推断层的参数为训练时的参数,所以需要参数共享。

参数分享的方法,通过paddle.attr.Param方法获取参数值,并参数值传入paddle.layer.trans\_full\_matrix\_projection对隐层输出向量hidden\_layer做一个矩阵右乘,从而得到最后的类别向量,将类别向量输入softmax做一个归一操作,从而得到最后的类别概率分布。
具体实现推断层的方法,先是通过 paddle.attr.Param 方法获取参数值,PaddlePaddle 会自行在模型中寻找相同参数名的参数并获取。然后使用 paddle.layer.trans\_full\_matrix\_projection 对隐层输出向量 hidden\_layer 做一个矩阵右乘,从而得到最后的类别向量,将类别向量输入 softmax 做一个归一操作,从而得到最后的类别概率分布。

代码实现如下:

```python
if is_train == True:
cost = paddle.layer.nce(
input=hidden_layer,
label=next_word,
num_classes=dict_size,
param_attr=paddle.attr.Param(name='nce_w'),
bias_attr=paddle.attr.Param(name='nce_b'),
act=paddle.activation.Sigmoid(),
num_neg_samples=25,
neg_distribution=None)
return cost
else:
with paddle.layer.mixed(
size=dict_size,
act=paddle.activation.Softmax(),
bias_attr=paddle.attr.Param(name='nce_b')) as prediction:
prediction += paddle.layer.trans_full_matrix_projection(
input=hidden_layer, param_attr=paddle.attr.Param(name='nce_w'))
with paddle.layer.mixed(
size=dict_size,
act=paddle.activation.Softmax(),
bias_attr=paddle.attr.Param(name='nce_b')) as prediction:
prediction += paddle.layer.trans_full_matrix_projection(
input=hidden_layer, param_attr=paddle.attr.Param(name='nce_w'))
```

## 参考文献
1. Mnih A, Kavukcuoglu K. [Learning word embeddings efficiently with noise-contrastive estimation](https://papers.nips.cc/paper/5165-learning-word-embeddings-efficiently-with-noise-contrastive-estimation.pdf)[C]//Advances in neural information processing systems. 2013: 2265-2273.
1. Mathematiques C D R. [Quick Training of Probabilistic Neural Nets by Importance Sampling](http://www.iro.umontreal.ca/~lisa/pointeurs/submit_aistats2003.pdf)[C]// 2002.

2. Mnih A, Kavukcuoglu K. [Learning word embeddings efficiently with noise-contrastive estimation](https://papers.nips.cc/paper/5165-learning-word-embeddings-efficiently-with-noise-contrastive-estimation.pdf)[C]//Advances in neural information processing systems. 2013: 2265-2273.

3. Morin, F., & Bengio, Y. (2005, January). [Hierarchical Probabilistic Neural Network Language Model](http://www.iro.umontreal.ca/~lisa/pointeurs/hierarchical-nnlm-aistats05.pdf). In Aistats (Vol. 5, pp. 246-252).

2. Morin, F., & Bengio, Y. (2005, January). [Hierarchical Probabilistic Neural Network Language Model](http://www.iro.umontreal.ca/~lisa/pointeurs/hierarchical-nnlm-aistats05.pdf). In Aistats (Vol. 5, pp. 246-252).
4. Mnih A, Teh Y W. [A Fast and Simple Algorithm for Training Neural Probabilistic Language Models](http://xueshu.baidu.com/s?wd=paperuri%3A%280735b97df93976efb333ac8c266a1eb2%29&filter=sc_long_sign&tn=SE_xueshusource_2kduw22v&sc_vurl=http%3A%2F%2Farxiv.org%2Fabs%2F1206.6426&ie=utf-8&sc_us=5770715420073315630)[J]. Computer Science, 2012:1751-1758.

0 comments on commit e92d8a3

Please sign in to comment.