Data Engineering 및 Infra

Python FastAPI와 Pytorch 딥러닝(deep learning) 모델을 활용한 FastAPI 예제(example)

이수진의 블로그 2023. 6. 19. 08:44
반응형
728x170

포스팅 개요

본 포스팅은 Python FastAPI에 대해서 정리하는 FastAPI 시리즈 포스팅 중 다섯 번째 글인 Pytorch 딥러닝(deep learning) 모델과 FastAPI를 활용한 FastAPI MLOps 예제(example)를 살펴보는 포스팅입니다.

FastAPI 포스팅은 아래와 같은 순서로 정리되어 있고, 정리할 예정입니다.

 

  1. Python FastAPI 시작하기 - FastAPI란? 설치 방법과 기본 예제(FastAPI example) (https://lsjsj92.tistory.com/648)
  2. FastAPI post 간단 예제와 비동기(Asynchronous) async 함수에 대해서 (https://lsjsj92.tistory.com/649)
  3. Python pydantic이란? Python에서 데이터 검증과 설정을 관리해보자(Feat. FastAPI) (https://lsjsj92.tistory.com/650)
  4. FastAPI router란? router 사용법과 예제(fastapi router example) (https://lsjsj92.tistory.com/651)
  5. Pytorch 딥러닝(deep learning) 모델과 FastAPI를 활용한 FastAPI 예제(example) (본 포스팅)
  6. Docker와 FastAPI를 활용해 pytorch 딥러닝 모델 배포하기(deploy pytorch model using docker, fastapi)

 

해당 포스팅을 작성하면서 제가 참고한 글과 데이터는 다음과 같습니다. 

본 글에서 작성하는 모든 코드는 아래 github repository에 올려두었습니다.

 

GitHub - lsjsj92/Fast_API: fast api with machine learning

fast api with machine learning. Contribute to lsjsj92/Fast_API development by creating an account on GitHub.

github.com


포스팅 본문

본 포스팅은 Pytorch 기반의 딥러닝(deep learning) 모델을 Python FastAPI와 결합해 API 식으로 배포하는 간단한 MLOps 예제를 살펴보려고 합니다. 

본 포스팅에서 진행한 저의 Python 환경과 Pytorch 및 FastAPI 버전은 등은 다음과 같습니다.

  • Python version : python3.8
  • OS : Mac Pro, Catalina 10
  • FastAPI : 0.88.0
  • pytorch : 1.13
  • uvicorn : 0.20.0

프로젝트 폴더 구조

본격적인 본문에 들어가기에 앞서, 프로젝트 폴더 구조를 살펴보고자 합니다. 프로젝트 폴더 구조는 다음과 같이 살펴보겠습니다.

 

1. 전체 폴더 구조

2. fastapi example 폴더

3. pytorch train model example 폴더

 

모든 코드는 제가 위에 작성한 github repository에 전부 올려두었습니다.

본 포스팅에서 사용한 데이터는 MovieLens1m 데이터 셋입니다. 해당 링크는 포스팅 개요에 참고한 링크로 넣어두었으니 참고하시면 될 것 같습니다. 

 

1. 전체 구조

이번 포스팅에서 작성하는 프로젝트 전체 구조는 위 사진과 같습니다. 본 포스팅에서 작성하는 주제가 Pytorch model과 FastAPI를 결합하는 방법이므로 Pytorch model을 training 하는 부분도 따로 존재하고 FastAPI 부분도 별도로 존재합니다.

본 구조에서는 크게 아래와 같이 나눌 수 있습니다.

  • data : MovieLens1m 데이터를 사용하였으며, 추천 시스템 모델의 data input으로 활용하였습니다. 약간의 데이터 가공처리를 진행해서 사용했습니다.
  • models : MovieLens1m 데이터를 활용해서 training한 pytorch model 입니다. 이때 사용한 model은 딥러닝(deep learning) 기반의 추천 시스템(recommender system) 모델입니다. 데이터는 pickle 데이터를 활용합니다.
  • example
    • fastapi : 제가 업로드 한 FastAPI 시리즈 글에 올린 모든 코드들의 예제가 다 담겨져 있습니다. 일종의 FastAPI example 코드입니다. 본 포스팅과는 무관한 코드들입니다. 
    • train_model : Pytorch를 이용해서 추천시스템(recommender system)을 training 하는 코드입니다. recommender system은 Neural Collaborative Filtering(NCF)과 Neural Factorization Machine(NFM) 2개의 추천 시스템 모델을 활용합니다. 
  • packages : FastAPI에 활용되는 각종 코드들을 모아놓은 곳입니다. config부터 데이터를 핸들링하는 handler, FastAPI 라우터(router) 코드 등 모든 코드가 해당 패키지 폴더에 존재합니다.
  • main.py : FastAPI with pytorch model의 main이 되는 파이썬 파일입니다.
  • Dockerfile : pytorch model과 FastAPI를 활용한 추천 시스템 model API를 docker를 활용해 배포(deploy)하기 위해 만들어둔 dockerfile입니다. 해당 dockerfile을 이용해 docker image를 만들고 실행하는 방법은 다음 포스팅에서 소개합니다. 
  • deploy.sh : 해당 프로젝트를 docker image로 생성하고 실행하는 쉘 스크립트(sh) 파일입니다.

여기서 example 폴더를 좀 더 자세히 살펴보겠습니다.

 

2. example fastapi

example/fastapi 폴더는 위에서 잠시 말씀드린 것과 같이 제가 포스팅에서 작성한 FastAPI 모든 예제(example)이 담겨 있는 폴더입니다. 본 포스팅에서 활용하는 pytorch with FastAPI와는 관련이 없는 폴더입니다. 

혹시라도 제 FastAPI 포스팅 시리즈 중 코드가 궁금하신 분들이 있으실까봐 올려두었습니다. 

 

3. example train model

example/train_model 폴더는 pytorch로 구현된 recommender system 모델을 훈련(training)하는 곳입니다. train.py를 실행하면 설정 값에 따라 Neural Collaborative Filtering(NCF)과 Neural Factorization Machine(NFM) 두 개의 추천 시스템 모델을 training 하고 해당 model의 pickle 결과 값을 프로젝트의 models 폴더에 저장됩니다.


FastAPI 코드 구성

이제 본격적으로 FastAPI와 Pytorch deep learning model을 연동해 API를 배포하는 간단한 MLOps 예제를 만들어보겠습니다. 

포스팅 길이상 전체 코드를 다 보여드리기보단, 핵심 코드만 소개하려고 합니다. 전체 코드는 위에서 소개한 github repository를 참고해주세요.

 

1. Pytorch model ( pickle data )

먼저, 미리 훈련을 해놓은 추천 시스템 모델을 아래와 같이 저장해둡니다. 저는 train_model 쪽에 있는 코드를 실행하면 pytorch 기반으로 recommender system model이 training 되고 그 결과가 models 폴더에 저장되도록 설정해두었습니다.

 

제가 저장한 파일입니다. 저는 저 파일들 중 Pickle 파일 (pkl)을 활용하였습니다.

 

2. router

저는 FastAPI를 구현하기 위해서 FastAPI router를 활용하였습니다. router의 역할은 두 개의 추천 시스템 모델 즉, ncf 결과인지 nfm 결과인지 구분하기 위해서 사용하였습니다.

router의 코드는 packages/routers에 두었습니다. 코드를 참고하실 분들은 참고해주세요. 그 중 ncf_router.py의 코드를 잠깐 살펴보면 다음과 같습니다. 

 

from fastapi import APIRouter
import torch

from packages.config import DataInput, PredictOutput
from packages.config import ProjectConfig


# Project config 설정
project_config = ProjectConfig('ncf')
# 모델 가져오기
model = project_config.load_model()
model.eval()

ncf = APIRouter(prefix='/ncf')

# router 마다 경로 설정
@ncf.get('/', tags=['ncf'])
async def start_ncf():
    return {'msg' : 'Here is NCF'}

@ncf.post('/predict', tags=['ncf'], response_model=PredictOutput)
async def start_ncf(data_request: DataInput):
    user_id = data_request.user_id
    item_id = data_request.movie_id
    predict = model(torch.tensor( [[user_id, item_id]] ))
    prob, prediction = predict, int(( predict > project_config.threshold ).float() * 1) 
    return {'prob' : prob, 'prediction' : prediction}

 

반응형

ncf_router 코드에서는 이미 저장되어 있던 ncf 모델을 load합니다. 그리고 해당 model을 model.eval()을 통해 evaluation 단계로 만들어줍니다. 

앞선 포스팅 중 FastAPI router를 소개했던 글(시리즈 네 번째 글, https://lsjsj92.tistory.com/651)처럼 FastAPI의 APIrouter 모듈을 활용해서 ncf 라는 하나의 object를 만들어줬습니다. 그리고 이 ncf는 prefix로 ncf로 올 때 반응하도록 설정하였구요.

만약, predict를 원할 때는 /ncf/predict와 같은 형태로 요청이 올 것입니다.

 

class DataInput(BaseModel):
    user_id: int = Field(ge=0, le=1000)
    movie_id:int = Field(ge=0, le=500)
    gender:int = Field(ge=0, le=1)
    age:int = Field(ge=0, le=6)
    occupation:int = Field(ge=0, le=10)
    genre:int = Field(ge=0, le=10)
    

class PredictOutput(BaseModel):
    prob:float
    prediction:int

 

이때 요청 데이터의 format은 DataInput 이라는 클래스 형태로 받게 됩니다.  DataInput은 Pydantic의 BaseModel을 상속받아서 데이터 type에 대한 검증을 수행하게 됩니다. DataInput을 통해 받아오는 데이터는 사용자 아이디, 영화 아이디 등등의 값을 가지고 오게 됩니다. 이때 사용자는 0부터 1000, gender는 0과 1의 값 등 값의 범위를 설정해주었습니다. Pydantic에 대한 설명은 제 FastAPI 시리즈 글 중 세 번째 글 (https://lsjsj92.tistory.com/650)을 참고하시면 도움이 되실것 같습니다. 또한, Predict 결과는 PredictOutput이라는 class format을 기반으로 output이 나가게 되는데요. PredictOutput 또한 Pydantic의 BaseModel을 상속 받은 class입니다. predict하는 prob은 float으로, 예측은 int 형태로 반환되게 설정해줍니다.

 

따라서, 어떤 요청이 들어오게 되면 model.predict를 수행하고 그 결과 값을 PredictOutput format이 맞는지 검증하고 그 format으로 return 해줍니다.

 

nfm router 또한 비슷하게 구현되어 있으므로 nfm router 코드는 생략하도록 하겠습니다. 자세한 것은 git repo 코드를 확인해주세요!

 

3. Main

다음은 FastAPI를 실행하는 Main 쪽 코드입니다. 코드는 다음과 같이 구성되어 있습니다.

 

main.py

from fastapi import FastAPI

app = FastAPI()

app.include_router(ncf)
app.include_router(nfm)

@app.get('/')
def read_results():
    return {'msg' : 'Main'}
    
if __name__ == "__main__":
    # python main.py --host 127.0.0.1 --port 8000
    parser = argparse.ArgumentParser()
    parser.add_argument('--host')
    parser.add_argument('--port')
    args = parser.parse_args()
    api = FastAPIRunner(args)
    api.run()

 

runner.py

class FastAPIRunner(ProjectConfig, DataHandler):
    def __init__(self, args):        
        self.host = args.host
        self.port = args.port
        DataHandler.__init__(self)

    def run(self):
        api_info_data = {
            'api_info': {
                'host': self.host,
                'port' : self.port,
            }
        }
        # API config data type 체크 
        api_info_data = self.check_type(APIConfig, api_info_data)
        uvicorn.run(f"{api_info_data.api_name}", host=api_info_data.api_info.host, port=api_info_data.api_info.port, reload=True)

 

300x250

main.py는 FastAPI 객체를 생성하고 include_router를 통해 위에서 설정한 router들을 셋팅해줍니다. 

그리고 runner.py에 있는 FastAPIRunner를 실행하고 그 결과 FastAPI 가 구동됩니다. runner.py에서는 들어온 정보 host와 port가 적절하게 들어왔는지 점검합니다. 그리고 이상 없으면 uvicorn을 활용해 FastAPI를 실행합니다.

 

 

4. 실행 결과

실행 결과를 살펴보겠습니다. 본 코드를 실행하는 방법은 아래 명령어를 실행하면 됩니다. 

 

python main.py --host 127.0.0.1 --port 8000

이렇게 실행하면 FastAPI가 실행됩니다. 

그러면 http://localhost:8000/docs 에 들어가서 API 구성을 확인해볼까요?

 

이렇게 FastAPI를 구동시키면 ncf와 nfm 두 개의 router가 잘 연결되어 나오는 것을 확인할 수 있습니다.

또한, 아래와 같이 데이터를 날려볼 수도 있습니다.

실제 Post를 날려보면 어떨까요? Python의 requests를 이용해서 해당 API에 post 데이터 통신을 날려보겠습니다.

Python requests 코드는 아래와 같습니다.

 

example/fastapi/req2.py

import requests
import random

headers = {
    "Content-type": "application/json",
    "accept": "application/json"
}


url = "http://localhost:8000/ncf/predict"
params={
    'user_id': 201,
    'movie_id' : 100,
    'gender' : 0,
    'age' : 5,
    'occupation' : 8,
    'genre' : 4
}

res = requests.post(url, json=params, headers=headers)

print("status : ", res.status_code)
print(res.json())

통신은 간단합니다. 통신하려하는 url과 데이터를 넘겨주면 됩니다. 그 결과는 다음과 같습니다.

잘 동작되는 것을 확인할 수 있습니다!


마무리

본 포스팅은 Python FastAPI와 Pytorch 기반의 딥러닝(deep learning) 추천 시스템(recommender system)모델을 결합해 API로 배포하는 간단한 MLOps 예제를 살펴보았습니다.

전체 코드는 포스팅 개요에 올려둔 제 github에 올려두었으니 참고바랍니다.

긴 글 읽어주셔서 감사합니다.

반응형
그리드형