You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用ONNX推理报错:输入缺失,求Keras模型输入签名配置方法

鸢尾花Keras模型转ONNX后的输入签名问题解决

问题描述

基于鸢尾花数据集构建并训练Keras序列模型后,转换为ONNX格式时遇到输入签名配置问题,推理时报错:

ValueError: Required inputs (['x']) are missing from input feed (['sepal length (cm)', 'sepal width (cm)', 'petal length (cm)', 'petal width (cm)'])

尝试将输入签名修改为4个TensorSpec(对应4个特征列)后仍未解决问题,完整脚本如下:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import onnxruntime as rt
import tf2onnx
import onnx

plt.style.use('ggplot')

from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder, StandardScaler

iris = load_iris()
X = iris['data']
y = iris['target']
names = iris['target_names']
feature_names = iris['feature_names']

# One hot encoding
enc = OneHotEncoder()
Y = enc.fit_transform(y[:, np.newaxis]).toarray()

# Scale data to have mean 0 and variance 1
# which is importance for convergence of the neural network
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# Split the data set into training and testing
X_train, X_test, Y_train, Y_test = train_test_split(
    X_scaled, Y, test_size=0.5, random_state=2)

n_features = X.shape[1]
n_classes = Y.shape[1]

# Visualize the data sets
plt.figure(figsize=(16, 6))
plt.subplot(1, 2, 1)
for target, target_name in enumerate(names):
    X_plot = X[y == target]
    plt.plot(X_plot[:, 0], X_plot[:, 1], linestyle='none', marker='o', label=target_name)
plt.xlabel(feature_names[0])
plt.ylabel(feature_names[1])
plt.axis('equal')
plt.legend();

plt.subplot(1, 2, 2)
for target, target_name in enumerate(names):
    X_plot = X[y == target]
    plt.plot(X_plot[:, 2], X_plot[:, 3], linestyle='none', marker='o', label=target_name)
plt.xlabel(feature_names[2])
plt.ylabel(feature_names[3])
plt.axis('equal')
plt.legend();

# In order to ignore FutureWarning
import warnings
warnings.simplefilter(action='ignore', category=FutureWarning)
warnings.simplefilter(action='ignore', category=DeprecationWarning)

import tensorflow as tf

def create_custom_model(input_dim, output_dim, nodes, n=1, name='model'):
    def create_model():
        # Create model
        model = tf.keras.Sequential(name=name)
        for i in range(n):
            model.add(tf.keras.layers.Dense(nodes, input_dim=input_dim, activation='relu'))
        model.add(tf.keras.layers.Dense(output_dim, activation='softmax'))

        # Compile model
        model.compile(loss='categorical_crossentropy',
                      optimizer='adam',
                      metrics=['accuracy'])
        return model
    return create_model

models = [create_custom_model(n_features, n_classes, 8, i, 'model_{}'.format(i))
          for i in range(1, 4)]

for create_model in models:
    create_model().summary()

history_dict = {}

# TensorBoard Callback
cb = tf.keras.callbacks.TensorBoard()

for create_model in models:
    model = create_model()
    print('Model name:', model.name)
    history_callback = model.fit(X_train, Y_train,
                                 batch_size=5,
                                 epochs=50,
                                 verbose=0,
                                 validation_data=(X_test, Y_test),
                                 callbacks=[cb])
    score = model.evaluate(X_test, Y_test, verbose=0)
    print('Test loss:', score[0])
    print('Test accuracy:', score[1])

    history_dict[model.name] = [history_callback, model]

create_model = create_custom_model(n_features, n_classes, 8, 3)

model = history_dict["model_3"][1] #model 3 was the most accurate
model.save("Iris.keras")
input_signature = [tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype, name="x")]
model.output_names=['output']
onnx_model, _ = tf2onnx.convert.from_keras(model, input_signature, opset=13)
onnx.save(onnx_model, "Irismodel.onnx")

sess = rt.InferenceSession("Irismodel.onnx")
input_name = sess.get_inputs()[0].name
label_name = sess.get_outputs()[0].name
dataset = pd.DataFrame({"sepal length (cm)" : X_test[:,0],"sepal width (cm)": X_test[:,1],
                        "petal length (cm)": X_test[:,2], "petal width (cm)": X_test[:,3]})

pred = sess.run([label_name], dataset)

核心问题分析

  1. 模型输入结构不匹配:你创建的Keras序列模型是单输入张量结构(形状为(batch_size, 4),包含4个特征),但推理时错误地将4个特征拆分为独立列名传递,导致输入名与ONNX模型期望的x不匹配。
  2. 错误的输入签名修改:将输入签名改为4个TensorSpec会强制模型接受4个独立输入,但原Keras模型并未设计成多输入结构,转换后的ONNX模型会期望4个输入张量,而你传递的DataFrame列数据是一维的,不符合模型要求的二维张量形状。

解决方案

方案1:保持原单输入模型结构(推荐)

不需要修改模型,只需调整推理时的输入传递方式,确保输入名和张量形状与ONNX模型一致:

修改脚本最后13行代码为:

model = history_dict["model_3"][1]  # model 3 was the most accurate
model.save("Iris.keras")

# 保持单输入签名,名字设为"x"(与ONNX模型输入名一致)
input_signature = [tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype, name="x")]
model.output_names=['output']
onnx_model, _ = tf2onnx.convert.from_keras(model, input_signature, opset=13)
onnx.save(onnx_model, "Irismodel.onnx")

sess = rt.InferenceSession("Irismodel.onnx")
input_name = sess.get_inputs()[0].name
label_name = sess.get_outputs()[0].name

# 直接传递X_test作为输入(形状为(N,4),与训练时格式一致)
pred = sess.run([label_name], {input_name: X_test})

# 可选:将输出转换为预测类别
pred_classes = np.argmax(pred[0], axis=1)
print(pred_classes)

方案2:修改模型为多命名输入(如需按特征名传递)

如果需要按特征列名作为输入名传递数据,需重构Keras模型为多输入结构:

  1. 修改模型创建函数:
def create_custom_model(input_dim, output_dim, nodes, n=1, name='model'):
    def create_model():
        # 定义4个带名字的输入层,每个特征对应一个输入
        inputs = [
            tf.keras.Input(shape=(1,), name="sepal length (cm)"),
            tf.keras.Input(shape=(1,), name="sepal width (cm)"),
            tf.keras.Input(shape=(1,), name="petal length (cm)"),
            tf.keras.Input(shape=(1,), name="petal width (cm)")
        ]
        # 将4个输入拼接为单张量,供后续Dense层处理
        x = tf.keras.layers.concatenate(inputs)
        for i in range(n):
            x = tf.keras.layers.Dense(nodes, activation='relu')(x)
        output = tf.keras.layers.Dense(output_dim, activation='softmax')(x)
        
        # 创建多输入模型
        model = tf.keras.Model(inputs=inputs, outputs=output, name=name)
        model.compile(loss='categorical_crossentropy',
                      optimizer='adam',
                      metrics=['accuracy'])
        return model
    return create_model
  1. 调整训练时的输入传递:
# 将训练数据拆分为字典格式,对应每个输入名
train_inputs = {
    "sepal length (cm)": X_train[:, 0:1],
    "sepal width (cm)": X_train[:, 1:2],
    "petal length (cm)": X_train[:, 2:3],
    "petal width (cm)": X_train[:, 3:4]
}

# 训练模型
history_callback = model.fit(train_inputs, Y_train,
                             batch_size=5,
                             epochs=50,
                             verbose=0,
                             validation_data=({
                                 "sepal length (cm)": X_test[:, 0:1],
                                 "sepal width (cm)": X_test[:, 1:2],
                                 "petal length (cm)": X_test[:, 2:3],
                                 "petal width (cm)": X_test[:, 3:4]
                             }, Y_test),
                             callbacks=[cb])
  1. 推理时按特征名传递数据:
# 转换为ONNX时无需额外指定输入签名(自动匹配输入层名字)
onnx_model, _ = tf2onnx.convert.from_keras(model, opset=13)
onnx.save(onnx_model, "Irismodel.onnx")

sess = rt.InferenceSession("Irismodel.onnx")
label_name = sess.get_outputs()[0].name

# 按特征名构造输入字典
pred_inputs = {
    "sepal length (cm)": X_test[:, 0:1],
    "sepal width (cm)": X_test[:, 1:2],
    "petal length (cm)": X_test[:, 2:3],
    "petal width (cm)": X_test[:, 3:4]
}

pred = sess.run([label_name], pred_inputs)
pred_classes = np.argmax(pred[0], axis=1)
print(pred_classes)

内容的提问来源于stack exchange,提问作者Some Dude

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 03:14:59