C++调用readNetFromTensorflow加载Keras导出模型报错求解决
作为深度学习新手,你已经走完了Keras训练模型并导出冻结模型的流程,但在OpenCV C++中加载时遇到了两个棘手的错误,我来帮你逐一排查修复:
先理清错误根源
第一个错误提示无法解析frozen_Model.pb,说明OpenCV无法正确读取这个冻结模型文件;第二个断言错误则和模型导入时的常量节点处理有关,大概率是导出过程中的细节没处理到位。
具体修复步骤
1. 检查冻结模型的导出流程
你的导出代码里,output_node_name的正确性是关键——必须和模型实际的输出节点名称完全匹配。你可以先在Keras里确认输出节点名:
print(model.output.name)
输出格式通常类似dense_1/Softmax:0,注意要去掉最后的:0,只保留dense_1/Softmax作为冻结时的output_node_name。
另外,你可以调整导出代码,让流程更严谨:
def export_model(MODEL_NAME, input_node_name, output_node_name): # 获取当前Keras会话和图 sess = K.get_session() # 保存文本格式的图结构 tf.train.write_graph(sess.graph_def, 'out', MODEL_NAME + '_graph.pbtxt', as_text=True) # 保存模型检查点 saver = tf.train.Saver() saver.save(sess, 'out/' + MODEL_NAME + '.chkp') # 冻结模型,参数要一一对应 freeze_graph.freeze_graph( input_graph='out/' + MODEL_NAME + '_graph.pbtxt', input_saver='', input_binary=False, input_checkpoint='out/' + MODEL_NAME + '.chkp', output_node_names=output_node_name, restore_op_name='save/restore_all', filename_tensor_name='save/Const:0', output_graph='out/frozen_' + MODEL_NAME + '.pb', clear_devices=True, initializer_nodes='' ) # 优化模型(可选,提升OpenCV兼容性) input_graph_def = tf.GraphDef() with tf.gfile.Open('out/frozen_' + MODEL_NAME + '.pb', "rb") as f: input_graph_def.ParseFromString(f.read()) output_graph_def = optimize_for_inference_lib.optimize_for_inference( input_graph_def, [input_node_name], [output_node_name], tf.float32.as_datatype_enum ) with tf.gfile.FastGFile('out/opt_' + MODEL_NAME + '.pb', "wb") as f: f.write(output_graph_def.SerializeToString())
2. 调整OpenCV的模型加载方式
你现在同时传入了frozen_Model.pb和Model_graph.pbtxt,但实际上冻结的.pb文件已经包含了图结构和权重,不需要额外的.pbtxt。尝试修改加载代码:
String modelPath = "frozen_Model.pb"; dnn::Net cvNet = cv::dnn::readNetFromTensorflow(modelPath);
如果还是报错,试试用优化后的opt_Model.pb文件加载:
String modelPath = "opt_Model.pb"; dnn::Net cvNet = cv::dnn::readNetFromTensorflow(modelPath);
3. 升级OpenCV版本
你使用的是OpenCV 4.0.0-pre,这个预览版本对TensorFlow模型的支持还不完善,存在不少兼容性bug。建议升级到OpenCV 4.5以上的稳定版本,新版本修复了很多TensorFlow模型导入的问题。
4. 验证模型文件完整性
检查frozen_Model.pb的文件大小,如果过小可能是导出失败。你可以用TensorFlow工具检查检查点文件是否正常:
python -m tensorflow.python.tools.inspect_checkpoint --file_name=out/Model.chkp
如果能正常输出变量信息,说明检查点没问题,问题大概率出在冻结步骤。
5. 换一种导出冻结模型的方式
如果上述方法都无效,可以试试用Keras原生的SavedModel格式导出,再转换为冻结模型:
# 先保存为SavedModel格式 model.save('out/saved_model') # 用TensorFlow新API冻结模型 import tensorflow as tf from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2 saved_model_dir = 'out/saved_model' # 获取模型的具体函数 concrete_func = tf.function(lambda x: model(x)).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.as_graph_def() # 保存新的冻结模型 with tf.io.gfile.GFile('out/frozen_Model_v2.pb', 'wb') as f: f.write(frozen_graph.SerializeToString())
然后用这个新生成的frozen_Model_v2.pb在OpenCV中加载。
总结
优先检查输出节点名是否正确,然后尝试去掉.pbtxt直接加载冻结模型,升级OpenCV版本也是很有效的解决手段。如果还是有问题,换一种导出冻结模型的方法试试。
内容的提问来源于stack exchange,提问作者Amin Golnari

