Skip to content

Commit d40bb72

Browse files
author
livc
committed
modify rnn_config_cn.rst
1 parent 660b310 commit d40bb72

File tree

1 file changed

+287
-0
lines changed

1 file changed

+287
-0
lines changed
Lines changed: 287 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,287 @@
1+
RNN 配置
2+
========
3+
4+
本教程将指导你如何在 PaddlePaddle
5+
中配置循环神经网络(RNN)。PaddlePaddle
6+
高度支持灵活和高效的循环神经网络配置。 在本教程中,您将了解如何:
7+
8+
- 准备用来学习循环神经网络的序列数据。
9+
- 配置循环神经网络架构。
10+
- 使用学习完成的循环神经网络模型生成序列。
11+
12+
我们将使用 vanilla 循环神经网络和 sequence to sequence
13+
模型来指导你完成这些步骤。sequence to sequence
14+
模型的代码可以在\ ``demo / seqToseq``\ 找到。
15+
16+
准备序列数据
17+
------------
18+
19+
PaddlePaddle
20+
不需要对序列数据进行任何预处理,例如填充。唯一需要做的是将相应类型设置为输入。例如,以下代码段定义了三个输入。
21+
它们都是序列,它们的大小是\ ``src_dict``\ \ ``trg_dict``\ \ ``trg_dict``\
22+
23+
.. code:: sourcecode
24+
25+
settings.input_types = [
26+
integer_value_sequence(len(settings.src_dict)),
27+
integer_value_sequence(len(settings.trg_dict)),
28+
integer_value_sequence(len(settings.trg_dict))]
29+
30+
\ ``process``\ 函数中,每个\ ``yield``\ 函数将返回三个整数列表。每个整数列表被视为一个整数序列:
31+
32+
.. code:: sourcecode
33+
34+
yield src_ids, trg_ids, trg_ids_next
35+
36+
有关如何编写数据提供程序的更多细节描述,请参考
37+
`PyDataProvider2 <../../ui/data_provider/index.html>`__\ 。完整的数据提供文件在
38+
``demo/seqToseq/dataprovider.py``\
39+
40+
配置循环神经网络架构
41+
--------------------
42+
43+
简单门控循环神经网络(Gated Recurrent Neural Network)
44+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45+
46+
循环神经网络在每个时间步骤顺序地处理序列。下面列出了 LSTM 的架构的示例。
47+
48+
.. figure:: ../../../tutorials/sentiment_analysis/bi_lstm.jpg
49+
:alt: image
50+
51+
image
52+
53+
一般来说,循环网络从 *t* = 1 到 *t* = *T* 或者反向地从 *t* = *T* 到 *t*
54+
= 1 执行以下操作。
55+
56+
*x*\ \ *t* + 1 = *f*\ \ *x*\ (*x*\ \ *t*\ ),\ *y*\ \ *t*\  = *f*\ \ *y*\ (*x*\ \ *t*\ )
57+
58+
其中 *f*\ \ *x*\ (.) 称为\ **单步函数**\ (即单时间步执行的函数,step
59+
function),而 *f*\ \ *y*\ (.) 称为\ **输出函数**\ 。在 vanilla
60+
循环神经网络中,单步函数和输出函数都非常简单。然而,PaddlePaddle
61+
可以通过修改这两个函数来实现复杂的网络配置。我们将使用 sequence to
62+
sequence
63+
模型演示如何配置复杂的循环神经网络模型。在本节中,我们将使用简单的
64+
vanilla
65+
循环神经网络作为使用\ ``recurrent_group``\ 配置简单循环神经网络的例子。
66+
注意,如果你只需要使用简单的RNN,GRU或LSTM,那么推荐使用\ ``grumemory``\ \ ``lstmemory``\ ,因为它们的计算效率比\ ``recurrent_group``\ 更高。
67+
68+
对于 vanilla RNN,在每个时间步长,\ **单步函数**\ 为:
69+
70+
*x*\ \ *t* + 1 = *W*\ \ *x*\ \ *x*\ \ *t*\  + *W*\ \ *i*\ \ *I*\ \ *t*\  + *b*
71+
72+
其中 *x*\ \ *t*\ 是RNN状态,并且 *I*\ \ *t*\ 是输入,\ *W*\ \ *x*\
73+
*W*\ \ *i*\ 分别是RNN状态和输入的变换矩阵。\ *b*
74+
是偏差。它的\ **输出函数**\ 只需要\ *x*\ \ *t*\ 作为输出。
75+
76+
``recurrent_group``\ 是构建循环神经网络的最重要的工具。
77+
它定义了\ **单步函数**\ \ **输出函数**\ 和循环神经网络的输入。注意,这个函数的\ ``step``\ 参数需要实现\ ``step function``\ (单步函数)和\ ``output function``\ (输出函数):
78+
79+
.. code:: sourcecode
80+
81+
def simple_rnn(input,
82+
size=None,
83+
name=None,
84+
reverse=False,
85+
rnn_bias_attr=None,
86+
act=None,
87+
rnn_layer_attr=None):
88+
def __rnn_step__(ipt):
89+
out_mem = memory(name=name, size=size)
90+
rnn_out = mixed_layer(input = [full_matrix_projection(ipt),
91+
full_matrix_projection(out_mem)],
92+
name = name,
93+
bias_attr = rnn_bias_attr,
94+
act = act,
95+
layer_attr = rnn_layer_attr,
96+
size = size)
97+
return rnn_out
98+
return recurrent_group(name='%s_recurrent_group' % name,
99+
step=__rnn_step__,
100+
reverse=reverse,
101+
input=input)
102+
103+
PaddlePaddle
104+
使用“Memory”(记忆模块)实现单步函数。\ **Memory**\ 是在PaddlePaddle中构造循环神经网络时最重要的概念。
105+
Memory是在单步函数中循环使用的状态,例如\ *x*\ \ *t* + 1 = *f*\ \ *x*\ (*x*\ \ *t*\ )。
106+
一个Memory包含\ **输出**\ \ **输入**\ 。当前时间步处的Memory的输出作为下一时间步Memory的输入。Memory也可以具有\ **boot
107+
layer(引导层)**\ ,其输出被用作Memory的初始值。
108+
在我们的例子中,门控循环单元的输出被用作输出Memory。请注意,\ ``rnn_out``\ 层的名称与\ ``out_mem``\ 的名称相同。这意味着\ ``rnn_out``
109+
(*x*\ \ *t* + 1)的输出被用作\ ``out_mem``\ Memory的\ **输出**\
110+
111+
Memory也可以是序列。在这种情况下,在每个时间步中,我们有一个序列作为循环神经网络的状态。这在构造非常复杂的循环神经网络时是有用的。
112+
其他高级功能包括定义多个Memory,以及使用子序列来定义分级循环神经网络架构。
113+
114+
我们在函数的结尾返回\ ``rnn_out``\ 。 这意味着 ``rnn_out``
115+
层的输出被用作门控循环神经网络的\ **输出**\ 函数。
116+
117+
Sequence to Sequence Model with Attention
118+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
119+
120+
我们将使用 sequence to sequence model with attention
121+
作为例子演示如何配置复杂的循环神经网络模型。该模型的说明如下图所示。
122+
123+
.. figure:: ../../../tutorials/text_generation/encoder-decoder-attention-model.png
124+
:alt: image
125+
126+
image
127+
128+
在这个模型中,源序列 *S* = {*s*\ 1, …, \ *s*\ \ *T*\ }
129+
用双向门控循环神经网络编码。双向门控循环神经网络的隐藏状态
130+
*H*\ \ *S*\  = {*H*\ 1, …, \ *H*\ \ *T*\ } 被称为
131+
*编码向量*\ 。解码器是门控循环神经网络。当解读每一个\ *y*\ \ *t*\ 时,
132+
这个门控循环神经网络生成一系列权重
133+
*W*\ \ *S*\ \ *t*\  = {*W*\ 1\ *t*\ , …, \ *W*\ \ *T*\ \ *t*\ },
134+
用于计算编码向量的加权和。加权和用来生成\ *y*\ \ *t*\
135+
136+
模型的编码器部分如下所示。它叫做\ ``grumemory``\ 来表示门控循环神经网络。如果网络架构简单,那么推荐使用循环神经网络的方法,因为它比
137+
``recurrent_group``
138+
更快。我们已经实现了大多数常用的循环神经网络架构,可以参考
139+
`Layers <../../ui/api/trainer_config_helpers/layers_index.html>`__
140+
了解更多细节。
141+
142+
我们还将编码向量投射到 ``decoder_size``
143+
维空间。这通过获得反向循环网络的第一个实例,并将其投射到
144+
``decoder_size`` 维空间完成:
145+
146+
.. code:: sourcecode
147+
148+
# 定义源语句的数据层
149+
src_word_id = data_layer(name='source_language_word', size=source_dict_dim)
150+
# 计算每个词的词向量
151+
src_embedding = embedding_layer(
152+
input=src_word_id,
153+
size=word_vector_dim,
154+
param_attr=ParamAttr(name='_source_language_embedding'))
155+
# 应用前向循环神经网络
156+
src_forward = grumemory(input=src_embedding, size=encoder_size)
157+
# 应用反向递归神经网络(reverse=True表示反向循环神经网络)
158+
src_backward = grumemory(input=src_embedding,
159+
size=encoder_size,
160+
reverse=True)
161+
# 将循环神经网络的前向和反向部分混合在一起
162+
encoded_vector = concat_layer(input=[src_forward, src_backward])
163+
164+
# 投射编码向量到 decoder_size
165+
encoder_proj = mixed_layer(input = [full_matrix_projection(encoded_vector)],
166+
size = decoder_size)
167+
168+
# 计算反向RNN的第一个实例
169+
backward_first = first_seq(input=src_backward)
170+
171+
# 投射反向RNN的第一个实例到 decoder size
172+
decoder_boot = mixed_layer(input=[full_matrix_projection(backward_first)], size=decoder_size, act=TanhActivation())
173+
174+
解码器使用 ``recurrent_group`` 来定义循环神经网络。单步函数和输出函数在
175+
``gru_decoder_with_attention`` 中定义:
176+
177+
.. code:: sourcecode
178+
179+
group_inputs=[StaticInput(input=encoded_vector,is_seq=True),
180+
StaticInput(input=encoded_proj,is_seq=True)]
181+
trg_embedding = embedding_layer(
182+
input=data_layer(name='target_language_word',
183+
size=target_dict_dim),
184+
size=word_vector_dim,
185+
param_attr=ParamAttr(name='_target_language_embedding'))
186+
group_inputs.append(trg_embedding)
187+
188+
# 对于配备有注意力机制的解码器,在训练中,
189+
# 目标向量(groudtruth)是数据输入,
190+
# 而源序列的编码向量可以被无边界的memory访问
191+
# StaticInput 意味着不同时间步的输入都是相同的值,
192+
# 否则它以一个序列输入,不同时间步的输入是不同的。
193+
# 所有输入序列应该有相同的长度。
194+
decoder = recurrent_group(name=decoder_group_name,
195+
step=gru_decoder_with_attention,
196+
input=group_inputs)
197+
198+
单步函数的实现如下所示。首先,它定义解码网络的\ **Memory**\ 。然后定义
199+
attention,门控循环单元单步函数和输出函数:
200+
201+
.. code:: sourcecode
202+
203+
def gru_decoder_with_attention(enc_vec, enc_proj, current_word):
204+
# 定义解码器的Memory
205+
# Memory的输出定义在 gru_step 内
206+
# 注意 gru_step 应该与它的Memory名字相同
207+
decoder_mem = memory(name='gru_decoder',
208+
size=decoder_size,
209+
boot_layer=decoder_boot)
210+
# 计算 attention 加权编码向量
211+
context = simple_attention(encoded_sequence=enc_vec,
212+
encoded_proj=enc_proj,
213+
decoder_state=decoder_mem)
214+
# 混合当前词向量和attention加权编码向量
215+
decoder_inputs = mixed_layer(inputs = [full_matrix_projection(context),
216+
full_matrix_projection(current_word)],
217+
size = decoder_size * 3)
218+
# 定义门控循环单元循环神经网络单步函数
219+
gru_step = gru_step_layer(name='gru_decoder',
220+
input=decoder_inputs,
221+
output_mem=decoder_mem,
222+
size=decoder_size)
223+
# 定义输出函数
224+
out = mixed_layer(input=[full_matrix_projection(input=gru_step)],
225+
size=target_dict_dim,
226+
bias_attr=True,
227+
act=SoftmaxActivation())
228+
return out
229+
230+
生成序列
231+
--------
232+
233+
训练模型后,我们可以使用它来生成序列。通常的做法是使用\ **beam search**
234+
生成序列。以下代码片段定义 beam search 算法。注意,\ ``beam_search``
235+
函数假设 ``step`` 的输出函数返回的是下一个时刻输出词的 softmax
236+
归一化概率向量。我们对模型进行了以下更改。
237+
238+
- 使用 ``GeneratedInput`` 来表示 trg\_embedding。 ``GeneratedInput``
239+
将上一时间步所生成的词的向量来作为当前时间步的输入。
240+
- 使用 ``beam_search`` 函数。这个函数需要设置:
241+
242+
- ``bos_id``: 开始标记。每个句子都以开始标记开头。
243+
- ``eos_id``: 结束标记。每个句子都以结束标记结尾。
244+
- ``beam_size``: beam search 算法中的beam大小。
245+
- ``max_length``: 生成序列的最大长度。
246+
247+
- 使用 ``seqtext_printer_evaluator``
248+
根据索引矩阵和字典打印文本。这个函数需要设置:
249+
250+
- ``id_input``: 数据的整数ID,用于标识生成的文件中的相应输出。
251+
- ``dict_file``: 用于将词ID转换为词的字典文件。
252+
- ``result_file``: 生成结果文件的路径。
253+
254+
代码如下:
255+
256+
.. code:: sourcecode
257+
258+
group_inputs=[StaticInput(input=encoded_vector,is_seq=True),
259+
StaticInput(input=encoded_proj,is_seq=True)]
260+
# 在生成时,解码器基于编码源序列和最后生成的目标词预测下一目标词。
261+
# 编码源序列(编码器输出)必须由只读Memory的 StaticInput 指定。
262+
# 这里, GeneratedInputs 自动获取上一个生成的词,并在最开始初始化为起始词,如 <s>。
263+
trg_embedding = GeneratedInput(
264+
size=target_dict_dim,
265+
embedding_name='_target_language_embedding',
266+
embedding_size=word_vector_dim)
267+
group_inputs.append(trg_embedding)
268+
beam_gen = beam_search(name=decoder_group_name,
269+
step=gru_decoder_with_attention,
270+
input=group_inputs,
271+
bos_id=0, # Beginnning token.
272+
eos_id=1, # End of sentence token.
273+
beam_size=beam_size,
274+
max_length=max_length)
275+
276+
seqtext_printer_evaluator(input=beam_gen,
277+
id_input=data_layer(name="sent_id", size=1),
278+
dict_file=trg_dict_path,
279+
result_file=gen_trans_file)
280+
outputs(beam_gen)
281+
282+
注意,这种生成技术只用于类似解码器的生成过程。如果你正在处理序列标记任务,请参阅
283+
`Semantic Role Labeling
284+
Demo <../../demo/semantic_role_labeling/index.html>`__
285+
了解更多详细信息。
286+
287+
完整的配置文件在\ ``demo/seqToseq/seqToseq_net.py``\

0 commit comments

Comments
 (0)