Skip to content

Commit 075df09

Browse files
authored
Merge pull request #15470 from JiabinYang/feature/imperative
Add simple RNN in imperative
2 parents b69996c + fff67a9 commit 075df09

File tree

6 files changed

+605
-10
lines changed

6 files changed

+605
-10
lines changed

paddle/fluid/imperative/tracer.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ void CreateGradOp(const framework::OpDesc& op_desc,
3131
framework::OpInfoMap::Instance()
3232
.Get(op_desc.Type())
3333
.GradOpMaker()(op_desc, no_grad_set, grad_to_var, grad_sub_block);
34+
3435
for (auto& desc : descs) {
3536
grad_op_descs->emplace_back(desc.release());
3637
}

paddle/fluid/inference/analysis/passes/memory_optimize_pass.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@
1313
// limitations under the License.
1414

1515
#pragma once
16-
16+
#include <string>
17+
#include <utility>
18+
#include <vector>
1719
#include "paddle/fluid/inference/analysis/analysis_pass.h"
1820
#include "paddle/fluid/platform/port.h"
1921

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
cc_library(benchmark SRCS benchmark.cc DEPS enforce)
22
cc_test(test_benchmark SRCS benchmark_tester.cc DEPS benchmark)
3-
cc_binary(visualizer SRCS visualizer.cc DEPS analysis
4-
paddle_pass_builder ir_pass_manager pass graph_viz_pass analysis_passes)
3+
#cc_binary(visualizer SRCS visualizer.cc DEPS analysis
4+
# paddle_pass_builder ir_pass_manager pass graph_viz_pass analysis_passes)

python/paddle/fluid/imperative/nn.py

Lines changed: 89 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,7 @@
2222
from ..framework import Variable, OpProtoHolder
2323
from ..param_attr import ParamAttr
2424
from ..initializer import Normal, Constant
25-
26-
__all__ = [
27-
'Conv2D',
28-
'Pool2D',
29-
'FC',
30-
'BatchNorm',
31-
]
25+
__all__ = ['Conv2D', 'Pool2D', 'FC', 'BatchNorm', 'Embedding']
3226

3327

3428
class Conv2D(layers.Layer):
@@ -414,3 +408,91 @@ def forward(self, input):
414408

415409
# Currently, we don't support inplace in imperative mode
416410
return self._helper.append_activation(batch_norm_out)
411+
412+
413+
class Embedding(layers.Layer):
414+
"""
415+
**Embedding Layer**
416+
417+
This layer is used to lookup embeddings of IDs, provided by :attr:`input`, in
418+
a lookup table. The result of this lookup is the embedding of each ID in the
419+
:attr:`input`.
420+
421+
All the input variables are passed in as local variables to the LayerHelper
422+
constructor.
423+
424+
Args:
425+
size(tuple|list): The shape of the look up table parameter. It should
426+
have two elements which indicate the size of the dictionary of
427+
embeddings and the size of each embedding vector respectively.
428+
is_sparse(bool): The flag indicating whether to use sparse update.
429+
is_distributed(bool): Whether to run lookup table from remote parameter server.
430+
padding_idx(int|long|None): If :attr:`None`, it makes no effect to lookup.
431+
Otherwise the given :attr:`padding_idx` indicates padding the output
432+
with zeros whenever lookup encounters it in :attr:`input`. If
433+
:math:`padding_idx < 0`, the :attr:`padding_idx` to use in lookup is
434+
:math:`size[0] + dim`.
435+
param_attr(ParamAttr): Parameters for this layer
436+
dtype(np.dtype|core.VarDesc.VarType|str): The type of data : float32, float_16, int etc
437+
438+
Returns:
439+
Variable: The tensor variable storing the embeddings of the \
440+
supplied inputs.
441+
442+
Examples:
443+
.. code-block:: python
444+
445+
dict_size = len(dataset.ids)
446+
input = fluid.layers.data(name='ids', shape=[32, 32], dtype='float32')
447+
embedding = fluid.imperative.Embedding(size=[dict_size, 16])
448+
fc = embedding(input)
449+
"""
450+
451+
def __init__(self,
452+
size,
453+
is_sparse=False,
454+
is_distributed=False,
455+
padding_idx=None,
456+
param_attr=None,
457+
dtype='float32'):
458+
459+
super(Embedding, self).__init__()
460+
self._size = size
461+
self._is_sparse = is_sparse
462+
self._is_distributed = is_distributed
463+
464+
self._padding_idx = -1 if padding_idx is None else padding_idx if padding_idx >= 0 else (
465+
size[0] + padding_idx)
466+
467+
self._param_attr = param_attr
468+
self._dtype = dtype
469+
self._remote_prefetch = self._is_sparse and (not self._is_distributed)
470+
if self._remote_prefetch:
471+
assert self._is_sparse is True and self._is_distributed is False
472+
473+
from ..layer_helper import LayerHelper
474+
self._helper = LayerHelper('embedding', param_attr=param_attr)
475+
self._w = self._helper.create_parameter(
476+
attr=self._param_attr,
477+
shape=self._size,
478+
dtype=self._dtype,
479+
is_bias=False)
480+
481+
def parameters(self):
482+
return [self._w]
483+
484+
def forward(self, input):
485+
out = self._helper.create_variable_for_type_inference(self._dtype)
486+
self._helper.append_op(
487+
type='lookup_table',
488+
inputs={'Ids': input,
489+
'W': self._w},
490+
outputs={'Out': out},
491+
attrs={
492+
'is_sparse': self._is_sparse,
493+
'is_distributed': self._is_distributed,
494+
'remote_prefetch': self._remote_prefetch,
495+
'padding_idx': self._padding_idx
496+
})
497+
498+
return out

python/paddle/fluid/tests/unittests/test_imperative.py

Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,128 @@ def forward(self, inputs):
6666
return x
6767

6868

69+
class SimpleRNNCell(fluid.imperative.Layer):
70+
def __init__(self, step_input_size, hidden_size, output_size, param_attr):
71+
super(SimpleRNNCell, self).__init__()
72+
self.step_input_size = step_input_size
73+
self.hidden_size = hidden_size
74+
self.output_size = output_size
75+
self._dype = core.VarDesc.VarType.FP32
76+
from paddle.fluid.layer_helper import LayerHelper
77+
self._helper = LayerHelper(
78+
'SimpleRNNCell', act="tanh", param_attr=param_attr)
79+
80+
def _build_once(self, inputs, pre_hidden):
81+
i2h_param_shape = [self.step_input_size, self.hidden_size]
82+
h2h_param_shape = [self.hidden_size, self.hidden_size]
83+
h2o_param_shape = [self.output_size, self.hidden_size]
84+
self._i2h_w = self._helper.create_parameter(
85+
attr=self._helper.param_attr,
86+
shape=i2h_param_shape,
87+
dtype=self._dtype,
88+
is_bias=False)
89+
self._h2h_w = self._helper.create_parameter(
90+
attr=self._helper.param_attr,
91+
shape=h2h_param_shape,
92+
dtype=self._dtype,
93+
is_bias=False)
94+
self._h2o_w = self._helper.create_parameter(
95+
attr=self._helper.param_attr,
96+
shape=h2o_param_shape,
97+
dtype=self._dtype,
98+
is_bias=False)
99+
100+
def forward(self, input, pre_hidden):
101+
102+
tmp_i2h = self._helper.create_variable_for_type_inference(self._dtype)
103+
tmp_h2h = self._helper.create_variable_for_type_inference(self._dtype)
104+
hidden = self._helper.create_variable_for_type_inference(self._dype)
105+
out = self._helper.create_variable_for_type_inference(self._dype)
106+
softmax_out = self._helper.create_variable_for_type_inference(
107+
self._dtype)
108+
reduce_out = self._helper.create_variable_for_type_inference(
109+
self._dtype)
110+
self._helper.append_op(
111+
type="mul",
112+
inputs={"X": input,
113+
"Y": self._i2h_w},
114+
outputs={"Out": tmp_i2h},
115+
attrs={"x_num_col_dims": 1,
116+
"y_num_col_dims": 1})
117+
118+
self._helper.append_op(
119+
type="mul",
120+
inputs={"X": pre_hidden,
121+
"Y": self._h2h_w},
122+
outputs={"Out": tmp_h2h},
123+
attrs={"x_num_col_dims": 1,
124+
"y_num_col_dims": 1})
125+
126+
self._helper.append_op(
127+
type="elementwise_add",
128+
inputs={'X': tmp_h2h,
129+
'Y': tmp_i2h},
130+
outputs={'Out': hidden},
131+
attrs={'axis': -1,
132+
'use_mkldnn': False})
133+
hidden = self._helper.append_activation(hidden)
134+
135+
self._helper.append_op(
136+
type="mul",
137+
inputs={"X": hidden,
138+
"Y": self._h2o_w},
139+
outputs={"Out": out},
140+
attrs={"x_num_col_dims": 1,
141+
"y_num_col_dims": 1})
142+
143+
self._helper.append_op(
144+
type="softmax",
145+
inputs={"X": out},
146+
outputs={"Out": softmax_out},
147+
attrs={"use_cudnn": False})
148+
149+
self._helper.append_op(
150+
type='reduce_sum',
151+
inputs={'X': softmax_out},
152+
outputs={'Out': reduce_out},
153+
attrs={'dim': None,
154+
'keep_dim': False,
155+
'reduce_all': True})
156+
157+
return reduce_out, hidden
158+
159+
160+
class SimpleRNN(fluid.imperative.Layer):
161+
def __init__(self):
162+
super(SimpleRNN, self).__init__()
163+
self.seq_len = 4
164+
self._cell = SimpleRNNCell(
165+
3,
166+
3,
167+
3,
168+
fluid.ParamAttr(initializer=fluid.initializer.Constant(value=0.1)))
169+
170+
def forward(self, inputs):
171+
outs = list()
172+
pre_hiddens = list()
173+
174+
init_hidden = fluid.layers.tensor.create_parameter(
175+
attr=fluid.ParamAttr(
176+
initializer=fluid.initializer.Constant(value=0.1)),
177+
shape=[1, 3],
178+
dtype='float32',
179+
is_bias=False)
180+
pre_hidden = init_hidden
181+
for i in range(self.seq_len):
182+
input = fluid.layers.slice(
183+
inputs, axes=[1], starts=[i], ends=[i + 1])
184+
input = fluid.layers.reshape(input, shape=[1, 3])
185+
out_softmax, pre_hidden = self._cell(input, pre_hidden)
186+
outs.append(out_softmax)
187+
188+
return outs, pre_hiddens
189+
190+
69191
class TestImperative(unittest.TestCase):
70192
def test_sum_op(self):
71193
x = np.ones([2, 2], np.float32)
@@ -211,6 +333,41 @@ def test_mlp(self):
211333
self.assertTrue(np.allclose(dy_out, static_out))
212334
self.assertTrue(np.allclose(dy_grad, static_grad))
213335

336+
def test_rnn(self):
337+
np_inp = np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0], [7.0, 8.0, 9.0],
338+
[10.0, 11.0, 12.0]])
339+
np_inp = np_inp.reshape((1, 4, 3))
340+
np_inp = np_inp.astype(np.float32)
341+
with fluid.imperative.guard():
342+
var_inp = fluid.imperative.base.to_variable(np_inp)
343+
var_inp = fluid.layers.reshape(var_inp, shape=[1, 4, 3])
344+
simple_rnn = SimpleRNN()
345+
outs, pre_hiddens = simple_rnn.forward(var_inp)
346+
dy_out = outs[3]._numpy()
347+
outs[3]._backward()
348+
dy_grad_h2o = simple_rnn._cell._h2o_w._gradient()
349+
dy_grad_h2h = simple_rnn._cell._h2h_w._gradient()
350+
dy_grad_i2h = simple_rnn._cell._i2h_w._gradient()
351+
352+
with new_program_scope():
353+
inp = fluid.layers.data(
354+
name="inp", shape=[1, 4, 3], append_batch_size=False)
355+
simple_rnn = SimpleRNN()
356+
outs, pre_hiddens = simple_rnn(inp)
357+
param_grads = fluid.backward.append_backward(outs[3])
358+
exe = fluid.Executor(fluid.CPUPlace())
359+
exe.run(fluid.default_startup_program())
360+
static_out, static_grad_h2o, static_grad_h2h, static_grad_i2h = exe.run(
361+
feed={inp.name: np_inp},
362+
fetch_list=[
363+
outs[3].name, param_grads[0][1].name,
364+
param_grads[1][1].name, param_grads[2][1].name
365+
])
366+
self.assertTrue(np.allclose(dy_out, static_out))
367+
self.assertTrue(np.allclose(dy_grad_h2o, static_grad_h2o))
368+
self.assertTrue(np.allclose(dy_grad_h2h, static_grad_h2h))
369+
self.assertTrue(np.allclose(dy_grad_i2h, static_grad_i2h))
370+
214371

215372
if __name__ == '__main__':
216373
unittest.main()

0 commit comments

Comments
 (0)