导出TensorFlow模型供Serving使用时,必须用@tf.function装饰前向函数并显式指定input_signature;signatures参数需传入dict[str, ConcreteFunction],key名须与客户端请求的signature_name严格一致;所有TensorSpec必须显式设置稳定name以保证接口兼容性。

导出模型时必须显式定义 tf.function 并用 @tf.function 装饰
TensorFlow Serving 只认签名(signature),而签名只能从带 @tf.function 装饰的函数生成。不加装饰器,tf.saved_model.save 会保存变量和图结构,但无法生成可调用的 signature_def。
常见错误现象:saved_model_cli show --dir xxx --all 输出里没有 signature_def,或者只有默认的 __saved_model_init_op;Serving 启动后返回 Method not found。
- 必须把前向逻辑封装成独立函数,并加上
@tf.function - 该函数参数需有明确类型和形状,推荐用
input_signature约束(尤其含动态 batch 维度时) - 避免在函数体内调用 Python 原生逻辑(如
print、len()、列表推导),否则 trace 失败或签名不可靠
@tf.function
def serve_fn(x):
return model(x, training=False)
<h1>推荐加 input_signature,防止 trace 出错</h1><p>serve_fn = serve_fn.get_concrete_function(
tf.TensorSpec(shape=[None, 224, 224, 3], dtype=tf.float32, name="input")
)
tf.saved_model.save 的 signatures 参数不能传 dict key 为字符串
签名名(如 "serving_default")必须作为字典的 key,但这个字典不能是普通 dict,必须是 dict[str, tf.function],且 key 名要和后续客户端请求的 signature_name 严格一致。
容易踩的坑:用 signatures={"predict": serve_fn} 导出后,客户端却发 serving_default —— 请求直接 400;或者误写成 signatures={"serving_default": lambda x: ...},导致 trace 失败。
- key 必须是字符串,值必须是已 concrete 的
tf.function(即调用过get_concrete_function) - 不指定
signatures参数时,默认只保留serving_default签名,且仅当模型有call方法并被@tf.function装饰时才自动创建 - 多任务模型建议显式定义多个签名,比如
"encode"和"predict",避免混用
tf.saved_model.save(
model,
export_dir="/path/to/saved_model",
signatures={
"serving_default": serve_fn,
"encode": encode_fn
}
)加载后验证签名是否生效,别只信 saved_model_cli
saved_model_cli show 只能看签名结构,不能验证输入输出是否对齐。很多问题(如 tensor name 不匹配、shape 推断失败)要等 Serving 实际收请求才暴露。
使用场景:CI/CD 流水线中自动验证导出模型,或本地调试 Serving 部署失败时快速定位。
- 用
tf.saved_model.load加载后,直接调用签名函数测试,比 CLI 更早发现问题 - 注意加载后的对象是
ConcreteFunction,不是原模型,调用方式为loaded.signatures["serving_default"](x=...) - 若报
ValueError: Input tensors must be from the same graph,说明导出时没固化图(漏了get_concrete_function)
loaded = tf.saved_model.load("/path/to/saved_model")
infer = loaded.signatures["serving_default"]
result = infer(x=tf.random.normal([1, 224, 224, 3]))签名里的 tensor name 决定客户端请求字段名,别依赖默认命名
Serving 的 REST/gRPC 接口会把 signature 中每个输入参数的 name 当作 JSON key 或 proto field 名。如果导出时没设 name,TensorFlow 会自动生成类似 input_1 这样的名字,极易在客户端出错。
性能影响不大,但兼容性极差——模型一更新,输入名可能变,下游服务全崩。
- 所有
tf.TensorSpec都要显式传name参数,且保持稳定 - 避免用
model.input.name这类动态名,它依赖构建顺序,不可控 - gRPC 客户端若用
predict_pb2.PredictRequest,字段名必须和签名中name完全一致
serve_fn = serve_fn.get_concrete_function(
tf.TensorSpec(shape=[None, 224, 224, 3], dtype=tf.float32, name="images")
)签名导出真正卡点不在语法,而在 trace 时的隐式依赖和 name 对齐——哪怕一个 name 拼错,Serving 就收不到数据。

















