Keras转OpenCV:readNetFromTensorflow加载pb模型报错排查
问题根因
报错是两个因素共同导致的:
- OpenCV 4.6版本的DNN模块TensorFlow导入器对Keras的
LayerNormalization层支持不完善,该层前向传播时生成的Shape、StridedSlice等动态shape算子无法被OpenCV解析,最终触发找不到输入节点的报错。 - 你当前导出pb时使用了文本格式存储(
as_text=True),且没有固定输入batch维度,进一步降低了模型和OpenCV的兼容性。
另外你的代码还有一处逻辑问题:模型最后一层已经使用了softmax激活,编译损失时SparseCategoricalCrossentropy的from_logits=True参数仅适用于未经过激活的原始输出,该错误会导致训练时损失计算异常,影响模型精度。
解决方案
按落地成本从低到高排序:
方案1:ONNX格式中转(无需重训,兼容性最优,优先选择)
放弃直接导出TensorFlow pb的方案,通过ONNX作为中间格式转换,转换流程会自动将LayerNorm拆解为OpenCV支持的基础算子,步骤如下:- 安装转换依赖:
pip install tf2onnx onnx- 执行模型转换:
import tf2onnx import onnx # 定义固定输入签名,batch维设为1即可 input_signature = [tf.TensorSpec(shape=(1, 200, 200, 3), dtype=tf.float32, name="input")] # 选择opset 13,OpenCV 4.6对该版本算子支持最完善 onnx_model, _ = tf2onnx.convert.from_keras(model, input_signature=input_signature, opset=13) # 保存ONNX模型 onnx.save(onnx_model, "./models/model.onnx")- OpenCV加载模型代码替换为:
net = cv2.dnn.readNetFromONNX("./models/model.onnx")方案2:替换不兼容层(可正常使用pb格式,需要重训模型)
如果你必须使用TensorFlow pb格式,可以将模型中所有LayerNormalization替换为OpenCV原生支持的BatchNormalization,修改后重新训练模型,再用你原来的冻结逻辑导出pb即可正常加载。注意导出pb时将as_text参数改为False,存储二进制格式pb兼容性更好。方案3:兼容旧版pb导出逻辑(无需重训,不保证100%兼容)
固定输入batch维度,使用TensorFlow v1兼容接口导出冻结二进制pb,代码如下:from tensorflow.python.framework import graph_io # 固定输入batch为1,消除动态shape model.input.set_shape((1,) + model.input_shape[1:]) sess = tf.compat.v1.keras.backend.get_session() # 导出冻结图 frozen_graph = tf.compat.v1.graph_util.convert_variables_to_constants( sess=sess, input_graph_def=sess.graph_def, output_node_names=[model.output.op.name] ) # 保存二进制pb graph_io.write_graph(frozen_graph, "./models", "model_frozen.pb", as_text=False)该方案导出的pb如果依然报LayerNorm相关解析错误,请直接使用方案1的ONNX中转流程。
内容的提问来源于stack exchange,提问作者Fox
相关产品推荐
相关产品推荐

