原文:
www.kdnuggets.com/2021/04/production-ready-machine-learning-nlp-api-fastapi-spacy.html
评论
作者:Julien Salinas,全栈开发者,NLPCloud.io 的创始人兼首席技术官
FastAPI 是一个新的 Python API 框架,今天越来越多地被用于生产环境中。我们在 NLP Cloud 的底层使用 FastAPI。NLP Cloud 是一个基于 spaCy 和 HuggingFace transformers 的 API,提供命名实体识别(NER)、情感分析、文本分类、摘要等服务。FastAPI 帮助我们快速构建了一个快速且稳健的机器学习 API,服务于 NLP 模型。
让我告诉你我们为什么做出这样的选择,并展示如何基于 FastAPI 和 spaCy 实现一个用于命名实体识别(NER)的 API。
直到最近,我一直使用 Django Rest Framework 来构建 Python API。但 FastAPI 提供了几个有趣的功能:
-
它非常快速
-
它文档齐全
-
它易于使用
-
它会自动为你生成 API 架构(如 OpenAPI)
-
它在底层使用 Pydantic 进行类型验证。对于像我这样习惯静态类型的 Go 开发者来说,能够利用这样的类型提示非常酷。它使代码更清晰,减少了错误的可能性。
FastAPI 的性能使其成为机器学习 API 的一个优秀候选者。考虑到我们在 NLP Cloud 中服务了许多基于 spaCy 和 transformers 的高要求 NLP 模型,FastAPI 是一个很好的解决方案。
你可以选择自己安装 FastAPI 和 Uvicorn(FastAPI 前面的 ASGI 服务器):
pip install fastapi[all]
如你所见,FastAPI 在一个 ASGI 服务器后面运行,这意味着它可以原生处理使用 asyncio 的异步 Python 请求。
然后你可以用类似这样的方式运行你的应用:
uvicorn main:app
另一个选择是使用 Sebastián Ramírez(FastAPI 的创始人)慷慨提供的 Docker 镜像。这些镜像已维护,并且开箱即用。
例如,Uvicorn + Gunicorn + FastAPI 镜像将 Gunicorn 添加到堆栈中,以处理并行进程。基本上,Uvicorn 处理单个 Python 进程中的多个并行请求,而 Gunicorn 处理多个并行 Python 进程。
如果你按照镜像文档正确操作,应用程序应该会自动通过 docker run
启动。
这些图像是可定制的。例如,你可以调整 Gunicorn 创建的并行进程数量。根据你的 API 需求调整这些参数非常重要。如果你的 API 服务一个需要几个 GB 内存的机器学习模型,你可能想要减少 Gunicorn 的默认并发,否则你的应用程序将快速消耗过多内存。
假设你想创建一个使用spaCy进行命名实体识别(NER)的 API 端点。基本上,NER 是从句子中提取实体如名字、公司、职位等。有关 NER 的更多细节,如果需要的话。
这个端点将接受一个句子作为输入,并返回一个实体列表。每个实体由实体第一个字符的位置、实体的最后位置、实体的类型以及实体文本本身组成。
端点将以 POST 请求的方式进行查询:
curl "http://127.0.0.1/entities" \
-X POST \
-d '{"text":"John Doe is a Go Developer at Google"}'
它将返回类似这样的内容:
[
{
"end": 8,
"start": 0,
"text": "John Doe",
"type": "PERSON"
},
{
"end": 25,
"start": 13,
"text": "Go Developer",
"type": "POSITION"
},
{
"end": 35,
"start": 30,
"text": "Google",
"type": "ORG"
},
]
下面是我们如何操作:
import spacy
from fastapi import FastAPI
from pydantic import BaseModel
from typing import List
model = spacy.load("en_core_web_lg")
app = FastAPI()
class UserRequestIn(BaseModel):
text: str
class EntityOut(BaseModel):
start: int
end: int
type: str
text: str
class EntitiesOut(BaseModel):
entities: List[EntityOut]
@app.post("/entities", response_model=EntitiesOut)
def read_entities(user_request_in: UserRequestIn):
doc = model(user_request_in.text)
return {
"entities": [
{
"start": ent.start_char,
"end": ent.end_char,
"type": ent.label_,
"text": ent.text,
} for ent in doc.ents
]
}
首要的是我们正在加载 spaCy 模型。对于我们的示例,我们使用了一个大型的 spaCy 预训练英文模型。大型模型占用更多内存和磁盘空间,但由于在更大的数据集上进行训练,因此提供了更好的准确性。
model = spacy.load("en_core_web_lg")
稍后,我们将通过以下方式使用这个 spaCy 模型进行 NER:
doc = model(user_request_in.text)
# [...]
doc.ents
第二个令人惊叹的 FastAPI 特性是使用 Pydantic 强制数据验证的能力。基本上,你需要提前声明用户输入的格式和 API 响应的格式。如果你是 Go 开发者,你会发现这与使用结构体进行 JSON 解组非常相似。例如,我们这样声明返回实体的格式:
class EntityOut(BaseModel):
start: int
end: int
type: str
text: str
请注意,start
和end
是句子中的位置,因此它们是整数,而type
和text
是字符串。如果 API 尝试返回一个未实现此格式的实体(例如,如果start
不是整数),FastAPI 将抛出错误。
如你所见,将一个验证类嵌套到另一个类中是可能的。这里我们返回的是一个实体列表,因此我们需要声明以下内容:
class EntitiesOut(BaseModel):
entities: List[EntityOut]
一些简单类型如int
和str
是内置的,但更复杂的类型如List
需要显式导入。
为了简洁起见,响应验证可以在装饰器中实现:
@app.post("/entities", response_model=EntitiesOut)
使用 FastAPI 和 Pydantic 可以做许多更高级的验证。例如,如果你需要用户输入的最小长度为 10 个字符,你可以这样做:
from pydantic import BaseModel, constr
class UserRequestIn(BaseModel):
text: constr(min_length=10)
现在,如果 Pydantic 验证通过,但你后来发现数据有问题,因此想要返回 HTTP 400 代码,该怎么办?
只需引发一个HTTPException
:
from fastapi import HTTPException
raise HTTPException(
status_code=400, detail="Your request is malformed")
这只是几个例子,你可以做更多!只需查看 FastAPI 和 Pydantic 文档即可。
在反向代理后运行这样的 API 是很常见的。例如,我们在 NLPCloud.io 背后使用的是 Traefik 反向代理。
在反向代理后运行时,一个棘手的事情是你的子应用程序(这里是 API)并不一定知道整个 URL 路径。实际上,这很好,因为它表明你的 API 与应用程序的其余部分是松散耦合的。
例如,我们希望 API 认为端点 URL 是 /entities
,但实际的 URL 可能是类似 /api/v1/entities
的东西。这里是如何通过设置根路径来实现:
app = FastAPI(root_path="/api/v1")
如果你手动启动 Uvicorn,你也可以通过传递额外的参数来实现:
uvicorn main:app --root-path /api/v1
如你所见,使用 FastAPI 创建 API 非常简单,而 Pydantic 的验证使得代码非常表达清晰(从而减少了对文档的需求)并且更不容易出错。
FastAPI 提供了出色的性能,并且可以直接使用 asyncio 进行异步请求,这对于需求高的机器学习模型非常有利。上面的示例关于使用 spaCy 和 FastAPI 的命名实体提取几乎可以认为是生产就绪的(当然,API 代码只是完整集群应用程序的一小部分)。到目前为止,FastAPI 从未成为我们 NLPCloud.io 基础设施的瓶颈。
如果你有任何问题,请随时提问!
简历: Julien Salinas 是一位全栈工程师,精通 Python/Django、Go、Vue.js、Linux 和 Docker。他是 NLPCloud.io 的创始人和首席技术官,该 API 帮助开发人员和数据科学家轻松地在生产环境中使用 NLP。他喜欢山脉、滑雪、拳击……同时也是两个男孩的父亲。
原文。经许可转载。
相关:
-
如何将 Transformers 应用于任何长度的文本
-
如何在 Kubernetes 中部署 Flask API 并与其他微服务连接
-
机器学习项目为何会失败?
1. Google 网络安全证书 - 快速开启网络安全职业生涯。
2. Google 数据分析专业证书 - 提升你的数据分析技能
3. 谷歌 IT 支持专业证书 - 支持你的组织在 IT 方面