OPENAI | 대화형 Fine-tuning

Ruah·2024년 11월 14일
post-thumbnail

이미지출처

예전에 개인 맞춤형 식단표 프로젝트를 진행한 적이 있는데, 그때는 단순 LLM과 RAG로만 구현했기 때문에 모델이 음식에 특화된 파인튜닝이 거쳐진 모델이면 결과가 더 좋았을 것이라는 생각을 한적이 있다.
해서, OpenAI 파인튜닝 기능을 시험해보고자 음식의 칼로리와 영양성분 정보를 정확히 제공하는 모델을 만들기 위해 약 400개의 음식 데이터를 사용해 파인튜닝을 진행하였다.

데이터 준비

데이터 수집과 정제

  • 데이터는 AI허브에서 제공하는 '음식 이미지 및 영양정보 텍스트'를 활용하고자하였다. 전체 파일의 크기는 2TB정도 되었기 때문에 나는 칼로리 정보만 담긴 엑셀 파일로 테스트 해보기로 했다.
  • AI HUB 웹 페이지
  • 엑셀 파일: 각 음식의 영양성분 정보가 담긴 엑셀 파일을 데이터셋으로 사용함.
    주요 컬럼은 음식명, 중량(g), 에너지(kcal), 탄수화물(g), 당류(g), 지방(g), 단백질(g), 칼슘(mg), 나트륨(mg) 등

JSONL 형식으로 변환

  • OpenAI 파인튜닝을 위해 JSONL 형식이 필요
  • 데이터를 messages 구조의 JSONL로 변환하여 대화형식으로 저장
import pandas as pd
import json

excel_file_path = "음식데이터.xlsx"

# 데이터셋 로드
df = pd.read_excel(excel_file_path, engine="openpyxl")

# JSONL 파일로 저장
with open("health_food_data.jsonl", "w") as f:
    for _, row in df.iterrows():
        prompt = f"{row['음 식 명']}의 칼로리와 영양 정보를 알려줘."

        completion = (
            f"중량: {row['중량(g)']}g, 에너지: {row['에너지(kcal)']}kcal, "
            f"탄수화물: {row['탄수화물(g)']}g, 당류: {row['당류(g)']}g, "
            f"지방: {row['지방(g)']}g, 단백질: {row['단백질(g)']}g, "
            f"칼슘: {row['칼슘(mg)']}mg, 나트륨: {row['나트륨(mg)']}mg, "
            f"인: {row['인(mg)']}mg, 칼륨: {row['칼륨(mg)']}mg, "
            f"마그네슘: {row['마그네슘(mg)']}mg, 철: {row['철(mg)']}mg, "
            f"아연: {row['아연(mg)']}mg, 콜레스테롤: {row['콜레스테롤(mg)']}mg, "
            f"트랜스지방: {row['트랜스지방(g)']}g"
        )

        chat_data = {
            "messages": [
                {"role": "user", "content": prompt},
                {"role": "assistant", "content": completion},
            ]
        }
        f.write(json.dumps(chat_data, ensure_ascii=False) + "\n")

파인튜닝 과정

데이터 업로드

  • OpenAI API를 사용해 JSONL 파일을 업로드한 과정

openai-api키 초기화

# init.py

import os
import openai
from dotenv import load_dotenv

load_dotenv()

openai.api_key = os.environ.get("OPENAI_API_KEY")

파일 업로드

  • 파일을 생성하고, id를 프린트해 저장해 놓는다.
# fine-tuning.py

# JSONL로 변환한 데이터 준비
file_path = ("/Users/ruahkim/coding/ai-test/openai-fine-tuning/docs/health_food_data.jsonl")

# 데이터셋 업로드
response = openai.files.create(file=open(file_path, "rb"), purpose="fine-tune")

# 파인튜닝 작업 id 확인
file_id = response.id
print(f"Uploaded file ID: {file_id}")
  • file_id는 이런식으로 생성이 된다.
    file_id = "file-file-qGjAqVzbmSj4eFRD6m4xRxw0"

파인튜닝 작업 생성

# 파인튜닝 작업생성
fine_tune_response = openai.fine_tuning.jobs.create(
    training_file=file_id, model="gpt-3.5-turbo"
)

# 파인튜닝 아이디 확인
fine_tune_id = fine_tune_response.id
print(f"find_tuning_job_id: {fine_tune_id}")
  • 파인튜닝 아이디는 이런식으로 생성이된다.
    fine_tune_id = "ftjob-e6ohdQ1kMpGiIOfObsyyZyif"

파인튜닝 상태 모니터링

# 파인튜닝 상태 모니터링
status = openai.fine_tuning.jobs.retrieve(fine_tuning_job_id=fine_tune_id)
print(f"fine-tune status : {status.status}")
  • 파인튜닝은 데이터가 400개 정도밖에 안되는데도 20~30분 정도 걸린 것같다.
  • 작업이 완료되면 succeeded 라고 뜨기때문에 status.status를 주기적으로 확인한다. 작업 중에는 running이라고 뜬다.
  • openai dashboard만 확인해봐도 파인튜닝하고 잇는 모델의 상태를 볼 수 있다.

파인튜닝 모델 사용

# 파인튜닝 완료 후 모델 사용
if status.status == "succeeded":
    fine_tuned_model = status.fine_tuned_model
    print(f"fine_tuned model : {fine_tuned_model}")

    # 모델을 사용하여 예측 수행
    # 프롬프트 설정
    messages = [
        {
            "role": "user",
            "content": "비빔밥 영양성분이 어떻게 되나요",
        }
    ]
    completion = openai.chat.completions.create(
        model=fine_tuned_model, messages=messages, max_tokens=300, temperature=0.78
    )
    print(f"Model Response : {completion.choices[0].message.content.strip()}")

응답 결과

파인튜닝을 통해 특정 음식의 영양성분 정보를 정확히 제공하는 모델을 간단히 만들어보았다.
기존 ChatGPT 모델도 일반적인 질문에는 답할 수 있지만, 파인튜닝된 모델은 제공된 데이터에 맞춰 더 정확한 정보를 제공한다.
JSONL 변환과 데이터 정제가 조금 번거로웠지만, 한 번 준비해 두면 이후 활용하기 좋고, openai의 단점인 hallusination도 어느정도 잡을 수 있다.
데이터를 건강쪽에 맞추어서 더 보강한다면 각 영양성분에 맞추어 개인 건강에 따라 음식을 추천해줄수 있을 것이라는 생각이 든다.

profile
집요한 주니어 개발자의 호되게 당했던 기록

0개의 댓글