diff --git a/hsigmoid/README.md b/hsigmoid/README.md index b8af766ba3712e55c8447b5f0fcd5763209ff6b4..5e891bce4eaf5c8fe4ab7f17cafd950752cf026e 100644 --- a/hsigmoid/README.md +++ b/hsigmoid/README.md @@ -57,7 +57,7 @@ def train_data(filename, word_dict, n): 图2. 网络配置结构

-代码实现如下: +代码如下: ```python def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True): @@ -86,31 +86,44 @@ def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True): param_attr=paddle.attr.Param( initial_std=1. / math.sqrt(embed_size * 8), learning_rate=1)) - if is_train == True: - cost = paddle.layer.hsigmoid( - input=hidden_layer, - label=target_word, - num_classes=dict_size, - param_attr=paddle.attr.Param(name="sigmoid_w"), - bias_attr=paddle.attr.Param(name="sigmoid_b")) - return cost - else: - prediction = paddle.layer.fc( - size=dict_size - 1, - input=hidden_layer, - act=paddle.activation.Sigmoid(), - bias_attr=paddle.attr.Param(name="sigmoid_b"), - param_attr=paddle.attr.Param(name="sigmoid_w")) - return prediction + return paddle.layer.hsigmoid( + input=hidden_layer, + label=target_word, + param_attr=paddle.attr.Param(name="sigmoid_w"), + bias_attr=paddle.attr.Param(name="sigmoid_b")) ``` -需要注意,在预测阶段,我们需要对hsigmoid参数做一次转置,这里输出的类别数为词典大小减1,对应非叶节点的数量。 +需要注意在 PaddlePaddle 中,hsigmoid 层将可学习参数存储为一个 `[类别数目 - 1 × 隐层向量宽度]` 大小的矩阵。预测时,需要将 hsigmoid 层替换为全连接运算**并固定以 `sigmoid` 为激活**。预测时输出一个宽度为`[batch_size x 类别数目 - 1]` 维度的矩阵(`batch_size = 1`时退化为一个向量)。矩阵行向量的每一维计算了一个输入向量属于一个内部结点的右孩子的概率。**全连接运算在加载 hsigmoid 层学习到的参数矩阵时,需要对参数矩阵进行一次转置**。代码片段如下: + +```python +return paddle.layer.mixed( + size=dict_size - 1, + input=paddle.layer.trans_full_matrix_projection( + hidden_layer, param_attr=paddle.attr.Param(name="sigmoid_w")), + act=paddle.activation.Sigmoid(), + bias_attr=paddle.attr.Param(name="sigmoid_b")) +``` +上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。 ## 训练阶段 训练比较简单,直接运行``` python train.py ```。程序第一次运行会检测用户缓存文件夹中是否包含imikolov数据集,如果未包含,则自动下载。运行过程中,每100个iteration会打印模型训练信息,主要包含训练损失和测试损失,每个pass会保存一次模型。 ## 预测阶段 -预测时,直接运行``` python infer.py ```,程序会首先load模型,然后按照batch方式进行预测,并打印预测结果。预测阶段最重要的就是根据概率得到编码路径,然后遍历路径获取最终的预测类别,这部分逻辑如下: +在命令行运行 : +```bash +python infer.py \ + --model_path "models/XX" \ + --batch_size 1 \ + --use_gpu false \ + --trainer_count 1 +``` +参数含义如下: +- `model_path`:指定训练好的模型所在的路径。必选。 +- `batch_size`:一次预测并行的样本数目。可选,默认值为 `1`。 +- `use_gpu`:是否使用 GPU 进行预测。可选,默认值为 `False`。 +- `trainer_count` : 预测使用的线程数目。可选,默认为 `1`。**注意:预测使用的线程数目必选大于一次预测并行的样本数目**。 + +预测阶段根据多个二分类概率得到编码路径,遍历路径获取最终的预测类别,逻辑如下: ```python def decode_res(infer_res, dict_size): @@ -143,6 +156,7 @@ def decode_res(infer_res, dict_size): return predict_lbls ``` -预测程序的输入数据格式与训练阶段相同,如have a dream that one,程序会根据have a dream that生成一组概率,通过对概率解码生成预测词,one作为真实词,方便评估。解码函数的输入是一个batch样本的预测概率以及词表的大小,里面的循环是对每条样本的输出概率进行解码,解码方式就是按照左0右1的准则,不断遍历路径,直至到达叶子节点。需要注意的是,本文选用的数据集需要较长的时间训练才能得到较好的结果,预测程序选用第一轮的模型,仅为展示方便,学习效果不能保证。 +预测程序的输入数据格式与训练阶段相同,如`have a dream that one`,程序会根据`have a dream that`生成一组概率,通过对概率解码生成预测词,`one`作为真实词,方便评估。解码函数的输入是一个batch样本的预测概率以及词表的大小,里面的循环是对每条样本的输出概率进行解码,解码方式就是按照左0右1的准则,不断遍历路径,直至到达叶子节点。 + ## 参考文献 1. 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). diff --git a/hsigmoid/index.html b/hsigmoid/index.html index c53e110fdb80fabe5d82865fc5d4eb3512007d4a..738e81f25a01cfce2ef948f1bef70a38932de6fa 100644 --- a/hsigmoid/index.html +++ b/hsigmoid/index.html @@ -99,7 +99,7 @@ def train_data(filename, word_dict, n): 图2. 网络配置结构

-代码实现如下: +代码如下: ```python def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True): @@ -128,31 +128,44 @@ def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True): param_attr=paddle.attr.Param( initial_std=1. / math.sqrt(embed_size * 8), learning_rate=1)) - if is_train == True: - cost = paddle.layer.hsigmoid( - input=hidden_layer, - label=target_word, - num_classes=dict_size, - param_attr=paddle.attr.Param(name="sigmoid_w"), - bias_attr=paddle.attr.Param(name="sigmoid_b")) - return cost - else: - prediction = paddle.layer.fc( - size=dict_size - 1, - input=hidden_layer, - act=paddle.activation.Sigmoid(), - bias_attr=paddle.attr.Param(name="sigmoid_b"), - param_attr=paddle.attr.Param(name="sigmoid_w")) - return prediction + return paddle.layer.hsigmoid( + input=hidden_layer, + label=target_word, + param_attr=paddle.attr.Param(name="sigmoid_w"), + bias_attr=paddle.attr.Param(name="sigmoid_b")) ``` -需要注意,在预测阶段,我们需要对hsigmoid参数做一次转置,这里输出的类别数为词典大小减1,对应非叶节点的数量。 +需要注意在 PaddlePaddle 中,hsigmoid 层将可学习参数存储为一个 `[类别数目 - 1 × 隐层向量宽度]` 大小的矩阵。预测时,需要将 hsigmoid 层替换为全连接运算**并固定以 `sigmoid` 为激活**。预测时输出一个宽度为`[batch_size x 类别数目 - 1]` 维度的矩阵(`batch_size = 1`时退化为一个向量)。矩阵行向量的每一维计算了一个输入向量属于一个内部结点的右孩子的概率。**全连接运算在加载 hsigmoid 层学习到的参数矩阵时,需要对参数矩阵进行一次转置**。代码片段如下: + +```python +return paddle.layer.mixed( + size=dict_size - 1, + input=paddle.layer.trans_full_matrix_projection( + hidden_layer, param_attr=paddle.attr.Param(name="sigmoid_w")), + act=paddle.activation.Sigmoid(), + bias_attr=paddle.attr.Param(name="sigmoid_b")) +``` +上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。 ## 训练阶段 训练比较简单,直接运行``` python train.py ```。程序第一次运行会检测用户缓存文件夹中是否包含imikolov数据集,如果未包含,则自动下载。运行过程中,每100个iteration会打印模型训练信息,主要包含训练损失和测试损失,每个pass会保存一次模型。 ## 预测阶段 -预测时,直接运行``` python infer.py ```,程序会首先load模型,然后按照batch方式进行预测,并打印预测结果。预测阶段最重要的就是根据概率得到编码路径,然后遍历路径获取最终的预测类别,这部分逻辑如下: +在命令行运行 : +```bash +python infer.py \ + --model_path "models/XX" \ + --batch_size 1 \ + --use_gpu false \ + --trainer_count 1 +``` +参数含义如下: +- `model_path`:指定训练好的模型所在的路径。必选。 +- `batch_size`:一次预测并行的样本数目。可选,默认值为 `1`。 +- `use_gpu`:是否使用 GPU 进行预测。可选,默认值为 `False`。 +- `trainer_count` : 预测使用的线程数目。可选,默认为 `1`。**注意:预测使用的线程数目必选大于一次预测并行的样本数目**。 + +预测阶段根据多个二分类概率得到编码路径,遍历路径获取最终的预测类别,逻辑如下: ```python def decode_res(infer_res, dict_size): @@ -185,7 +198,8 @@ def decode_res(infer_res, dict_size): return predict_lbls ``` -预测程序的输入数据格式与训练阶段相同,如have a dream that one,程序会根据have a dream that生成一组概率,通过对概率解码生成预测词,one作为真实词,方便评估。解码函数的输入是一个batch样本的预测概率以及词表的大小,里面的循环是对每条样本的输出概率进行解码,解码方式就是按照左0右1的准则,不断遍历路径,直至到达叶子节点。需要注意的是,本文选用的数据集需要较长的时间训练才能得到较好的结果,预测程序选用第一轮的模型,仅为展示方便,学习效果不能保证。 +预测程序的输入数据格式与训练阶段相同,如`have a dream that one`,程序会根据`have a dream that`生成一组概率,通过对概率解码生成预测词,`one`作为真实词,方便评估。解码函数的输入是一个batch样本的预测概率以及词表的大小,里面的循环是对每条样本的输出概率进行解码,解码方式就是按照左0右1的准则,不断遍历路径,直至到达叶子节点。 + ## 参考文献 1. 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). diff --git a/hsigmoid/infer.py b/hsigmoid/infer.py index df6fd1f7c167da1e5ac10020869801d761c9d06e..36a15f50714dac6ce3d9390a7bf43f5b58a8efdf 100644 --- a/hsigmoid/infer.py +++ b/hsigmoid/infer.py @@ -1,6 +1,7 @@ import os import logging import gzip +import click import paddle.v2 as paddle from network_conf import ngram_lm @@ -51,10 +52,29 @@ def infer_a_batch(batch_ins, idx_word_dict, dict_size, inferer): for w in ins]) + " -> " + predict_words[i]) -def infer(model_path, batch_size): - assert os.path.exists(model_path), "trained model does not exist." - - paddle.init(use_gpu=False, trainer_count=1) +@click.command("infer") +@click.option( + "--model_path", + default="", + help="The path of the trained model for generation.") +@click.option( + "--batch_size", + default=1, + help="The number of testing examples in one forward batch in inferring.") +@click.option( + "--use_gpu", default=False, help="Whether to use GPU in inference or not.") +@click.option( + "--trainer_count", + default=1, + help="Whether to use GPU in inference or not.") +def infer(model_path, batch_size, use_gpu, trainer_count): + assert os.path.exists(model_path), "The trained model does not exist." + assert (batch_size and trainer_count and batch_size >= trainer_count), ( + "batch_size and trainer_count must both be greater than 0. " + "And batch_size must be equal to " + "or greater than trainer_count.") + + paddle.init(use_gpu=use_gpu, trainer_count=trainer_count) word_dict = paddle.dataset.imikolov.build_dict(min_word_freq=2) dict_size = len(word_dict) prediction_layer = ngram_lm( @@ -79,4 +99,4 @@ def infer(model_path, batch_size): if __name__ == "__main__": - infer("models/hsigmoid_batch_00010.tar.gz", 20) + infer() diff --git a/hsigmoid/network_conf.py b/hsigmoid/network_conf.py index 072c28c9219af801e6b154647c9fc5c48e67d457..721db84feb7e6e4ddedd197efea52cd85250654d 100644 --- a/hsigmoid/network_conf.py +++ b/hsigmoid/network_conf.py @@ -1,6 +1,7 @@ import math import paddle.v2 as paddle +from paddle.v2.layer import parse_network def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True): @@ -30,18 +31,28 @@ def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True): initial_std=1. / math.sqrt(embed_size * 8), learning_rate=1)) if is_train == True: - cost = paddle.layer.hsigmoid( + return paddle.layer.hsigmoid( input=hidden_layer, label=target_word, num_classes=dict_size, param_attr=paddle.attr.Param(name="sigmoid_w"), bias_attr=paddle.attr.Param(name="sigmoid_b")) - return cost else: - prediction = paddle.layer.fc( + return paddle.layer.mixed( size=dict_size - 1, - input=hidden_layer, + input=paddle.layer.trans_full_matrix_projection( + hidden_layer, param_attr=paddle.attr.Param(name="sigmoid_w")), act=paddle.activation.Sigmoid(), - bias_attr=paddle.attr.Param(name="sigmoid_b"), - param_attr=paddle.attr.Param(name="sigmoid_w")) - return prediction + bias_attr=paddle.attr.Param(name="sigmoid_b")) + + +if __name__ == "__main__": + # this is to test and debug the network topology defination. + # please set the hyper-parameters as needed. + print(parse_network( + ngram_lm( + hidden_size=512, + embed_size=512, + dict_size=1024, + gram_num=4, + is_train=False))) diff --git a/hsigmoid/train.py b/hsigmoid/train.py index b78a6bfe4c064490b6bd0c549e2e76aee9bce337..75769c30ac015dfa3eb1438edf260c94ce981c09 100644 --- a/hsigmoid/train.py +++ b/hsigmoid/train.py @@ -16,6 +16,11 @@ def main(save_dir="models"): paddle.init(use_gpu=False, trainer_count=1) word_dict = paddle.dataset.imikolov.build_dict(min_word_freq=2) dict_size = len(word_dict) + + adam_optimizer = paddle.optimizer.Adam( + learning_rate=3e-3, + regularization=paddle.optimizer.L2Regularization(8e-4)) + cost = ngram_lm(hidden_size=256, embed_size=32, dict_size=dict_size) parameters = paddle.parameters.create(cost) diff --git a/nce_cost/README.md b/nce_cost/README.md index 81c26c288874d59d1a3da2f6ca6fdd3cfe304d8a..c0001ea49bcbb52418b6f4c4d438d6d81d1ec7e4 100644 --- a/nce_cost/README.md +++ b/nce_cost/README.md @@ -17,7 +17,7 @@ J^h(\theta )=E_{ P_d^h }\left[ \log { P^h(D=1|w,\theta ) } \right] +kE_{ P_n }\ $$ \\\\\qquad =E_{ P_d^h }\left[ \log { \sigma (\Delta s_\theta(w,h)) } \right] +kE_{ P_n }\left[ \log (1-\sigma (\Delta s_\theta(w,h))) \right]$$ -总体上来说,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `softmax ` 分类需要计算每个类别的分数,然后归一化得到概率,节约了大量的计算时间。 +简单来讲,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `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) 接口来方便调用这些数据,如果当前目录没有找到数据它会自动下载并验证文件的完整性。并提供大小为5的滑动窗口对数据做预处理工作,方便后期处理。语料语种为英文,共有42068句训练数据,3761句测试数据。 @@ -52,7 +52,7 @@ N-gram 神经概率语言模型详细网络结构见图1: 在模型文件`network_conf.py`中 NCE 调用代码如下: ```python -cost = paddle.layer.nce( +return paddle.layer.nce( input=hidden_layer, label=next_word, num_classes=dict_size, @@ -73,22 +73,34 @@ NCE 层的一些重要参数解释如下: | act | 使用何种激活函数| 根据 NCE 的原理,这里应该使用 sigmoid 函数 | ## 预测 -1. 首先修改 `infer.py` 脚本的 `main` 函数指定需要测试的模型。 -2. 需要注意的是,**预测和训练的计算逻辑不同**,需要以一个全连接层:`paddle.layer.fc`替换训练使用的`paddle.train.nce`层, 并直接加载NCE学习到的参数,代码如下: - - ```python - prediction = paddle.layer.fc( - size=dict_size, - act=paddle.activation.Softmax(), - bias_attr=paddle.attr.Param(name="nce_b"), - input=hidden_layer, - param_attr=paddle.attr.Param(name="nce_w")) - ``` -3. 运行 `python infer.py`。程序首先会加载指定的模型,然后按照 batch 大小依次进行预测,并打印预测结果。预测的输出格式如下: - +1. 在命令行运行 : + ```bash + python infer.py \ + --model_path "models/XX" \ + --batch_size 1 \ + --use_gpu false \ + --trainer_count 1 + ``` + 参数含义如下: + - `model_path`:指定训练好的模型所在的路径。必选。 + - `batch_size`:一次预测并行的样本数目。可选,默认值为 `1`。 + - `use_gpu`:是否使用 GPU 进行预测。可选,默认值为 `False`。 + - `trainer_count` : 预测使用的线程数目。可选,默认为 `1`。**注意:预测使用的线程数目必选大于一次预测并行的样本数目**。 + +2. 需要注意的是:**预测和训练的计算逻辑不同**。预测使用全连接矩阵乘法后接`softmax`激活,输出基于各类别的概率分布,需要替换训练中使用的`paddle.train.nce`层。在PaddlePaddle中,NCE层将可学习参数存储为一个 `[类别数目 × 上一层输出向量宽度]` 大小的矩阵,预测时,**全连接运算在加载NCE层学习到参数时,需要进行转置**,代码如下: + ```python + return paddle.layer.mixed( + size=dict_size, + input=paddle.layer.trans_full_matrix_projection( + hidden_layer, param_attr=paddle.attr.Param(name="nce_w")), + act=paddle.activation.Sigmoid(), + bias_attr=paddle.attr.Param(name="nce_b")) + ``` + 上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。 + +3. 预测的输出格式如下: ```text 0.6734 their may want to move - ``` 每一行是一条预测结果,内部以“\t”分隔,共计3列: diff --git a/nce_cost/index.html b/nce_cost/index.html index 8ceaf6859452f8123ffcca862895a4257a282fe0..ba3ae6fbf04f4ddddeaac0d2d92a81926bc87ca9 100644 --- a/nce_cost/index.html +++ b/nce_cost/index.html @@ -59,7 +59,7 @@ J^h(\theta )=E_{ P_d^h }\left[ \log { P^h(D=1|w,\theta ) } \right] +kE_{ P_n }\ $$ \\\\\qquad =E_{ P_d^h }\left[ \log { \sigma (\Delta s_\theta(w,h)) } \right] +kE_{ P_n }\left[ \log (1-\sigma (\Delta s_\theta(w,h))) \right]$$ -总体上来说,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `softmax ` 分类需要计算每个类别的分数,然后归一化得到概率,节约了大量的计算时间。 +简单来讲,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `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) 接口来方便调用这些数据,如果当前目录没有找到数据它会自动下载并验证文件的完整性。并提供大小为5的滑动窗口对数据做预处理工作,方便后期处理。语料语种为英文,共有42068句训练数据,3761句测试数据。 @@ -94,7 +94,7 @@ N-gram 神经概率语言模型详细网络结构见图1: 在模型文件`network_conf.py`中 NCE 调用代码如下: ```python -cost = paddle.layer.nce( +return paddle.layer.nce( input=hidden_layer, label=next_word, num_classes=dict_size, @@ -115,22 +115,34 @@ NCE 层的一些重要参数解释如下: | act | 使用何种激活函数| 根据 NCE 的原理,这里应该使用 sigmoid 函数 | ## 预测 -1. 首先修改 `infer.py` 脚本的 `main` 函数指定需要测试的模型。 -2. 需要注意的是,**预测和训练的计算逻辑不同**,需要以一个全连接层:`paddle.layer.fc`替换训练使用的`paddle.train.nce`层, 并直接加载NCE学习到的参数,代码如下: - - ```python - prediction = paddle.layer.fc( - size=dict_size, - act=paddle.activation.Softmax(), - bias_attr=paddle.attr.Param(name="nce_b"), - input=hidden_layer, - param_attr=paddle.attr.Param(name="nce_w")) - ``` -3. 运行 `python infer.py`。程序首先会加载指定的模型,然后按照 batch 大小依次进行预测,并打印预测结果。预测的输出格式如下: - +1. 在命令行运行 : + ```bash + python infer.py \ + --model_path "models/XX" \ + --batch_size 1 \ + --use_gpu false \ + --trainer_count 1 + ``` + 参数含义如下: + - `model_path`:指定训练好的模型所在的路径。必选。 + - `batch_size`:一次预测并行的样本数目。可选,默认值为 `1`。 + - `use_gpu`:是否使用 GPU 进行预测。可选,默认值为 `False`。 + - `trainer_count` : 预测使用的线程数目。可选,默认为 `1`。**注意:预测使用的线程数目必选大于一次预测并行的样本数目**。 + +2. 需要注意的是:**预测和训练的计算逻辑不同**。预测使用全连接矩阵乘法后接`softmax`激活,输出基于各类别的概率分布,需要替换训练中使用的`paddle.train.nce`层。在PaddlePaddle中,NCE层将可学习参数存储为一个 `[类别数目 × 上一层输出向量宽度]` 大小的矩阵,预测时,**全连接运算在加载NCE层学习到参数时,需要进行转置**,代码如下: + ```python + return paddle.layer.mixed( + size=dict_size, + input=paddle.layer.trans_full_matrix_projection( + hidden_layer, param_attr=paddle.attr.Param(name="nce_w")), + act=paddle.activation.Sigmoid(), + bias_attr=paddle.attr.Param(name="nce_b")) + ``` + 上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。 + +3. 预测的输出格式如下: ```text 0.6734 their may want to move - ``` 每一行是一条预测结果,内部以“\t”分隔,共计3列: diff --git a/nce_cost/infer.py b/nce_cost/infer.py index db1c8c619c8ce6fcf66a59eb31142a736d722b15..5498c84868c6d6fc0c1fe62363bc8701cc9b0942 100644 --- a/nce_cost/infer.py +++ b/nce_cost/infer.py @@ -1,5 +1,6 @@ import os import gzip +import click import numpy as np import paddle.v2 as paddle @@ -14,13 +15,33 @@ def infer_a_batch(inferer, test_batch, id_to_word): " ".join([id_to_word[w] for w in res[0]]))) -def infer(model_path, batch_size): - assert os.path.exists(model_path), "the trained model does not exist." +@click.command("infer") +@click.option( + "--model_path", + default="", + help="The path of the trained model for generation.") +@click.option( + "--batch_size", + default=1, + help="The number of testing examples in one forward batch in inferring.") +@click.option( + "--use_gpu", default=False, help="Whether to use GPU in inference or not.") +@click.option( + "--trainer_count", + default=1, + help="Whether to use GPU in inference or not.") +def infer(model_path, batch_size, use_gpu, trainer_count): + assert os.path.exists(model_path), "The trained model does not exist." + assert (batch_size and trainer_count and batch_size >= trainer_count), ( + "batch_size and trainer_count must both be greater than 0. " + "And batch_size must be equal to " + "or greater than trainer_count.") + word_to_id = paddle.dataset.imikolov.build_dict() id_to_word = dict((v, k) for k, v in word_to_id.items()) dict_size = len(word_to_id) - paddle.init(use_gpu=False, trainer_count=1) + paddle.init(use_gpu=use_gpu, trainer_count=trainer_count) # load the trained model. with gzip.open(model_path) as f: @@ -44,4 +65,4 @@ def infer(model_path, batch_size): if __name__ == "__main__": - infer("models/model_pass_00000_00020.tar.gz", 10) + infer() diff --git a/nce_cost/network_conf.py b/nce_cost/network_conf.py index a37b031da462199ce5eca6497a0cbd62b4f7daac..f226bc992fff91fa0e578578793212dcd67d6bb0 100644 --- a/nce_cost/network_conf.py +++ b/nce_cost/network_conf.py @@ -1,5 +1,7 @@ import math + import paddle.v2 as paddle +from paddle.v2.layer import parse_network def ngram_lm(hidden_size, emb_size, dict_size, gram_num=4, is_train=True): @@ -13,7 +15,6 @@ def ngram_lm(hidden_size, emb_size, dict_size, gram_num=4, is_train=True): emb_layers.append( paddle.layer.embedding( input=word, size=emb_size, param_attr=embed_param_attr)) - next_word = paddle.layer.data( name="__target_word__", type=paddle.data_type.integer_value(dict_size)) @@ -26,7 +27,7 @@ def ngram_lm(hidden_size, emb_size, dict_size, gram_num=4, is_train=True): param_attr=paddle.attr.Param(initial_std=1. / math.sqrt(emb_size * 8))) if is_train: - cost = paddle.layer.nce( + return paddle.layer.nce( input=hidden_layer, label=next_word, num_classes=dict_size, @@ -35,13 +36,22 @@ def ngram_lm(hidden_size, emb_size, dict_size, gram_num=4, is_train=True): act=paddle.activation.Sigmoid(), num_neg_samples=25, neg_distribution=None) - return cost else: - prediction = paddle.layer.fc( + return paddle.layer.mixed( size=dict_size, - act=paddle.activation.Softmax(), - bias_attr=paddle.attr.Param(name="nce_b"), - input=hidden_layer, - param_attr=paddle.attr.Param(name="nce_w")) + input=paddle.layer.trans_full_matrix_projection( + hidden_layer, param_attr=paddle.attr.Param(name="nce_w")), + act=paddle.activation.Sigmoid(), + bias_attr=paddle.attr.Param(name="nce_b")) + - return prediction +if __name__ == "__main__": + # this is to test and debug the network topology defination. + # please set the hyper-parameters as needed. + print(parse_network( + ngram_lm( + hidden_size=256, + emb_size=256, + dict_size=1024, + gram_num=4, + is_train=True))) diff --git a/nce_cost/train.py b/nce_cost/train.py index 40d899be838f67284c6e2a7afa28ff213be213e6..e76df867d9091193b71281cc5f91ba27cee19e40 100644 --- a/nce_cost/train.py +++ b/nce_cost/train.py @@ -28,8 +28,7 @@ def train(model_save_dir): if event.batch_id and not event.batch_id % 10: logger.info("Pass %d, Batch %d, Cost %f" % (event.pass_id, event.batch_id, event.cost)) - - if isinstance(event, paddle.event.EndPass): + elif isinstance(event, paddle.event.EndPass): result = trainer.test( paddle.batch(paddle.dataset.imikolov.test(word_dict, 5), 64)) logger.info("Test Pass %d, Cost %f" % (event.pass_id, result.cost))