README.md 25.0 KB
Newer Older
W
wangxuguang 已提交
1 2
# 情感分析
## 背景介绍
W
wangxuguang 已提交
3 4 5
在自然语言处理中,情感分析一般是指判断一段文本所表达的情绪状态。其中,一段文本可以是一个句子,一个段落或一个文档。情绪状态可以是两类,如(正面,负面),(高兴,悲伤);也可以是三类,如(积极,消极,中性)等等。   

情感分析的应用场景十分广泛,如把用户在购物网站(亚马逊、天猫、淘宝等)、旅游网站、电影评论网站上发表的评论分成正面评论和负面评论。为了分析用户对于某一产品的整体使用感受,抓取产品的用户评论并进行情感分析等等。表格1展示了对电影评论进行情感分析的例子:
W
wangxuguang 已提交
6 7 8 9 10

| 电影评论       | 类别  |
| --------     | -----  |
| 在冯小刚这几年的电影里,算最好的一部的了| 正面 |
| 很不好看,好像一个地方台的电视剧     | 负面 |
W
wangxuguang 已提交
11 12
| 圆方镜头全程炫技,色调背景美则美矣,但剧情拖沓,口音不伦不类,一直努力却始终无法入戏| 负面|
|剧情四星。但是圆镜视角加上婺源的风景整个非常有中国写意山水画的感觉,看得实在太舒服了。。|正面|
W
wangxuguang 已提交
13

14
<p align="center">表格 1 电影评论情感分析</p>
W
wangxuguang 已提交
15 16

在自然语言处理中,情感分析属于典型的**文本分类**问题,即,把需要进行情感分析的文本划分为其所属类别。文本分类涉及文本表示和分类方法。在深度学习的方法出现之前,主流的文本表示方法为BOW(bag of words),话题模型等等;分类方法有SVM(support vector machine), LR(logistic regression), [Boosting](https://en.wikipedia.org/wiki/Boosting_(machine_learning))等等。BOW忽略了词的顺序信息,而且是高维度的稀疏向量表示,它并不能充分表示文本的语义信息。例如,句子“这部电影糟糕透了”和“一个乏味,空洞,没有内涵的作品”在情感分析中具有很高的语义相似度,但是它们的BOW表示的相似度为0。又如,句子“一个空洞,没有内涵的作品”和“一个不空洞而且有内涵的作品”的BOW相似度很高,但实际上它们的意思很不一样。本章我们所要介绍的深度学习模型克服了BOW表示的上述缺陷,它在考虑词的顺序的基础上把文本映射到低维度的语义空间,并且以端对端(end to end)的方式进行文本表示及分类,其性能相对于传统方法有显著的提升。
W
wangxuguang 已提交
17
## 模型概览
W
wangxuguang 已提交
18 19 20
本章所使用的文本表示模型为卷积神经网络(Convolutional Neural Networks)和循环神经网络(Recurrent Neural Networks)及其拓展。我们首先介绍处理文本的卷积神经网络。
### 文本卷积神经网络(CNN)
卷积神经网络经常用来处理具有类似网格拓扑结构(grid-like topology)的数据。例如,图像可以视为2D网格的像素点,自然语言可以视为1D的词序列。卷积神经网络可以提取多种局部特征,并对其进行组合抽象得到更高级的特征表示,且其对于数据的某些变化具有不变性。大量实验表明,卷积神经网络能高效的对图像及文本问题进行建模处理。本小结我们讲解如何使用卷积神经网络处理文本(以句子为例)\[[1](#参考文献)\]
21
<p align="center">
W
wangxuguang 已提交
22
<img src="image/text_cnn.png" width = "90%" height = "90%" align="center"/><br/>
23 24
图 1 卷积神经网络文本分类模型
</p>
W
wangxuguang 已提交
25
假设一个句子的长度为$n$,其中第$i$个词的词向量(word embedding)为$x_i\in\mathbb{R}^k$,维度大小为$k$。我们可以将整个句子表示为$x_{1:n}=x_1\oplus x_2\oplus \ldots \oplus x_n$,其中,$\oplus$表示拼接(concatenation)操作。一般地,我们用$x_{i:i+j}$表示词序列$x_{i},x_{i+1},\ldots,x_{i+j}$的拼接。卷积操作把卷积核(kernel)$w\in\mathbb{R}^{hk}$应用于包含$h$个词的窗口$x_{i:i+h-1}$,得到这$h$个词的特征值$c_i$:
W
wangxuguang 已提交
26

W
wangxuguang 已提交
27
$$c_i=f(w\cdot x_{i:i+h-1}+b)$$
W
wangxuguang 已提交
28

W
wangxuguang 已提交
29
其中$b\in\mathbb{R}$为偏置项(bias),$f$为非线性激活函数,如$sigmoid$。将卷积核应用于句子中所有的词窗口${x_{1:h},x_{2:h+1},\ldots,x_{n-h+1:n}}$,产生一个特征图(feature map):
W
wangxuguang 已提交
30

W
wangxuguang 已提交
31
$$c=[c_1,c_2,\ldots,c_{n-h+1}], c \in \mathbb{R}^{n-h+1}$$
W
wangxuguang 已提交
32

W
wangxuguang 已提交
33
接下来我们对特征图采用时间维度上的最大池化(max pooling over time)操作得到此卷积核对应的整句话的特征$\hat c$,它是特征图中所有元素的最大值:
W
wangxuguang 已提交
34

W
wangxuguang 已提交
35
$$\hat c=max(c)$$
W
wangxuguang 已提交
36

W
wangxuguang 已提交
37 38 39 40 41
在实际应用中,我们会使用多个卷积核来处理句子,窗口大小相同的卷积核堆叠起来形成一个矩阵(上文中的单个卷积核参数$w$相当于矩阵的某一行),这样可以更高效的完成运算。另外,我们也可使用窗口大小不同的卷积核来处理句子,最后,将所有卷积核得到的特征拼接起来即为文本的定长向量表示。对于文本分类问题,将其连接至softmax即构建出完整的模型。图1是使用卷积神经网络进行文本分类的一个示意图(只画了四个卷积核,黄色的卷积核窗口大小为3,红色的为2)。

对于一般的短文本分类问题,上文所述的简单的文本卷积网络即可达到很高的正确率\[[1](#参考文献)\]。若想得到更抽象更高级的文本特征表示,可以构建深层文本卷积神经网络\[[2](#参考文献),[3](#参考文献)\]
### 循环神经网络(RNN)
循环神经网络是一种能对序列数据进行精确建模的有力工具。实际上,循环神经网络的理论计算能力是图灵完备的\[[4](#参考文献)\]。自然语言是一种典型的序列数据(词序列),近年来,循环神经网络及其变体(如long short term memory\[[5](#参考文献)\]等)在自然语言处理的多个领域取得了丰硕的成果,如在语言模型、句法解析、语义角色标注(或一般的序列标注)、语义表示、图文生成、对话、机器翻译等任务上均表现优异甚至成为目前效果最好的方法。
42
<p align="center">
W
wangxuguang 已提交
43
<img src="image/rnn.png" width = "70%" height = "70%" align="center"/><br/>
44 45
图 2 循环神经网络按时间展开的示意图
</p>
W
wangxuguang 已提交
46
循环神经网络按时间展开后如图2所示:在第$t$时刻,网络读入第$t$个输入$x_t$(向量表示)及前一时刻隐层的状态$h_{t-1}$(向量表示,$h_0$一般初始化为$0$向量),计算得出本时刻隐藏层的值$h_t$,重复这一步骤直至读完所有输入。如果将循环神经网络所表示的函数记为$f$,则其公式可表示为:
W
wangxuguang 已提交
47

W
wangxuguang 已提交
48
$$h_t=f(x_t,h_{t-1})=\sigma(W_{xh}x_t+W_{hh}h_{h-1}+b_h)$$
W
wangxuguang 已提交
49

W
wangxuguang 已提交
50 51 52 53 54 55 56 57
其中$W_{xh}$是输入到隐层的矩阵参数,$W_{hh}$是隐层到隐层的矩阵参数,$b_h$为隐层的偏置向量(bias)参数,$\sigma$为逐元素(elementwise)的$sigmoid$函数。  
  
在处理自然语言时,一般会先将词(one-hot表示)映射为其词向量(word embedding)表示,然后再作为循环神经网络每一时刻的输入$x_t$。此外,可以根据实际需要的不同在循环神经网络的隐层上连接其它层。如,可以把一个循环神经网络的隐层输出连接至下一个循环神经网络的输入构建深层(deep or stacked)循环神经网络,或者提取最后一个时刻的隐层状态作为句子表示进而使用分类模型等等。  

### 长时短期记忆(LSTM)
循环神经网络隐状态的输入来源于当前输入和前一时刻隐状态的值,这会导致很久以前的输入容易被覆盖掉。实际上,人们发现当序列很长时,循环神经网络就会表现很差(远距离依赖问题),训练过程中会出现梯度消失或爆炸现象\[[6](#参考文献)\]。为了解决这一问题,Hochreiter S, Schmidhuber J. (1997)\[[5](#参考文献)\]提出了lstm(long short term memory)。  

相比于简单的循环神经网络,lstm增加了记忆单元$c$、输入门$i$、遗忘门$f$及输出门$o$,这些门及记忆单元组合起来大大提升了循环神经网络处理远距离依赖问题的能力,若将基于lstm的循环神经网络表示的函数记为$F$,则其公式为:
W
wangxuguang 已提交
58

W
wangxuguang 已提交
59
$$ h_t=F(x_t,h_{t-1})$$
W
wangxuguang 已提交
60

W
wangxuguang 已提交
61
$F$由下列公式组合而成\[[7](#参考文献)\]
W
wangxuguang 已提交
62
\begin{align}
W
wangxuguang 已提交
63 64
i_t & = \sigma(W_{xi}x_t+W_{hi}h_{h-1}+W_{ci}c_{t-1}+b_i)\\\\
f_t & = \sigma(W_{xf}x_t+W_{hf}h_{h-1}+W_{cf}c_{t-1}+b_f)\\\\
W
wangxuguang 已提交
65
c_t & = f_t\odot c_{t-1}+i_t\odot tanh(W_{xc}x_t+W_{hc}h_{h-1}+b_c)\\\\
W
wangxuguang 已提交
66
o_t & = \sigma(W_{xo}x_t+W_{ho}h_{h-1}+W_{co}c_{t}+b_o)\\\\
W
wangxuguang 已提交
67 68
h_t & = o_t\odot tanh(c_t)\\\\
\end{align}
W
wangxuguang 已提交
69
其中,$i_t, f_t, c_t, o_t$分别表示输入门,遗忘门,记忆单元及输出门的向量值,带角标的$W$及$b$为模型参数,$tanh$为逐元素的双曲正切函数,$\odot$表示逐元素的乘法操作。输入门控制着新输入进入记忆单元$c$的强度,遗忘门控制着记忆单元维持上一时刻值的强度,输出门控制着输出记忆单元的强度。三种门的计算方式类似,但有着完全不同的参数,即各自以不同的方式控制着记忆单元$c$,如图3所示:
W
wangxuguang 已提交
70
<p align="center">
W
wangxuguang 已提交
71 72
<img src="image/lstm.png" width = "70%" height = "70%" align="center"/><br/>
图 3 时刻$t$的lstm
W
wangxuguang 已提交
73
</p>
W
wangxuguang 已提交
74
lstm通过给简单的循环神经网络增加记忆及控制门的方式,增强了其处理远距离依赖问题的能力。类似原理的对于简单循环神经网络的改进还有Gated Recurrent Unit (GRU)\[[8](#参考文献)\],其设计更为简洁一些。**这些改进虽然各有不同,但是它们的宏观描述却与简单的循环神经网络一样(如图2所示),即隐状态依据当前输入及前一时刻的隐状态来改变,不断的循环这一过程直至输入处理完毕:**
W
wangxuguang 已提交
75

W
wangxuguang 已提交
76
$$ h_t=Recrurent(x_t,h_{t-1})$$
W
wangxuguang 已提交
77

W
wangxuguang 已提交
78 79 80
对于正常顺序的循环神经网络,$h_t$包含了$t$时刻之前的输入信息,也就是上文信息。同样,为了得到下文信息,我们可以使用反方向(将输入逆序处理)的循环神经网络。结合构建深层循环神经网络的方法,我们可以通过构建更加强有力的栈式双向循环神经网络,来对时序数据进行建模。
#### 栈式双向LSTM(Stacked Bidirectional LSTM)
考虑到深层神经网络往往能得到更抽象和高级的特征表示,我们构建基于lstm的栈式双向循环神经网络\[[9](#参考文献)\]。如图4所示(以三层为例),奇数层lstm正向,偶数层lstm反向,高一层的lstm使用低一层lstm及之前所有层的信息作为输入,对最高层lstm序列使用时间维度上的最大池化即可得到文本的定长向量表示。**这一表示充分融合了文本的上下文信息,并且对文本进行了深层次抽象。**最后我们将文本表示连接至softmax构建分类模型。
81 82
<p align="center">
<img src="image/stacked_lstm.jpg"><br/>
W
wangxuguang 已提交
83
图 4 栈式双向LSTM用于文本分类
84 85 86
</p>
## 数据准备
### 数据介绍与下载
W
wangxuguang 已提交
87
我们以IMDB情感分析数据集为例进行介绍。IMDB数据集的训练集和测试集分别包含25000个已标注过的电影评论。其中,负面评论的得分小于等于4,正面评论的得分大于等于7,满分10分。您可以使用下面的脚本下载 IMDB 数椐集和[Moses](http://www.statmt.org/moses/)工具:
W
wangxuguang 已提交
88

89 90 91
```
./get_imdb.sh
```
W
wangxuguang 已提交
92
如果数椐获取成功,您将在目录```data```中看到下面的文件:
W
wangxuguang 已提交
93

94 95 96
```
aclImdb  get_imdb.sh  imdb  mosesdecoder-master
```
W
wangxuguang 已提交
97

98 99 100
* aclImdb: 从外部网站上下载的原始数椐集。
* imdb: 仅包含训练和测试数椐集。
* mosesdecoder-master: Moses 工具。
W
wangxuguang 已提交
101

102
### 数据预处理
W
wangxuguang 已提交
103
我们只使用已标注的训练集和测试集。将训练集随机打乱排序,默认在训练集上构建字典。Moses 工具中的脚本`tokenizer.perl` 用于切分单词和标点符号。执行下面的命令就可以预处理数椐:
W
wangxuguang 已提交
104

105 106 107 108
```
data_dir="./data/imdb"
python preprocess.py -i data_dir
```
W
wangxuguang 已提交
109

110 111 112
* data_dir: 输入数椐所在目录。
* preprocess.py: 预处理脚本。

W
wangxuguang 已提交
113
运行成功后目录`./data/pre-imdb` 结构如下:
114 115 116 117 118

```
dict.txt  labels.list  test.list  test_part_000  train.list  train_part_000
```

W
wangxuguang 已提交
119 120
* test\_part\_000 和 train\_part\_000: 所有标记的测试集和训练集, 训练集已经随机打乱。
* train.list 和 test.list: 训练集和测试集文件列表。
121
* dict.txt: 利用训练集生成的字典。
W
wangxuguang 已提交
122
* labels.list: 类别标签列表,标签0表示负面评论,标签1表示正面评论。
123 124

### 提供数据给PaddlePaddle
W
wangxuguang 已提交
125
PaddlePaddle可以读取Python写的传输数据脚本,下面`dataprovider.py`文件给出了完整例子,主要包括两部分:
126

W
wangxuguang 已提交
127 128
* hook函数: 定义文本信息、类别Id的数据类型。文本被定义为整数序列`integer_value_sequence`,类别被定义为整数`integer_value`
* process函数: 使用yield关键字返回文本信息和类别Id,和hook里定义顺序一致。process读取的文件的行为类别和评论文本,以`'\t\t'`分隔。
129 130 131 132 133 134

```python
from paddle.trainer.PyDataProvider2 import *

def hook(settings, dictionary, **kwargs):
    settings.word_dict = dictionary
W
wangxuguang 已提交
135 136 137 138
    settings.input_types = {
        'word':  integer_value_sequence(len(settings.word_dict)),
        'label': integer_value(2)
    }
139 140 141 142 143 144 145 146 147 148 149 150 151
    settings.logger.info('dict len : %d' % (len(settings.word_dict)))


@provider(init_hook=hook)
def process(settings, file_name):
    with open(file_name, 'r') as fdata:
        for line_count, line in enumerate(fdata):
            label, comment = line.strip().split('\t\t')
            label = int(label)
            words = comment.split()
            word_slot = [
                settings.word_dict[w] for w in words if w in settings.word_dict
            ]
W
wangxuguang 已提交
152 153 154 155
            yield {
                'word': word_slot,
                'label': label
            }
156 157 158
```

## 模型配置说明
W
wangxuguang 已提交
159 160
`trainer_config.py` 是一个配置文件的例子。
### 数据定义
161
```python
W
wangxuguang 已提交
162 163 164 165 166 167 168
define_py_data_sources2(
    train_list,
    test_list,
    module="dataprovider",
    obj="process",
    args={'dictionary': word_dict})
```
169

W
wangxuguang 已提交
170
在模型配置中利用define_py_data_sources2加载数据:
171

W
wangxuguang 已提交
172 173 174 175
* train.list、test.list: 指定训练、测试数据。
* module="dataprovider": 数据处理Python文件名。
* obj="process": 指定生成数据的函数。
* args={"dictionary": word_dict}: 额外的参数,这里指定词典。
176 177 178 179

### 优化算法配置

```python
W
wangxuguang 已提交
180 181 182 183 184 185
settings(
    batch_size=128,
    learning_rate=2e-3,
    learning_method=AdamOptimizer(),
    regularization=L2Regularization(8e-4),
    gradient_clipping_threshold=25)
186 187
```

W
wangxuguang 已提交
188 189 190 191 192
* 设置batch size大小为128。
* 设置全局学习率。
* 使用 adam 优化。
* 设置L2正则。
* 设置梯度截断(clipping)阈值。
193 194

### 模型结构
W
wangxuguang 已提交
195 196 197 198
我们用PaddlePaddle分别基于[文本卷积神经网络](#文本卷积神经网络(CNN))[栈式双向LSTM](#栈式双向LSTM(Stacked Bidirectional LSTM))实现文本分类。
#### 文本卷积神经网络的实现
```python
def convolution_net(input_dim,
199 200 201 202
                    class_dim=2,
                    emb_dim=128,
                    hid_dim=128,
                    is_predict=False):
W
wangxuguang 已提交
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
    # 网络输入:id表示的词序列,词典大小为input_dim
    data = data_layer("word", input_dim)
    # 将id表示的词序列映射为embedding序列
    emb = embedding_layer(input=data, size=emb_dim)
    # 卷积及最大化池操作,卷积核窗口大小为3
    conv_3 = sequence_conv_pool(input=emb, context_len=3, hidden_size=hid_dim)
    # 卷积及最大化池操作,卷积核窗口大小为4
    conv_4 = sequence_conv_pool(input=emb, context_len=4, hidden_size=hid_dim)
    # 将conv_3和conv_4拼接起来输入给softmax分类,类别数为class_dim
    output = fc_layer(
        input=[conv_3, conv_4], size=class_dim, act=SoftmaxActivation())

    if not is_predict:
        lbl = data_layer("label", 1)    #网络输入:类别标签
        outputs(classification_cost(input=output, label=lbl))
    else:
        outputs(output)
```

其中,我们仅用一个[`sequence_conv_pool`](https://github.com/PaddlePaddle/Paddle/blob/develop/python/paddle/trainer_config_helpers/networks.py)方法就实现了卷积和池化操作,卷积核的数量为hidden_size参数。
#### 栈式双向LSTM的实现

```python
def stacked_lstm_net(input_dim,
227 228 229 230 231 232
                     class_dim=2,
                     emb_dim=128,
                     hid_dim=512,
                     stacked_num=3,
                     is_predict=False):

W
wangxuguang 已提交
233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289
    hid_lr = 1e-3
    assert stacked_num % 2 == 1
    # 设置神经网络层的属性
    layer_attr = ExtraLayerAttribute(drop_rate=0.5)
    # 设置参数的属性
    fc_para_attr = ParameterAttribute(learning_rate=hid_lr)
    lstm_para_attr = ParameterAttribute(initial_std=0., learning_rate=1.)
    para_attr = [fc_para_attr, lstm_para_attr]
    bias_attr = ParameterAttribute(initial_std=0., l2_rate=0.)
    # 激活函数
    relu = ReluActivation()
    linear = LinearActivation()
    # 网络输入:id表示的词序列,词典大小为input_dim
    data = data_layer("word", input_dim)
    # 将id表示的词序列映射为embedding序列
    emb = embedding_layer(input=data, size=emb_dim)

    fc1 = fc_layer(input=emb, size=hid_dim, act=linear, bias_attr=bias_attr)
    # 基于lstm的循环神经网络
    lstm1 = lstmemory(
        input=fc1, act=relu, bias_attr=bias_attr, layer_attr=layer_attr)

    # 由fc_layer和lstmemory构建深度为stacked_num的栈式双向LSTM
    inputs = [fc1, lstm1]
    for i in range(2, stacked_num + 1):
        fc = fc_layer(
            input=inputs,
            size=hid_dim,
            act=linear,
            param_attr=para_attr,
            bias_attr=bias_attr)
        lstm = lstmemory(
            input=fc,
            # 奇数层正向,偶数层反向。
            reverse=(i % 2) == 0,
            act=relu,
            bias_attr=bias_attr,
            layer_attr=layer_attr)
        inputs = [fc, lstm]

    # 对最后一层fc_layer使用时间维度上的最大池化得到定长向量
    fc_last = pooling_layer(input=inputs[0], pooling_type=MaxPooling())
    # 对最后一层lstmemory使用时间维度上的最大池化得到定长向量
    lstm_last = pooling_layer(input=inputs[1], pooling_type=MaxPooling())
    # 将fc_last和lstm_last拼接起来输入给softmax分类,类别数为class_dim
    output = fc_layer(
        input=[fc_last, lstm_last],
        size=class_dim,
        act=SoftmaxActivation(),
        bias_attr=bias_attr,
        param_attr=para_attr)

    if is_predict:
        outputs(output)
    else:
        outputs(classification_cost(input=output, label=data_layer('label', 1)))
```
290 291

## 训练模型
W
wangxuguang 已提交
292
使用`train.sh`脚本可以开启本地的训练:
293 294 295 296 297

```
./train.sh
```

W
wangxuguang 已提交
298
train.sh内容如下:
299

W
wangxuguang 已提交
300 301 302
```bash
paddle train --config=trainer_config.py \
             --save_dir=./model_output \
303 304 305 306 307 308 309 310 311 312 313
             --job=train \
             --use_gpu=false \
             --trainer_count=4 \
             --num_passes=10 \
             --log_period=20 \
             --dot_period=20 \
             --show_parameter_stats_period=100 \
             --test_all_data_in_one_period=1 \
             2>&1 | tee 'train.log'
```

W
wangxuguang 已提交
314 315
* \--config=trainer_config.py: 设置模型配置。
* \--save\_dir=./model_output: 设置输出路径以保存训练完成的模型。
316
* \--job=train: 设置工作模式为训练。
W
wangxuguang 已提交
317
* \--use\_gpu=false: 使用CPU训练,如果您安装GPU版本的PaddlePaddle,并想使用GPU来训练可将此设置为true。
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340
* \--trainer\_count=4:设置线程数(或GPU个数)。
* \--num\_passes=15: 设置pass,PaddlePaddle中的一个pass意味着对数据集中的所有样本进行一次训练。
* \--log\_period=20: 每20个batch打印一次日志。
* \--show\_parameter\_stats\_period=100: 每100个batch打印一次统计信息。
* \--test\_all_data\_in\_one\_period=1: 每次测试都测试所有数据。

如果运行成功,输出日志保存在 `train.log`中,模型保存在目录`model_output/`中。  输出日志说明如下:

```
Batch=20 samples=2560 AvgCost=0.681644 CurrentCost=0.681644 Eval: classification_error_evaluator=0.36875  CurrentEval: classification_error_evaluator=0.36875
...
Pass=0 Batch=196 samples=25000 AvgCost=0.418964 Eval: classification_error_evaluator=0.1922
Test samples=24999 cost=0.39297 Eval: classification_error_evaluator=0.149406
```

* Batch=xx: 表示训练了xx个Batch。
* samples=xx: 表示训练了xx个样本。
* AvgCost=xx: 从第0个batch到当前batch的平均损失。
* CurrentCost=xx: 最新log_period个batch的损失。
* Eval: classification\_error\_evaluator=xx: 表示第0个batch到当前batch的分类错误。
* CurrentEval: classification\_error\_evaluator: 最新log_period个batch的分类错误。
* Pass=0: 通过所有训练集一次称为一个Pass。 0表示第一次经过训练集。

W
wangxuguang 已提交
341
我们的模型配置`trainer_config.py`默认使用`stacked_lstm_net`网络,如果要使用`convolution_net`,注释相应的行即可。
342 343 344 345 346 347 348 349 350
## 应用模型
### 测试模型

测试模型是指使用训练出的模型评估已标记的数据集。

```
./test.sh
```

W
wangxuguang 已提交
351
测试脚本`test.sh`的内容如下,其中函数`get_best_pass`通过对分类错误率进行排序来获得最佳模型:
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378

```bash
function get_best_pass() {
  cat $1  | grep -Pzo 'Test .*\n.*pass-.*' | \
  sed  -r 'N;s/Test.* error=([0-9]+\.[0-9]+).*\n.*pass-([0-9]+)/\1 \2/g' | \
  sort | head -n 1
}

log=train.log
LOG=`get_best_pass $log`
LOG=(${LOG})
evaluate_pass="model_output/pass-${LOG[1]}"

echo 'evaluating from pass '$evaluate_pass

model_list=./model.list
touch $model_list | echo $evaluate_pass > $model_list
net_conf=trainer_config.py
paddle train --config=$net_conf \
             --model_list=$model_list \
             --job=test \
             --use_gpu=false \
             --trainer_count=4 \
             --config_args=is_test=1 \
             2>&1 | tee 'test.log'
```

W
wangxuguang 已提交
379
与训练不同,测试时需要指定`--job = test`和模型路径`--model_list = $model_list`。如果测试成功,日志将保存在`test.log`中。 在我们的测试中,最好的模型是`model_output/pass-00002`,分类错误率是0.115645:
380 381 382 383 384 385

```
Pass=0 samples=24999 AvgCost=0.280471 Eval: classification_error_evaluator=0.115645
```

### 预测
W
wangxuguang 已提交
386
`predict.py`脚本提供了一个预测接口。预测IMDB中未标记评论的示例如下:
387 388 389 390 391 392 393 394 395 396 397

```
./predict.sh
```
predict.sh:

```bash
model=model_output/pass-00002/
config=trainer_config.py
label=data/pre-imdb/labels.list
cat ./data/aclImdb/test/pos/10007_10.txt | python predict.py \
W
wangxuguang 已提交
398
     --tconf=$config \
399 400 401 402 403 404 405 406 407 408 409 410
     --model=$model \
     --label=$label \
     --dict=./data/pre-imdb/dict.txt \
     --batch_size=1
```

* `cat ./data/aclImdb/test/pos/10007_10.txt` : 输入预测样本。
* `predict.py` : 预测接口脚本。
* `--tconf=$config` : 设置网络配置。
* `--model=$model` : 设置模型路径。
* `--label=$label` : 设置标签类别字典,这个字典是整数标签和字符串标签的一个对应。
* `--dict=data/pre-imdb/dict.txt` : 设置文本数据字典文件。
W
wangxuguang 已提交
411
* `--batch_size=1` : 预测时的batch size大小。
412

W
wangxuguang 已提交
413
注意应该确保默认模型路径`model_output/pass-00002`存在或更改为其它模型路径。
414 415 416 417 418 419 420 421 422

本示例的预测结果:

```
Loading parameters from model_output/pass-00002/
./data/aclImdb/test/pos/10014_7.txt: predicting label is pos
```

## 总结
W
wangxuguang 已提交
423
本章我们以情感分析为例,介绍了使用深度学习的方法进行端对端的短文本分类,并且使用PaddlePaddle完成了全部相关实验。同时,我们简要介绍了两种文本处理模型:卷积神经网络和循环神经网络。在后续的章节中我们会看到这两种基本的深度学习模型在其它任务上的应用。
W
wangxuguang 已提交
424
## 参考文献
W
wangxuguang 已提交
425 426 427 428 429 430 431 432 433
1. Kim Y. [Convolutional neural networks for sentence classification](http://arxiv.org/pdf/1408.5882)[J]. arXiv preprint arXiv:1408.5882, 2014.
2. Kalchbrenner N, Grefenstette E, Blunsom P. [A convolutional neural network for modelling sentences](http://arxiv.org/pdf/1404.2188.pdf?utm_medium=App.net&utm_source=PourOver)[J]. arXiv preprint arXiv:1404.2188, 2014.
3. Yann N. Dauphin, et al. [Language Modeling with Gated Convolutional Networks](https://arxiv.org/pdf/1612.08083v1.pdf)[J] arXiv preprint arXiv:1612.08083, 2016.
4. Siegelmann H T, Sontag E D. [On the computational power of neural nets](http://research.cs.queensu.ca/home/akl/cisc879/papers/SELECTED_PAPERS_FROM_VARIOUS_SOURCES/05070215382317071.pdf)[C]//Proceedings of the fifth annual workshop on Computational learning theory. ACM, 1992: 440-449.
5. Hochreiter S, Schmidhuber J. [Long short-term memory](http://web.eecs.utk.edu/~itamar/courses/ECE-692/Bobby_paper1.pdf)[J]. Neural computation, 1997, 9(8): 1735-1780.
6. Bengio Y, Simard P, Frasconi P. [Learning long-term dependencies with gradient descent is difficult](http://www-dsi.ing.unifi.it/~paolo/ps/tnn-94-gradient.pdf)[J]. IEEE transactions on neural networks, 1994, 5(2): 157-166.
7. Graves A. [Generating sequences with recurrent neural networks](http://arxiv.org/pdf/1308.0850)[J]. arXiv preprint arXiv:1308.0850, 2013.
8. Cho K, Van Merriënboer B, Gulcehre C, et al. [Learning phrase representations using RNN encoder-decoder for statistical machine translation](http://arxiv.org/pdf/1406.1078)[J]. arXiv preprint arXiv:1406.1078, 2014.
9. Zhou J, Xu W. [End-to-end learning of semantic role labeling using recurrent neural networks](http://www.aclweb.org/anthology/P/P15/P15-1109.pdf)[C]//Proceedings of the Annual Meeting of the Association for Computational Linguistics. 2015.