상세 컨텐츠

본문 제목

[CI/CD] LLMOps / MLOps 찍먹해보기

홈서버

by 박집실 2024. 5. 16. 16:14

본문

서론

요새 핫한 분야인 AI 쪽 분야에서 가장 중요한 것 중 하나가 모델을 만든 후에 이를 어떻게 배포하여서 해당 모델을 실 사용자들이 사용할 수 있는 서비스를 가능케 할 것인가가 서비스 개발자가 고려해야할 중요한 이슈들 중 하나라고 생각한다. 이러한 이슈 중에서 이를 고민하고 발전시키기 위해서 이를 전문적으로 가르키는 용어도 따로 나오게 되었는데 바로 LLMOps/MLOps 가 바로 그것이다. Ops에 관한 정의와 내용은 다음 블로그에서 잘 정리하고 있는 것 같아서 공유한다.

https://www.opsnow.com/ops에-대해서/

 

Ops에 대해서! - OpsNow

최근 몇 년간 다양한 리서치를 하면서 가장 자주 접한 단어를 꼽자면 'Cloud', 'As a Service', 'AI/ML', 그리고 'Ops' 등이 있습니다. 이 중에서 오늘은 Ops에 대해서 공유해 드릴까 합니다. 다들 아시겠지

www.opsnow.com

다양한 Ops 분야에서도 당연 요즘 가장 핫한 것은 바로 LLMOps/MLOps 라고 생각한다. 특히 LLMOps는 2018년에 'Attention is All You Need'라는 기념비적인 논문이 나오고 해당 논문에서 소개된 트랜스포머 아키텍쳐를 기반으로 LLM 이 등장했고 그 전까지의 모델들과 비교했을 때 압도적인 성능을 보여주고 있어 BERT 이후로 다양한 모델들이 나오게 되었고 현재에 와서는 오픈AI의 chatgpt와 같이 우리 삶의 일부분이 되었을 정도로 그 영향력은 엄청나다. 오늘은 이 부분을 살짝 맛만 봐보고자 한다.

 

 

본론

 

오늘 LLMOps을 간단하게 찍먹 느낌으로 구현해보기 위해서 먼저 전에 base 모델에 관련 데이터셋으로 파인튜닝하여 구현한 텍스트 분류 모델을 huggigface에 등록하여 이를 다시 불러와서 활용할 예정이다.(허깅페이스에서는 다양한 모델을 오픈소스 형태로 제공하고 클라우드 상에서 다양한 모델을 접근 다운로드하여 활용하는 것이 가능하다,)

 

import os
from fastapi import FastAPI
from transformers import AutoModelForSequenceClassification, AutoTokenizer, pipeline

os.environ["TRANSFORMERS_CACHE"] = "./cache/"
os.environ["HF_HOME"] = "./cache/"

model_id = 'MODEL_ID'
tokenizer = AutoTokenizer.from_pretrained(
model_id
)

model = AutoModelForSequenceClassification.from_pretrained(model_id)
pipe = pipeline("text-classification", model=model,
tokenizer=tokenizer)

# question = ""

# print(pipe(question))

app = FastAPI(
title="Model Server",
version="1.0",
description="A simple API server using huggingface's Runnable interfaces",
)

@app.get("/proc")
async def agent_endpoint(query: str):
response = pipe(query)
return {"message": response}


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


 

먼저 fastapi 와 trasfomers을 활용해서 허깅페이스에 올려둔 모댈을 불러와서 해당 모델과 해당 모델의 목적, 모델, tokenizer 등을 정의해주고 파이프라인을 생성 후에 fastapi을 활용해서 get 요청으로 쿼리 스트링에 목표가 되는 글을 넣어서 모델을 요청할 수 있도록 한 후에  파이프라인을 통과하여 모델의 결과값을 반환해주는 방식으로 개발하였다.(transformers을 활용하기 위해서는 선행적으로 해당 환경에 pytorch 혹은 tensorflow가 설치되어야함)

 

# Dockerfile
FROM --platform=linux/arm64 continuumio/miniconda3:24.3.0-0

WORKDIR /root

SHELL ["/bin/bash", "--login", "-c"]
COPY environment.yml .
RUN conda env create -f environment.yml

RUN conda init bash

ADD model_api.py .
CMD ["conda", "run", "-n", "myenv", "python", "model_api.py"]

Dockerfile

 

라즈베리파이에 배포하기 위한 Dockerfile을 작성해준다. 먼저 위애서 생성 한 .py 파일을 돌리기 위한 환경과 라이브러리를 배포 시에 도커 컨테이너에서 설치하기 위한 이미지를 만들기 위해서 miniconda 환경이 미리 세팅된 이미지를 택하고 배포하기 위한 라즈베리파이가 linux/arm64 기반이기 때문에 해당 환경에 맞게 설정된 이미지를 가져와 준다. 이제 미리 작성된 environment.yml 로 콘다 가상환경을 만들어 준 후에 해당 환경에서 model_api.py을 구동시킬 수 있게 해준다.(해당 환경에서 설정된 명령어가 잘 듣지 않는 경우가 있어서 선행적으로 SHELL ["/bin/bash", "--login", "-c"] 을 추가해주었다.)

 

https://hub.docker.com/r/continuumio/miniconda3/tags

 

Docker

 

hub.docker.com

 

 

# envrionment.yml
 
name: myenv
channels:
- conda-forge
dependencies:
- python=3.12
- fastapi
- transformers
- pytorch

environment.yml

 

윗 도커 파일에서 쓰일 environment.yml 구성은 다음과 같다. 우리가 배포할 .py 은 다양한 라이브러리들을 필요로 하기 땜에 해당 라이브러리들과 필요한 파이썬 등을 설치하기 위한 콘다 환경 구축과 해당 환경에 설치할 model_api.py 구동에 필요한 라이브러리들을 명시해준다.

 

name: Python package

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:

runs-on: ubuntu-latest
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
python-version: ["3.12"]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip

- name: Build Docker & Push
run : |
docker login -u ${{ secrets.DOCKER_USERNAME}} -p ${{ secrets.DOCKER_PASSWORD }}
docker buildx create --name mybuilder --use
docker buildx build --platform linux/arm64/v8 -t ${{ secrets.DOCKER_REPO }}/model-api-app --push .

- name : Docker deploy
uses : appleboy/ssh-action@master
with :
host : ${{ secrets.HOST }}
username : ${{ secrets.USERNAME }}
key : ${{ secrets.SSH_PRIVATE_KEY }}
port : ${{ secrets.SSH_PORT }}
command_timeout: 200m
script: |
cd app
sudo rm concal.log
sudo docker stop $(docker ps -q) && sudo docker rm $(docker ps -a -q)
sudo docker image rm ${{ secrets.DOCKER_REPO }}/model-api-app
sudo docker pull ${{ secrets.DOCKER_REPO }}/model-api-app
sudo docker run -p 50000:8000 -d ${{ secrets.DOCKER_REPO }}/model-api-app
 


이번에도 CI/CD 을 위한 플랫폼으로 github actions 을 활용을 하였다. 배포하기 위한 변수 등을 secrets에 정의해주고 docker hub에 이미지를 생성 후 푸쉬할 때 miniconda 환경을 활용하기 위해서 docker buildx 을 활용하였고 라즈베리파이 환경에 맞는 이미지를 위해 

--platform linux/arm64/v8 설정해주는 것을 잊지 말아야한다.

 

자세한 라즈베리파이에 대한 CI/CD 전에 작성한 해당 글을 참조바란다.

https://learningcodingreal.tistory.com/66

 

 

PostMan에서 날려보았을 때 다음과 같이 잘 오는 것을 확인 할 수 있다.

 

 

 

결론

 

-  transformers 등 대부분의 ML 관련 라이브러리 등은 파이썬 환경에서 많이 제공하는데 이를 위해서는 miniconda 와 같은 환경을 미리 세팅한 이미지 등을 활용하는 것이 중요.

 

- conda 환경 등을 구축하고 도커파일의 From에서 특정 버전을 명시한 이미지를 가져오기 위해서는 docker buildx 을 적절히 활용해야함.

 

- 배포하고자 하는 서버의 프로세서가 무엇을 쓰는 지에 따라 이미지의 --platform을 잘 명시하여 만드는 것이 매우 중요.

 

이 밖에도 LLM과의 채팅 등의 기능을 구현하기 위한 LangChain 와 같은 전문화된 라이브러리가 존재하여서 이를 활용하여 기능의 필요한 파이프라인을 비교적 간단하게 생성하는 것이 가능하다. 다음에는 이러한 라이브러리를 활용하여 더 복잡한 서비스를 생성해볼 예정이다.(텍스트 분류에서는 지원하지 않고 굳이 필요성을 느끼지 않아 활용하지 않았다.) 

관련글 더보기