Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modify model conversion interfaces and model load methods #1358

Merged
merged 2 commits into from
Aug 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
119 changes: 94 additions & 25 deletions python/paddle_serving_app/local_predict.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from .proto import general_model_config_pb2 as m_config
import paddle.inference as paddle_infer
import logging
import glob

logging.basicConfig(format="%(asctime)s - %(levelname)s - %(message)s")
logger = logging.getLogger("LocalPredictor")
Expand Down Expand Up @@ -51,6 +52,23 @@ def __init__(self):
self.fetch_names_to_idx_ = {}
self.fetch_names_to_type_ = {}

def search_suffix_files(self, model_path, target_suffix):
"""
Find all files with the suffix xxx in the specified directory.

Args:
model_path: model directory, not None.
target_suffix: filenames with target suffix, not None. e.g: *.pdmodel

Returns:
file_list, None, [] or [path, ] .
"""
if model_path is None or target_suffix is None:
return None

file_list = glob.glob(os.path.join(model_path, target_suffix))
return file_list

def load_model_config(self,
model_path,
use_gpu=False,
Expand Down Expand Up @@ -97,11 +115,30 @@ def load_model_config(self,
f = open(client_config, 'r')
model_conf = google.protobuf.text_format.Merge(
str(f.read()), model_conf)

# Init paddle_infer config
# Paddle's model files and parameter files have multiple naming rules:
# 1) __model__, __params__
# 2) *.pdmodel, *.pdiparams
# 3) __model__, conv2d_1.w_0, conv2d_2.w_0, fc_1.w_0, conv2d_1.b_0, ...
pdmodel_file_list = self.search_suffix_files(model_path, "*.pdmodel")
pdiparams_file_list = self.search_suffix_files(model_path,
"*.pdiparams")
if os.path.exists(os.path.join(model_path, "__params__")):
# case 1) initializing
config = paddle_infer.Config(
os.path.join(model_path, "__model__"),
os.path.join(model_path, "__params__"))
elif pdmodel_file_list and len(
pdmodel_file_list) > 0 and pdiparams_file_list and len(
pdiparams_file_list) > 0:
# case 2) initializing
logger.info("pdmodel_file_list:{}, pdiparams_file_list:{}".format(
pdmodel_file_list, pdiparams_file_list))
config = paddle_infer.Config(pdmodel_file_list[0],
pdiparams_file_list[0])
else:
# case 3) initializing.
config = paddle_infer.Config(model_path)

logger.info(
Expand Down Expand Up @@ -201,25 +238,18 @@ def predict(self, feed=None, fetch=None, batch=False, log_id=0):
Run model inference by Paddle Inference API.

Args:
feed: feed var
fetch: fetch var
feed: feed var list, None is not allowed.
fetch: fetch var list, None allowed. when it is None, all fetch
vars are returned. Otherwise, return fetch specified result.
batch: batch data or not, False default.If batch is False, a new
dimension is added to header of the shape[np.newaxis].
log_id: for logging

Returns:
fetch_map: dict
"""
if feed is None or fetch is None:
raise ValueError("You should specify feed and fetch for prediction.\
log_id:{}".format(log_id))
fetch_list = []
if isinstance(fetch, str):
fetch_list = [fetch]
elif isinstance(fetch, list):
fetch_list = fetch
else:
raise ValueError("Fetch only accepts string and list of string.\
if feed is None:
raise ValueError("You should specify feed vars for prediction.\
log_id:{}".format(log_id))

feed_batch = []
Expand All @@ -231,18 +261,20 @@ def predict(self, feed=None, fetch=None, batch=False, log_id=0):
raise ValueError("Feed only accepts dict and list of dict.\
log_id:{}".format(log_id))

fetch_names = []
fetch_list = []
if fetch is not None:
if isinstance(fetch, str):
fetch_list = [fetch]
elif isinstance(fetch, list):
fetch_list = fetch

# Filter invalid fetch names
fetch_names = []
for key in fetch_list:
if key in self.fetch_names_:
fetch_names.append(key)

if len(fetch_names) == 0:
raise ValueError(
"Fetch names should not be empty or out of saved fetch list.\
log_id:{}".format(log_id))

# Assemble the input data of paddle predictor
# Assemble the input data of paddle predictor, and filter invalid inputs.
input_names = self.predictor.get_input_names()
for name in input_names:
if isinstance(feed[name], list):
Expand Down Expand Up @@ -282,11 +314,15 @@ def predict(self, feed=None, fetch=None, batch=False, log_id=0):
input_tensor_handle.copy_from_cpu(feed[name][np.newaxis, :])
else:
input_tensor_handle.copy_from_cpu(feed[name])

# set output tensor handlers
output_tensor_handles = []
output_name_to_index_dict = {}
output_names = self.predictor.get_output_names()
for output_name in output_names:
for i, output_name in enumerate(output_names):
output_tensor_handle = self.predictor.get_output_handle(output_name)
output_tensor_handles.append(output_tensor_handle)
output_name_to_index_dict[output_name] = i

# Run inference
self.predictor.run()
Expand All @@ -296,10 +332,43 @@ def predict(self, feed=None, fetch=None, batch=False, log_id=0):
for output_tensor_handle in output_tensor_handles:
output = output_tensor_handle.copy_to_cpu()
outputs.append(output)
outputs_len = len(outputs)

# Copy fetch vars. If fetch is None, it will copy all results from output_tensor_handles.
# Otherwise, it will copy the fields specified from output_tensor_handles.
fetch_map = {}
for i, name in enumerate(fetch):
fetch_map[name] = outputs[i]
if len(output_tensor_handles[i].lod()) > 0:
fetch_map[name + ".lod"] = np.array(output_tensor_handles[i]
.lod()[0]).astype('int32')
if fetch is None:
for i, name in enumerate(output_names):
fetch_map[name] = outputs[i]
if len(output_tensor_handles[i].lod()) > 0:
fetch_map[name + ".lod"] = np.array(output_tensor_handles[
i].lod()[0]).astype('int32')
else:
# Because the save_inference_model interface will increase the scale op
# in the network, the name of fetch_var is different from that in prototxt.
# Therefore, it is compatible with v0.6.x and the previous model save format,
# and here is compatible with the results that do not match.
fetch_match_num = 0
for i, name in enumerate(fetch):
output_index = output_name_to_index_dict.get(name)
if output_index is None:
continue

fetch_map[name] = outputs[output_index]
fetch_match_num += 1
if len(output_tensor_handles[output_index].lod()) > 0:
fetch_map[name + ".lod"] = np.array(output_tensor_handles[
output_index].lod()[0]).astype('int32')

# Compatible with v0.6.x and lower versions model saving formats.
if fetch_match_num == 0:
logger.debug("fetch match num is 0. Retrain the model please!")
for i, name in enumerate(fetch):
if i >= outputs_len:
break
fetch_map[name] = outputs[i]
if len(output_tensor_handles[i].lod()) > 0:
fetch_map[name + ".lod"] = np.array(
output_tensor_handles[i].lod()[0]).astype('int32')

return fetch_map
37 changes: 25 additions & 12 deletions python/paddle_serving_client/io/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ def save_dygraph_model(serving_model_folder, client_config_folder, model):
}
config = model_conf.GeneralModelConfig()

#int64 = 0; float32 = 1; int32 = 2;
for key in feed_var_dict:
feed_var = model_conf.FeedVar()
feed_var.alias_name = key
Expand Down Expand Up @@ -127,7 +126,6 @@ def save_dygraph_model(serving_model_folder, client_config_folder, model):
def var_type_conversion(dtype):
"""
Variable type conversion

Args:
dtype: type of core.VarDesc.VarType.xxxxx
(https://github.com/PaddlePaddle/Paddle/blob/release/2.1/python/paddle/framework/dtype.py)
Expand Down Expand Up @@ -184,7 +182,9 @@ def save_model(server_model_folder,
main_program=None,
encryption=False,
key_len=128,
encrypt_conf=None):
encrypt_conf=None,
model_filename=None,
params_filename=None):
executor = Executor(place=CPUPlace())

feed_var_names = [feed_var_dict[x].name for x in feed_var_dict]
Expand All @@ -194,15 +194,27 @@ def save_model(server_model_folder,
target_vars.append(fetch_var_dict[key])
target_var_names.append(key)

if not os.path.exists(server_model_folder):
os.makedirs(server_model_folder)
if not encryption:
save_inference_model(
server_model_folder,
feed_var_names,
target_vars,
executor,
model_filename="__model__",
params_filename="__params__",
main_program=main_program)
if not model_filename:
model_filename = "model.pdmodel"
if not params_filename:
params_filename = "params.pdiparams"

new_model_path = os.path.join(server_model_folder, model_filename)
new_params_path = os.path.join(server_model_folder, params_filename)

with open(new_model_path, "wb") as new_model_file:
new_model_file.write(main_program.desc.serialize_to_string())

paddle.static.save_vars(
executor=executor,
dirname=server_model_folder,
main_program=main_program,
vars=None,
predicate=paddle.static.io.is_persistable,
filename=params_filename)
else:
if encrypt_conf == None:
aes_cipher = CipherFactory.create_cipher()
Expand Down Expand Up @@ -296,7 +308,8 @@ def inference_model_to_serving(dirname,
}
fetch_dict = {x.name: x for x in fetch_targets}
save_model(serving_server, serving_client, feed_dict, fetch_dict,
inference_program, encryption, key_len, encrypt_conf)
inference_program, encryption, key_len, encrypt_conf,
model_filename, params_filename)
feed_names = feed_dict.keys()
fetch_names = fetch_dict.keys()
return feed_names, fetch_names
Loading