ML/MLOps

직접 Language Model을 Fine-Tuning 해보고 Container 환경에 API 서버 구축해보기

minturtle 2024. 12. 16. 23:30
반응형

개요

이번에 저는 LM을 Fine-Tuning해보고 API 서버를 구축해보며 Ops의 관점에서 어느 부분이 불편하고 고민해 볼만 하는가 생각해 보는 시간을 가졌습니다. 목표는 다음과 같습니다.

  • Model을 Fine-Tuning
  • Model을 저장하고, FastAPI 구축
  • FastAPI를 Docker Image로 구축하고, 컨테이너로 실행시키기

 

Fine-Tuning하기

먼저 처음으로 모델을 Fine-Tuning 해보았는데요, Dataset 까지 정제하기에는 시간이 너무 오래걸릴 것 같아 전처리 과정은 생략하고, HuggingFace의 Dataset을 가져와서 Pre-Trained Model에 학습 시키기로 결정했습니다. 따라서 사용하는 DataSet에 따라 어떤 서비스를 만들지가 결정되는데요, 저는 아래의 DataSet을 사용하기로 결정했습니다.

https://huggingface.co/datasets/wicho/kor_3i4k

이 DataSet은 총 7개의 Label로 구성되어 사용자의 발화 의도를 예측하는 Dataset으로, 한글로 이루어져 있습니다. 저는 이 DataSet을 활용하기로 하였고, 모델은 BERT 기반의 한글로 Pre-Train된 아래의 모델을 사용하였습니다.


https://huggingface.co/kykim/bert-kor-base


DataSet은 90%의 train 데이터와 10%의 test data로 나누어졌는데, 저는 90%의 train 데이터를 8:2 비율로 나누어 72% train, 18% validation, 10% test data로 활용하였습니다.


그 후 학습을 진행했는데요, 저는 일단 하이퍼 파라미터를 잘 모르기 때문에 일단 가능한 epoch 수만 조절해서 모델을 학습 시켰습니다.

  • epoch 3 : loss률 56%, Train Loss 0.1로 과적합이 되었다고 판단되어 epoch를 2로 줄여보았습니다.
  • epoch 2 : loss률 36%, Train Loss 0.2로 epoch 3보다 정답을 잘 맞춘 것을 확인할 수 있었습니다.

제가 파인 튜닝 했던 jupyter 노트북 파일은 아래의 Github에 업로드 해놓았습니다.


https://github.com/minturtle/fine-tuning-practice

모델 저장 후 API로 만들기

모델을 학습 완료한 후, 정답률이 높았던 epoch 2 모델을 기반으로 API 서버와 Dockerfile을 만들어 실행해 보았습니다.

from transformers import pipeline
from fastapi import FastAPI
from model.classificationDto import ClassficationRequest
import torch

print(torch.cuda.is_available())
print(torch.cuda.device_count())

pipe = pipeline("text-classification", model="./epoch2", device=0)

app = FastAPI()

@app.post("/classification")
def classification(req: ClassficationRequest):
    result = pipe(req.content)

    converted_label = convert_label(result[0]["label"])

    return { 'result' : f'제시한 문장의 의도 분석 결과는 {converted_label}입니다.'}

def convert_label(label_text: str):
    if label_text == "LABEL_0":
        return "단편"
    if label_text == "LABEL_1":
        return "평서문"
    if label_text == "LABEL_2":
        return "의문문"
    if label_text == "LABEL_3":
        return "명령문"
    if label_text == "LABEL_4":
        return "수사적 의문문"
    if label_text == "LABEL_5":
        return "수사적 명령문"
    if label_text == "LABEL_6":
        return "억양에 따라 달라지는 발화"
    return "unknown"
FROM nvidia/cuda:12.6.3-runtime-ubuntu22.04

WORKDIR /app

COPY requirements.txt /app/requirements.txt
COPY model /app/model
COPY app.py /app/main.py
COPY epoch2 /app/epoch2


RUN apt-get update && apt-get install -y \
    wget build-essential zlib1g-dev libssl-dev libncurses-dev libffi-dev \
    libsqlite3-dev libreadline-dev libbz2-dev liblzma-dev

RUN wget https://www.python.org/ftp/python/3.9.13/Python-3.9.13.tgz \
    && tar -xvf Python-3.9.13.tgz \
    && cd Python-3.9.13 \
    && ./configure --enable-optimizations \
    && make -j$(nproc) \
    && make install \
    && ln -s /usr/local/bin/python3.9 /usr/bin/python3



RUN pip3 install --upgrade pip
RUN pip3 install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121

RUN pip3 install -r requirements.txt

EXPOSE 8000

CMD ["uvicorn", "main:app"]

MLOps가 무엇을 할 수 있을까?

이렇게 모델을 간단하게 학습시키고 컨테이너 환경에서 API로 배포해 보았는데요, 이러한 일련의 과정에서 불편했던 점과 Ops가 해결할 수 있는 점이 무엇인가 한번 생각해 보았습니다. 먼저 아래의 점이 불편하거나, 고민해야 하는 점이라고 생각했습니다.

  1. 학습 환경 구축
    • 저는 Colab을 사용해 손쉽게 GPU Jupyter 환경을 구축했지만, On-Premise 환경에서 Jupyter를 실행하기 위해선 어려움이 있을 것이라 생각했습니다.
  2. Data의 저장 및 관리
    • 데이터 전처리는 이번에 해보지 않았지만, 계속해서 학습할 때마다 새로운 데이터를 어디에 어떻게 저장할 것인지, 데이터 엔지니어 분들이 편하게 데이터 전처리를 하기 위해서 어떤 것을 Infra적인 측면에서 도와줄 수 있을지에 대한 생각이 들었습니다.
  1. 모델의 버젼 관리 및 학습 간편화
    • 모델의 하이퍼 파라미터를 수정할 때마다, 또는 데이터가 변경될 때마다 새로 학습해야하는 것에 대한 불편함이 들었는데요, 주기적으로 모델을 자동으로 학습 시키고 하이퍼 파라미터도 수정해 최적의 성능을 내는 모델을 주기적으로 갖고 올 수 있으면 좋겠다라고 생각했습니다.
  2. API 서버 환경 구축
    • API 서버 또한 학습 환경 처럼 GPU 설정을 해줘야 하는데요, 저도 실제로 API 서버를 Docker로 구축하며 GPU가 모델에 물려지지 않아서 많은 어려움을 겪었습니다. 학습된 모델에 따라 요구하는 Python 버젼이나 API 서버가 실행되는 GPU 환경에서 Python과 Python 라이브러리의 버전 관리는 어떻게 더 편하게 할 수 있을까? 라는 고민이 들었습니다.

 

반응형