将 pandas 数据帧传递给 FastAPI 用于 NLP ML

Posted

技术标签:

【中文标题】将 pandas 数据帧传递给 FastAPI 用于 NLP ML【英文标题】:Passing a pandas dataframe to FastAPI for NLP ML 【发布时间】:2020-11-20 17:48:15 【问题描述】:

我第一次尝试部署 NLP ML 模型。为此,建议我使用 FastAPI 和 uvicorn。我在让 FastAPI 做出响应方面取得了一些成功;但是,我无法成功传递数据帧并让它处理它。我尝试过使用字典,甚至尝试将传递的 json 转换为数据帧。

data_dict = data.dict() 我得到: ValueError: Iterable over raw text documents expected, string object received.

data_dict = pd.DataFrame(data.dict()) 我得到: ValueError: If using all scalar values, you must pass an index

我相信我理解这个问题,我的 Data 类需要一个字符串,而这不是;但是,我无法确定如何设置和/或传递预期数据,以便 fit_transform() 起作用。最终,我将根据提交的消息值返回预测。如果我可以传递 1 行或更多行的数据帧并为每一行做出并返回预测,则奖励。响应将包括 id、项目和预测,以便我们将来能够利用此响应将预测发布回原始(请求)系统。

test_connection.py

#%%
import requests
import pandas as pd
import json
import os
from pprint import pprint

url = 'http://127.0.0.1:8000/predict'
print(os.getcwd())
#%%
df = pd.DataFrame(
    
        'id': ['ab410483801c38', 'cd34148639180'],
        'project': ['project1', 'project2'], 
        'messages': ['This is message 1', 'This is message 2']
    
)
to_predict_dict = df.iloc[0].to_dict()
#%%
r = requests.post(url, json=to_predict_dict)

main.py

#!/usr/bin/env python
# coding: utf-8

import pickle
import pandas as pd
import numpy as np
from pydantic import BaseModel
from sklearn.feature_extraction.text import TfidfVectorizer

# Server
import uvicorn
from fastapi import FastAPI
# Model
import xgboost as xgb


app = FastAPI()

clf = pickle.load(open('data/xgbmodel.pickle', 'rb'))

class Data(BaseModel):
    # id: str
    project: str
    messages: str

@app.get("/ping")
async def test():
    return "ping": "pong"

@app.post("/predict")
async def predict(data: Data):
#    data_dict = data.dict()
    data_dict = pd.DataFrame(data.dict())
    tfidf_vect = TfidfVectorizer(stop_words="english", analyzer='word', token_pattern=r'\w1,')
    tfidf_vect.fit_transform(data_dict['messages'])
#   to_predict = tfidf_vect.transform(data_dict['messages'])
#   prediction = clf.predict(to_predict)

    return "response": "Success"

【问题讨论】:

如果没有DataFrame in main.py 就不能这样做吗? fit_transform(data.messages) ? 不,那是我收到 ValueError 字符串的时候。抱歉,我的帖子中没有明确说明,但这些错误实际上发生在 fit_transform() 步骤中。 我要补充一点,我没有尝试过使用点表示法,我只尝试过使用括号。不确定有什么不同,但会试一试。 跳过整个 data_dict = data.dict() 并简单地使用 data.messages 不起作用。问题是我的 Data 类,其中我将数据特征定义为 str 并且 fit_transofrm 需要原始文本文档。 我的错误 - 名称 messages 具有误导性 - 我认为它提供了消息列表。对于单个消息(单个字符串),我将使用名称 message 而不使用 s 【参考方案1】:

可能不是最优雅的解决方案,但我使用以下方法取得了进展:

def predict(data: Data):
    data_dict = pd.DataFrame(
        
            'id': [data.id],
            'project': [data.project],
            'messages': [data.messages]
        
    )

【讨论】:

取消注释剩余代码,tfidf_vect,to_predict,预测,并尝试return "Prediction": prediction results in a dump of data ending in in input data`和错误JSONDecodeError: Expecting value: line 1 column 1 (char 0) 如果我有很多(例如40+)列,这个解决方案会不会很难实现?【参考方案2】:

我可以通过简单地将data.messages 转换为一个列表来解决这个问题。我还必须进行一些不相关的更改,我未能腌制我的矢量化器(字符串标记器)。

import pickle
import pandas as pd
import numpy as np
import json
import time
from pydantic import BaseModel
from sklearn.feature_extraction.text import TfidfVectorizer

# Server / endpoint
import uvicorn
from fastapi import FastAPI
# Model
import xgboost as xgb


app = FastAPI(debug=True)

clf = pickle.load(open('data/xgbmodel.pickle', 'rb'))
vect = pickle.load(open('data/tfidfvect.pickle', 'rb'))

class Data(BaseModel):
    id: str = None
    project: str
    messages: str

@app.get("/ping")
async def ping():
    return "ping": "pong"

@app.post("/predict/")
def predict(data: Data):
    start = time.time()
    data_l = [data.messages] # make messages iterable.
    to_predict = vect.transform(data_l)
    prediction = clf.predict(to_predict)

    exec_time = round((time.time() - start), 3)
    return 
        "id": data.id,
        "project": data.project,
        "prediction": prediction[0], 
        "execution_time": exec_time
        

if __name__ == "__main__":
    uvicorn.run(app, host="127.0.0.1", port=8000)

【讨论】:

【参考方案3】:

首先,将您的数据帧df 编码为面向 JSON 记录:

r = requests.post(url, json=df.to_json(orient='records')).

然后,解码/predict/ 端点内的数据:

df = pd.DataFrame(jsonable_encoder(data))

记得导入模块from fastapi.encoders import jsonable_encoder

【讨论】:

正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center。

以上是关于将 pandas 数据帧传递给 FastAPI 用于 NLP ML的主要内容,如果未能解决你的问题,请参考以下文章

将 csv 读入 pandas 数据帧但避免 NaN 行

在 pandas 数据帧上并行调用函数

Django:从 Pandas 到 Django Rest 框架的模型查询集

FastAPI Web框架 [1.11]

FastAPI Web框架 [1.11]

将 pandas 数据框传递给 Django 模板