본문 바로가기
파이썬

[Python] HuggingFace로 Llama모델 학습(파인튜닝) 시키기 [05]

by dev_drive 2025. 6. 7.
반응형

 

[Python] HuggingFace로 오픈소스 AI모델 구동해보기 [04]

[Python] GPU로 AI 모델을 구동하기 위한 PyTorch + CUDA + cuDNN 설정 방법 [03][Python] LangChain으로 Llama3 언어 모델 구동해보기 [02][Python] 메타의 대규모 AI 언어모델 Llama3 설치 및 사용방법 [01]메타에서 AI기

dev-drive.tistory.com

 
 
이번엔 허깅페이스에 올라와 있는 오픈소스 모델을 학습시키는 과정을 진행해보겠습니다. 
 

 
 

우선 허깅페이스 허브가 설치되어 있지 않다면 설치를 진행해주세요.
pip install huggingface_hub
 

그 다음 로그인을 진행합니다.
huggingface-cli login
 
 
허깅페이스 계정이 없다면 만들어주시고 아래 링크에 접속해서 토큰을 생성합니다.
https://huggingface.co/settings/tokens

 

 

토큰 생성 후 복사하여 터미널에 붙여넣습니다. 
(우클릭으로 붙여 넣을 수 있고 토큰이 보이지 않아도 엔터를 누르면 됩니다)
 
 
 
가볍게 테스트해보기 위해 llama3.2의 제일 작은 모델(1B)을 사용하겠습니다.
아래 링크에 접속하여 약관에 동의하고 설치를 진행합니다.
(요청에 대한 응답이 와야하기 때문에 바로 모델을 사용하지 못할 수 있습니다)

meta-llama/Llama-3.2-1B-Instruct · Hugging Face

Model Information The Llama 3.2 collection of multilingual large language models (LLMs) is a collection of pretrained and instruction-tuned generative models in 1B and 3B sizes (text in/text out). The Llama 3.2 instruction-tuned text only models are optimi

huggingface.co

 
pip install transformers
pip install accelerate
pip install llama-stack 
위 명령어로 필요한 라이브러리들을 설치해주시고 이미 있는 경우 -U를 붙여서 업그레이드 해줍니다.
 
 
 

import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, TextStreamer

model_id = "meta-llama/Llama-3.2-1B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token 

# 모델 로드 (GPU 환경)
model = AutoModelForCausalLM.from_pretrained(
    model_id,
    torch_dtype=torch.float16,  
    device_map="auto"  
)

messages = [
    {"role": "user", "content": "Who are you?"}
]

prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")  # 입력을 GPU로 이동
streamer = TextStreamer(tokenizer, skip_special_tokens=True)

# 응답 생성 (스트리밍)
with torch.no_grad():  
    outputs = model.generate(
        **inputs,
        max_new_tokens=500,
        do_sample=True,  		
        temperature=0.7,  		
        top_p=0.9,  
        pad_token_id=tokenizer.pad_token_id,  
        streamer=streamer 
    )

토크나이저에 모델을 지정해서 실행하면 모델 다운로드도 같이 진행됩니다.
 
만약 모델 다운로드에서 오류가 발생하는 경우 아래 코드로 수동으로 다운로드 해보세요.

from huggingface_hub import snapshot_download

snapshot_download(repo_id="meta-llama/Llama-3.2-1B-Instruct", repo_type="model")

 
 

모델 다운로드가 끝난 후 Who are you? 라는 질문에 대한 결과이며
많은 학습이 된듯 해당 질문에는 거의 같은 대답을 내놓습니다.
 
 

하지만 질문을 한글로 했을 때는 답변 언어가 계속 달라지거나 이상한 답변을 내놓습니다.

 
 

초창기 ChatGPT도 한글을 잘 이해하지 못해서 영어로 번역해서 질문하고 했었는데
요즘 LLM은 대부분 다국어 학습을 하긴 하지만 별다른 튜닝이 없으면 만족스러운 답변을 얻기 어렵습니다.
 
프롬프트 엔지니어링이나 시스템 프롬프트로 해결할 수도 있지만
모델 자체가 한글로 질문하면 한글로 답변하도록 파인튜닝을 진행해보겠습니다. 
핵심은 "입력 언어 > 동일 언어의 출력" 패턴으로 학습시키는 것입니다.
 
 
 

1. 학습 시킬 JSONL 형식의 데이터 준비

{"messages": [{"role": "user", "content": "혹시 한글은 누가 만들었어??"}, {"role": "assistant", "content": "세종대왕이 만들었어요."}]}
{"messages": [{"role": "user", "content": "프로그래밍 배우고 싶은데 뭐부터 시작할까? 알려줄래?"}, {"role": "assistant", "content": "Python이 초보자에게 가장 좋습니다."}]}
{"messages": [{"role": "user", "content": "혹시 한국의 전통 음식 알려줘?"}, {"role": "assistant", "content": "김치, 비빔밥, 불고기 등이 있어요."}]}
{"messages": [{"role": "user", "content": "오늘 날씨 어때? 궁금해."}, {"role": "assistant", "content": "저는 실시간 정보를 알 수 없지만, 날씨 앱을 확인해 보세요!"}]}
{"messages": [{"role": "user", "content": "우주는 얼마나 커? 알려줄래?"}, {"role": "assistant", "content": "우주의 크기는 측정할 수 없을 정도로 큽니다."}]}
{"messages": [{"role": "user", "content": "사과는 영어로 뭐야? 알려줄래?"}, {"role": "assistant", "content": "'Apple'이라고 해요."}]}

JSONL(JSON Line)형식의 파일 QA List 입니다. (데이터의 일부이며 실제 데이터는 1000라인을 준비했습니다)
유저가 어떤 형식으로 질문했을 때 LLM이 이런 형식으로 답변했으면 좋겠다고 학습시키기 위한 데이터입니다.
직접 작성해도 되겠지만 데이터가 많을수록 학습이 잘되기 때문에 힘들게 직접 만들 필요는 없고
GPT같은 상용 AI에게 형식을 지정하고 JSONL파일로 만들어달라고 하면 금방 만들어줍니다.
 
 
 

2. JSONL 데이터로 프롬프트 생성하고 토큰화하여 학습 레이블 생성

import torch
from datasets import load_dataset
from transformers import (
    AutoTokenizer, AutoModelForCausalLM,
    TrainingArguments, Trainer,
    DataCollatorForLanguageModeling
)
from peft import get_peft_model, LoraConfig, prepare_model_for_kbit_training

# 1. 모델 및 토크나이저 로드
model_id = "meta-llama/Llama-3.2-1B-Instruct"
tokenizer = AutoTokenizer.from_pretrained(model_id)
tokenizer.pad_token = tokenizer.eos_token  # padding token 설정

model = AutoModelForCausalLM.from_pretrained(
    model_id,
    torch_dtype=torch.float16,
    device_map="auto"
)

# 2. PEFT: LoRA 적용
peft_config = LoraConfig(
    r=8,
    lora_alpha=16,
    lora_dropout=0.1,
    bias="none",
    task_type="CAUSAL_LM"
)
model = prepare_model_for_kbit_training(model)
model = get_peft_model(model, peft_config)

# 3. 데이터셋 로딩 (JSONL)
dataset = load_dataset("json", data_files="korean_qa_1000.jsonl", split="train")

# 4. 채팅 템플릿 적용 및 토크나이징
def tokenize(example):
    prompt = tokenizer.apply_chat_template(example["messages"], tokenize=False, add_generation_prompt=False)
    encoding = tokenizer(prompt, truncation=True, padding="max_length", max_length=512)
    
    # 라벨 생성 및 마스킹
    labels = encoding["input_ids"][:]
    tokens = tokenizer.convert_ids_to_tokens(encoding["input_ids"])
    assistant_found = False
    for i, tok in enumerate(tokens):
        if "<|start_header_id|>assistant" in tok:
            assistant_found = True
        if not assistant_found:
            labels[i] = -100		# user 입력과 시스템 텍스트는 loss 계산에서 제외 (-100은 무시됨)
    encoding["labels"] = labels
    return encoding

tokenized_dataset = dataset.map(tokenize, remove_columns=dataset.column_names)

# 5. 학습 하이퍼파라미터 설정
training_args = TrainingArguments(
    output_dir="./llama3_korean_qa_finetuned",
    per_device_train_batch_size=4,
    gradient_accumulation_steps=2,
    num_train_epochs=3,
    logging_steps=10,
    save_strategy="epoch",
    save_total_limit=2,
    fp16=True,
    report_to="none"
)

# 6. 트레이너 구성
trainer = Trainer(
    model=model,
    args=training_args,
    train_dataset=tokenized_dataset,
    tokenizer=tokenizer,
    data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)
)

# 7. 학습 시작
trainer.train()

 

JSONL 데이터를 전처리하여 모델 학습 또는 추론에 사용할 수 있는 프롬프트를 생성, 적용하고
사용자 입력 부분을 손실 계산에서 제외하도록 학습 레이블을 생성하는 전처리 과정입니다.
 
학습 시간은 데이터 양, GPU에 따라 몇 분 ~ 수 시간이 소요될 수 있습니다.
 
 
 

PEFT, LoRA란?

 
PEFT(Param-Efficient Fine-Tuning)와 LoRA(Low-Rank Adaptation)는
대규모 언어 모델을 효율적으로 미세조정(fine-tuning)하기 위한 방법입니다.
 
 
 

학습 레이블이란? 

 
LLM은 입력된 텍스트 시퀀스의 다음 토큰을 예측하도록 학습하며,
다음에 올 확률이 높은 텍스트를 예측하는 방식으로 답변을 진행하는데,
학습 레이블이란 모델이 학습하는 동안 예측해야 할 정답 데이터를 의미합니다. 

 

손실 제외란?

 

"사용자 입력 부분은 손실 계산에서 제외"한다는 것은 모델이 사용자 입력을 예측 대상으로 삼지 않고,
오직 모델이 생성한 출력(응답)만 학습하도록 설계하는 것을 의미합니다.
 
 
 

학습을 진행하는 동안 학습 결과에 대한 내용이 계속 나타나며,
loss는 학습 오차, grad_norm은 파라미터가 얼마나 빠르게 바뀌는지 나타내는 지표, 
learning_rate는 학습률, epoch는 전체 데이터 반복 횟수를 의미합니다.
 
 
 

3. 학습이 끝난 후 답변 확인

from transformers import TextStreamer

messages = [{"role": "user", "content": "사과는 영어로 뭐야?"}]
prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
inputs = tokenizer(prompt, return_tensors="pt").to("cuda")

streamer = TextStreamer(tokenizer, skip_special_tokens=True)
with torch.no_grad():
    model.generate(
        **inputs,
        max_new_tokens=200,
        do_sample=True,
        temperature=0.7,
        top_p=0.7,
        streamer=streamer
    )

저는 일부 중복된 1000개의 QA데이터로 학습을 진행했는데
기준이 되는 모델이 1B의 비교적 작은 모델이기도 하고
제대로 파인튜닝하기 위해서는 훨씬 더 많은 데이터가 필요합니다. 
 
(학습량이 적어서 하이퍼파라미터 일부를 조정했습니다)

 
학습 후 답변 내용들인데 만족할만한 결과는 아니지만 처음 모델보단 나아진 것 같습니다.
 
간단한 예제를 위해 한글 질문 > 한글 답변 위주로 간단하게 파인튜닝 해봤는데
원하는 목적에 따라 답변 어조를 바꾼다던지, 특정 문서를 학습시켜서 대답을 하게 만든다던지
하는 등의 파인튜닝을 진행할 수 있을 것 같습니다.
 
 

반응형

댓글