티스토리 뷰

Deep-learning

[LLM] Fine-Tuning

Sims. 2025. 2. 6. 20:00
728x90

LLM을 하다보면 '모델명-it/chat'과 같은 형식으로 허깅페이스에서 제공하는 모델들을 볼 수 있다.

이러한 모델은 chatting 형식으로 fine-tuning하여 실제 모델과 이야기를 주고받는 형식으로 답변을 생성해준다.

 

이번에는 튜닝의 방식과 대표적인 데이터셋, 튜닝 방법에 대해 정리를 해보고자 한다.

 

 

 

SFT - Supervised Fine-Tuning ( 정답이 존재하는 상태에서 진행 - next token prediction )

SFT는 두가지로 나뉜다.

 

1) Full Fine-Tuning

모델 전체의 파라미터를 수정하여 학습시킨다. LLM은 대체로 파라미터가 상당히 많으므로 상당한 GPU 자원이 필요한 단점이 있다.

 

2) Parameter-Efficient Fine-Tuning(PEFT)

모델의 '특정부분'만 파라미터를 업데이트 하는 방식. FFT보다 상대적으로 적은 GPU 자원이 필요함.

 

특히 PEFT 방식중에서는 LoRA 방식을 많이 사용함. 

 

LoRA 

독립된 연산을 통해 LLM의 일부 파라미터와 결합한 형태로 vector값을 수정함.

LLM은 Freeze한 상태라, 업데이트 되지 않으며, 독립적으로 연산되는 레이어만 업데이트를 진행하여 최소한의 파라미터로 LLM 모델을 튜닝할 수 있음. 

한마디로 '어댑터'를 하나 만들어 적은 파라미터를 학습하는 것.

 

 

두가지 방식으로 어댑터를 모델에 추가할 수 있다.

 

1) model.add_adapter(peft_config)

2) get_peft_model(model, peft_config)

 

두가지 방법이 있어서 어떤 차이점이 있나 확인해보니, 둘다 큰 차이점은 없다.

Trainable parameters도 동일하며, 모델 내에서 LoRA가 적용되는 것 또한 동일하였다.

 

한가지 차이점이라면, get_peft_model은 기본 모델을 wrapper로 한번 더 쌓아 model 구조를 출력해보면 PeftModelForCausalLM 이라는걸로 감싸져 있는 것을 볼 수 있는 정도였다. 내부적으로는 큰 차이가 없어 동일하다고 보면 된다. ( get_peft_model로 진행하면 차제적으로 구성해놓은 추가적인 함수들을 사용할 수 있다. ) 

get_peft_model vs .add_adapter

 

3. 데이터셋

it / chat 형식으로 모델을 튜닝하려면, Q&A 데이터셋처럼 만들어서 최종적으론 chat template 형식으로 만들어 학습을 진행한다.

 

손쉽게 사용해 볼 수 있는 데이터셋 예제를 한번 보자.

이처럼 Q & A 형태의 데이터셋을 이용하여 학습을 진행하기 위해서는 하나의 문장으로 만들어 주는 과정이 필요하다.

기존 Dataset은 위와같은 feature를 가지고 있고 이중 instruction을 user, output을 model이 답변하는 형태로 진행을 해보려고 한다.

이미 it 형식으로 파인튜닝 된 모델을 사용할 예정이므로 기존의 chat template를 이용해서 만들어보도록 하겠다.

 

이처럼 채팅 형식을 만들어 기존에 있는 dataset에 'text'에다가 새롭게 추가하였다.

모델에 학습을 위해서는, 모델이 읽을 수 있도록 숫자로 변환하는 Tokenize 과정이 한번 더 필요하다.

이러한 과정을 거치면, input_ids, attention_mask가 생기게 된다.

 

여기서 모델에 필요한 정보는 input_ids가 필요하다. !!반드시!! 

attention_mask의 경우는, Batch 처리를 하려면 Batch로 들어온 텍스트의 길이를 맞춰주기 위해 padding을 넣게되는데, 이렇게되면 의미없는 텍스트가 추가되는 것이라, 어디까지가 실제로 사용되야 할 텍스트인지 알아야 하는데, 이게 attention_mask를 통해 알 수 있다. ( 실제로, Padding을 넣지않고 학습에서 batch 진행해도 상관없음 - SFTTrainer)

 

 

결론

학습을 위해서는 input_ids ( token화된 결과물)이 필요하다!

 

import torch
import transformers
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig
from peft import prepare_model_for_kbit_training, LoraConfig, get_peft_model
from datasets import load_dataset

from datasets import load_dataset
data = load_dataset("beomi/KoAlpaca-v1.1a")
data["train"][0]

model_id = "google/gemma-2-2b-it"
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_use_double_quant=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=bnb_config, device_map="auto")

def preprocessing(x):
     
    return tokenizer.apply_chat_template([
        {"role" : "user" , "content": x['instruction']},
        {"role" : "model" , "content": x["output"]}
    ], tokenize = False)
    
data = data.map(
    lambda x: {'text': preprocessing(x) }
)
data = data.map(lambda samples: tokenizer(samples["text"]), batched=True)
# make model
config = LoraConfig(
    r=8,
    lora_alpha=32,
    lora_dropout=0.05,
    bias="none",
    task_type="CAUSAL_LM"
)

model = get_peft_model(model, config)
print(model.print_trainable_parameters())

from transformers import TrainingArguments , Trainer
from trl import SFTTrainer
# needed for gpt-neo-x tokenizer
tokenizer.pad_token = tokenizer.eos_token

training_args = TrainingArguments(
    output_dir="./keywords_gemma_results",
    # num_train_epochs=1, # 1epoch에 250step정도 진행함 
    # num_train_epochs가 기존에 알고있던 epoch 수
    num_train_epochs = 1,
    # max_steps를 지정하면, 배치사이즈에 상관없이 step으로 하기때문에 시간이 늘어남.
    # max_steps=800,
    per_device_train_batch_size=1,
    per_device_eval_batch_size=1,
    warmup_steps=0,
    weight_decay=0.01,
    learning_rate=2e-4,
    logging_dir="./logs",
    logging_steps=100,
    )


trainer = SFTTrainer(
    model=model,
    train_dataset=data["train"],

    args=training_args,
    peft_config=config,
    # formatting_func=lambda x: x['input_ids']
)

trainer.train()
반응형

'Deep-learning' 카테고리의 다른 글

[LLM] Tokenizer 기본  (0) 2025.02.01
[LLM] huggingface LLM 기본 내용 ( 양자화 )  (0) 2025.01.30
YOLOv11 이해하기 (4) - Loss  (0) 2024.11.19
YOLOv11 이해하기 (3) - Head  (0) 2024.11.18
What is Attention?(어텐션 이란?)  (0) 2024.11.17
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
글 보관함