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

TensorFlow模型部署FastAPI遇Dictionary Update Sequence Error求助

问题

在Google Colab训练了TensorFlow模型,本地运行可正常预测,但部署到FastAPI后,传入相同DataFrame数据时报错。错误日志显示Dictionary Update Sequence Error及vars()参数无__dict__属性,推测和输入维度有关,但本地使用相同数据时模型可成功预测。以下是相关代码和错误日志:

完整错误日志

INFO:     Started server process [283]
INFO:uvicorn.error:Started server process [283]
INFO:     Waiting for application startup.
INFO:uvicorn.error:Waiting for application startup.
Public URL: NgrokTunnel: "https://f287-35-238-142-115.ngrok.io" -> "http://localhost:10000"
INFO:     Application startup complete.
INFO:uvicorn.error:Application startup complete.
INFO:     Uvicorn running on http://127.0.0.1:10000 (Press CTRL+C to quit)
INFO:uvicorn.error:Uvicorn running on http://127.0.0.1:10000 (Press CTRL+C to quit)
1/1 [==============================] - 0s 68ms/step
INFO:     54.86.50.139:0 - "POST /predict HTTP/1.1" 500 Internal Server Error
ERROR:    Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 152, in jsonable_encoder
    data = dict(obj)
ValueError: dictionary update sequence element #0 has length 1; 2 is required

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 157, in jsonable_encoder
    data = vars(obj)
TypeError: vars() argument must have __dict__ attribute

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/uvicorn/protocols/http/h11_impl.py", line 394, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.8/dist-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/applications.py", line 270, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
    raise e
  File "/usr/local/lib/python3.8/dist-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 706, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/routing.py", line 255, in app
    content = await serialize_response(
  File "/usr/local/lib/python3.8/dist-packages/fastapi/routing.py", line 152, in serialize_response
    return jsonable_encoder(response_content)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 117, in jsonable_encoder
    encoded_value = jsonable_encoder(
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 160, in jsonable_encoder
    raise ValueError(errors) from e
ValueError: [ValueError('dictionary update sequence element #0 has length 1; 2 is required'), TypeError('vars() argument must have __dict__ attribute')]
ERROR:uvicorn.error:Exception in ASGI application
Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 152, in jsonable_encoder
    data = dict(obj)
ValueError: dictionary update sequence element #0 has length 1; 2 is required

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 157, in jsonable_encoder
    data = vars(obj)
TypeError: vars() argument must have __dict__ attribute

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/local/lib/python3.8/dist-packages/uvicorn/protocols/http/h11_impl.py", line 394, in run_asgi
    result = await app(self.scope, self.receive, self.send)
  File "/usr/local/lib/python3.8/dist-packages/uvicorn/middleware/proxy_headers.py", line 45, in __call__
    return await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/applications.py", line 270, in __call__
    await super().__call__(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/applications.py", line 124, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/exceptions.py", line 79, in __call__
    raise exc
  File "/usr/local/lib/python3.8/dist-packages/starlette/middleware/exceptions.py", line 68, in __call__
    await self.app(scope, receive, sender)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/middleware/asyncexitstack.py", line 21, in __call__
    raise e
  File "/usr/local/lib/python3.8/dist-packages/fastapi/middleware/asyncexitstack.py", line 18, in __call__
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 706, in __call__
    await route.handle(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 276, in handle
    await self.app(scope, receive, send)
  File "/usr/local/lib/python3.8/dist-packages/starlette/routing.py", line 66, in app
    response = await func(request)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/routing.py", line 255, in app
    content = await serialize_response(
  File "/usr/local/lib/python3.8/dist-packages/fastapi/routing.py", line 152, in serialize_response
    return jsonable_encoder(response_content)
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 117, in jsonable_encoder
    encoded_value = jsonable_encoder(
  File "/usr/local/lib/python3.8/dist-packages/fastapi/encoders.py", line 160, in jsonable_encoder
    raise ValueError(errors) from e
ValueError: [ValueError('dictionary update sequence element #0 has length 1; 2 is required'), TypeError('vars() argument must have __dict__ attribute')]
INFO:     Shutting down
INFO:uvicorn.error:Shutting down
INFO:     Waiting for application shutdown.
INFO:uvicorn.error:Waiting for application shutdown.
INFO:     Application shutdown complete.
INFO:uvicorn.error:Application shutdown complete.
INFO:     Finished server process [283]
INFO:uvicorn.error:Finished server process [283]

模型保存代码

import pickle

Pkl_Filename = "model_tree.pkl"  

with open(Pkl_Filename, 'wb') as file:  
    pickle.dump(NN_model, file)

FastAPI部署代码

class Music(BaseModel):
    SRC: float 
    CN: float 
    DS: float 
    AGE: float 
    MKE: float 
    MDL: float 
    CS: float 
    FB: float
    CB: float
    M: float 
    VC: float
    class Config:
        schema_extra = {
            "example": {
                "SRC": 1, 
                "CN": 215, 
                "DS": 16.0,
                "AGE": 34,
                "MKE": 13,
                "MDL": 399,
                "CS": 16,
                "FB": 1,
                "CB": 9,
                "M": 4,
                "VC": 4

            }
        }

from fastapi import FastAPI
import pickle

app = FastAPI()

@app.on_event("startup")
def load_model():
    global model
    model = pickle.load(open("model_tree.pkl", "rb"))

@app.get('/')
def index():
    return {'message': 'This is the homepage of the API '}

@app.post('/predict')
def get_music_category(data: Music):
    received = data.dict()
    SRC = received['SRC']
    CN = received['CN']
    DS = received['DS']
    AGE = received['AGE']
    MKE = received['MKE']
    MDL = received['MDL']
    CS = received['CS']
    FB = received['FB']
    CB = received['CB']
    M = received['M']
    VC = received['VC']

    temp_list = [SRC, CN, DS, AGE, MKE, MDL, CS, FB, CB, M, VC]

    # temp_df = pd.DataFrame(temp_list).transpose()
    # temp_df = temp_df.astype(float)
    temp_df = pd.concat([pd.Series(x, name=f'column_{i}') for i,x in enumerate(temp_list)], axis=1)

    # df = pd.read_json(temp_df)
    pred_name = model.predict(temp_df)

    return {'prediction': pred_name}

解决方案

问题根源

报错并非输入维度问题,而是TensorFlow模型的预测结果无法被FastAPI的jsonable_encoder序列化为JSON。model.predict()返回的是TensorFlow的Tensor对象或numpy数组,这类对象无法直接被FastAPI转换成JSON格式,从而触发序列化错误。

另外,构建temp_df的方式冗余且可能导致列名不匹配模型训练时的特征名,但这不是当前报错的直接原因。

修复步骤

  1. 转换预测结果为可序列化格式
    将model.predict()的结果转换为Python原生类型(如列表或标量):

    pred_name = model.predict(temp_df)
    # 转换为Python列表
    pred_result = pred_name.tolist()
    # 单样本预测时取第一个元素,避免嵌套列表
    pred_result = pred_result[0] if len(pred_result) == 1 else pred_result
    
  2. 简化输入数据构建
    替换复杂的pd.concat方式,直接创建符合模型输入要求的DataFrame,确保列名和训练时一致:

    # 需与模型训练时的特征列顺序保持一致
    feature_cols = ["SRC", "CN", "DS", "AGE", "MKE", "MDL", "CS", "FB", "CB", "M", "VC"]
    temp_df = pd.DataFrame([received], columns=feature_cols)
    
  3. 优化模型保存方式(可选但推荐)
    不建议用pickle保存TensorFlow模型,官方推荐使用TensorFlow原生保存方式:

    # 保存模型
    NN_model.save("model_tree")
    # 加载模型
    from tensorflow.keras.models import load_model
    model = load_model("model_tree")
    

修改后的完整预测函数

@app.post('/predict')
def get_music_category(data: Music):
    received = data.dict()
    feature_cols = ["SRC", "CN", "DS", "AGE", "MKE", "MDL", "CS", "FB", "CB", "M", "VC"]
    temp_df = pd.DataFrame([received], columns=feature_cols)
    
    pred_name = model.predict(temp_df)
    # 转换为可序列化的Python类型
    pred_result = pred_name.tolist()
    # 处理单样本预测的嵌套列表问题
    if len(pred_result) == 1 and isinstance(pred_result[0], list):
        pred_result = pred_result[0]
    
    return {'prediction': pred_result}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 07:40:24