冻结Keras模型转C++ API时调用freeze_session报错如何修复
问题背景
需要完成TensorFlow/Keras模型冻结操作,实现模型调用API从Python到C++的迁移,执行如下冻结代码时触发报错:
frozen_graph = freeze_session(sess,output_names=[out.op.name for out in model.outputs])
触发的报错信息如下:
Keras symbolic inputs/outputs do not implement
op. You may be trying to pass Keras symbolic inputs/outputs to a TF API that does not register dispatching, preventing Keras from automatically converting the API call to a lambda layer in the Functional Model.
问题原因
该报错是TensorFlow 2.x版本搭配tf.keras接口时的典型适配问题:
- 多数流传的
freeze_session工具函数是TensorFlow 1.x静态图时代的实现,未适配2.x版本默认启用的动态图机制 - tf.keras构建的模型,
model.inputs、model.outputs返回的是Keras封装的符号张量对象,不是TensorFlow原生静态图张量,直接访问.op属性会触发Keras的调度拦截,无法正常获取节点信息
修复方案
优先使用TensorFlow 2.x官方提供的原生冻结逻辑,兼容性最好,不需要依赖过时的会话API:
- 冻结前先执行一次模型推理,确保所有权重加载完成,同时将推理模式设为
training=False,避免BN、Dropout等层保留训练逻辑导致部署后精度异常 - 使用官方接口完成图冻结,替换原有
freeze_session相关代码,参考实现如下:
import tensorflow as tf from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2 # 将Keras模型封装为tf.function,转换为静态具象函数 concrete_func = tf.function(lambda x: model(x, training=False)).get_concrete_function( tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype) ) # 将图中所有变量转换为常量,得到冻结图 frozen_func = convert_variables_to_constants_v2(concrete_func) frozen_graph = frozen_func.graph # 后续可直接从frozen_func中提取输入输出节点名,格式示例: # input_node_name = frozen_func.inputs[0].name # output_node_names = [out.name for out in frozen_func.outputs]
- 如果需要将冻结图保存为pb文件供C++ API调用,直接执行序列化写入即可:
tf.io.write_graph( graph_or_graph_def=frozen_graph.as_graph_def(), logdir="./frozen_model", name="frozen_graph.pb", as_text=False )
如果必须沿用旧的freeze_session会话式写法,需要做两处适配:
- 代码开头添加
tf.compat.v1.disable_eager_execution(),关闭默认动态图执行模式,适配TF1.x风格的静态图会话逻辑 - 修改输出节点名的获取逻辑,先将Keras符号张量转换为TF原生张量再访问
.op属性,替换原传参为:
output_names=[tf.identity(out).op.name for out in model.outputs]
注意事项
- 冻结完成后需要核对输入输出节点的完整名称,TF2.x导出的节点通常带有
Identity后缀,C++调用时需要写全节点名,否则会触发节点不存在的报错 - 不要在冻结时传入训练模式下的模型输出,否则部署后推理结果会和Python侧推理结果存在偏差
内容的提问来源于stack exchange,提问作者Reza Lotfi
相关产品推荐
相关产品推荐

