Skip to content

Commit 2a971f3

Browse files
jacquesqiaojetfuel
authored andcommitted
Add inferencer infer (#10445)
* add Inference.infer * optimize code * update no_test_word2vec_new_api.py * update trainer * split check_and_get_place * use inference_program to save inference model in Trainer * update demo * update save_inference_model * clean code
1 parent aaab92b commit 2a971f3

File tree

4 files changed

+104
-70
lines changed

4 files changed

+104
-70
lines changed

python/paddle/fluid/inferencer.py

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,31 +16,42 @@
1616
import framework
1717
import executor
1818
import io
19+
from trainer import check_and_get_place
20+
1921
__all__ = ['Inferencer', ]
2022

2123

2224
class Inferencer(object):
23-
def __init__(self, network_func, param_path=None, place=None):
24-
# 1. we need to generate a framework.Program by calling
25-
# network_func. Reference: fluid.program_guard in test_word2vec.py
26-
27-
# 2. move the default_main_program to self.program.
28-
29-
# 3. run the default_startup program.
30-
31-
# 4. load params from param_path into scope
25+
def __init__(self, param_path, place=None):
26+
"""
27+
:param param_path: the path where the inference model is saved by fluid.io.save_inference_model
28+
:param place: place to do the inference
29+
"""
30+
self.param_path = param_path
3231
self.scope = core.Scope()
33-
self.place = place
34-
self.startup_program = framework.Program()
35-
# TODO: generate the startup_program with network_func
36-
37-
exe = executor.Executor(place)
38-
exe.run(self.startup_program, scope=self.scope)
3932

40-
if param_path:
33+
self.exe = executor.Executor(check_and_get_place(place))
34+
with executor.scope_guard(self.scope):
4135
# load params from param_path into scope
42-
io.load_persistables(exe, dirname=param_path)
43-
44-
def infer(self, inputs):
45-
# run self.program
46-
pass
36+
[self.inference_program, _,
37+
self.fetch_targets] = io.load_inference_model(
38+
executor=self.exe, dirname=param_path)
39+
40+
def infer(self, inputs, return_numpy=True):
41+
"""
42+
:param inputs: a map of {"input_name": input_var} that will be feed into the inference program
43+
to get the predict value
44+
:param return_numpy: if return numpy value for row tensor
45+
:return: the predict value of the inference model
46+
"""
47+
if not isinstance(inputs, dict):
48+
raise ValueError(
49+
"inputs should be a map of {'input_name': input_var}")
50+
51+
with executor.scope_guard(self.scope):
52+
results = self.exe.run(self.inference_program,
53+
feed=inputs,
54+
fetch_list=self.fetch_targets,
55+
return_numpy=return_numpy)
56+
57+
return results

python/paddle/fluid/io.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,9 @@ def get_inference_program(target_vars, main_program=None):
263263
def prepend_feed_ops(inference_program,
264264
feed_target_names,
265265
feed_holder_name='feed'):
266+
if len(feed_target_names) == 0:
267+
return
268+
266269
global_block = inference_program.global_block()
267270
feed_var = global_block.create_var(
268271
name=feed_holder_name,
@@ -323,9 +326,10 @@ def save_inference_model(dirname,
323326
if isinstance(feeded_var_names, basestring):
324327
feeded_var_names = [feeded_var_names]
325328
else:
326-
if not (bool(feeded_var_names) and all(
327-
isinstance(name, basestring) for name in feeded_var_names)):
328-
raise ValueError("'feed_var_names' should be a list of str.")
329+
if len(feeded_var_names) > 0:
330+
if not (bool(feeded_var_names) and all(
331+
isinstance(name, basestring) for name in feeded_var_names)):
332+
raise ValueError("'feed_var_names' should be a list of str.")
329333

330334
if isinstance(target_vars, Variable):
331335
target_vars = [target_vars]

python/paddle/fluid/tests/book/high-level-api/word2vec/no_test_word2vec_new_api.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -99,45 +99,45 @@ def train(use_cuda, is_sparse, save_path):
9999
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
100100

101101
def event_handler(event):
102-
# print type(event)
103102
if isinstance(event, fluid.EndEpochEvent):
104103
outs = trainer.test(reader=test_reader)
105104
avg_cost = outs[0]
106105
print("loss= ", avg_cost)
107106

108107
if avg_cost < 5.0:
109-
trainer.save_params(save_path)
108+
trainer.save_inference_model(save_path)
110109
return
111110
if math.isnan(avg_cost):
112111
sys.exit("got NaN loss, training failed.")
113112

114113
trainer = fluid.Trainer(
115114
partial(train_program, is_sparse),
115+
partial(inference_program, is_sparse),
116116
fluid.optimizer.SGD(learning_rate=0.001),
117117
place=place)
118118
trainer.train(
119-
reader=train_reader, num_epochs=100, event_handler=event_handler)
119+
reader=train_reader, num_epochs=1, event_handler=event_handler)
120120

121121

122122
def infer(use_cuda, is_sparse, save_path):
123123
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace()
124-
inferencer = fluid.Inferencer(
125-
partial(inference_program, is_sparse),
126-
param_path=save_path,
127-
place=place)
124+
inferencer = fluid.Inferencer(param_path=save_path, place=place)
128125

129126
lod = [0, 1]
130127
first_word = create_random_lodtensor(lod, place, low=0, high=dict_size - 1)
131128
second_word = create_random_lodtensor(lod, place, low=0, high=dict_size - 1)
132129
third_word = create_random_lodtensor(lod, place, low=0, high=dict_size - 1)
133130
fourth_word = create_random_lodtensor(lod, place, low=0, high=dict_size - 1)
134-
result = inferencer.infer({
135-
'firstw': first_word,
136-
'secondw': second_word,
137-
'thirdw': third_word,
138-
'forthw': fourth_word
139-
})
140-
print(result)
131+
132+
result = inferencer.infer(
133+
{
134+
'firstw': first_word,
135+
'secondw': second_word,
136+
'thirdw': third_word,
137+
'forthw': fourth_word
138+
},
139+
return_numpy=False)
140+
print(np.array(result[0]))
141141

142142

143143
def main(use_cuda, is_sparse):

python/paddle/fluid/trainer.py

Lines changed: 51 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
import data_feeder
2020
import contextlib
2121
import io
22-
import transpiler
22+
import unique_name
2323

2424
# optimizer is same as the parameter of Trainer.__init__. Rename it to opt_module
2525
import optimizer as opt_module
@@ -56,26 +56,62 @@ def __init__(self, epoch_id, step_id):
5656
self.step = step_id
5757

5858

59+
def check_and_get_place(place):
60+
"""
61+
Check the type of place or get the default place
62+
Args:
63+
place(None|core.CUDAPlace|core.CPUPlace): the place that trainer will be executed on.
64+
65+
Raises:
66+
TypeError if the type mismatched.
67+
68+
Returns:
69+
the original place if it is not None.
70+
if fluid is compiled with CUDA, returns CUDAPlace(0) by default.
71+
Otherwise returns CPUPlace by default.
72+
"""
73+
if place is None:
74+
if core.is_compiled_with_cuda():
75+
return core.CUDAPlace(0)
76+
else:
77+
return core.CPUPlace()
78+
else:
79+
if not isinstance(place, core.CUDAPlace) and not isinstance(
80+
place, core.CPUPlace):
81+
raise TypeError("Place should be either CUDAPlace or CPUPlace")
82+
return place
83+
84+
5985
class Trainer(object):
6086
"""
6187
6288
Args:
63-
program_func(callable): A function which will return loss. The loss must be a scaler.
89+
train_func(callable): A function which will return loss. The loss must be a scalar.
90+
infer_func(callable): A function which will return predict, used to save inference model
6491
optimizer(optimizer.Optimizer): The optimizer should be an instance of Optimizer
6592
place: The device place of this trainer.
6693
"""
6794

68-
def __init__(self, program_func, optimizer, param_path=None, place=None):
95+
def __init__(self,
96+
train_func,
97+
infer_func,
98+
optimizer,
99+
param_path=None,
100+
place=None):
69101
# 1. we need to generate a framework.Program by calling
70102
# program_func. Reference: fluid.program_guard in
71103
# test_word2vec.py
104+
if not isinstance(optimizer, opt_module.Optimizer):
105+
raise TypeError("The optimizer should be an instance of Optimizer")
106+
107+
self.infer_func = infer_func
72108
self.scope = core.Scope()
73109

74110
self.startup_program = framework.Program()
75111
self.train_program = framework.Program()
76112

77113
with framework.program_guard(self.train_program, self.startup_program):
78-
program_func_outs = program_func()
114+
program_func_outs = train_func()
79115
self.test_outputs = program_func_outs if isinstance(
80116
program_func_outs, list) else [program_func_outs]
81117
self.test_program = self.train_program.clone()
@@ -86,9 +122,9 @@ def __init__(self, program_func, optimizer, param_path=None, place=None):
86122
loss = self.test_outputs[0]
87123
optimize_ops, params_grads = optimizer.minimize(loss)
88124

89-
self.place = Trainer._check_and_get_place(place)
125+
self.place = check_and_get_place(place)
90126

91-
self.dist_transpile_if_necessary(optimize_ops, params_grads)
127+
self._dist_transpile_if_necessary(optimize_ops, params_grads)
92128

93129
# 2. move the default_main_program to self.program and run the
94130
# default_startup program on an empty core.Scope()
@@ -101,7 +137,7 @@ def __init__(self, program_func, optimizer, param_path=None, place=None):
101137
# load params from param_path into scope
102138
io.load_persistables(exe, dirname=param_path)
103139

104-
def dist_transpile_if_necessary(self, optimize_ops, params_grads):
140+
def _dist_transpile_if_necessary(self, optimize_ops, params_grads):
105141
if "PADDLE_TRAINING_ROLE" not in os.environ:
106142
return
107143

@@ -190,31 +226,14 @@ def save_params(self, param_path):
190226
exe = executor.Executor(self.place)
191227
io.save_persistables(exe, dirname=param_path)
192228

193-
@staticmethod
194-
def _check_and_get_place(place):
195-
"""
196-
Check the type of place or get the default place
197-
Args:
198-
place(None|core.CUDAPlace|core.CPUPlace): the place that trainer will be executed on.
199-
200-
Raises:
201-
TypeError if the type mismatched.
202-
203-
Returns:
204-
the original place if it is not None.
205-
if fluid is compiled with CUDA, returns CUDAPlace(0) by default.
206-
Otherwise returns CPUPlace by default.
207-
"""
208-
if place is None:
209-
if core.is_compiled_with_cuda():
210-
return core.CUDAPlace(0)
211-
else:
212-
return core.CPUPlace()
213-
else:
214-
if not isinstance(place, core.CUDAPlace) and not isinstance(
215-
place, core.CPUPlace):
216-
raise TypeError("Place should be either CUDAPlace or CPUPlace")
217-
return place
229+
def save_inference_model(self, model_path):
230+
inference_program = framework.Program()
231+
with framework.program_guard(inference_program):
232+
with unique_name.guard():
233+
predict_var = self.infer_func()
234+
predict_var = self.train_program.block(0).var(predict_var.name)
235+
exe = executor.Executor(self.place)
236+
io.save_inference_model(model_path, [], [predict_var], exe)
218237

219238
@contextlib.contextmanager
220239
def _prog_and_scope_guard(self):

0 commit comments

Comments
 (0)