AI 또는 풀스택 개발에 어려움을 겪고 계신가요? 맞춤형 조언, 기술 통합 등 유니티 전문가가 도와드리겠습니다. 다음 연락처로 문의하세요. [email protected].

FastAPI 및 트랜스포머를 사용한 분류를 위한 프로덕션 지원 머신 러닝 자연어 처리 API

2018년 말에 FastAPI의 첫 번째 버전이 출시되었습니다. 출시되었으며, 그 이후로 프로덕션 환경의 많은 애플리케이션에서 점점 더 많이 사용되고 있습니다. (FastAPI 웹 사이트 참조). 바로 바로 이것이 NLP Cloud에서 사용하고 있는 기능입니다. 수백 개의 자연어 처리 모델을 쉽고 효율적으로 서비스할 수 있는 수백 가지의 자연어 처리 모델, 엔티티 추출(NER), 텍스트 분류, 감정 분석, 질문 답변, 요약... FastAPI는 트랜스포머 기반 딥 러닝 모델을 제공하는 훌륭한 방법이라는 것을 알게 되었습니다. 학습 모델.

이 글에서는 FastAPI를 사용하여 허깅 페이스 트랜스포머에 기반한 자연어 처리 API를 구현하는 방법을 보여드리는 것이 흥미로울 것 같았습니다.

왜 FastAPI를 사용해야 하나요?

FastAPI 이전에는 기본적으로 Python API에 Django Rest Framework를 사용했지만, 다음과 같은 이유로 빠르게 다음과 같은 이유로 FastAPI에 관심을 갖게 되었습니다:

이러한 뛰어난 성능으로 인해 FastAPI는 우리와 같은 트랜스포머 기반 모델을 제공하는 머신 러닝 API에 완벽하게 적합합니다.

FastAPI 설치

FastAPI가 작동하도록 하기 위해, 우리는 이를 Uvicorn ASGI 서버와 결합하고 있으며, 이는 최신 방식입니다. 비동기 파이썬 요청을 기본적으로 처리하는 최신 방식입니다. 다음 중 하나를 선택할 수 있습니다. Uvicorn과 함께 FastAPI를 수동으로 설치하거나 바로 사용할 수 있는 Docker 이미지를 다운로드할 수 있습니다. 먼저 수동 설치를 먼저 보여드리겠습니다:

pip install fastapi[all]

그런 다음 다음과 같이 시작할 수 있습니다:

uvicorn main:app

FastAPI의 창시자인 Sebastián Ramírez는 바로 사용할 수 있는 몇 가지 Docker 이미지를 제공합니다. 매우 쉽게 사용할 수 있는 몇 가지 이미지를 제공합니다. Uvicorn + Gunicorn + FastAPI 이미지는 여러 프로세스를 병렬로 사용하기 위해 Gunicorn을 활용합니다. (여기 이미지 참조). 결국, 덕분에 유비콘 덕분에 동일한 파이썬 프로세스 내에서 여러 개의 FastAPI 인스턴스를 처리할 수 있고, 구니콘 덕분에 덕분에 여러 개의 파이썬 프로세스를 생성할 수 있습니다.

Docker 컨테이너를 시작할 때 다음과 같이 FastAPI 애플리케이션이 자동으로 시작됩니다: docker run.

Gunicorn이 생성하는 병렬 프로세스 수와 같이 조정할 수 있는 몇 가지 설정이 있으므로 이러한 Docker 이미지의 설명서를 제대로 읽는 것이 중요합니다. 조정할 수 있는 몇 가지 설정(예: Gunicorn에 의해 생성된 병렬 프로세스 수)이 있으므로 이러한 도커 이미지의 설명서를 읽는 것이 중요합니다. 기본적으로 이미지는 머신의 CPU 코어 수만큼 프로세스를 생성합니다. 하지만 자연어 처리 트랜잭션과 같은 자연어 처리 트랜스포머와 같은 까다로운 머신 러닝 모델의 경우, 수십 GB의 메모리가 빠르게 사용될 수 있습니다. 한 가지 전략은 구니콘 옵션을 활용하는 것입니다. (--preload), 모델을 로드하기 위해 모델을 메모리에 한 번만 로드하고 모든 FastAPI 파이썬 프로세스 간에 공유할 수 있습니다. 또 다른 옵션은 구니콘 프로세스의 수를 제한하는 것입니다. 두 가지 방법 모두 장단점이 있지만 이 글의 이 글의 범위를 벗어납니다.

텍스트 분류를 위한 간단한 FastAPI + 트랜스포머 API

텍스트 분류는 텍스트가 무엇을 말하는지 결정하는 과정입니다(공간? 비즈니스? 음식?...). 텍스트에 대한 자세한 내용은 분류에 대한 자세한 내용은 여기를 참조하세요.

Facebook의 Bart Large MNLI 모델을 사용하여 텍스트 분류를 수행하는 API 포인트를 만들고 싶습니다. 분류에 완벽하게 적합한 모델입니다.

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와 트랜스포머로 이를 달성하는 방법은 다음과 같습니다:

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를 로드하고 분류를 위해 적절하게 초기화합니다:

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

그리고 나중에 이렇게 해서 모델을 사용하고 있습니다:

classifier(user_request_in.text, user_request_in.labels)

두 번째로 중요한 것은 Pydantic 덕분에 데이터 유효성 검사를 수행하고 있다는 점입니다. Pydantic을 사용하면 API의 입력 및 출력 형식을 미리 선언하도록 하는데, 이는 문서화 측면에서도 훌륭하지만 관점에서도 훌륭하지만, 잠재적인 실수를 제한하기 때문이기도 합니다. Go에서는 거의 동일한 작업을 수행합니다. 과 거의 동일한 작업을 수행할 수 있습니다. 다음은 쉬운 방법입니다. "텍스트" 필드에 최소 1자의 문자가 있어야 한다고 선언합니다: 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, start 그리고 position. 방법은 다음과 같습니다:

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

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

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

지금까지는 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 코드가 애플리케이션의 나머지 부분과 느슨하게 연결되도록 하기 위해 이 전체 URL을 하드코딩하고 싶지 않습니다. 이렇게 할 수 있습니다:

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

또는 Uvicorn을 시작할 때 파라미터를 전달할 수도 있습니다:

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

결론

자연어 처리 API에 FastAPI가 얼마나 편리한지 성공적으로 보여드렸기를 바랍니다. Pydantic은 코드를 매우 표현력이 풍부하고 오류가 적습니다.

FastAPI는 성능이 뛰어나며, 바로 사용할 수 있는 파이썬 비동기화 기능을 갖추고 있어 Transformer 기반 자연어 처리 모델과 같은 까다로운 머신 러닝 모델에 적합합니다. 저희는 FastAPI를 거의 1년 동안 사용해 왔으며 지금까지 단 한 번도 실망한 적이 없습니다.

궁금한 점이 있으시면 주저하지 마시고 언제든지 문의해 주세요!

Julien Salinas
NLP 클라우드의 CTO