TensorFlow Serving部署目标检测模型请求维度报错问题咨询
TensorFlow Serving目标检测请求张量维度不匹配问题
问题现象
完成TensorFlow Serving目标检测实例搭建后,发起预测请求触发张量维度不匹配报错。使用tolist()将图像numpy数组转为JSON兼容格式后,TensorFlow Serving解析得到形状为[339450,3]的张量,与模型预期输入形状不符。
报错信息
Data: {"signature_name": "serving_default", "instances": ... 58, 63], [35, 59, 63], [37, 58, 63], [43, 67, 71]]]} {'error': 'Specified a list with shape [?,?,3] from a tensor with shape [339450,3]\n\t [[{{function_node __inference_call_func_9686}}{{node map/TensorArrayUnstack/TensorListFromTensor}}]]'}
原始请求代码
import requests import json from PIL import Image import numpy # Load image img = Image.open("Hilarious-Car-License-Plates-1.jpg") img_np = numpy.array(img.getdata()) img_np.resize(tuple([1] + list(img_np.shape))) data = json.dumps({"signature_name": "serving_default", "instances": img_np.tolist()}) print('Data: {} ... {}'.format(data[:50], data[len(data)-52:])) headers = {"content-type": "application/json"} json_response = requests.post('http://localhost:8501/v1/models/plate_detect:predict', data=data, headers=headers) response = json.loads(json_response.text) print(response)
模型输入要求
从模型元数据可以看到,serving_default签名的输入张量input_tensor要求形状为[1, -1, -1, 3],即batch大小为1、通道数为3的任意尺寸RGB图像,数据类型为DT_UINT8。
完整元数据如下:
{ "model_spec":{ "name": "plate_detect", "signature_name": "", "version": "1" } , "metadata": {"signature_def": { "signature_def": { "serving_default": { "inputs": { "input_tensor": { "dtype": "DT_UINT8", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "-1", "name": "" }, { "size": "-1", "name": "" }, { "size": "3", "name": "" } ], "unknown_rank": false }, "name": "serving_default_input_tensor:0" } }, "outputs": { "detection_boxes": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "100", "name": "" }, { "size": "4", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:1" }, "raw_detection_boxes": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "1917", "name": "" }, { "size": "4", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:6" }, "detection_scores": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "100", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:4" }, "raw_detection_scores": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "1917", "name": "" }, { "size": "2", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:7" }, "detection_anchor_indices": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "100", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:0" }, "detection_multiclass_scores": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "100", "name": "" }, { "size": "2", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:3" }, "detection_classes": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" }, { "size": "100", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:2" }, "num_detections": { "dtype": "DT_FLOAT", "tensor_shape": { "dim": [ { "size": "1", "name": "" } ], "unknown_rank": false }, "name": "StatefulPartitionedCall:5" } }, "method_name": "tensorflow/serving/predict" }, "__saved_model_init_op": { "inputs": {}, "outputs": { "__saved_model_init_op": { "dtype": "DT_INVALID", "tensor_shape": { "dim": [], "unknown_rank": true }, "name": "NoOp" } }, "method_name": "" } } } } }
问题根因
- 图像预处理逻辑错误:
img.getdata()返回的是所有像素值的平铺迭代器,直接转numpy数组得到的形状是(总像素数, 3),也就是报错里的[339450,3],完全丢失了图像原有的高度、宽度二维结构。 - 维度变换API使用错误:
numpy.ndarray.resize()是原地修改数组的方法,当目标形状的元素总数和原数组不匹配时,会自动截断/重复填充元素,不会按语义增加维度,无法得到预期的四维输入结构。 - 发起请求时不需要显式指定输入图像形状,只要传入的嵌套列表结构和模型要求的张量维度对齐即可,当前报错完全是输入数组维度预处理错误导致的。
修复方案
修正图像加载和维度处理逻辑,不要用img.getdata()读取像素,直接对PIL Image对象转numpy数组即可保留(高度, 宽度, 3)的原始图像结构,再通过np.expand_dims增加batch维度,得到符合模型要求的四维数组。
修正后的代码如下:
import requests import json from PIL import Image import numpy as np # 加载图像 img = Image.open("Hilarious-Car-License-Plates-1.jpg") # 直接转numpy数组,保留(高, 宽, 3)的原始图像结构,指定类型为uint8匹配模型输入要求 img_np = np.array(img, dtype=np.uint8) # 增加batch维度,得到形状为(1, 高, 宽, 3)的输入,完全匹配模型输入要求 img_np = np.expand_dims(img_np, axis=0) # 建议打印形状确认,避免维度错误 print(f"输入张量形状: {img_np.shape}") data = json.dumps({"signature_name": "serving_default", "instances": img_np.tolist()}) headers = {"content-type": "application/json"} json_response = requests.post('http://localhost:8501/v1/models/plate_detect:predict', data=data, headers=headers) response = json.loads(json_response.text) print(response)
注意事项
- 不要使用原地操作的
ndarray.resize()做维度变换,这类API会按内存平铺顺序重排元素,不适合做语义层面的维度增删,维度调整优先使用np.expand_dims、np.reshape等不会打乱元素顺序的API。 - 数组转列表发请求前,一定要打印
shape属性确认维度符合模型输入要求,再做序列化操作。
内容的提问来源于stack exchange,提问作者agm
相关产品推荐
相关产品推荐

