Docker部署MediaPipe Hands模型后调用MLFlow API遇400错误排查
MediaPipe Hands模型MLFlow Docker部署HTTP 400错误解决
问题背景
测试将MediaPipe Hands模型通过MLFlow部署至Docker环境,模型预期输入为{'input_1': img},其中img是形状为(1,224,224,3)的float32类型4维numpy数组。本地测试模型预测正常,但通过requests调用部署后的API时持续触发HTTP 400错误。已知MLFlow API仅支持四种输入格式,由于输入是4维数组,只能选择符合TF Serving API规范的Tensor输入,但多次尝试仍未解决。
重现步骤(已完成操作)
1. 模型转ONNX格式
因MLFlow不支持tflite模型,使用tf2onnx转换:
pip install tensorflow onnxruntime tf2onnx
import tf2onnx tf2onnx.convert.from_tflite("hand_model/hand_landmark_full.tflite", output_path="hand_model/hand_landmark_full2.onnx");
2. MLFlow中记录模型
pip install mlflow onnx
import mlflow import onnx import os from mlflow.tracking import MlflowClient mlflow_client = MlflowClient() EXPERIMENT_NAME = "ONNX_Hand" experiment_details = mlflow_client.get_experiment_by_name(EXPERIMENT_NAME) if experiment_details is not None: experiment_id = experiment_details.experiment_id else: experiment_id = mlflow.create_experiment(EXPERIMENT_NAME) with mlflow.start_run(experiment_id=experiment_id, run_name="handdatasetrfrun") as run: model = onnx.load("./onnx/hand_landmark_full2.onnx") mlflow.onnx.log_model(model, artifact_path="model") run_id = run.info.run_id print('Run ID: {}'.format(run_id))
3. 本地测试模型
处理图片并验证预测正常:
import mlflow import numpy as np import cv2 # 读取并处理图片 img = cv2.imread("hand.JPG") RGB_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) resized_img = cv2.resize(RGB_img, dsize=(224,224)) image_data = cv2.flip(resized_img, 1) float_image = image_data.astype(np.float32) im = np.array([np.divide(float_image,255)]) print(im.shape) logged_model = 'C:/Workspace/MLFrameworks/mpHanddetection/mlruns/1/9a44d5671a6140988fbafaa939c6f9d9/artifacts/model' loaded_model = mlflow.pyfunc.load_model(logged_model) data = {'input_1': im} predictions = loaded_model.predict(data) print(predictions)
4. 构建Docker镜像并运行
mlflow models build-docker -m "C:/Workspace/MLFrameworks/mpHanddetection/mlruns/1/9a44d5671a6140988fbafaa939c6f9d9/artifacts/model" -n "handmodel"
docker run -p 5001:8080 "handmodel"
运行时出现numpy相关警告,暂不影响服务启动。
5. API调用错误复现
import cv2 import requests import numpy as np img = cv2.imread("C:/Workspace/MLFrameworks/mpHanddetection/hand.JPG") RGB_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) resized_img = cv2.resize(RGB_img, dsize=(224,224)) image_data = cv2.flip(resized_img, 1) float_image = image_data.astype(np.float32) im = np.divide([float_image],255) headers = {"content-type": "application/json"} response = requests.post(url="http://127.0.0.1:5001/invocations", data={"inputs":{'input_1': im}}, headers=headers) print(response.raise_for_status())
错误日志:
requests.exceptions.HTTPError: 400 Client Error: BAD REQUEST for url: http://127.0.0.1:5001/invocations
问题原因
- 请求格式错误:直接将numpy数组放入字典传递给
requests.post的data参数,numpy数组无法被正确序列化为JSON,且data参数传字典默认会以form-data格式发送,而非JSON格式。 - 未遵循TF Serving Tensor格式:MLFlow要求Tensor输入需符合TF Serving的规范,需明确指定张量的
dtype和shape,并将数组转为列表。
解决方法
修改API调用代码,按照TF Serving的JSON格式构造请求体,并用json.dumps序列化后发送:
import cv2 import requests import numpy as np import json # 图片处理部分不变 img = cv2.imread("C:/Workspace/MLFrameworks/mpHanddetection/hand.JPG") RGB_img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) resized_img = cv2.resize(RGB_img, dsize=(224,224)) image_data = cv2.flip(resized_img, 1) float_image = image_data.astype(np.float32) im = np.divide([float_image], 255) # 构造符合TF Serving规范的请求体 input_data = { "inputs": { "input_1": { "dtype": "float32", "shape": im.shape, "data": im.flatten().tolist() } } } # 使用json参数自动序列化并发送请求 response = requests.post( url="http://127.0.0.1:5001/invocations", json=input_data ) # 检查响应 if response.status_code == 200: predictions = response.json() print(predictions) else: print(f"Error: {response.status_code}, {response.text}")
关键说明
- 使用
json参数而非data参数:requests.post的json参数会自动将字典序列化为JSON字符串,并设置正确的Content-Type头,无需手动指定headers。 - 张量格式要求:必须包含
dtype(与模型输入类型一致,此处为float32)、shape(输入数组的形状,此处为(1,224,224,3))、data(数组扁平化后的列表,因为JSON不支持多维数组)。
内容的提问来源于stack exchange,提问作者MLnoob
相关产品推荐
相关产品推荐

