# pip install datasets==2.20.0 transformers==4.41.2 peft==0.10.0 evaluate==0.4.2 scikit-learn==1.4.2 accelerate -U
Model fine tuning
1. Install
아래 코드를 실행하고 런타임 재시작
이전 chapter에서는 전체적으로 미세조정을 하지 않고 모델과 각 태스크의 대략적인 구조만 학습했다.
아무래도 학습을 하지 않고 바로 predict를 하니 결과가 만족스럽지 않았다.
이번 chapter에서는 미세조정을 해보는 코드를 배워볼 것이다.
2. Encoder Sequence Classification
2-1 Model
import torch
from transformers import AutoTokenizer, AutoModelForSequenceClassification
= "klue/bert-base"
model_name = AutoTokenizer.from_pretrained(model_name)
tokenizer = AutoModelForSequenceClassification.from_pretrained(model_name) model
/root/anaconda3/envs/asdf/lib/python3.9/site-packages/huggingface_hub/file_download.py:797: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
Some weights of BertForSequenceClassification were not initialized from the model checkpoint at klue/bert-base and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
2-2. Dataset
from datasets import load_dataset
= load_dataset("klue", "sts")
dataset
def process_data(batch):
= tokenizer(
result "sentence1"],
batch[=batch["sentence2"],
text_pair=128,
max_length="max_length",
padding=True,
truncation="np",
return_tensors
)"labels"] = [x["binary-label"] for x in batch["labels"]]
result[return result
= dataset.map(
tokenized_dataset
process_data,=True,
batched=dataset["train"].column_names,
remove_columns
)"train"].column_names tokenized_dataset[
['labels', 'input_ids', 'token_type_ids', 'attention_mask']
2-3 Train
from transformers import (
# 모델 학습을 위한 훈련 도구
Trainer, # 학습을 위한 하이퍼파라미터 및 설정을 정의하는 클래스
TrainingArguments, # 콜레이터
default_data_collator, # early stop 함수
EarlyStoppingCallback
)import evaluate
def custom_metrics(pred): # micro f1 score 평가 지표를 로드하는 함수
= evaluate.load("f1")
f1 = pred.label_ids
labels = pred.predictions.argmax(-1)
preds
return f1.compute(predictions=preds, references=labels, average="micro")
= TrainingArguments( # 학습 argument 설정
training_args =64, # 학습할 때 배치 크기
per_device_train_batch_size=64, # 평가할 때 배치 크기
per_device_eval_batch_size=5e-6,
learning_rate=1, # 그래디언트 클리핑 (그래디언트 폭발 방지)
max_grad_norm=10,
num_train_epochs="steps", # 일정 step마다 검증 실행
evaluation_strategy="steps", # 일정 스텝마다 로그 저장
logging_strategy=100, # 100 step마다 로그 출력
logging_steps="data/logs", # 로그 저장 경로 (현재 실행중인 폴더를 기준으로 저장되므로 현재 폴더를 잘 확인해야함)
logging_dir="steps", # 일정 step마다 체크포인트 저장
save_strategy=100, # 100 step마다 체크포인트 저장
save_steps="data/ckpt", # 로그 저장 경로 (현재 실행중인 폴더를 기준으로 저장되므로 현재 폴더를 잘 확인해야함)
output_dir= True, # 학습 종료 후 가장 좋은 모델 로드
load_best_model_at_end ='tensorboard', # tensorboard에 학습 로그 기록
report_to
)
= Trainer(
trainer =model,
model=training_args,
args=tokenized_dataset["train"],
train_dataset=tokenized_dataset["validation"],
eval_dataset=tokenizer,
tokenizer=default_data_collator,
data_collator=custom_metrics,
compute_metrics= [EarlyStoppingCallback(early_stopping_patience=2)] # 2번 연속으로 검증 성능이 향상되지 않으면 학습 중단
callbacks
)
trainer.train()
/root/anaconda3/envs/asdf/lib/python3.9/site-packages/transformers/training_args.py:1474: FutureWarning: `evaluation_strategy` is deprecated and will be removed in version 4.46 of 🤗 Transformers. Use `eval_strategy` instead
warnings.warn(
huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...
To disable this warning, you can either:
- Avoid using `tokenizers` before the fork if possible
- Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)
Using the latest cached version of the module from /root/.cache/huggingface/modules/evaluate_modules/metrics/evaluate-metric--f1/0ca73f6cf92ef5a268320c697f7b940d1030f8471714bffdb6856c641b818974 (last modified on Tue Apr 1 08:33:18 2025) since it couldn't be found locally at evaluate-metric--f1, or remotely on the Hugging Face Hub.
Using the latest cached version of the module from /root/.cache/huggingface/modules/evaluate_modules/metrics/evaluate-metric--f1/0ca73f6cf92ef5a268320c697f7b940d1030f8471714bffdb6856c641b818974 (last modified on Tue Apr 1 08:33:18 2025) since it couldn't be found locally at evaluate-metric--f1, or remotely on the Hugging Face Hub.
Using the latest cached version of the module from /root/.cache/huggingface/modules/evaluate_modules/metrics/evaluate-metric--f1/0ca73f6cf92ef5a268320c697f7b940d1030f8471714bffdb6856c641b818974 (last modified on Tue Apr 1 08:33:18 2025) since it couldn't be found locally at evaluate-metric--f1, or remotely on the Hugging Face Hub.
Using the latest cached version of the module from /root/.cache/huggingface/modules/evaluate_modules/metrics/evaluate-metric--f1/0ca73f6cf92ef5a268320c697f7b940d1030f8471714bffdb6856c641b818974 (last modified on Tue Apr 1 08:33:18 2025) since it couldn't be found locally at evaluate-metric--f1, or remotely on the Hugging Face Hub.
Step | Training Loss | Validation Loss | F1 |
---|---|---|---|
100 | 0.045700 | 0.930365 | 0.786127 |
200 | 0.083700 | 0.718717 | 0.805395 |
300 | 0.071400 | 0.800480 | 0.786127 |
400 | 0.060000 | 0.828407 | 0.799615 |
TrainOutput(global_step=400, training_loss=0.06518504738807679, metrics={'train_runtime': 268.0555, 'train_samples_per_second': 435.283, 'train_steps_per_second': 6.827, 'total_flos': 1678122311086080.0, 'train_loss': 0.06518504738807679, 'epoch': 2.185792349726776})
2-4. Predict
미세조정으로 400 step에서 체크포인트로 저장한 경로에서 모델과 토크나이저를 불러와 검증 게이터 중 10개 샘플을 입력해 간단한 추론을 진행한다.
import torch
from datasets import load_dataset
from transformers import (
AutoTokenizer,
BertForSequenceClassification,
DataCollatorWithPadding
)
# tokenizer, model
= "data/ckpt/checkpoint-400"
model_name = AutoTokenizer.from_pretrained(model_name)
tokenizer = BertForSequenceClassification.from_pretrained(model_name)
model
= DataCollatorWithPadding(tokenizer) # 콜레이터 설정
collator = collator([tokenized_dataset["validation"][i] for i in range(10)])
batch
# inference
with torch.no_grad():
= model(**batch).logits
logits logits
tensor([[-3.8785, 3.4420],
[ 3.2443, -2.9451],
[ 1.9444, -1.9935],
[-3.5267, 3.0033],
[ 0.2386, -0.7375],
[ 3.3531, -2.7962],
[-3.2954, 2.9353],
[ 3.8899, -3.2844],
[ 4.2035, -3.6925],
[ 4.1651, -3.5504]])
2-5. Evaluate
import evaluate
= logits.argmax(dim=1).cpu().numpy()
pred_labels = batch["labels"].numpy()
true_labels
= evaluate.load("f1")
f1 =pred_labels, references=true_labels, average="micro") f1.compute(predictions
Using the latest cached version of the module from /root/.cache/huggingface/modules/evaluate_modules/metrics/evaluate-metric--f1/0ca73f6cf92ef5a268320c697f7b940d1030f8471714bffdb6856c641b818974 (last modified on Tue Apr 1 08:33:18 2025) since it couldn't be found locally at evaluate-metric--f1, or remotely on the Hugging Face Hub.
{'f1': 1.0}
‘klue/bert-base’ 모델을 불러와서 AutoModelForSequenceClassification
헤드를 붙혔다.
그 후 학습을 진행하였고 학습된 모델을 불러와서 예측을 하였으므로 미세조정(fine tuning)이다.
3. Decoder Causal LM
3-1. model
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM
= "skt/kogpt2-base-v2"
model_name = AutoTokenizer.from_pretrained(
tokenizer
model_name,="</s>",
bos_token="</s>",
eos_token="<unk>",
unk_token="<pad>",
pad_token="<mask>"
mask_token
)= AutoModelForCausalLM.from_pretrained(model_name) model
3-2. Dataset
from datasets import load_dataset
# 데이터셋 구조화
= {
split_dict "train": "train[:8000]",
"test": "train[8000:10000]",
"unused": "train[10000:]",
}= load_dataset("heegyu/kowikitext", split=split_dict)
dataset del dataset["unused"] # 사용하지 않는 unused 데이터를 삭제
# 토큰화
= dataset.map(
tokenized_dataset lambda batch: tokenizer([f"{ti}\n{te}" for ti, te in zip(batch["title"], batch["text"])]),
=True, # 배치로 묶어서 처리
batched=2, # 2개 프로세스 병렬 실행
num_proc=dataset["train"].column_names, # 기존의 'title','text' 컬럼을 삭제 후 토큰화 결과만 남김
remove_columns
)
# 최대 길이로 그룹화
= 512
max_length def group_texts(batched_sample):
= {k: v[0] for k, v in batched_sample.items()} # 데이터셋에서 각 key의 첫 번째 값만 가져오기.
sample
if sample["input_ids"][-1] != tokenizer.eos_token_id: # 마지막 토큰이 <eos>가 아니라면 <eos> 추가.
for k in sample.keys():
sample[k].append(if k == "input_ids" else sample[k][-1]
tokenizer.eos_token_id # sample['input_ids']라면 <eos> 토큰을 추가. / sample['input_ids']가 아니라면 기존 값 유지.(sample[k][-1])
)
= {
result + max_length] for i in range(0, len(v), max_length)] # 문장이 길면 여러 개의 샘플로 분할
k: [v[i: i for k, v in sample.items()
}return result
= tokenized_dataset.map(
grouped_dataset
group_texts,=True,
batched=1,
batch_size=2,
num_proc
)"train"].column_names grouped_dataset[
['input_ids', 'attention_mask']
3-3. Fine tuning
from transformers import Trainer, TrainingArguments, DataCollatorForLanguageModeling
= TrainingArguments(
training_args =4,
per_device_train_batch_size=4,
per_device_eval_batch_size=5e-6,
learning_rate=1,
max_grad_norm=3,
num_train_epochs="steps",
evaluation_strategy="steps",
logging_strategy=500,
logging_steps="data/logs",
logging_dir="data/ckpt",
output_dir="tensorboard",
report_to
)
= DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)
collator
= Trainer(
trainer =model,
model=training_args,
args=grouped_dataset["train"],
train_dataset=grouped_dataset["test"],
eval_dataset=tokenizer,
tokenizer=collator,
data_collator
)
trainer.train()"data/model") trainer.save_model(
/root/anaconda3/envs/asdf/lib/python3.9/site-packages/transformers/training_args.py:1474: FutureWarning: `evaluation_strategy` is deprecated and will be removed in version 4.46 of 🤗 Transformers. Use `eval_strategy` instead
warnings.warn(
Step | Training Loss | Validation Loss |
---|---|---|
500 | 4.284300 | 4.471501 |
1000 | 4.219000 | 3.416660 |
1500 | 4.206900 | 2.995688 |
2000 | 4.185800 | 2.903164 |
2500 | 4.168300 | 2.876508 |
3000 | 4.151000 | 2.805254 |
3500 | 4.151500 | 2.801232 |
4000 | 4.160500 | 2.774476 |
4500 | 4.133300 | 2.774085 |
5000 | 4.085200 | 2.745091 |
5500 | 4.062300 | 2.747127 |
6000 | 4.066100 | 2.767898 |
6500 | 4.037000 | 2.726495 |
7000 | 4.048100 | 2.735216 |
7500 | 4.060500 | 2.741515 |
8000 | 4.031300 | 2.703305 |
8500 | 4.050100 | 2.725095 |
9000 | 4.062700 | 2.712202 |
9500 | 4.020300 | 2.688988 |
10000 | 3.996800 | 2.699451 |
10500 | 4.008100 | 2.701477 |
11000 | 3.989600 | 2.698209 |
11500 | 3.984000 | 2.686813 |
12000 | 4.007600 | 2.688273 |
12500 | 3.992500 | 2.678961 |
13000 | 3.982600 | 2.687267 |
13500 | 4.009200 | 2.686797 |
3-4. Predict by before fine tuning model
import torch
from transformers import AutoTokenizer, GPT2LMHeadModel
# 미세조정 이전
= "skt/kogpt2-base-v2"
origin_name = AutoTokenizer.from_pretrained(
origin_tokenizer
origin_name,="</s>",
bos_token="</s>",
eos_token="<unk>",
unk_token="<pad>",
pad_token="<mask>"
mask_token
)= GPT2LMHeadModel.from_pretrained(origin_name)
origin_model
= origin_tokenizer(
inputs1 "우리는 누구나 희망을 가지고",
="pt"
return_tensors
).to(origin_model.device)= origin_model.generate(inputs1.input_ids, max_length=128, repetition_penalty=2.0)
outputs1 = origin_tokenizer.batch_decode(outputs1, skip_special_tokens=True)
result1 print(result1[0])
/root/anaconda3/envs/asdf/lib/python3.9/site-packages/huggingface_hub/file_download.py:797: FutureWarning: `resume_download` is deprecated and will be removed in version 1.0.0. Downloads always resume when possible. If you want to force a new download, use `force_download=True`.
warnings.warn(
우리는 누구나 희망을 가지고 살아갈 수 있는 사회를 만들어야 한다"고 강조했다.
이날 행사에는 박근혜 대통령, 황우여 새누리당 대표 등 여권 지도부와 김무성 전 대표가 참석해 축사를 했다.
김영삼 정부 시절인 지난 2007년 대선 당시 이명박 후보의 당선을 위해 '국민통합21'을 이끌었던 이 후보는 "우리나라에서 가장 큰 문제는 경제"라며 "이명박은 경제를 살리고 서민을 위한 정치를 하겠다고 약속했지만 현실은 그렇지 못했다"며 이같이 말했다.
그는 이어 "나는 지금 대한민국을 걱정하고 있다, 경제가 어렵다면 우리 모두 힘을 모아 위기를 극복해야 한다고 생각한다
3-5. Predict by after fine tuning model
# 미세조정 이후
= "data/model"
finetuned_name = AutoTokenizer.from_pretrained(finetuned_name)
finetuned_tokenizer = GPT2LMHeadModel.from_pretrained(finetuned_name)
finetuned_model
= finetuned_tokenizer(
inputs2 "우리는 누구나 희망을 가지고",
="pt").to(finetuned_model.device)
return_tensors= finetuned_model.generate(
outputs2
inputs2.input_ids,=128,
max_length=2.0
repetition_penalty
)= finetuned_tokenizer.batch_decode(outputs2, skip_special_tokens=True)
result2 print(result2[0])
우리는 누구나 희망을 가지고 살아갈 수 있는 사회를 만들자는 것이었다. 그러나 그 희망은 결국 좌절되고 말았다. 이 절망적인 상황 속에서, 사람들은 자신들의 삶을 포기하고 다른 사람들의 삶으로 돌아가고자 하였다. 이러한 상황에서 그들은 자신의 삶에 대한 책임을 회피하고 자기 자신을 희생하는 선택을 하게 되었다. 그리하여 많은 사람들이 자살을 선택하게 되었고, 이는 곧 자살로 이어지게 되었다.
이러한 상황에 대해서, 현대 사회는 개인의 존엄성을 존중하지 않는 사회라고 비판하였다. 또한 개인들이 스스로 목숨을 끊는 것을 막기 위해 노력하기도 했다. 하지만 그러한 극단적인 행동들은 오히려 사람들을 죽음으로 내몰게 만드는 결과를 가져왔다. 예를 들어, 한 개인이 사망할 경우 가족들의 동의 없이 강제로 죽음을
4. Encoder-Decoder Conditional Generation
어떤 문장이 주어졌을 때, 입력한 문장을 기반으로 새로운 문장을 작성하는 조건부 생성 태스크로 미세조정한다.
번역 Task
4-1. model
from transformers import AutoTokenizer, AutoModelForSeq2SeqLM
= 'hyunwoongko/kobart'
model_name = AutoTokenizer.from_pretrained(model_name)
tokenizer = AutoModelForSeq2SeqLM.from_pretrained(model_name) model
You passed along `num_labels=3` with an incompatible id to label map: {'0': 'NEGATIVE', '1': 'POSITIVE'}. The number of labels wil be overwritten to 2.
4-2. Dataset
번역 모델에서는 text_target이라는 인자를 사용하고 한국어를 입력했을 때 영어를 출력으로 생성하는 목적으로 text_target = batch['english']
를 사용한다
from datasets import load_dataset
= load_dataset('msarmi9/korean-english-multitarget-ted-talks-task')
dataset = dataset.map(
tokenized_dataset lambda batch: (tokenizer(batch['korean'], text_target = batch['english'], max_length=128, truncation = True)
= True, batch_size = 1000, num_proc = 2, remove_columns = dataset['train'].column_names)
),batched
'train'].column_names tokenized_dataset[
['input_ids', 'attention_mask', 'labels']
input_ids
: 입력 문장이 토큰화 되어서 사전에 매핑된 숫자로 변환된다.
attention mask
: 진짜 입력과 패딩을 구별하도록 도와준다. input_ids
의 길이가 모두 같아야 모델이 한 꺼번에 처리할 수 있기에 문장이 짧은 경우엔 뒤에 0
또는 [PAD]
를 넣어서 길이를 맞춘다. 이때, 어떤 부분이 실제 문장이고 어떤 부분이 패딩인지 알려주는 게 바로 attention mask이다.
labels
: 정답 토큰 시퀀스이다. Seq2Seq 모델에서 모델이 예측해야 할 목표 문장을 의미한다. labels도 길이를 맞추기 위해 padding을 넣을 수 있는데 padding 부분은 Loss 계산에서 무시해야하므로 보통 -100으로 채운다.
labels가 있다는 건 정답이 있다는 의미!
번역 Task에서 정답은 번역하고자 하는 언어로 쓰여진 문장이므로 여기서는 영어 문장이다.
영어 문장은 Dataset에 이미 포함되어있고 학습을 위해 Loss 계산을 할 때 사용된다.
4-3. fine tuning
from transformers import Trainer, TrainingArguments, DataCollatorForSeq2Seq
= TrainingArguments(
training_args =32,
per_device_train_batch_size=32,
per_device_eval_batch_size=2e-5,
learning_rate=1,
max_grad_norm=2,
num_train_epochs="steps",
evaluation_strategy="steps",
logging_strategy=500,
logging_steps="data/logs",
logging_dir="steps",
save_strategy=1000,
save_steps="data/ckpt",
output_dir="tensorboard",
report_to
)
= Trainer(
trainer =model,
model=training_args,
args=tokenized_dataset["train"],
train_dataset=tokenized_dataset["test"],
eval_dataset=tokenizer,
tokenizer=DataCollatorForSeq2Seq(tokenizer=tokenizer, model=model),
data_collator
)
trainer.train()"data/model") trainer.save_model(
-
GPU 사용할 때 학습 시간은 2시간이 조금 넘기에 학습은 생략했습니다.
4-4. predict
import torch
from transformers import (
AutoTokenizer,
AutoModelForSeq2SeqLM,
DataCollatorForSeq2Seq,
GenerationConfig
)
= "data/model" # 미세조정한 model
model_name = AutoTokenizer.from_pretrained(model_name)
tokenizer = AutoModelForSeq2SeqLM.from_pretrained(model_name)
model
= DataCollatorForSeq2Seq(
collator =tokenizer,
tokenizer=model,
model="max_length",
padding=512,
max_length
)= collator([tokenized_dataset["test"][i] for i in range(2)])
batch
= model.generate(batch["input_ids"], max_length=128, do_sample=False)
outputs = tokenizer.batch_decode(outputs, skip_special_tokens=True)
result = tokenizer.batch_decode(batch["input_ids"], skip_special_tokens=True)
origin print(f"원본 : {origin[0]} -> 영어 : {result[0]}")
print(f"원본 : {origin[1]} -> 영어 : {result[1]}")