在 FastAPI 中使用 Pydantic 模型进行基于模型的预测时出现错误“值不是有效的字典”

我正在尝试使用带有 FastAPI 的 Pydantic 模型进行多个预测(用于输入列表)。问题是不能将 Pydantic 模型直接传递给 model.predict() 函数,所以我将其转换为字典,但是,我收到以下错误:

AttributeError: 'list' object has no attribute 'dict'

我的代码:

from fastapi import FastAPI
import uvicorn
from pydantic import BaseModel
import pandas as pd
from typing import List

app = FastAPI()

class Inputs(BaseModel):
    id: int
    f1: float
    f2: float
    f3: str

class InputsList(BaseModel):
    inputs: List[Inputs]

@app.post('/predict')
def predict(input_list: InputsList):
    df = pd.DataFrame(input_list.inputs.dict())
    prediction = classifier.predict(df.loc[:, df.columns != 'id'])
    probability = classifier.predict_proba(df.loc[:, df.columns != 'id'])
    return {'id': df["id"].tolist(), 'prediction': prediction.tolist(), 'probability': probability.tolist()}

我也有 return 的问题,我需要输出类似于:

    [
      {
        "id": 123,
        "prediction": "class1",
        "probability": 0.89
      },
      {
        "id": 456,
        "prediction": "class3",
        "probability": 0.45
      }
    ]

PS: id 类中的 Inputs 不会发生在预测中(不是特征),但我需要将它显示在其预测旁边(以引用它)。

请求enter image description here

stack overflow Getting error "value is not a valid dict" when using Pydantic models in FastAPI for model-based predictions
原文答案
author avatar

接受的答案

首先,您的架构的 ,f1 属性以及您发送的 f2 有效负载中都有不必要的逗号 JSON 。因此,您的架构应该是:

class Inputs(BaseModel):
    id: int
    f1: float
    f2: float
    f3: str

第二422 错误是由于您发送的 JSON 有效负载与您的架构不匹配。正如@MatsLindh 所述,您的 JSON 有效负载应如下所示:

{
  "inputs": [
    {
      "id": 1,
      "f1": 1.0,
      "f2": 1.0,
      "f3": "text"
    },
    {
      "id": 2,
      "f1": 2.0,
      "f2": 2.0,
      "f3": "text"
    }
  ]
}

第三,您正在以破旧的方式创建 DataFrame。您试图在 dict() 对象上调用 list 方法;因此, AttributeError: 'list' object has no attribute 'dict' 。相反,如 here 所示,您应该对 .dict() 中的每个项目调用 list 方法,如下所示:

df = pd.DataFrame([i.dict() for i in input_list.inputs])

最后,要以问题中提到的输出格式返回结果,请使用以下内容。 注意 predict_proba() 返回一个列表数组,其中包含 input 的类概率。如果您只想返回特定类的 probability ,请改用该类的索引,例如 prob[0]

results = []
for (id, pred, prob) in zip(df["id"].tolist(), prediction.tolist(), probability.tolist()):
    results.append({"id": id, "prediction": pred, "probability": prob})
return results

或者,您可以使用 DataFrame 并调用其 to_dict() 方法将其转换为字典,如下所示。如果您有大量数据并且发现下面的方法返回结果非常慢,请查看 this answer 以了解替代方法。

results = pd.DataFrame({'id': df["id"].tolist(),'prediction': prediction.tolist(),'probability': probability.tolist()})
return results.to_dict(orient="records") 

如果您想在使用 DataFrame 时仅返回特定类的 probability ,您可以提取它并将其添加到新的 list 中,例如 prob_list = [item[0] for item in probability.tolist()] 或使用 operator.itemgetter()prob_list = list(map(itemgetter(0), probability.tolist())) ,并在创建 DataFrame 时使用该 list


答案:

作者头像

您对视图函数的输入模式的定义与您发送的内容不匹配:

class Inputs(BaseModel):
    id: int
    f1: float
    f2: float
    f3: str

class InputsList(BaseModel):
    inputs: List[Inputs]

这匹配以下格式的请求正文:

{
  "inputs": [
    {
      "id": 1,
      "f1": 1.0,
      "f2": 1.0,
      "f3": "foo"
    }, {
      "id": 2,
      "f1": 2.0,
      "f2": 2.0,
      "f3": "bar"
    }
  ]
}

您发送的请求正文与预期格式不匹配,因此您会收到 422 响应。

更改您要发送的对象以匹配 FastAPI 预期的格式,或者删除 InputsList 包装器并将输入设置为 input_list: List[Inputs]