如何解决Keras转ONNX时的'NoneType' object is not subscriptable错误
Keras含LSTM模型转ONNX报错解决方案
环境信息
- Python 3.8
- TensorFlow==2.2.0
- Keras==2.4.0
- 问题:使用keras2onnx转换含LSTM节点的Keras模型时触发
TypeError: 'NoneType' object is not subscriptable,手动指定input_shape无效
模型构建代码
import keras from keras import models import numpy as np from keras import layers import tensorflow as tf from keras.models import Sequential from keras.layers import Dense, Dropout, LSTM, BatchNormalization, Activation, Embedding from keras import callbacks tf.keras.backend.set_floatx('float64') # 读取打乱后的数据 dataread = DataReader() data_array, fin_risk = dataread.get_shuffle_data() batchsz = 1024 # 批次大小 val_size = 1800 # 验证集大小 train_db = tf.data.Dataset.from_tensor_slices((data_array[val_size:, :, :], fin_risk[val_size:])) # 划分训练集 val_db = tf.data.Dataset.from_tensor_slices((data_array[:val_size, :, :], fin_risk[:val_size])) # 划分验证集 train_db = train_db.batch(batchsz) val_db = val_db.batch(batchsz) print(train_db) # 输出:((None, 23, 102), (None,)), types: (tf.float64, tf.float64) print(val_db) model = Sequential() model.add(Dense(10)) #model.add(BatchNormalization()) model.add(LSTM(units, input_shape=(None,3,6), return_sequences=True, activation="tanh")) #model.add(LSTM(units, dropout=0.2, return_sequences=True, unroll=True)) model.add(LSTM(units, activation="tanh")) #model.add(BatchNormalization()) model.add(Dense(10, activation='tanh')) model.add(Dense(1))
ONNX转换代码
import onnx import keras2onnx from keras import backend as K from keras.models import load_model onnx_model_name = 'N_256_T3.onnx' model = load_model('logs/last3.h5') onnx_model = keras2onnx.convert_keras(model, model.name) onnx.save_model(onnx_model, onnx_model_name)
错误信息
tf executing eager_mode: True tf.keras model eager_mode: False --------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In [1], line 11 8 onnx_model_name = 'N_256_T3.onnx' 10 model = load_model('logs/last3.h5') ---> 11 onnx_model = keras2onnx.convert_keras(model, model.name) 12 onnx.save_model(onnx_model, onnx_model_name) File ~\Anaconda3\envs\python_3_8_backup\lib\site-packages\keras2onnx\main.py:62, in convert_keras(model, name, doc_string, target_opset, channel_first_inputs, debug_mode, custom_op_conversions) 60 output_dict = {} 61 if is_tf2 and is_tf_keras: ---> 62 tf_graph = build_layer_output_from_model(model, output_dict, input_names, output_names) 63 else: 64 tf_graph = model.outputs[0].graph if is_tf2 else keras.backend.get_session().graph File ~\Anaconda3\envs\python_3_8_backup\lib\site-packages\keras2onnx\_parser_tf.py:304, in build_layer_output_from_model(model, output_dict, input_names, output_names) 302 return extract_outputs_from_subclassing_model(model, output_dict, input_names, output_names) 303 else: --> 304 graph = model.outputs[0].graph 305 output_names.extend([n.name for n in model.outputs]) 306 output_dict.update(extract_outputs_from_inbound_nodes(model)) TypeError: 'NoneType' object is not subscriptable
解决方案
核心原因
报错是因为模型加载后model.outputs为空——Keras模型需要至少完成一次前向传播(即输入数据跑一次预测/推理),才能确定输入输出节点的形状和图结构,否则保存的模型缺少必要的图信息,导致转换工具无法解析。
具体修复步骤
1. 训练阶段确保模型完成前向传播再保存
在训练代码中,添加示例输入执行一次预测,再保存模型:
# 生成符合输入形状的示例数据(对应你的输入(23,102)) dummy_input = np.random.randn(1, 23, 102).astype(np.float64) # 执行一次前向传播,让Keras确定模型的输入输出结构 model.predict(dummy_input) # 之后再保存模型 model.save('logs/last3.h5')
2. 修正模型定义的input_shape错误
原模型中第一层Dense未指定input_shape,且LSTM的input_shape(None,3,6)与实际输入(23,102)不匹配,修正如下:
model = Sequential() # 第一层指定正确的输入形状:(时间步长, 特征数),对应你的数据(23,102) model.add(Dense(10, input_shape=(23, 102))) model.add(LSTM(units, return_sequences=True, activation="tanh")) model.add(LSTM(units, activation="tanh")) model.add(Dense(10, activation='tanh')) model.add(Dense(1))
注意:LSTM层不需要手动指定batch维度,只需要传入(时间步长, 特征数)即可。
3. 已保存模型的补救方法
如果不想重新训练,加载模型后手动执行一次前向传播再转换:
import onnx import keras2onnx from keras.models import load_model import tensorflow as tf onnx_model_name = 'N_256_T3.onnx' model = load_model('logs/last3.h5') # 传入符合形状的示例输入,触发模型构建 dummy_input = tf.random.uniform((1, 23, 102), dtype=tf.float64) model(dummy_input) # 再执行转换 onnx_model = keras2onnx.convert_keras(model, model.name) onnx.save_model(onnx_model, onnx_model_name)
4. 替换转换工具(推荐)
keras2onnx对TensorFlow 2.x早期版本兼容性一般,建议使用tf2onnx替代,对TF2.x的支持更完善:
import tf2onnx import tensorflow as tf from keras.models import load_model model = load_model('logs/last3.h5') # 定义输入签名,指定形状和数据类型 spec = (tf.TensorSpec((None, 23, 102), tf.float64, name="input"),) output_path = "N_256_T3.onnx" # 执行转换 tf2onnx.convert.from_keras(model, input_signature=spec, output_path=output_path)
内容的提问来源于stack exchange,提问作者ELI
相关产品推荐
相关产品推荐

