Skip to content

Commit 249aa15

Browse files
committed
fix the bug that parameters are note transposed.
1 parent d53d098 commit 249aa15

File tree

10 files changed

+149
-92
lines changed

10 files changed

+149
-92
lines changed

hsigmoid/README.md

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ def train_data(filename, word_dict, n):
5757
图2. 网络配置结构
5858
</p>
5959

60-
代码实现如下
60+
代码如下
6161

6262
```python
6363
def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True):
@@ -86,31 +86,30 @@ def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True):
8686
param_attr=paddle.attr.Param(
8787
initial_std=1. / math.sqrt(embed_size * 8), learning_rate=1))
8888

89-
if is_train == True:
90-
cost = paddle.layer.hsigmoid(
91-
input=hidden_layer,
92-
label=target_word,
93-
num_classes=dict_size,
94-
param_attr=paddle.attr.Param(name="sigmoid_w"),
95-
bias_attr=paddle.attr.Param(name="sigmoid_b"))
96-
return cost
97-
else:
98-
prediction = paddle.layer.fc(
99-
size=dict_size - 1,
100-
input=hidden_layer,
101-
act=paddle.activation.Sigmoid(),
102-
bias_attr=paddle.attr.Param(name="sigmoid_b"),
103-
param_attr=paddle.attr.Param(name="sigmoid_w"))
104-
return prediction
89+
return paddle.layer.hsigmoid(
90+
input=hidden_layer,
91+
label=target_word,
92+
param_attr=paddle.attr.Param(name="sigmoid_w"),
93+
bias_attr=paddle.attr.Param(name="sigmoid_b"))
10594
```
10695

107-
需要注意,在预测阶段,我们需要对hsigmoid参数做一次转置,这里输出的类别数为词典大小减1,对应非叶节点的数量。
96+
需要注意在 PaddlePaddle 中,hsigmoid 层将可学习参数存储为一个 `[类别数目 - 1 × 隐层向量宽度]` 大小的矩阵。预测时,需要将 hsigmoid 层替换为全连接运算**并固定以 `sigmoid` 为激活**。预测时输出一个宽度为`[batch_size x 类别数目 - 1]` 维度的矩阵(`batch_size = 1`时退化为一个向量)。矩阵行向量的每一维计算了一个输入向量属于一个内部结点的右孩子的概率。**全连接运算在加载 hsigmoid 层学习到的参数矩阵时,需要对参数矩阵进行一次转置**。代码片段如下:
97+
98+
```python
99+
return paddle.layer.mixed(
100+
size=dict_size - 1,
101+
input=paddle.layer.trans_full_matrix_projection(
102+
hidden_layer, param_attr=paddle.attr.Param(name="sigmoid_w")),
103+
act=paddle.activation.Sigmoid(),
104+
bias_attr=paddle.attr.Param(name="sigmoid_b"))
105+
```
106+
上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。
108107

109108
## 训练阶段
110109
训练比较简单,直接运行``` python train.py ```。程序第一次运行会检测用户缓存文件夹中是否包含imikolov数据集,如果未包含,则自动下载。运行过程中,每100个iteration会打印模型训练信息,主要包含训练损失和测试损失,每个pass会保存一次模型。
111110

112111
## 预测阶段
113-
预测时,直接运行``` python infer.py ```程序会首先load模型,然后按照batch方式进行预测,并打印预测结果。预测阶段最重要的就是根据概率得到编码路径,然后遍历路径获取最终的预测类别,这部分逻辑如下:
112+
预测时,在命令行运行 `infer.py --model_path XX`,通过`model_path`指定训练好的模型所在的路径。程序会首先load模型,然后按照batch方式进行预测,并打印预测结果。预测阶段最重要的就是根据概率得到编码路径,然后遍历路径获取最终的预测类别,这部分逻辑如下:
114113

115114
```python
116115
def decode_res(infer_res, dict_size):

hsigmoid/index.html

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
图2. 网络配置结构
100100
</p>
101101

102-
代码实现如下
102+
代码如下
103103

104104
```python
105105
def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True):
@@ -128,31 +128,30 @@
128128
param_attr=paddle.attr.Param(
129129
initial_std=1. / math.sqrt(embed_size * 8), learning_rate=1))
130130

131-
if is_train == True:
132-
cost = paddle.layer.hsigmoid(
133-
input=hidden_layer,
134-
label=target_word,
135-
num_classes=dict_size,
136-
param_attr=paddle.attr.Param(name="sigmoid_w"),
137-
bias_attr=paddle.attr.Param(name="sigmoid_b"))
138-
return cost
139-
else:
140-
prediction = paddle.layer.fc(
141-
size=dict_size - 1,
142-
input=hidden_layer,
143-
act=paddle.activation.Sigmoid(),
144-
bias_attr=paddle.attr.Param(name="sigmoid_b"),
145-
param_attr=paddle.attr.Param(name="sigmoid_w"))
146-
return prediction
131+
return paddle.layer.hsigmoid(
132+
input=hidden_layer,
133+
label=target_word,
134+
param_attr=paddle.attr.Param(name="sigmoid_w"),
135+
bias_attr=paddle.attr.Param(name="sigmoid_b"))
147136
```
148137

149-
需要注意,在预测阶段,我们需要对hsigmoid参数做一次转置,这里输出的类别数为词典大小减1,对应非叶节点的数量。
138+
需要注意在 PaddlePaddle 中,hsigmoid 层将可学习参数存储为一个 `[类别数目 - 1 × 隐层向量宽度]` 大小的矩阵。预测时,需要将 hsigmoid 层替换为全连接运算**并固定以 `sigmoid` 为激活**。预测时输出一个宽度为`[batch_size x 类别数目 - 1]` 维度的矩阵(`batch_size = 1`时退化为一个向量)。矩阵行向量的每一维计算了一个输入向量属于一个内部结点的右孩子的概率。**全连接运算在加载 hsigmoid 层学习到的参数矩阵时,需要对参数矩阵进行一次转置**。代码片段如下:
139+
140+
```python
141+
return paddle.layer.mixed(
142+
size=dict_size - 1,
143+
input=paddle.layer.trans_full_matrix_projection(
144+
hidden_layer, param_attr=paddle.attr.Param(name="sigmoid_w")),
145+
act=paddle.activation.Sigmoid(),
146+
bias_attr=paddle.attr.Param(name="sigmoid_b"))
147+
```
148+
上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。
150149

151150
## 训练阶段
152151
训练比较简单,直接运行``` python train.py ```。程序第一次运行会检测用户缓存文件夹中是否包含imikolov数据集,如果未包含,则自动下载。运行过程中,每100个iteration会打印模型训练信息,主要包含训练损失和测试损失,每个pass会保存一次模型。
153152

154153
## 预测阶段
155-
预测时,直接运行``` python infer.py ```,程序会首先load模型,然后按照batch方式进行预测,并打印预测结果。预测阶段最重要的就是根据概率得到编码路径,然后遍历路径获取最终的预测类别,这部分逻辑如下:
154+
预测时,在命令行运行 `infer.py --model_path XX`,通过`model_path`指定训练好的模型所在的路径。程序会首先load模型,然后按照batch方式进行预测,并打印预测结果。预测阶段最重要的就是根据概率得到编码路径,然后遍历路径获取最终的预测类别,这部分逻辑如下:
156155

157156
```python
158157
def decode_res(infer_res, dict_size):

hsigmoid/infer.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import os
22
import logging
33
import gzip
4+
import click
45

56
import paddle.v2 as paddle
67
from network_conf import ngram_lm
@@ -51,7 +52,22 @@ def infer_a_batch(batch_ins, idx_word_dict, dict_size, inferer):
5152
for w in ins]) + " -> " + predict_words[i])
5253

5354

54-
def infer(model_path, batch_size):
55+
@click.command("infer")
56+
@click.option(
57+
"--model_path",
58+
default="",
59+
help="The path of the trained model for generation.")
60+
@click.option(
61+
"--batch_size",
62+
default=1,
63+
help="The number of testing examples in one forward batch in inferring.")
64+
@click.option(
65+
"--use_gpu", default=False, help="Whether to use GPU in inference or not.")
66+
@click.option(
67+
"--trainer_count",
68+
default=1,
69+
help="Whether to use GPU in inference or not.")
70+
def infer(model_path, batch_size, use_gpu, trainer_count):
5571
assert os.path.exists(model_path), "trained model does not exist."
5672

5773
paddle.init(use_gpu=False, trainer_count=1)
@@ -79,4 +95,4 @@ def infer(model_path, batch_size):
7995

8096

8197
if __name__ == "__main__":
82-
infer("models/hsigmoid_batch_00010.tar.gz", 20)
98+
infer()

hsigmoid/network_conf.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import math
22

33
import paddle.v2 as paddle
4+
from paddle.v2.layer import parse_network
45

56

67
def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True):
@@ -30,18 +31,27 @@ def ngram_lm(hidden_size, embed_size, dict_size, gram_num=4, is_train=True):
3031
initial_std=1. / math.sqrt(embed_size * 8), learning_rate=1))
3132

3233
if is_train == True:
33-
cost = paddle.layer.hsigmoid(
34+
return paddle.layer.hsigmoid(
3435
input=hidden_layer,
3536
label=target_word,
36-
num_classes=dict_size,
3737
param_attr=paddle.attr.Param(name="sigmoid_w"),
3838
bias_attr=paddle.attr.Param(name="sigmoid_b"))
39-
return cost
4039
else:
41-
prediction = paddle.layer.fc(
40+
return paddle.layer.mixed(
4241
size=dict_size - 1,
43-
input=hidden_layer,
42+
input=paddle.layer.trans_full_matrix_projection(
43+
hidden_layer, param_attr=paddle.attr.Param(name="sigmoid_w")),
4444
act=paddle.activation.Sigmoid(),
45-
bias_attr=paddle.attr.Param(name="sigmoid_b"),
46-
param_attr=paddle.attr.Param(name="sigmoid_w"))
47-
return prediction
45+
bias_attr=paddle.attr.Param(name="sigmoid_b"))
46+
47+
48+
if __name__ == "__main__":
49+
# this is to test and debug the network topology defination.
50+
# please set the hyper-parameters as needed.
51+
print(parse_network(
52+
ngram_lm(
53+
hidden_size=512,
54+
embed_size=512,
55+
dict_size=1024,
56+
gram_num=4,
57+
is_train=False)))

hsigmoid/train.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,13 @@ def main(save_dir="models"):
1616
paddle.init(use_gpu=False, trainer_count=1)
1717
word_dict = paddle.dataset.imikolov.build_dict(min_word_freq=2)
1818
dict_size = len(word_dict)
19+
20+
adam_optimizer = paddle.optimizer.Adam(
21+
learning_rate=3e-3,
22+
regularization=paddle.optimizer.L2Regularization(8e-4))
23+
1924
cost = ngram_lm(hidden_size=256, embed_size=32, dict_size=dict_size)
25+
parameters = paddle.parameters.create(cost)
2026

2127
def event_handler(event):
2228
if isinstance(event, paddle.event.EndPass):
@@ -35,10 +41,6 @@ def event_handler(event):
3541
"Pass %d, Batch %d, Cost %f, Test Cost %f" %
3642
(event.pass_id, event.batch_id, event.cost, result.cost))
3743

38-
parameters = paddle.parameters.create(cost)
39-
adam_optimizer = paddle.optimizer.Adam(
40-
learning_rate=3e-3,
41-
regularization=paddle.optimizer.L2Regularization(8e-4))
4244
trainer = paddle.trainer.SGD(cost, parameters, adam_optimizer)
4345

4446
trainer.train(

nce_cost/README.md

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ J^h(\theta )=E_{ P_d^h }\left[ \log { P^h(D=1|w,\theta ) } \right] +kE_{ P_n }\
1717
$$
1818
\\\\\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]$$
1919

20-
总体上来说,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `softmax ` 分类需要计算每个类别的分数,然后归一化得到概率,节约了大量的计算时间。
20+
简单来讲,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `softmax ` 分类需要计算每个类别的分数,然后归一化得到概率,节约了大量的计算时间。
2121

2222
## 实验数据
2323
本文采用 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:
5252
在模型文件`network_conf.py`中 NCE 调用代码如下:
5353

5454
```python
55-
cost = paddle.layer.nce(
55+
return paddle.layer.nce(
5656
input=hidden_layer,
5757
label=next_word,
5858
num_classes=dict_size,
@@ -73,22 +73,22 @@ NCE 层的一些重要参数解释如下:
7373
| act | 使用何种激活函数| 根据 NCE 的原理,这里应该使用 sigmoid 函数 |
7474

7575
## 预测
76-
1. 首先修改 `infer.py` 脚本的 `main` 函数指定需要测试的模型。
77-
2. 需要注意的是,**预测和训练的计算逻辑不同**,需要以一个全连接层:`paddle.layer.fc`替换训练使用的`paddle.train.nce`层, 并直接加载NCE学习到的参数,代码如下:
78-
79-
```python
80-
prediction = paddle.layer.fc(
81-
size=dict_size,
82-
act=paddle.activation.Softmax(),
83-
bias_attr=paddle.attr.Param(name="nce_b"),
84-
input=hidden_layer,
85-
param_attr=paddle.attr.Param(name="nce_w"))
86-
```
76+
1. 在命令行运行 `infer.py --model_path XX`,通过`model_path`指定训练好的模型所在的路径。
77+
2. 需要注意的是:**预测和训练的计算逻辑不同**。预测使用全连接矩阵乘法后接`softmax`激活,输出基于各类别的概率分布,需要替换训练中使用的`paddle.train.nce`层。在PaddlePaddle中,NCE层将可学习参数存储为一个 `[类别数目 × 上一层输出向量宽度]` 大小的矩阵,预测时,**全连接运算在加载NCE层学习到参数时,需要进行转置**,代码如下:
78+
```python
79+
return paddle.layer.mixed(
80+
size=dict_size,
81+
input=paddle.layer.trans_full_matrix_projection(
82+
hidden_layer, param_attr=paddle.attr.Param(name="nce_w")),
83+
act=paddle.activation.Sigmoid(),
84+
bias_attr=paddle.attr.Param(name="nce_b"))
85+
```
86+
上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。
87+
8788
3. 运行 `python infer.py`。程序首先会加载指定的模型,然后按照 batch 大小依次进行预测,并打印预测结果。预测的输出格式如下:
8889

8990
```text
9091
0.6734 their may want to move
91-
9292
```
9393

9494
每一行是一条预测结果,内部以“\t”分隔,共计3列:

nce_cost/index.html

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
$$
6060
\\\\\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]$$
6161

62-
总体上来说,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `softmax ` 分类需要计算每个类别的分数,然后归一化得到概率,节约了大量的计算时间。
62+
简单来讲,NCE 是通过构造逻辑回归(logistic regression),对正样例和负样例做二分类,对于每一个样本,将自身的预测词 label 作为正样例,同时采样出 $k$ 个其他词 label 作为负样例,从而只需要计算样本在这 $k+1$ 个 label 上的概率。相比原始的 `softmax ` 分类需要计算每个类别的分数,然后归一化得到概率,节约了大量的计算时间。
6363

6464
## 实验数据
6565
本文采用 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 @@
9494
在模型文件`network_conf.py`中 NCE 调用代码如下:
9595

9696
```python
97-
cost = paddle.layer.nce(
97+
return paddle.layer.nce(
9898
input=hidden_layer,
9999
label=next_word,
100100
num_classes=dict_size,
@@ -115,22 +115,22 @@
115115
| act | 使用何种激活函数| 根据 NCE 的原理,这里应该使用 sigmoid 函数 |
116116

117117
## 预测
118-
1. 首先修改 `infer.py` 脚本的 `main` 函数指定需要测试的模型。
119-
2. 需要注意的是,**预测和训练的计算逻辑不同**,需要以一个全连接层:`paddle.layer.fc`替换训练使用的`paddle.train.nce`层, 并直接加载NCE学习到的参数,代码如下:
120-
121-
```python
122-
prediction = paddle.layer.fc(
123-
size=dict_size,
124-
act=paddle.activation.Softmax(),
125-
bias_attr=paddle.attr.Param(name="nce_b"),
126-
input=hidden_layer,
127-
param_attr=paddle.attr.Param(name="nce_w"))
128-
```
118+
1. 在命令行运行 `infer.py --model_path XX`,通过`model_path`指定训练好的模型所在的路径。
119+
2. 需要注意的是:**预测和训练的计算逻辑不同**。预测使用全连接矩阵乘法后接`softmax`激活,输出基于各类别的概率分布,需要替换训练中使用的`paddle.train.nce`层。在PaddlePaddle中,NCE层将可学习参数存储为一个 `[类别数目 × 上一层输出向量宽度]` 大小的矩阵,预测时,**全连接运算在加载NCE层学习到参数时,需要进行转置**,代码如下:
120+
```python
121+
return paddle.layer.mixed(
122+
size=dict_size,
123+
input=paddle.layer.trans_full_matrix_projection(
124+
hidden_layer, param_attr=paddle.attr.Param(name="nce_w")),
125+
act=paddle.activation.Sigmoid(),
126+
bias_attr=paddle.attr.Param(name="nce_b"))
127+
```
128+
上述代码片段中的 `paddle.layer.mixed` 必须以 PaddlePaddle 中 `paddle.layer.×_projection` 为输入。`paddle.layer.mixed` 将多个 `projection` (输入可以是多个)计算结果求和作为输出。`paddle.layer.trans_full_matrix_projection` 在计算矩阵乘法时会对参数$W$进行转置。
129+
129130
3. 运行 `python infer.py`。程序首先会加载指定的模型,然后按照 batch 大小依次进行预测,并打印预测结果。预测的输出格式如下:
130131

131132
```text
132133
0.6734 their may want to move
133-
134134
```
135135

136136
每一行是一条预测结果,内部以“\t”分隔,共计3列:

0 commit comments

Comments
 (0)