Gemma 모델 파인튜닝 (Hugging Face)¶
- Gemma 7B 모델을 SQuAD 한국어 QA Dataset으로 파인튜닝합니다.
- Gemma Fine-tuning 공식 예시를 기준으로 작성되었습니다.
1. 환경 설정¶
- 라이브러리 다운로드
- transformers의 경우 4.38.0 이전 버전에서는 Gemma 관련 버그가 존재하여 4.38.1으로 업데이트 해주셔야 합니다.
In [ ]:
!pip3 install -q -U bitsandbytes==0.42.0
!pip3 install -q -U peft==0.8.2
!pip3 install -q -U trl==0.7.10
!pip3 install -q -U accelerate==0.27.1
!pip3 install -q -U datasets==2.17.0
!pip3 install -q -U transformers==4.38.1
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 105.0/105.0 MB 15.8 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 183.4/183.4 kB 4.5 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 280.0/280.0 kB 30.1 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 150.9/150.9 kB 4.5 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 536.7/536.7 kB 43.2 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 79.8/79.8 kB 12.3 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 116.3/116.3 kB 16.0 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 134.8/134.8 kB 17.8 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 279.7/279.7 kB 6.4 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 536.6/536.6 kB 13.5 MB/s eta 0:00:00
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.5/8.5 MB 64.6 MB/s eta 0:00:00
- API 설정
In [ ]:
import os
from google.colab import userdata
os.environ["HF_TOKEN"] = userdata.get('HUGGINGFACEHUB_API_TOKEN')
In [ ]:
from huggingface_hub import notebook_login
notebook_login()
VBox(children=(HTML(value='<center> <img\nsrc=https://huggingface.co/front/assets/huggingface_logo-noborder.sv…
2. Gemma 모델 로드¶
In [ ]:
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, BitsAndBytesConfig, GemmaTokenizer
model_id = "google/gemma-7b"
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
tokenizer = AutoTokenizer.from_pretrained(model_id, token=os.environ['HF_TOKEN'])
model = AutoModelForCausalLM.from_pretrained(model_id, quantization_config=bnb_config, device_map={"":0}, token=os.environ['HF_TOKEN'])
tokenizer_config.json: 0%| | 0.00/1.11k [00:00<?, ?B/s]
tokenizer.model: 0%| | 0.00/4.24M [00:00<?, ?B/s]
tokenizer.json: 0%| | 0.00/17.5M [00:00<?, ?B/s]
special_tokens_map.json: 0%| | 0.00/555 [00:00<?, ?B/s]
config.json: 0%| | 0.00/629 [00:00<?, ?B/s]
model.safetensors.index.json: 0%| | 0.00/20.9k [00:00<?, ?B/s]
Downloading shards: 0%| | 0/4 [00:00<?, ?it/s]
model-00001-of-00004.safetensors: 0%| | 0.00/5.00G [00:00<?, ?B/s]
model-00002-of-00004.safetensors: 0%| | 0.00/4.98G [00:00<?, ?B/s]
model-00003-of-00004.safetensors: 0%| | 0.00/4.98G [00:00<?, ?B/s]
model-00004-of-00004.safetensors: 0%| | 0.00/2.11G [00:00<?, ?B/s]
Loading checkpoint shards: 0%| | 0/4 [00:00<?, ?it/s]
generation_config.json: 0%| | 0.00/137 [00:00<?, ?B/s]
- 모델 테스트
In [ ]:
text = "Quote: Imagination is more"
device = "cuda:0"
inputs = tokenizer(text, return_tensors="pt").to(device)
outputs = model.generate(**inputs, max_new_tokens=20)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Quote: Imagination is more important than knowledge.
In [ ]:
os.environ["WANDB_DISABLED"] = "true"
- Lora 환경 설정
In [ ]:
from peft import LoraConfig
lora_config = LoraConfig(
r=8,
target_modules=["q_proj", "o_proj", "k_proj", "v_proj", "gate_proj", "up_proj", "down_proj"],
task_type="CAUSAL_LM",
)
3. 학습 데이터 로드¶
- [엑소브레인QA Datasets(ETRI)] SQuAD 한국어 QA Dataset
- SQuAD 질문의 위키피디아 한국어 번역 QA datasets (표준태깅, 339개)
In [ ]:
from google.colab import drive
drive.mount('/content/drive') # 구글 드라이브를 사용하는 경우
Mounted at /content/drive
In [ ]:
import pandas as pd
df = pd.read_csv("/content/drive/MyDrive/Project/Gemma/SQuAD_ko_QA_dataset.csv")
In [ ]:
df.columns
Out[ ]:
Index(['번호', '질문(영어 원문)', '정답(영어 원문)', '정답근거(영어 원문)',
'출처\n(Standford QA dataset 이름)', '질문(한국어 번역문)', '질문 난이도(상/중/하)',
'Tagged 질문(QF, LAT)', 'QF', 'LAT', 'SAT', '정답유형 분류', '의미적 분류', '구조적 분류',
'도메인 분류', 'Q_COM_MOD', 'Q_COM_ADVERB', 'Q_TE', 'Q_SE', '정답', '정답 이형태',
'정답 개체명 타입', '정답 근거1', '위키피디아 제목', '위키피디아 URL'],
dtype='object')
In [ ]:
df = df[['질문(한국어 번역문)', '정답', '정답 근거1']]
- Gemma 학습 데이터 구조는 다음과 같습니다.
<start_of_turn>user
Write a hello world program<end_of_turn>
<start_of_turn>model
In [ ]:
df["prompt"] = "<start_of_turn>user \n" + df['질문(한국어 번역문)'] \
+ "<end_of_turn>\n<start_of_turn>model\nAnswer: " \
+ df['정답'] + "\nReason: " + df['정답 근거1'] + "<end_of_turn>"
In [ ]:
df["prompt"][0]
Out[ ]:
'<start_of_turn>user \n1961년부터 1972년까지 미국항공우주국에 의해 이루어진 일련의 유인 우주 비행 탐사 계획은 무엇인가?<end_of_turn>\n<start_of_turn>model\nAnswer: 아폴로 계획\nReason: 아폴로 계획(-計劃, 영어: Project Apollo)은 1961년부터 1972년까지 미국항공우주국에 의해 이루어진 일련의 유인 우주 비행 탐사 계획이다. 아폴로 계획의 목표는 1960년대 존 F. 케네디 대통령의 연설에서 언급되었던 "인간을 달에 착륙시킨 후 무사히 지구로 귀환시키는" 것으로 요약할 수 있다. 이 목표는 1969년 아폴로 11호에 의해 달성되었다.<end_of_turn>'
- Hugging Face Dataset 구조로 변환합니다.
In [ ]:
from datasets import Dataset
data = Dataset.from_pandas(df)
In [ ]:
data
Out[ ]:
Dataset({
features: ['질문(한국어 번역문)', '정답', '정답 근거1', 'prompt'],
num_rows: 339
})
- 학습할 데이터에 대해 토큰화합니다.
In [ ]:
# data = load_dataset("Abirate/english_quotes")
data = data.map(lambda samples: tokenizer(samples["prompt"]), batched=True)
Map: 0%| | 0/339 [00:00<?, ? examples/s]
In [ ]:
data
Out[ ]:
Dataset({
features: ['질문(한국어 번역문)', '정답', '정답 근거1', 'prompt', 'input_ids', 'attention_mask'],
num_rows: 339
})
- train 데이터셋과 test 데이터셋으로 분리합니다.
- 여기서는 train 데이터셋만 사용합니다.
In [ ]:
data = data.train_test_split(test_size=0.2)
In [ ]:
data
Out[ ]:
DatasetDict({
train: Dataset({
features: ['질문(한국어 번역문)', '정답', '정답 근거1', 'prompt', 'input_ids', 'attention_mask'],
num_rows: 271
})
test: Dataset({
features: ['질문(한국어 번역문)', '정답', '정답 근거1', 'prompt', 'input_ids', 'attention_mask'],
num_rows: 68
})
})
4. 모델 학습¶
In [ ]:
import transformers
from trl import SFTTrainer
def formatting_func(example):
return example['prompt']
trainer = SFTTrainer(
model=model,
train_dataset=data["train"],
args=transformers.TrainingArguments(
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
warmup_steps=2,
max_steps=100,
learning_rate=2e-4,
fp16=True,
logging_steps=1,
push_to_hub=True,
push_to_hub_model_id="ko-QA-gemma-7b-finetuned",
push_to_hub_token=userdata.get('HUGGINGFACEHUB_API_TOKEN'),
save_strategy="epoch",
output_dir="outputs",
optim="paged_adamw_8bit",
),
peft_config=lora_config,
formatting_func=formatting_func,
)
trainer.train()
Using the `WANDB_DISABLED` environment variable is deprecated and will be removed in v5. Use the --report_to flag to control the integrations used for logging result (for instance --report_to none).
Map: 0%| | 0/271 [00:00<?, ? examples/s]
[100/100 02:11, Epoch 1/2]
Step | Training Loss |
---|---|
1 | 2.597200 |
2 | 2.605500 |
3 | 2.310100 |
4 | 2.056500 |
5 | 1.828700 |
6 | 1.820900 |
7 | 1.388600 |
8 | 1.454600 |
9 | 1.240600 |
10 | 1.195000 |
11 | 1.707600 |
12 | 1.401400 |
13 | 1.586000 |
14 | 1.312800 |
15 | 1.602900 |
16 | 1.069200 |
17 | 0.954500 |
18 | 1.250700 |
19 | 1.234900 |
20 | 1.062100 |
21 | 1.302200 |
22 | 0.911800 |
23 | 0.970700 |
24 | 1.133500 |
25 | 0.993600 |
26 | 1.188400 |
27 | 1.155400 |
28 | 1.089500 |
29 | 1.355400 |
30 | 1.127800 |
31 | 0.984800 |
32 | 1.004200 |
33 | 1.046900 |
34 | 1.155100 |
35 | 0.839600 |
36 | 0.993300 |
37 | 1.369500 |
38 | 1.194800 |
39 | 1.124700 |
40 | 1.135900 |
41 | 0.920000 |
42 | 0.880200 |
43 | 0.749000 |
44 | 1.023300 |
45 | 1.088300 |
46 | 0.942300 |
47 | 0.713800 |
48 | 0.863100 |
49 | 0.990000 |
50 | 0.957200 |
51 | 0.835200 |
52 | 0.785600 |
53 | 0.869600 |
54 | 1.414800 |
55 | 0.685000 |
56 | 1.207700 |
57 | 0.683800 |
58 | 1.048100 |
59 | 0.999300 |
60 | 1.008900 |
61 | 0.878600 |
62 | 0.834700 |
63 | 1.269400 |
64 | 0.895400 |
65 | 0.739200 |
66 | 1.103100 |
67 | 0.641400 |
68 | 0.932500 |
69 | 0.736600 |
70 | 0.699400 |
71 | 0.550400 |
72 | 0.638600 |
73 | 0.728600 |
74 | 0.536100 |
75 | 0.713600 |
76 | 0.750800 |
77 | 0.516000 |
78 | 0.693900 |
79 | 0.509600 |
80 | 0.406100 |
81 | 0.699500 |
82 | 0.791700 |
83 | 0.559900 |
84 | 0.858700 |
85 | 0.743500 |
86 | 0.702200 |
87 | 0.828800 |
88 | 0.465500 |
89 | 0.854500 |
90 | 0.490400 |
91 | 0.755000 |
92 | 0.808500 |
93 | 0.559500 |
94 | 0.650300 |
95 | 1.120100 |
96 | 0.426200 |
97 | 0.581300 |
98 | 0.800500 |
99 | 0.591300 |
100 | 0.451100 |
Out[ ]:
TrainOutput(global_step=100, training_loss=1.009339314997196, metrics={'train_runtime': 133.1395, 'train_samples_per_second': 3.004, 'train_steps_per_second': 0.751, 'total_flos': 2364555710085120.0, 'train_loss': 1.009339314997196, 'epoch': 1.48})
5. 모델 테스트¶
In [ ]:
data["train"]['질문(한국어 번역문)'][1]
Out[ ]:
'북미 본토에서 벌어진 전투 기간은 언제 시작하였나?'
In [ ]:
data["train"]['정답'][1]
Out[ ]:
'1754년'
In [ ]:
data["train"]['정답 근거1'][1]
Out[ ]:
'실제로 북미 본토에서 벌어진 전투 기간은 1754년의\xa0주몬빌 글렌 전투에서 1760년의\xa0몬트리올 공략까지 6년간이다.'
In [ ]:
text = data["train"]['질문(한국어 번역문)'][1]
device = "cuda:0"
inputs = tokenizer(text, return_tensors="pt").to(device)
outputs = model.generate(**inputs, max_new_tokens=50)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
북미 본토에서 벌어진 전투 기간은 언제 시작하였나?
Answer: 1754년
Reason: 북미 본토에서 벌어진 전투 기간은 1754년에 시작하여 1760년에 끝나게 된다. 이 기
'IT > 인공지능' 카테고리의 다른 글
[GPU] RAPIDS: 대규모 데이터 세트 분석을 위한 GPU 가속 프레임워크 (0) | 2024.02.27 |
---|---|
[생성형AI][LLM] RAG 기반 기술문서 QA Gemma 모델 (Hugging Face) (0) | 2024.02.24 |
[생성형AI][Text2Video] Sora: 콘텐츠 제작의 미래를 선도하는 비디오 생성 모델 (0) | 2024.02.20 |
[생성형AI][RAG] 증상 기반 법정감염병 판별 챗봇 (0) | 2024.02.09 |
[생성형AI][LLM] 데이터 없이 생성형 AI를 활용하여 개체명인식(NER) 분류 - 금융 도메인 (4) | 2024.02.07 |