如何使用tf.saved_model加载模型并调用预测函数 [TENSORFLOW 2.0 API]

7

我对TensorFlow尤其是2.0版本非常陌生,因为API的示例不足,但它似乎比1.x版本更加方便。

到目前为止,我已经使用tf.estimator API训练了一个线性模型,并使用tf.estimator.exporter保存了该模型。

之后,我想使用tf.saved_model API加载此模型,并认为我成功了。但是,我对我的过程有一些疑问,因此这是我的代码快速查看:

所以,我使用tf.feature_column API创建了一个特征数组,它看起来像这样:

feature_columns = 
[NumericColumn(key='geoaccuracy', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None),
 NumericColumn(key='longitude', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None),
 NumericColumn(key='latitude', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None),
 NumericColumn(key='bidfloor', shape=(1,), default_value=None, dtype=tf.float32, normalizer_fn=None),
 VocabularyListCategoricalColumn(key='adid', vocabulary_list=('115', '124', '139', '122', '121', '146', '113', '103', '123', '104', '147', '114', '149', '148'), dtype=tf.string, default_value=-1, num_oov_buckets=0),
 VocabularyListCategoricalColumn(key='campaignid', vocabulary_list=('36', '31', '33', '28'), dtype=tf.string, default_value=-1, num_oov_buckets=0),
 VocabularyListCategoricalColumn(key='exchangeid', vocabulary_list=('1241', '823', '1240', '1238'), dtype=tf.string, default_value=-1, num_oov_buckets=0),
...]

接下来,我使用我的特征列数组定义一个估计器,并对其进行训练。到目前为止,没有问题。

linear_est = tf.estimator.LinearClassifier(feature_columns=feature_columns)

在训练完我的模型后,我希望将其保存下来。这里开始产生了疑虑,以下是我的操作方式,但我不确定是否正确:

serving_input_parse = tf.feature_column.make_parse_example_spec(feature_columns=feature_columns)

""" view of the variable : serving_input_parse = 
 {'adid': VarLenFeature(dtype=tf.string),
 'at': VarLenFeature(dtype=tf.string),
 'basegenres': VarLenFeature(dtype=tf.string),
 'bestkw': VarLenFeature(dtype=tf.string),
 'besttopic': VarLenFeature(dtype=tf.string),
 'bidfloor': FixedLenFeature(shape=(1,), dtype=tf.float32, default_value=None),
 'browserid': VarLenFeature(dtype=tf.string),
 'browserlanguage': VarLenFeature(dtype=tf.string)
 ...} """

# exporting the model :
linear_est.export_saved_model(export_dir_base='./saved',
 serving_input_receiver_fn=tf.estimator.export.build_parsing_serving_input_receiver_fn(serving_input_receiver_fn),
 as_text=True)

现在我尝试加载模型,但是我不知道如何使用已加载的模型,以便使用来自pandas dataframe的原始数据来调用预测。

loaded = tf.saved_model.load('saved/1573144361/')

还有一件事,我试图查看模型的签名,但我无法真正理解我的输入形状发生了什么

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['classification']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_STRING
        shape: (-1)
        name: input_example_tensor:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['classes'] tensor_info:
        dtype: DT_STRING
        shape: (-1, 2)
        name: head/Tile:0
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 2)
        name: head/predictions/probabilities:0
  Method name is: tensorflow/serving/classify

signature_def['predict']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['examples'] tensor_info:
        dtype: DT_STRING
        shape: (-1)
        name: input_example_tensor:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['all_class_ids'] tensor_info:
        dtype: DT_INT32
        shape: (-1, 2)
        name: head/predictions/Tile:0
    outputs['all_classes'] tensor_info:
        dtype: DT_STRING
        shape: (-1, 2)
        name: head/predictions/Tile_1:0
    outputs['class_ids'] tensor_info:
        dtype: DT_INT64
        shape: (-1, 1)
        name: head/predictions/ExpandDims:0
    outputs['classes'] tensor_info:
        dtype: DT_STRING
        shape: (-1, 1)
        name: head/predictions/str_classes:0
    outputs['logistic'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: head/predictions/logistic:0
    outputs['logits'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: linear/linear_model/linear/linear_model/linear/linear_model/weighted_sum:0
    outputs['probabilities'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 2)
        name: head/predictions/probabilities:0
  Method name is: tensorflow/serving/predict

signature_def['regression']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_STRING
        shape: (-1)
        name: input_example_tensor:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['outputs'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 1)
        name: head/predictions/logistic:0
  Method name is: tensorflow/serving/regress

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_STRING
        shape: (-1)
        name: input_example_tensor:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['classes'] tensor_info:
        dtype: DT_STRING
        shape: (-1, 2)
        name: head/Tile:0
    outputs['scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 2)
        name: head/predictions/probabilities:0
  Method name is: tensorflow/serving/classify

请查看此处提供的解决方案 https://dev59.com/Hbfna4cB1Zd3GeqPv552#4rsuoYgBc1ULPQZFMXOx - Avi Vajpeyi
3个回答

2

saved_model.load(...) 文档 演示了基本的机制,如下所示:

imported = tf.saved_model.load(path)
f = imported.signatures["serving_default"]
print(f(x=tf.constant([[1.]])))

我本身对这还不太熟悉,但是使用 saved_model.save(...) 时,serving_default 似乎是默认的签名。

我的理解是,saved_model.save(...) 并不保存模型,而是保存图。为了解释图,你需要显式地存储定义图操作的 "签名"。如果没有显式指定,则 "serve_default" 将是您唯一的签名。

下面提供了一个实现,有几个值得注意的细节:

  1. 输入必须是张量;因此,我需要手动进行转换。
  2. 输出是一个字典。文档将其描述为 "具有 signatures 属性的可跟踪对象,该属性将 signature 键映射到函数"。

在我的情况下,字典的键是相对随意的 "dense_83"。这似乎有点... 特定。因此,我通过迭代器将解决方案泛化以忽略键:

import tensorflow as tf
input_data = tf.constant(input_data, dtype=tf.float32)
prediction_tensors = signature_collection.signatures["serving_default"](input_data)
for _, values in prediction_tensors.items():
    predictions = values.numpy()[0]
    return predictions
raise Exception("Expected a response from predict(...).")

1
非常高兴,@ThinkTeamwork。这个社区帮助了我很多。我很乐意回报一点点。 - Eric McLachlan

0

顺便提一下,我找到了一个替代方案,使用 tensorflow.keras.models.load_model()。这里是一个例子

创建、训练和保存模型的方法:

import tensorflow as tf

## create a model
mdl = tf.keras.Sequential(...)
mdl.compile(...)

## train it
mdl.fit(...)

## save it
mdl.save('trained-model-file')

加载已保存的模型:

import tensorflow as tf

## load it with same file path
mdl = tf.keras.models.load_model('trained-model-file')

## predict the data
mdl.predict(...)

参考资料:https://www.geeksforgeeks.org/save-and-load-models-in-tensorflow/


0

看起来你在最后一节输出中使用了 saved_model_cli 命令行工具。从中可以看到一个名为“predict”的函数,它显示了输入类型、列等信息。当我这样做时,我会看到所有的输入列。而在你的情况下,它只显示了一个名为 examples 的字符串输入。这看起来不正确。

以下是 $ saved_model_cli show --dir /somedir/export/exporter/123456789 --all 输出的摘录。在输出中,点号表示已删除的行,因为它们看起来相似。

signature_def['predict']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['feature_num_1'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1)
        name: Placeholder_29:0
...
...
 The given SavedModel SignatureDef contains the following output(s):
    outputs['all_class_ids'] tensor_info:
        dtype: DT_INT32
        shape: (-1, 2)
        name: dnn/head/predictions/Tile:0
    outputs['all_classes'] tensor_info:
        dtype: DT_STRING
        shape: (-1, 2)
        name: dnn/head/predictions/Tile_1:0
...
...
  Method name is: tensorflow/serving/predict

谢谢您的答复,非常有趣。能否分享一下生成此保存模型的代码?你是否使用 tf.estimator API 建立了模型?因为我用 tf.keras 试过,虽然它可以产生良好的标记,但在 tf.estimator 上却遇到了困难…… - elhoudev
我使用了一个估算器。我想也许你正在使用单个字符串作为输入。所以也许我是错误的。我也在学习,所以请记住这一点。我不明白为什么你有六个或更多的特征,但是你的预测函数只有一个字符串作为特征。你的servingbinput函数有一个参数还是多个参数? - netskink
我的服务输入确实有多个参数,而且不止六个,我更期望你能提供类似于你所拥有的东西。 - elhoudev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接