Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
PaddlePaddle
models
提交
9c955cc0
M
models
项目概览
PaddlePaddle
/
models
大约 1 年 前同步成功
通知
222
Star
6828
Fork
2962
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
602
列表
看板
标记
里程碑
合并请求
255
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
M
models
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
602
Issue
602
列表
看板
标记
里程碑
合并请求
255
合并请求
255
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
9c955cc0
编写于
10月 13, 2017
作者:
P
peterzhang2029
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
refine introduction in doc
上级
43098129
变更
2
显示空白变更内容
内联
并排
Showing
2 changed file
with
14 addition
and
246 deletion
+14
-246
nested_sequence/text_classification/README.md
nested_sequence/text_classification/README.md
+14
-9
nested_sequence/text_classification/index.html
nested_sequence/text_classification/index.html
+0
-237
未找到文件。
nested_sequence/text_classification/README.md
浏览文件 @
9c955cc0
#
双层序列
文本分类
#
基于双层序列的
文本分类
## 简介
序列
数据是自然语言处理任务面对的一种主要输入数据类型:一句话是由词语构成的序列,多句话进一步构成了段落。因此,段落可以看作是一个嵌套的双层的序列
,这个序列的每个元素又是一个序列。
序列
是自然语言处理任务面对的一种主要输入数据类型:句子由词语构成,而多个句子进一步构成了段落。因此,段落可以看作是一个嵌套的序列(或者叫作:双层序列)
,这个序列的每个元素又是一个序列。
双层序列是
`PaddlePaddle`
支持的一种非常灵活的数据组织方式,帮助我们更好地描述段落、多轮对话等更为复杂的语言数据。基于双层序列输入,我们可以设计一个层次化的网络,分别从词语和句子级别编码输入数据,
更好地完成一些复杂的语言理解任务。
双层序列是
PaddlePaddle 支持的一种非常灵活的数据组织方式,能够帮助我们更好地描述段落、多轮对话等更为复杂的语言数据。以双层序列作为输入,我们可以设计一个层次化的网络,分别从词语和句子级别编码输入数据,从而
更好地完成一些复杂的语言理解任务。
本
示例将演示如何使用
`PaddlePaddle`
来组织双层序列文本数据,完成文本
分类任务。
本
例将演示如何在 PaddlePaddle 中将长文本输入(通常能达到段落或者篇章基本)组织为双层序列,完成对长文本的
分类任务。
## 模型介绍
对于文本分类,我们将一段文本看成句子的数组,每个句子又是单词的数组,这便是一种双层序列的输入数据。而将这个段落的每一句话用卷积神经网络编码为一个向量,再将每句话的表示向量经过池化层编码成一个段落的向量, 即可得到段落的表示向量。对于分类任务,将段落表示向量作为分类器的输入可以得到分类结果。
我们将一段文本看成句子的序列,而每个句子又是词语的序列。
我们首先用卷积神经网络编码段落中的每一句话;然后,将每句话的表示向量经过池化层得到段落的编码向量;最后将段落的编码向量作为分类器(以softmax层的全连接层)输入,得到最终的分类结果。
**模型结构如下图所示**
<p
align=
"center"
>
<img
src=
"images/model.jpg"
width =
"60%"
align=
"center"
/><br/>
图1.
本例中
的文本分类模型
图1.
基于双层序列
的文本分类模型
</p>
PaddlePaddle 实现该网络结构的代码见
`network_conf.py`
。
对于双层序列的处理,需要先将双层时间序列数据先变换成单层时间序列数据,再对每一个单层时间序列进行处理。 PaddlePaddle提供了
`recurrent_group`
接口进行转换,在本例中,我们将文本数据的每一段,通过 recurrent_group 进行拆解,拆解成的每一句话再通过一个 CNN网络学习对应的向量表示。
对双层时间序列的处理,需要先将双层时间序列数据变换成单层时间序列数据,再对每一个单层时间序列进行处理。 在 PaddlePaddle 中 ,
`recurrent_group`
是帮助我们构建处理双层序列的层次化模型的主要工具。这里,我们使用两个嵌套的
`recurrent_group`
。外层的
`recurrent_group`
将段落拆解为句子,
`step`
函数中拿到的输入是句子序列;内层的
`recurrent_group`
将句子拆解为词语,
`step`
函数中拿到的输入是非序列的词语。
在词语级别,我们通过 CNN 网络以词向量为输入输出学习到的句子表示;在段落级别,将每个句子的表示通过池化作用得到段落表示。
```
python
nest_group
=
paddle
.
layer
.
recurrent_group
(
input
=
[
paddle
.
layer
.
SubsequenceInput
(
emb
),
hidden_size
],
step
=
cnn_cov_group
)
```
使用
`recurrent_group`
接口进行变换时,需要将输入序列传入
`input`
属性。 由于本例要实现的变换是
`双层时间序列 => 单层时间序列`
,所以我们需要将输入数据标记成
`SubsequenceInput`
。
拆解后的单层序列数据经过一个CNN网络学习对应的向量表示,CNN的网络结构包含以下部分:
...
...
@@ -77,7 +82,7 @@ python infer.py
### 训练
1.
数据组织
假设有如下格式的训练数据
:每一行为一条样本,以
`\t`
分隔,第一列是类别标签,第二列是输入文本的内容。以下是两条示例数据:
输入数据格式如下
:每一行为一条样本,以
`\t`
分隔,第一列是类别标签,第二列是输入文本的内容。以下是两条示例数据:
```
1 This movie is very good. The actor is so handsome.
...
...
nested_sequence/text_classification/index.html
已删除
100644 → 0
浏览文件 @
43098129
<html>
<head>
<script
type=
"text/x-mathjax-config"
>
MathJax
.
Hub
.
Config
({
extensions
:
[
"
tex2jax.js
"
,
"
TeX/AMSsymbols.js
"
,
"
TeX/AMSmath.js
"
],
jax
:
[
"
input/TeX
"
,
"
output/HTML-CSS
"
],
tex2jax
:
{
inlineMath
:
[
[
'
$
'
,
'
$
'
]
],
displayMath
:
[
[
'
$$
'
,
'
$$
'
]
],
processEscapes
:
true
},
"
HTML-CSS
"
:
{
availableFonts
:
[
"
TeX
"
]
}
});
</script>
<script
src=
"https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.0/MathJax.js"
async
></script>
<script
type=
"text/javascript"
src=
"../.tools/theme/marked.js"
>
</script>
<link
href=
"http://cdn.bootcss.com/highlight.js/9.9.0/styles/darcula.min.css"
rel=
"stylesheet"
>
<script
src=
"http://cdn.bootcss.com/highlight.js/9.9.0/highlight.min.js"
></script>
<link
href=
"http://cdn.bootcss.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css"
rel=
"stylesheet"
>
<link
href=
"https://cdn.jsdelivr.net/perfect-scrollbar/0.6.14/css/perfect-scrollbar.min.css"
rel=
"stylesheet"
>
<link
href=
"../.tools/theme/github-markdown.css"
rel=
'stylesheet'
>
</head>
<style
type=
"text/css"
>
.markdown-body
{
box-sizing
:
border-box
;
min-width
:
200px
;
max-width
:
980px
;
margin
:
0
auto
;
padding
:
45px
;
}
</style>
<body>
<div
id=
"context"
class=
"container-fluid markdown-body"
>
</div>
<!-- This block will be replaced by each markdown file content. Please do not change lines below.-->
<div
id=
"markdown"
style=
'display:none'
>
# 双层序列文本分类
## 简介
序列数据是自然语言处理任务面对的一种主要输入数据类型:一句话是由词语构成的序列,多句话进一步构成了段落。因此,段落可以看作是一个嵌套的双层的序列,这个序列的每个元素又是一个序列。
双层序列是`PaddlePaddle`支持的一种非常灵活的数据组织方式,帮助我们更好地描述段落、多轮对话等更为复杂的语言数据。基于双层序列输入,我们可以设计一个层次化的网络,分别从词语和句子级别编码输入数据,更好地完成一些复杂的语言理解任务。
本示例将演示如何使用`PaddlePaddle`来组织双层序列文本数据,完成文本分类任务。
## 模型介绍
对于文本分类,我们将一段文本看成句子的数组,每个句子又是单词的数组,这便是一种双层序列的输入数据。而将这个段落的每一句话用卷积神经网络编码为一个向量,再将每句话的表示向量经过池化层编码成一个段落的向量, 即可得到段落的表示向量。对于分类任务,将段落表示向量作为分类器的输入可以得到分类结果。
**模型结构如下图所示**
<p
align=
"center"
>
<img
src=
"images/model.jpg"
width =
"60%"
align=
"center"
/><br/>
图1. 本例中的文本分类模型
</p>
PaddlePaddle 实现该网络结构的代码见 `network_conf.py`。
对于双层序列的处理,需要先将双层时间序列数据先变换成单层时间序列数据,再对每一个单层时间序列进行处理。 PaddlePaddle提供了 `recurrent_group` 接口进行转换,在本例中,我们将文本数据的每一段,通过 recurrent_group 进行拆解,拆解成的每一句话再通过一个 CNN网络学习对应的向量表示。
``` python
nest_group = paddle.layer.recurrent_group(input=[paddle.layer.SubsequenceInput(emb),
hidden_size],
step=cnn_cov_group)
```
使用`recurrent_group`接口进行变换时,需要将输入序列传入 `input` 属性。 由于本例要实现的变换是`双层时间序列 => 单层时间序列`,所以我们需要将输入数据标记成 `SubsequenceInput`。
拆解后的单层序列数据经过一个CNN网络学习对应的向量表示,CNN的网络结构包含以下部分:
- **卷积层**: 文本分类中的卷积在时间序列上进行,卷积核的宽度和词向量层产出的矩阵一致,卷积后得到的结果为“特征图”, 使用多个不同高度的卷积核,可以得到多个特征图。本例代码默认使用了大小为 3(图1红色框)和 4(图1蓝色框)的卷积核。
- **最大池化层**: 对卷积得到的各个特征图分别进行最大池化操作。由于特征图本身已经是向量,因此最大池化实际上就是选出各个向量中的最大元素。将所有最大元素又被拼接在一起,组成新的向量。
- **线性投影层**: 将不同卷积得到的结果经过最大池化层之后拼接为一个长向量, 然后经过一个线性投影得到对应单层序列的表示向量。
CNN网络具体代码实现如下:
```python
def cnn_cov_group(group_input, hidden_size):
conv3 = paddle.networks.sequence_conv_pool(
input=group_input, context_len=3, hidden_size=hidden_size)
conv4 = paddle.networks.sequence_conv_pool(
input=group_input, context_len=4, hidden_size=hidden_size)
output_group = paddle.layer.fc(input=[conv3, conv4],
size=hidden_size,
param_attr=paddle.attr.ParamAttr(name='_cov_value_weight'),
bias_attr=paddle.attr.ParamAttr(name='_cov_value_bias'),
act=paddle.activation.Linear())
return output_group
```
PaddlePaddle 中已经封装好的带有池化的文本序列卷积模块:`paddle.networks.sequence_conv_pool`,可直接调用。
在得到每个句子的表示向量之后, 将所有句子表示向量经过一个平均池化层, 得到一个样本的向量表示, 向量经过一个全连接层输出最终的预测结果。 代码如下:
```python
avg_pool = paddle.layer.pooling(input=nest_group, pooling_type=paddle.pooling.Avg(),
agg_level=paddle.layer.AggregateLevel.TO_NO_SEQUENCE)
prob = paddle.layer.mixed(size=class_num,
input=[paddle.layer.full_matrix_projection(input=avg_pool)],
act=paddle.activation.Softmax())
```
## 使用 PaddlePaddle 内置数据运行
### 训练
在终端执行:
```bash
python train.py
```
将以 PaddlePaddle 内置的情感分类数据集: `imdb` 运行本例。
### 预测
训练结束后模型将存储在指定目录当中(默认models目录),在终端执行:
```bash
python infer.py
```
默认情况下,预测脚本将加载训练一个pass的模型对 `imdb的测试集` 进行测试。
## 使用自定义数据训练和预测
### 训练
1.数据组织
假设有如下格式的训练数据:每一行为一条样本,以 `\t` 分隔,第一列是类别标签,第二列是输入文本的内容。以下是两条示例数据:
```
1 This movie is very good. The actor is so handsome.
0 What a terrible movie. I waste so much time.
```
2.编写数据读取接口
自定义数据读取接口只需编写一个 Python 生成器实现**从原始输入文本中解析一条训练样本**的逻辑。以下代码片段实现了读取原始数据返回类型为: `paddle.data_type.integer_value_sub_sequence` 和 `paddle.data_type.integer_value`
```python
def train_reader(data_dir, word_dict):
"""
Reader interface for training data
:param data_dir: data directory
:type data_dir: str
:param word_dict: path of word dictionary,
the dictionary must has a "UNK" in it.
:type word_dict: Python dict
"""
def reader():
UNK_ID = word_dict['
<unk>
']
word_col = 1
lbl_col = 0
for file_name in os.listdir(data_dir):
file_path = os.path.join(data_dir, file_name)
if not os.path.isfile(file_path):
continue
with open(file_path, "r") as f:
for line in f:
line_split = line.strip().split("\t")
doc = line_split[word_col]
doc_ids = []
for sent in doc.strip().split("."):
sent_ids = [
word_dict.get(w, UNK_ID)
for w in sent.split()]
if sent_ids:
doc_ids.append(sent_ids)
yield doc_ids, int(line_split[lbl_col])
return reader
```
需要注意的是, 本例中以英文句号`'.'`作为分隔符, 将一段文本分隔为一定数量的句子, 且每个句子表示为对应词表的索引数组(`sent_ids`)。 由于当前样本的表示(`doc_ids`)中包含了该段文本的所有句子, 因此,它的类型为:`paddle.data_type.integer_value_sub_sequence`。
3.指定命令行参数进行训练
`train.py`训练脚本中包含以下参数:
```
--train_data_dir TRAIN_DATA_DIR
path of training dataset (default: None). if this
parameter is not set, imdb dataset will be used.
--test_data_dir TEST_DATA_DIR
path of testing dataset (default: None). if this
parameter is not set, imdb dataset will be used.
--word_dict WORD_DICT
path of word dictionary (default: None).if this
parameter is not set, imdb dataset will be used.if
this parameter is set, but the file does not exist,
word dictionay will be built from the training data
automatically.
--class_num CLASS_NUM
class number.
--batch_size BATCH_SIZE
the number of training examples in one
forward/backward pass
--num_passes NUM_PASSES
number of passes to train
--model_save_dir MODEL_SAVE_DIR
path to save the trained models.
```
修改`train.py`脚本中的启动参数,可以直接运行本例。 以`data`目录下的示例数据为例,在终端执行:
```bash
python train.py --train_data_dir 'data/train_data' --test_data_dir 'data/test_data' --word_dict 'dict.txt'
```
即可对样例数据进行训练。
### 预测
1.修改 `infer.py` 中以下变量,指定使用的模型、指定测试数据。
```python
model_path = "models/params_pass_00000.tar.gz" # 指定模型所在的路径
assert os.path.exists(model_path), "the trained model does not exist."
infer_path = 'data/infer.txt' # 指定测试文件所在的目录
word_dict = 'dict.txt' # 指定字典所在的路径
```
2.在终端中执行 `python infer.py`。
</div>
<!-- You can change the lines below now. -->
<script
type=
"text/javascript"
>
marked
.
setOptions
({
renderer
:
new
marked
.
Renderer
(),
gfm
:
true
,
breaks
:
false
,
smartypants
:
true
,
highlight
:
function
(
code
,
lang
)
{
code
=
code
.
replace
(
/&/g
,
"
&
"
)
code
=
code
.
replace
(
/>/g
,
"
>
"
)
code
=
code
.
replace
(
/</g
,
"
<
"
)
code
=
code
.
replace
(
/ /g
,
"
"
)
return
hljs
.
highlightAuto
(
code
,
[
lang
]).
value
;
}
});
document
.
getElementById
(
"
context
"
).
innerHTML
=
marked
(
document
.
getElementById
(
"
markdown
"
).
innerHTML
)
</script>
</body>
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录