|
|
|
@ -20,6 +20,7 @@ from ..framework import Program, Variable, Operator
|
|
|
|
|
from ..layer_helper import LayerHelper, unique_name
|
|
|
|
|
from ..initializer import force_init_on_cpu
|
|
|
|
|
from ops import logical_and, logical_not, logical_or
|
|
|
|
|
import numpy
|
|
|
|
|
|
|
|
|
|
__all__ = [
|
|
|
|
|
'split_lod_tensor',
|
|
|
|
@ -1314,6 +1315,39 @@ class IfElse(object):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class DynamicRNN(object):
|
|
|
|
|
"""
|
|
|
|
|
Dynamic RNN.
|
|
|
|
|
|
|
|
|
|
This RNN can process a batch of sequence data. The length of each sample
|
|
|
|
|
sequence can be different. This API automatically process them in batch.
|
|
|
|
|
|
|
|
|
|
The input lod must be set. Please reference `lod_tensor`
|
|
|
|
|
|
|
|
|
|
>>> import paddle.fluid as fluid
|
|
|
|
|
>>> data = fluid.layers.data(name='sentence', dtype='int64', lod_level=1)
|
|
|
|
|
>>> embedding = fluid.layers.embedding(input=data, size=[65535, 32],
|
|
|
|
|
>>> is_sparse=True)
|
|
|
|
|
>>>
|
|
|
|
|
>>> drnn = fluid.layers.DynamicRNN()
|
|
|
|
|
>>> with drnn.block():
|
|
|
|
|
>>> word = drnn.step_input(embedding)
|
|
|
|
|
>>> prev = drnn.memory(shape=[200])
|
|
|
|
|
>>> hidden = fluid.layers.fc(input=[word, prev], size=200, act='relu')
|
|
|
|
|
>>> drnn.update_memory(prev, hidden) # set prev to hidden
|
|
|
|
|
>>> drnn.output(hidden)
|
|
|
|
|
>>>
|
|
|
|
|
>>> # last is the last time step of rnn. It is the encoding result.
|
|
|
|
|
>>> last = fluid.layers.sequence_last_step(drnn())
|
|
|
|
|
|
|
|
|
|
The dynamic RNN will unfold sequence into timesteps. Users need to define
|
|
|
|
|
how to process each time step during the :code:`with` block.
|
|
|
|
|
|
|
|
|
|
The `memory` is used staging data cross time step. The initial value of
|
|
|
|
|
memory can be zero or another variable.
|
|
|
|
|
|
|
|
|
|
The dynamic RNN can mark multiple variables as its output. Use `drnn()` to
|
|
|
|
|
get the output sequence.
|
|
|
|
|
"""
|
|
|
|
|
BEFORE_RNN = 0
|
|
|
|
|
IN_RNN = 1
|
|
|
|
|
AFTER_RNN = 2
|
|
|
|
@ -1336,6 +1370,15 @@ class DynamicRNN(object):
|
|
|
|
|
self.mem_link = []
|
|
|
|
|
|
|
|
|
|
def step_input(self, x):
|
|
|
|
|
"""
|
|
|
|
|
Mark a sequence as a dynamic RNN input.
|
|
|
|
|
Args:
|
|
|
|
|
x(Variable): The input sequence.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The current timestep in the input sequence.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
self._assert_in_rnn_block_("step_input")
|
|
|
|
|
if not isinstance(x, Variable):
|
|
|
|
|
raise TypeError(
|
|
|
|
@ -1379,6 +1422,15 @@ class DynamicRNN(object):
|
|
|
|
|
return array_read(array=input_array, i=self.step_idx)
|
|
|
|
|
|
|
|
|
|
def static_input(self, x):
|
|
|
|
|
"""
|
|
|
|
|
Mark a variable as a RNN input. The input will not be scattered into
|
|
|
|
|
time steps.
|
|
|
|
|
Args:
|
|
|
|
|
x(Variable): The input variable.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
The input variable that can access in RNN.
|
|
|
|
|
"""
|
|
|
|
|
self._assert_in_rnn_block_("static_input")
|
|
|
|
|
if not isinstance(x, Variable):
|
|
|
|
|
raise TypeError(
|
|
|
|
@ -1400,6 +1452,10 @@ class DynamicRNN(object):
|
|
|
|
|
|
|
|
|
|
@contextlib.contextmanager
|
|
|
|
|
def block(self):
|
|
|
|
|
"""
|
|
|
|
|
The block for user to define operators in RNN. See the class docstring
|
|
|
|
|
for more details.
|
|
|
|
|
"""
|
|
|
|
|
if self.status != DynamicRNN.BEFORE_RNN:
|
|
|
|
|
raise ValueError("rnn.block() can only be invoke once")
|
|
|
|
|
self.step_idx = fill_constant(
|
|
|
|
@ -1426,6 +1482,9 @@ class DynamicRNN(object):
|
|
|
|
|
x=each_array, table=self.lod_rank_table))
|
|
|
|
|
|
|
|
|
|
def __call__(self, *args, **kwargs):
|
|
|
|
|
"""
|
|
|
|
|
Get the output of RNN. This API should only be invoked after RNN.block()
|
|
|
|
|
"""
|
|
|
|
|
if self.status != DynamicRNN.AFTER_RNN:
|
|
|
|
|
raise ValueError(("Output of the dynamic RNN can only be visited "
|
|
|
|
|
"outside the rnn block."))
|
|
|
|
@ -1440,6 +1499,70 @@ class DynamicRNN(object):
|
|
|
|
|
value=0.0,
|
|
|
|
|
need_reorder=False,
|
|
|
|
|
dtype='float32'):
|
|
|
|
|
"""
|
|
|
|
|
Create a memory variable.
|
|
|
|
|
|
|
|
|
|
If the :code:`init` is not None, :code:`memory` will be initialized by
|
|
|
|
|
this variable. The :code:`need_reorder` is used to reorder the memory as
|
|
|
|
|
the input variable. It should be set to true when the initialized memory
|
|
|
|
|
depends on the input sample.
|
|
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
>>> import paddle.fluid as fluid
|
|
|
|
|
>>> sentence = fluid.layers.data(
|
|
|
|
|
>>> name='sentence', dtype='float32', shape=[32])
|
|
|
|
|
>>> boot_memory = fluid.layers.data(
|
|
|
|
|
>>> name='boot', dtype='float32', shape=[10])
|
|
|
|
|
>>>
|
|
|
|
|
>>> drnn = fluid.layers.DynamicRNN()
|
|
|
|
|
>>> with drnn.block():
|
|
|
|
|
>>> word = drnn.step_input(sentence)
|
|
|
|
|
>>> memory = drnn.memory(init=boot_memory, need_reorder=True)
|
|
|
|
|
>>> hidden = fluid.layers.fc(
|
|
|
|
|
>>> input=[word, memory], size=10, act='tanh')
|
|
|
|
|
>>> drnn.update_memory(ex_mem=memory, new_mem=hidden)
|
|
|
|
|
>>> drnn.output(hidden)
|
|
|
|
|
>>> rnn_output = drnn()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Otherwise, if :code:`shape`, :code:`value`, :code:`dtype` are set, the
|
|
|
|
|
:code:`memory` will be initialized by this :code:`value`.
|
|
|
|
|
|
|
|
|
|
For example,
|
|
|
|
|
|
|
|
|
|
>>> import paddle.fluid as fluid
|
|
|
|
|
>>> sentence = fluid.layers.data(
|
|
|
|
|
>>> name='sentence', dtype='float32', shape=[32])
|
|
|
|
|
>>>
|
|
|
|
|
>>> drnn = fluid.layers.DynamicRNN()
|
|
|
|
|
>>> with drnn.block():
|
|
|
|
|
>>> word = drnn.step_input(sentence)
|
|
|
|
|
>>> memory = drnn.memory(shape=[10], dtype='float32', value=0)
|
|
|
|
|
>>> hidden = fluid.layers.fc(
|
|
|
|
|
>>> input=[word, memory], size=10, act='tanh')
|
|
|
|
|
>>> drnn.update_memory(ex_mem=memory, new_mem=hidden)
|
|
|
|
|
>>> drnn.output(hidden)
|
|
|
|
|
>>> rnn_output = drnn()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
init(Variable|None): The initialized variable.
|
|
|
|
|
|
|
|
|
|
shape(list|tuple): The memory shape. NOTE the shape does not contain
|
|
|
|
|
batch_size.
|
|
|
|
|
|
|
|
|
|
value(float): the initalized value.
|
|
|
|
|
|
|
|
|
|
need_reorder(bool): True if the initialized memory depends on the
|
|
|
|
|
input sample.
|
|
|
|
|
|
|
|
|
|
dtype(str|numpy.dtype): The data type of the initialized memory.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
the memory variable.
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
self._assert_in_rnn_block_('memory')
|
|
|
|
|
if init is not None:
|
|
|
|
|
if not isinstance(init, Variable):
|
|
|
|
@ -1507,6 +1630,16 @@ class DynamicRNN(object):
|
|
|
|
|
return self.memory(init=init)
|
|
|
|
|
|
|
|
|
|
def update_memory(self, ex_mem, new_mem):
|
|
|
|
|
"""
|
|
|
|
|
Update the memory from ex_mem to new_mem. NOTE that the shape and data
|
|
|
|
|
type of :code:`ex_mem` and :code:`new_mem` must be same.
|
|
|
|
|
Args:
|
|
|
|
|
ex_mem(Variable): the memory variable.
|
|
|
|
|
new_mem(Variable): the plain variable generated in RNN block.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
None
|
|
|
|
|
"""
|
|
|
|
|
self._assert_in_rnn_block_('update_memory')
|
|
|
|
|
if not isinstance(ex_mem, Variable):
|
|
|
|
|
raise TypeError("The input arg `ex_mem` of update_memory() must "
|
|
|
|
@ -1524,6 +1657,15 @@ class DynamicRNN(object):
|
|
|
|
|
self.mem_link.append((new_mem, mem_array))
|
|
|
|
|
|
|
|
|
|
def output(self, *outputs):
|
|
|
|
|
"""
|
|
|
|
|
mark the RNN output variables.
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
outputs: The output variables.
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
None
|
|
|
|
|
"""
|
|
|
|
|
self._assert_in_rnn_block_('output')
|
|
|
|
|
parent_block = self._parent_block_()
|
|
|
|
|
for each in outputs:
|
|
|
|
|