Fine-tuning LLM (5) fine-tuning

2025. 6. 11. 22:59·IT, Digital

썸네일

서론

지난 포스팅에서는 데이터를 curating하고 저장하는데 까지 진행했다. 이번 포스팅에서는 저장한 데이터를 가져와서, openai 모델을 파인튜닝하는걸 진행하도록 하겠다. 드디어 파인튜닝 시리즈의 마지막 단계가 온 것이다. 만약에 이전까지의 과정을 못봤다면 지난 포스팅을 꼭 보길 바란다. 여기서부터 시작할수가 없음. 지난 포스팅은 아래 링크부터 시작해서 링크의 마지막 숫자를 2,3,4로 바꾸면 됨.

 

Fine-tuning LLM (1) Data Curation과 데이터 수집 및 로드

서론이번 포스팅에서는 Fine-tuning을 위한 과정에 대해서 서술하고자 한다. 모델 성능 개선 방법에는 크게 세가지가 있다. 1. Prompting 2. RAG 3. Fine-tuning. 이 중 파인튜닝은 모델 자체를 학습시키는 것

quiseol.com


데이터 로딩

우선 지난번에 저장했던 pkl파일을 로드하고, fine_tuning을 위한 train data를 500개, validation data를 50개로 나눈다. OpenAI 피셜상 llm을 학습할 때는 50~100개정도의 데이터를 추천한다고 한다. 왜냐하면 너무 데이터가 많으면 기존 모델에 영향을 주고 데이터가 너무 적으면 학습이 안되기 때문이다. 그래서 추천하는게 50~100개임. 그리고 OpenAI에서는 fine-tuning할 때 JSONL 확장자를 원하기 때문에 그렇게 바꿔줘야된다.

with open('train_set.pkl', 'rb') as file:
    train = pickle.load(file)

with open('test_set.pkl', 'rb') as file:
    test = pickle.load(file)

fine_tune_train = train[:200]
fine_tune_validation = train[200:220]

포멧을 JSONL로 바꾸기

JSONL로 확장자를 바꾸기 전에 openai 모델이 학습하기 좋게 prompt를 다시 만들어야 된다. 그동안 작성했던 프롬프트를 보면 nearest dollar를 알려주는 것을 요구했는데, 이런 구문을 지워도 된다. 트레이닝을 시키려면 모델이 가장 이해하기 쉽게 만들어야 되기 때문이다. 그 다음 JSONL 파일로 변환하는 과정을 가진다. 각 row는 {"messages" : [{"role": "system", "content": "You estimate prices..."}]}와 같은 형태를 갖고 있다. 그리고 현재 messages_for를 통한 형식은 [{"role": "system", "content": "You estimate prices..."}] 이런 형태를 띄고 있다. openai가 요구하는 jsonl형식으로 변형하기 위해서는 messages_for로 받은 프롬프트 앞단에는 {"messages": 를 넣고 뒷단에는 }\n을 넣어야 된다. 그렇게 만들어 주는 함수가 아래에 있는 make_jsonl함수다.

def messages_for(item):
    system_message = "You estimate prices of items. Reply only with the price, no explanation"
    user_prompt = item.test_prompt().replace(" to the nearest dollar","").replace("\n\nPrice is $","")
    return [
        {"role": "system", "content": system_message},
        {"role": "user", "content": user_prompt},
        {"role": "assistant", "content": f"Price is ${item.price:.2f}"}]
def make_jsonl(items):
    result = ""
    for item in items:
        messages = messages_for(item)
        messages_str = json.dumps(messages)
        result += '{"messages": ' + messages_str +'}\n'
    return result.strip()

아직까지 jsonl포멧으로 만든게 아니기 때문에 이어서 JSONL 포멧으로 바꾸는 파이썬 함수를 적겠다. 바로 위에서 이용한 jsonl 함수를 이용해서 jsonl 확장자를 작성하는 것이다. write_jsonl(fine_tune_train, "fine_tune_train.jsonl")이라고 적으면 fine_tune_train 데이터를 fine_tune_train.jsonl로 작성한다.

def write_jsonl(items, filename):
    with open(filename, "w") as f:
        jsonl = make_jsonl(items)
        f.write(jsonl)

저장한 JSONL 파일 로드

with open("fine_tune_train.jsonl", "rb") as f:
    train_file = openai.files.create(file=f, purpose="fine-tune")
with open("fine_tune_validation.jsonl", "rb") as f:
    validation_file = openai.files.create(file=f, purpose="fine-tune")

이제 위 방법으로 저장한 JSONL 확장자 파일을 불러와서 OpenAI모델을 훈련하도록 하겠다. 위 코드를 통해 파일을 읽은 다음, 해당 파일을 아래 코드에다가 넣는다. 아래 코드를 실행하면 바로 학습이 시작된다. Hyperparameter같은 경우는 이 문서를 참고하길 바란다. 저 문서를 보면 batch size 관련 얘기는 하다가 말았던데, 뭐... 읽으면 하이퍼파라미터 관련 지식을 얻을 순 있다. 아무튼 hyper parameter는 필수가 아닌 옵션이다. data가 500정도면 추천사항(100개)보다 훨씬 많기 때문에 1 epoch을 추천한다고 한다.

openai.fine_tuning.jobs.create(
    training_file=train_file.id,
    validation_file=validation_file.id,
    model="gpt-4o-mini-2024-07-18",
    seed=42,
    hyperparameters={"n_epochs": 1, 
                     "learning_rate_multiplier": "auto",
                     "batch_size": "auto"
                    },
    suffix="pricer"
)

다시한번 말하지만 이걸 선언하는 순간 학습이 진행됨 이건 걍 값을 셋팅하는게 아니라 셋팅과 동시에 학습이 시작되는 것임 그말인 즉슨 이거하면 돈나가기 시작하는데.. 의외로 꽤 나가니까 조심하길 바란다.


vaildation data 여부 차이

사실 validation data는 있어도 되고 없어도 된다. 그런데 validation data는 있으면 과적합 여부를 알 수 있다는 장점이 잇다. 그래서 추천하는 추세지만 결론적으로는 있어도 되고 없어도 된다. 해당 내용에 대해선 아래에 표로 정리하겠다.

  Validation data (O) Validation data (X) (train loss만 존재)
overfitting 판단 가능 어려움
모니터링 train/validation loss 비교 가능 train loss만 나옴
추천 여부 추천 작은 실험에는 생략 가능

학습 모니터링

학습중일때 얼만큼 했는지, loss는 어떤지에 대한 모니터링은 다음 코드를 통해 확인 가능하다. limit 파라미터 숫자를 변경하면 output에 그만큼 정보가 더 나온다는건 직관적으로 알 수 있을 것이다. 이건 업데이트할때마다 진행이 되고, 혹 전체 학습 개수가 1000이면 선언할때마다 현재 1000개중 몇개째 학습중인지 나온다. 현재 20번째 내용에 대해 학습중이면 20번째부터 11번째까지 아웃풋에 나온다는 것임. 지금 진행사항에 대해 딱히 궁금하지 않다면 아래 코드는 꼭 선언 안 해도 된다. 왜냐 OpenAI가 학습이 완료되면 칼같이 메일을 주기 때문이다.

openai.fine_tuning.jobs.list_events(fine_tuning_job_id=job_id, limit=10).data

그래도 어느정도 진행됐는 지에 대한 사항을 알 수 있는 코드다. 답답함을 없애 준다는것이다. 아무튼 학습이 완료되면 메일에 내가 학습한 모델명과 함께 메일이 온다. 근데 꼭 그걸 복사 붙여넣기 안 해도 된다. fine_tuned_model_name = openai.fine_tuning.jobs.retrieve(job_id).fine_tuned_model 이걸 선언하면 모델명이 나오기 때문이다.


학습한 모델 사용하기

놀랍게도 openai.fine_tuning.jobs.create 함수를 진행한 순간 파인튜닝은 끝났다. 어쩌면 결과를 보면서 바꿔줘야된다는 점에서 또 다른 시작일수도 있기에 첫 사이클을 끝냈다고 하는게 맞겠다. 결과를 보려면 위에서 얻은 모델명을 아래 코드에 넣어주고, 이전에 진행했던 Tester.test(gpt_fine_tuned, test)를 돌리면 내가 모은것들을 바탕으로 gpt가 예측한 가격이 나오게 된다.

def gpt_fine_tuned(item):
    response = openai.chat.completions.create(
        model=fine_tuned_model_name,
        messages=messages_for(item),
        seed=42,
        max_tokens=7
    )
    reply = response.choices[0].message.content
    return get_price(reply)

끝으로

파인튜닝에 대한 과정이 끝났다. 개인적으로 하는 프로젝트가 있어서 또 다른 학습을 하려고 하는데 어투나 이런 저런 것들을 학습해야되기에 그 과정에 대해선 나중에 기회가 되면 포스팅하도록 하겠다. 아마 다음 포스팅도 fine-tuning에 대한 것인데(끝날 때 까지 끝난 게 아니다.) 이번에는 open-source llm model을 이용한 fine-tuning을 적으려고한다. 그럼 다음 포스팅에서 봅시다!

'IT, Digital' 카테고리의 다른 글

Fine-tuning LLM (4) Load & Download data  (0) 2025.06.10
Fine-tuning LLM (3) Item Parsing Class  (0) 2025.06.06
Fine-tuning LLM (2) Dataset investigation  (0) 2025.06.05
Fine-tuning LLM (1) Data Curation과 데이터 수집 및 로드  (1) 2025.06.03
[RAG] Chroma와 FAISS 차이, 장단점 간단 정리  (0) 2025.05.28
'IT, Digital' 카테고리의 다른 글
  • Fine-tuning LLM (4) Load & Download data
  • Fine-tuning LLM (3) Item Parsing Class
  • Fine-tuning LLM (2) Dataset investigation
  • Fine-tuning LLM (1) Data Curation과 데이터 수집 및 로드
QUISEOL
QUISEOL
제품 사용기, 프로그래밍 언어 공부 블로그 입니다.
  • QUISEOL
    QUISEOL
    QUISEOL
    • 분류 전체보기 (119)
      • IT, Digital (69)
      • 여행 (12)
      • 쇼핑 (21)
      • 그 외 경험기 (17)
  • 블로그 메뉴

    • 링크

      • insta
    • 공지사항

    • 인기 글

    • 최근 글

    • hELLO· Designed By정상우.v4.10.3
    QUISEOL
    Fine-tuning LLM (5) fine-tuning
    상단으로

    티스토리툴바