未验证 提交 4d236354 编写于 作者: 王明冬 提交者: GitHub

clip op extra information when export model. (#35447)

* clip op extra information when export model,test=ocr

* rename clip_extra parameter to kwargs in save_inference_model, test=ocr
上级 86d4af39
...@@ -460,6 +460,11 @@ void OpDesc::RemoveOutput(const std::string &name) { ...@@ -460,6 +460,11 @@ void OpDesc::RemoveOutput(const std::string &name) {
need_update_ = true; need_update_ = true;
} }
void OpDesc::RemoveInput(const std::string &name) {
inputs_.erase(name);
need_update_ = true;
}
bool OpDesc::HasProtoAttr(const std::string &name) const { bool OpDesc::HasProtoAttr(const std::string &name) const {
auto &op_info = OpInfoMap::Instance(); auto &op_info = OpInfoMap::Instance();
if (op_info.Has(desc_.type())) { if (op_info.Has(desc_.type())) {
......
...@@ -68,6 +68,8 @@ class OpDesc { ...@@ -68,6 +68,8 @@ class OpDesc {
const std::vector<std::string> &args); const std::vector<std::string> &args);
void RemoveOutput(const std::string &name); void RemoveOutput(const std::string &name);
void RemoveInput(const std::string &name);
bool HasAttr(const std::string &name) const { bool HasAttr(const std::string &name) const {
return attrs_.find(name) != attrs_.end(); return attrs_.find(name) != attrs_.end();
} }
......
...@@ -267,6 +267,7 @@ void BindOpDesc(pybind11::module *m) { ...@@ -267,6 +267,7 @@ void BindOpDesc(pybind11::module *m) {
self.SetOutput(name, vec_var_name); self.SetOutput(name, vec_var_name);
}) })
.def("remove_output", &pd::OpDesc::RemoveOutput) .def("remove_output", &pd::OpDesc::RemoveOutput)
.def("remove_input", &pd::OpDesc::RemoveInput)
.def("input_arg_names", &pd::OpDesc::InputArgumentNames) .def("input_arg_names", &pd::OpDesc::InputArgumentNames)
.def("output_arg_names", &pd::OpDesc::OutputArgumentNames) .def("output_arg_names", &pd::OpDesc::OutputArgumentNames)
.def("_rename_input", &pd::OpDesc::RenameInput) .def("_rename_input", &pd::OpDesc::RenameInput)
......
...@@ -495,7 +495,8 @@ class ImperativeQuantizeOutputs(object): ...@@ -495,7 +495,8 @@ class ImperativeQuantizeOutputs(object):
executor=exe, executor=exe,
main_program=infer_program.clone(), main_program=infer_program.clone(),
model_filename=model_filename, model_filename=model_filename,
params_filename=params_filename) params_filename=params_filename,
clip_extra=True)
if is_dynamic_mode: if is_dynamic_mode:
paddle.disable_static() paddle.disable_static()
......
...@@ -169,9 +169,11 @@ class TestQuantizationScalePass(unittest.TestCase): ...@@ -169,9 +169,11 @@ class TestQuantizationScalePass(unittest.TestCase):
f.write(str(server_program)) f.write(str(server_program))
with fluid.scope_guard(scope): with fluid.scope_guard(scope):
fluid.io.save_inference_model('quant_scale_model' + dev_name, fluid.io.save_inference_model(
['image', 'label'], [loss], exe, 'quant_scale_model' + dev_name, ['image', 'label'], [loss],
server_program) exe,
server_program,
clip_extra=True)
def test_quant_scale_cuda(self): def test_quant_scale_cuda(self):
if fluid.core.is_compiled_with_cuda(): if fluid.core.is_compiled_with_cuda():
......
...@@ -141,9 +141,11 @@ class TestQuantizeProgramPass(unittest.TestCase): ...@@ -141,9 +141,11 @@ class TestQuantizeProgramPass(unittest.TestCase):
qt.convert(test_program, scope) qt.convert(test_program, scope)
if not for_ci: if not for_ci:
with fluid.scope_guard(scope): with fluid.scope_guard(scope):
fluid.io.save_inference_model('./infer_model', fluid.io.save_inference_model(
['image', 'label'], [loss], exe, './infer_model', ['image', 'label'], [loss],
test_program) exe,
test_program,
clip_extra=True)
def test_gpu_1(self): def test_gpu_1(self):
if fluid.core.is_compiled_with_cuda(): if fluid.core.is_compiled_with_cuda():
......
...@@ -201,7 +201,8 @@ def train(net_type, use_cuda, save_dirname, is_local): ...@@ -201,7 +201,8 @@ def train(net_type, use_cuda, save_dirname, is_local):
fluid.io.save_inference_model( fluid.io.save_inference_model(
save_dirname, ["pixel"], [predict], save_dirname, ["pixel"], [predict],
exe, exe,
main_program=train_program) main_program=train_program,
clip_extra=True)
return return
if is_local: if is_local:
...@@ -258,8 +259,13 @@ def infer(use_cuda, save_dirname=None): ...@@ -258,8 +259,13 @@ def infer(use_cuda, save_dirname=None):
print("infer results: ", results[0]) print("infer results: ", results[0])
fluid.io.save_inference_model(save_dirname, feed_target_names, fluid.io.save_inference_model(
fetch_targets, exe, inference_program) save_dirname,
feed_target_names,
fetch_targets,
exe,
inference_program,
clip_extra=True)
def main(net_type, use_cuda, is_local=True): def main(net_type, use_cuda, is_local=True):
......
...@@ -258,8 +258,11 @@ class TestQuantizeTranspiler(unittest.TestCase): ...@@ -258,8 +258,11 @@ class TestQuantizeTranspiler(unittest.TestCase):
# Convert parameter to 8-bit. # Convert parameter to 8-bit.
quant_transpiler.convert_to_int8(test_program, place) quant_transpiler.convert_to_int8(test_program, place)
# Save the 8-bit parameter and model file. # Save the 8-bit parameter and model file.
fluid.io.save_inference_model('model_8bit', ['image', 'label'], fluid.io.save_inference_model(
[loss], exe, test_program) 'model_8bit', ['image', 'label'], [loss],
exe,
test_program,
clip_extra=True)
# Test whether the 8-bit parameter and model file can be loaded successfully. # Test whether the 8-bit parameter and model file can be loaded successfully.
[infer, feed, fetch] = fluid.io.load_inference_model('model_8bit', [infer, feed, fetch] = fluid.io.load_inference_model('model_8bit',
exe) exe)
......
...@@ -855,7 +855,8 @@ def save(layer, path, input_spec=None, **configs): ...@@ -855,7 +855,8 @@ def save(layer, path, input_spec=None, **configs):
model_filename=model_filename, model_filename=model_filename,
params_filename=params_filename, params_filename=params_filename,
export_for_deployment=configs._export_for_deployment, export_for_deployment=configs._export_for_deployment,
program_only=configs._program_only) program_only=configs._program_only,
clip_extra=False)
# NOTE(chenweihang): [ Save extra variable info ] # NOTE(chenweihang): [ Save extra variable info ]
# save_inference_model will lose some important variable information, including: # save_inference_model will lose some important variable information, including:
...@@ -1342,7 +1343,7 @@ class TracedLayer(object): ...@@ -1342,7 +1343,7 @@ class TracedLayer(object):
return self._run(self._build_feed(inputs)) return self._run(self._build_feed(inputs))
@switch_to_static_graph @switch_to_static_graph
def save_inference_model(self, path, feed=None, fetch=None): def save_inference_model(self, path, feed=None, fetch=None, **kwargs):
""" """
Save the TracedLayer to a model for inference. The saved Save the TracedLayer to a model for inference. The saved
inference model can be loaded by C++ inference APIs. inference model can be loaded by C++ inference APIs.
...@@ -1360,6 +1361,7 @@ class TracedLayer(object): ...@@ -1360,6 +1361,7 @@ class TracedLayer(object):
saved inference model. If None, all output variables of the saved inference model. If None, all output variables of the
TracedLayer object would be the outputs of the saved inference TracedLayer object would be the outputs of the saved inference
model. Default None. model. Default None.
kwargs: Supported keys including 'clip_extra'.set to True if you want to clip extra information for every operator.
Returns: Returns:
None None
...@@ -1409,7 +1411,7 @@ class TracedLayer(object): ...@@ -1409,7 +1411,7 @@ class TracedLayer(object):
for f in fetch: for f in fetch:
check_type(f, "each element of fetch", int, check_type(f, "each element of fetch", int,
"fluid.dygraph.jit.TracedLayer.save_inference_model") "fluid.dygraph.jit.TracedLayer.save_inference_model")
clip_extra = kwargs.get('clip_extra', False)
# path check # path check
file_prefix = os.path.basename(path) file_prefix = os.path.basename(path)
if file_prefix == "": if file_prefix == "":
...@@ -1449,4 +1451,5 @@ class TracedLayer(object): ...@@ -1449,4 +1451,5 @@ class TracedLayer(object):
executor=self._exe, executor=self._exe,
main_program=self._program.clone(), main_program=self._program.clone(),
model_filename=model_filename, model_filename=model_filename,
params_filename=params_filename) params_filename=params_filename,
clip_extra=clip_extra)
...@@ -5135,7 +5135,7 @@ class Program(object): ...@@ -5135,7 +5135,7 @@ class Program(object):
res._sync_with_cpp() res._sync_with_cpp()
return res return res
def _remove_training_info(self): def _remove_training_info(self, clip_extra=True):
""" """
This method will create a new program and do following adjustments on it: This method will create a new program and do following adjustments on it:
1. Remove all variable's `is_parameter` attribute if exist. 1. Remove all variable's `is_parameter` attribute if exist.
...@@ -5160,6 +5160,71 @@ class Program(object): ...@@ -5160,6 +5160,71 @@ class Program(object):
for var in block.all_vars(): for var in block.all_vars():
var.clear_is_parameter() var.clear_is_parameter()
var.clear_stop_gradient() var.clear_stop_gradient()
if not clip_extra:
continue
for op_idx in range(0, block.op_size()):
op = block.op(op_idx)
if op.type() not in OpProtoHolder.instance().op_proto_map:
continue
proto = OpProtoHolder.instance().get_op_proto(op.type())
remove_input_list = []
for name in op.input_names():
find = False
for input_proto in proto.inputs:
if input_proto.name != name:
continue
if input_proto.extra:
remove_input_list.append(name)
find = True
break
if not find:
remove_input_list.append(name)
for name in remove_input_list:
op.remove_input(name)
remove_output_list = []
for name in op.output_names():
find = False
for output_proto in proto.outputs:
if output_proto.name != name:
continue
if output_proto.extra:
remove_output_list.append(name)
find = True
break
if not find:
remove_output_list.append(name)
for name in remove_output_list:
op.remove_output(name)
remove_attr_list = []
op_quant_name = core.op_proto_and_checker_maker.kOpWithQuantAttrName(
)
quant = bool(op.attr(op_quant_name)
) if op_quant_name in op.attr_names() else False
quant_attrs = [
op_quant_name, "quantization_type", "skip_quant",
"activation_bits", "bit_length", "quantize_weight_bits",
"weight_quant_scale"
]
for name in op.attr_names():
if quant:
if name in quant_attrs:
continue
if name.endswith("_threshold"):
continue
find = False
for attr_proto in proto.attrs:
if attr_proto.name != name:
continue
if attr_proto.extra:
remove_attr_list.append(name)
find = True
break
if not find:
remove_attr_list.append(name)
for name in remove_attr_list:
op.remove_attr(name)
return res return res
@staticmethod @staticmethod
......
...@@ -1251,7 +1251,8 @@ def save_inference_model(dirname, ...@@ -1251,7 +1251,8 @@ def save_inference_model(dirname,
model_filename=None, model_filename=None,
params_filename=None, params_filename=None,
export_for_deployment=True, export_for_deployment=True,
program_only=False): program_only=False,
clip_extra=False):
""" """
:api_attr: Static Graph :api_attr: Static Graph
...@@ -1432,14 +1433,16 @@ def save_inference_model(dirname, ...@@ -1432,14 +1433,16 @@ def save_inference_model(dirname,
main_program.desc._set_version() main_program.desc._set_version()
paddle.fluid.core.save_op_version_info(main_program.desc) paddle.fluid.core.save_op_version_info(main_program.desc)
with open(model_basename, "wb") as f: with open(model_basename, "wb") as f:
f.write(main_program._remove_training_info() f.write(
.desc.serialize_to_string()) main_program._remove_training_info(clip_extra=clip_extra)
.desc.serialize_to_string())
else: else:
# TODO(panyx0718): Save more information so that it can also be used # TODO(panyx0718): Save more information so that it can also be used
# for training and more flexible post-processing. # for training and more flexible post-processing.
with open(model_basename + ".main_program", "wb") as f: with open(model_basename + ".main_program", "wb") as f:
f.write(main_program._remove_training_info() f.write(
.desc.serialize_to_string()) main_program._remove_training_info(clip_extra=clip_extra)
.desc.serialize_to_string())
if program_only: if program_only:
warnings.warn( warnings.warn(
......
...@@ -86,8 +86,10 @@ def train(use_cuda, save_dirname, is_local, use_bf16, pure_bf16): ...@@ -86,8 +86,10 @@ def train(use_cuda, save_dirname, is_local, use_bf16, pure_bf16):
fetch_list=[avg_cost]) fetch_list=[avg_cost])
if avg_loss_value[0] < 10.0: if avg_loss_value[0] < 10.0:
if save_dirname is not None: if save_dirname is not None:
paddle.static.save_inference_model(save_dirname, [x], paddle.static.save_inference_model(
[y_predict], exe) save_dirname, [x], [y_predict],
exe,
clip_extra=False)
return return
if math.isnan(float(avg_loss_value)): if math.isnan(float(avg_loss_value)):
sys.exit("got NaN loss, training failed.") sys.exit("got NaN loss, training failed.")
......
...@@ -111,8 +111,13 @@ class QuantDequantTest(unittest.TestCase): ...@@ -111,8 +111,13 @@ class QuantDequantTest(unittest.TestCase):
def _save_models(self, dirname, feeded_var_names, target_vars, executor, def _save_models(self, dirname, feeded_var_names, target_vars, executor,
program, scope): program, scope):
with fluid.scope_guard(scope): with fluid.scope_guard(scope):
fluid.io.save_inference_model(dirname, feeded_var_names, fluid.io.save_inference_model(
target_vars, executor, program) dirname,
feeded_var_names,
target_vars,
executor,
program,
clip_extra=True)
def _get_paddle_outs(self, feed, fetch_list, executor, program, scope): def _get_paddle_outs(self, feed, fetch_list, executor, program, scope):
''' '''
......
...@@ -115,7 +115,8 @@ class TestImperativeStaticModelRunnerMnist(unittest.TestCase): ...@@ -115,7 +115,8 @@ class TestImperativeStaticModelRunnerMnist(unittest.TestCase):
self.save_dirname, ["img"], [prediction], self.save_dirname, ["img"], [prediction],
exe, exe,
model_filename=self.model_filename, model_filename=self.model_filename,
params_filename=self.params_filename) params_filename=self.params_filename,
clip_extra=False)
def load_and_train_dygraph(self): def load_and_train_dygraph(self):
place = fluid.CUDAPlace(0) if core.is_compiled_with_cuda( place = fluid.CUDAPlace(0) if core.is_compiled_with_cuda(
......
...@@ -104,7 +104,8 @@ class TestImperativeStaticModelRunnerWhile(unittest.TestCase): ...@@ -104,7 +104,8 @@ class TestImperativeStaticModelRunnerWhile(unittest.TestCase):
self.save_dirname, ["img"], [pred], self.save_dirname, ["img"], [pred],
exe, exe,
model_filename=self.model_filename, model_filename=self.model_filename,
params_filename=self.params_filename) params_filename=self.params_filename,
clip_extra=False)
def load_and_train_dygraph(self): def load_and_train_dygraph(self):
place = fluid.CUDAPlace(0) if core.is_compiled_with_cuda( place = fluid.CUDAPlace(0) if core.is_compiled_with_cuda(
......
...@@ -81,6 +81,8 @@ class TestOperator(unittest.TestCase): ...@@ -81,6 +81,8 @@ class TestOperator(unittest.TestCase):
self.assertEqual(mul_op.attr("y_num_col_dims"), 1) self.assertEqual(mul_op.attr("y_num_col_dims"), 1)
self.assertEqual(mul_op.idx, 0) self.assertEqual(mul_op.idx, 0)
self.assertEqual(mul_out.op, mul_op) self.assertEqual(mul_out.op, mul_op)
mul_op.desc.remove_input("X")
self.assertEqual(mul_op.input_names, ["Y"])
def test_mult_input(self): def test_mult_input(self):
program = Program() program = Program()
......
...@@ -447,8 +447,9 @@ def save_inference_model(path_prefix, feed_vars, fetch_vars, executor, ...@@ -447,8 +447,9 @@ def save_inference_model(path_prefix, feed_vars, fetch_vars, executor,
fetch_vars(Variable | list[Variable]): Variables returned by inference. fetch_vars(Variable | list[Variable]): Variables returned by inference.
executor(Executor): The executor that saves the inference model. You can refer executor(Executor): The executor that saves the inference model. You can refer
to :ref:`api_guide_executor_en` for more details. to :ref:`api_guide_executor_en` for more details.
kwargs: Supported keys including 'program'.Attention please, kwargs is used for backward compatibility mainly. kwargs: Supported keys including 'program' and "clip_extra". Attention please, kwargs is used for backward compatibility mainly.
- program(Program): specify a program if you don't want to use default main program. - program(Program): specify a program if you don't want to use default main program.
- clip_extra(bool): set to True if you want to clip extra information for every operator.
Returns: Returns:
None None
...@@ -509,9 +510,11 @@ def save_inference_model(path_prefix, feed_vars, fetch_vars, executor, ...@@ -509,9 +510,11 @@ def save_inference_model(path_prefix, feed_vars, fetch_vars, executor,
_check_vars('fetch_vars', fetch_vars) _check_vars('fetch_vars', fetch_vars)
program = _get_valid_program(kwargs.get('program', None)) program = _get_valid_program(kwargs.get('program', None))
clip_extra = kwargs.get('clip_extra', False)
program = normalize_program(program, feed_vars, fetch_vars) program = normalize_program(program, feed_vars, fetch_vars)
# serialize and save program # serialize and save program
program_bytes = _serialize_program(program._remove_training_info()) program_bytes = _serialize_program(
program._remove_training_info(clip_extra=clip_extra))
save_to_file(model_path, program_bytes) save_to_file(model_path, program_bytes)
# serialize and save params # serialize and save params
params_bytes = _serialize_persistables(program, executor) params_bytes = _serialize_persistables(program, executor)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册