正在为人工智能或全栈开发而苦恼?我们的专家将为您提供指导:量身定制的建议、技术整合等。联系我们 [email protected].

具备生产条件的机器学习自然语言处理API的分类与FastAPI和变压器

FastAPI的第一个版本已经在2018年底发布 到2018年底,此后它被越来越多地用于许多生产中的应用中 (见FastAPI的网站). 这就是 这就是我们在NLP Cloud背后使用的东西。它是一种很好的方式,可以轻松有效地服务于我们的 数以百计的自然语言处理模型,用于实体提取(NER)、文本分类、情感分析、问题回答和总结。 回答、总结...。我们发现,FastAPI是为基于转化器的深度学习模型提供服务的一种很好的方式。 学习模型。

在这篇文章中,我们认为向你展示我们是如何使用FastAPI实现基于Hugging Face变换器的自然语言处理API的,会很有趣。 的自然语言处理API。

为什么使用FastAPI?

在FastAPI之前,我们基本上使用Django Rest Framework来处理我们的Python API,但我们很快就对FastAPI产生了兴趣,原因如下。 对FastAPI感兴趣,原因如下。

这些出色的性能使FastAPI完美地适用于机器学习API,服务于 像我们这样基于变换器的模型。

安装FastAPI

为了让FastAPI工作,我们将其与Uvicorn ASGI服务器耦合,这是一种现代的方式,可以用 这是用asyncio原生处理异步Python请求的现代方式。你可以决定 手动安装FastAPI和Uvicorn,或者下载一个可使用的Docker镜像。让我们来看看 首先展示手动安装。

pip install fastapi[all]

然后你可以用。

uvicorn main:app

FastAPI的创建者Sebastián Ramírez提供了几个现成的Docker镜像,使其在生产中非常容易使用FastAPI。 在生产中使用FastAPI非常容易。Uvicorn + Gunicorn + FastAPI 镜像利用了Gunicorn的优势,以便平行地使用几个进程 (在这里看到的图像). 最后,由于有了 Uvicorn,你可以在同一个Python进程中处理多个FastAPI实例,并且由于Gunicorn 你可以催生多个Python进程。

在启动Docker容器时,你的FastAPI应用程序将自动启动,具体如下。 docker run.

正确阅读这些Docker镜像的文档是很重要的,因为有些设置你可能想要调整。 你可能想调整一下,比如说Gunicorn创建的并行进程的数量。在默认情况下。 镜像生成的进程数量与你机器上的CPU核心数量相同。但如果是要求较高的 但是在要求很高的机器学习模型(如自然语言处理变换器)的情况下,它可以很快导致几十GB的内存使用。一个 策略是利用Gunicorn选项 (--preload), 以便在内存中只加载 你的模型在内存中只加载一次,并在所有的 FastAPI Python 进程中共享它。另一个选择是 是限制Gunicorn进程的数量。两者都有优点和缺点,但这超出了本文的 但这超出了本文的范围。

用于文本分类的简单FastAPI + Transformers API

文本分类是确定一段文本在谈论什么的过程(空间?商业? 食品?...)。 关于文本的更多细节 在此分类。

我们想创建一个API端点,使用Facebook的Bart Large MNLI进行文本分类。 模型,这是一个基于Hugging Face变换器的预训练模型,完全适用于文本 分类。

我们的API端点将接受一段文本作为输入,以及潜在的类别(称为标签)。 它将为每个类别返回一个分数(越高,越有可能)。

我们将用POST请求来请求这个端点,像这样。

curl "https://api.nlpcloud.io/v1/bart-large-mnli/classification" \
-H "Authorization: Token e7f6539e5a5d7a16e15" \
-X POST -d '{
    "text":"John Doe is a Go Developer at Google. He has been working there for 10 years and has been awarded employee of the year.",
    "labels":["job", "nature", "space"]
}'

而作为回报,我们会得到这样的回应。

{
    "labels": [
        "job",
        "space",
        "nature"
    ],
    "scores": [
        0.9258803129196167,
        0.19384843111038208,
        0.010988432914018631
    ]
}

下面是如何用FastAPI和Transformers实现的。

from fastapi import FastAPI
from pydantic import BaseModel, constr, conlist
from typing import List
from transformers import pipeline

classifier = pipeline("zero-shot-classification",
                model="facebook/bart-large-mnli")
app = FastAPI()

class UserRequestIn(BaseModel):
    text: constr(min_length=1)
    labels: conlist(str, min_items=1)

class ScoredLabelsOut(BaseModel):
    labels: List[str]
    scores: List[float]

@app.post("/classification", response_model=ScoredLabelsOut)
def read_classification(user_request_in: UserRequestIn):
    return classifier(user_request_in.text, user_request_in.labels)

首先:我们从Hugging Face资源库中加载Facebook的Bart Large MNLI,并为分类目的正确初始化它。 为分类目的正确初始化它,这要感谢Transformer Pipeline。

classifier = pipeline("zero-shot-classification",
                model="facebook/bart-large-mnli")

后来我们通过这样做来使用这个模型。

classifier(user_request_in.text, user_request_in.labels)

第二件重要的事情:由于Pydantic,我们正在进行数据验证。Pydantic迫使你 事先声明你的API的输入和输出格式,这从文档的角度来说是非常好的 角度来说是非常好的,而且还因为它限制了潜在的错误。在Go中,你会做几乎相同的事情 在Go中,你会用结构体来做与JSON解读差不多的事情。这里有一个简单的方法来 声明 "文本 "字段应该至少有一个字符。 constr(min_length=1). 而下面规定,输入的标签列表应该在列表中包含一个元素。 conlist(str, min_items=1). 这一行意味着 "标签 "输出字段应该是一个字符串的列表。 List[str]. 而这个意味着分数应该是一个浮点数的列表。 List[float]. 如果模型返回的结果不符合这个格式,FastAPI将自动引发一个错误。

class UserRequestIn(BaseModel):
    text: constr(min_length=1)
    labels: conlist(str, min_items=1)

class ScoredLabelsOut(BaseModel):
    labels: List[str]
    scores: List[float]

最后,下面的装饰器可以很容易地指定你只接受POST请求,在一个特定的端点。 @app.post("/entities", response_model=EntitiesOut).

更高级的数据验证

你可以做很多更复杂的验证事情,比如说,组成。例如,我们假设 你正在做命名实体识别(NER),所以你的模型会返回一个实体的列表。每个实体 将有4个字段。 text, type, startposition. 以下是你可以做的。

class EntityOut(BaseModel):
    start: int
    end: int
    type: str
    text: str

class EntitiesOut(BaseModel):
    entities: List[EntityOut]

@app.post("/entities", response_model=EntitiesOut) 
# [...]

到目前为止,我们一直让Pydantic处理验证问题。这在大多数情况下是有效的,但是有时候你可能希望 自己动态地提出一个错误,基于复杂的条件,而这些条件并不是Pydantic所能处理的。 Pydantic所不能处理的复杂条件。例如,如果你想手动返回一个HTTP 400错误,你可以做以下事情。

from fastapi import HTTPException

raise HTTPException(status_code=400, 
        detail="Your request is malformed")

当然,你可以做得更多!

设置根部路径

如果你在反向代理后面使用FastAPI,你很可能需要在根路径上做文章。

困难的是,在一个反向代理后面,应用程序不知道整个URL路径。 所以我们必须明确地告诉它这是什么。

例如,在这里,我们端点的完整URL可能不是简单的 /classification. 但它可能是这样的 /api/v1/classification. 我们不希望硬编码这个完整的URL,以便 我们的API代码要与应用程序的其他部分松散耦合。我们可以这样做。

app = FastAPI(root_path="/api/v1")

或者,你也可以在启动Uvicorn时给它传递一个参数。

uvicorn main:app --root-path /api/v1

总结

我希望我们成功地向你展示了FastAPI对于自然语言处理API来说是多么方便。Pydantic使代码 非常具有表现力,而且不容易出错。

FastAPI有很好的性能,使其有可能使用Python asyncio,这对要求很高的机器学习模型,如基于Transformer的自然语言处理模型,是非常好的。 对于要求很高的机器学习模型,如基于Transformer的自然语言处理模型,是非常好的。我们使用FastAPI已经有 我们在NLP Cloud使用FastAPI将近一年了,到目前为止,我们从未感到失望。

如果有任何问题,请不要犹豫,这将是一个愉快的评论!

Julien Salinas
NLP Cloud的首席技术官