granite-3.0-8b
해당 포스트는 다음 글을 참고하여 작성하였다.
https://www.ibm.com/granite/docs/how-to/fine-tuning/granite/
포스트를 참고하여 fine tuning 진행 도중, 모델 학습 후 저장하고 불러오는 과정에서 문제가 발생하여 그 부분을 함께 다룬다.
!pip install "transformers>4.45.2" datasets accelerate bitsandbytes peft trl
데이터셋은 아래 링크의 데이터셋을 사용하였다.
https://huggingface.co/datasets/Ammad1Ali/Korean-conversational-dataset
granite 3.0 모델은 학습 데이터의 형식이 사용자의 질문에 해당하는 prompt, answer에 해당하는 response로 이루어져 있어, 해당 데이터셋의 [INST]
와 [/INST]
를 기준으로 prompt, response로 변환하여 korean-conversation.csv
로 파일 생성 후 사용하였다.
from datasets import load_dataset
# load local csv file
dataset = load_dataset('csv', data_files='korean-conversation.csv')
dataset = dataset['train'].train_test_split(test_size=0.2)
# check data
print(dataset['train'][0])
낮은 GPU 성능으로도 fine tuning이 가능하도록 양자화를 진행한다. (peft 라이브러리가 도와주는 것 같다. 추후 자세히 다루어 볼 예정)
import torch
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, BitsAndBytesConfig
from peft import LoraConfig
from trl import SFTTrainer
# hugging face 에서 미리 로컬에 다운받아놓은 IBM granite-3.0 original model
model_checkpoint = "./granite-3.0-8b-instruct"
tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit = True,
bnb_4bit_quant_type = "nf4",
bnb_4bit_use_double_quant = True,
bnb_4bit_compute_dtype = torch.float16 # 미설정 시 느린 속도로 경고가 뜸
)
model = AutoModelForCausalLM.from_pretrained(
model_checkpoint,
quantization_config = bnb_config,
device_map = "auto"
)
모델 세부 튜닝 전, 로드된 모델의 간단한 검증을 수행한다. 결과를 확인한 후 fine tuning 후 똑같이 시도해보고 모델의 출력을 관찰한다.
input_text = "<|user>오늘 날씨 좋다\n<|assistant|>\n"
inputs = tokenizer(input_text, return_tensors="pt")
outputs = model.generate(**inputs, max_new_tokens=100)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
이 섹션에서는 훈련을 위한 환경을 준비한다.
총 세가지이다.
1. 모델로부터 기대하는 출력에 대한 학습 prompt를 정의한다. (formatting_prompts_func
)
2. qLoRA 적용.
3. SFTTrainer 셋업
def formatting_prompts_func(example):
output_texts = []
for i in range(len(example['prompt'])):
text = f"<|system|>\n너는 매우 유능한 어시스턴트야. 한국어로 대답해주어야 해. 문장의 앞에 너가 인공지능이어서 못해드립니다 같은 말은 빼줘\n<|user|>\n{example['prompt'][i]}\n<|assistant|>\n{example['response'][i]}<|endoftext|>"
output_texts.append(text)
return output_texts
response_template = "\n<|assistant|>\n"
from trl import DataCollatorForCompletionOnlyLM
response_template_ids = tokenizer.encode(response_template, add_special_tokens=False)[2:]
collator = DataCollatorForCompletionOnlyLM(response_template_ids, tokenizer=tokenizer)
# Apply qLoRA
qlora_config = LoraConfig(
r=16, # The rank of the Low-Rank Adaptation
lora_alpha=32, # Scaling factor for the adapted layers
target_modules=["q_proj", "v_proj"], # Layer names to apply LoRA to
lora_dropout=0.1,
bias="none"
)
# Initialize the SFTTrainer
training_args = TrainingArguments(
output_dir="./results",
learning_rate=2e-4,
per_device_train_batch_size=6,
per_device_eval_batch_size=6,
num_train_epochs=3,
logging_steps=100,
fp16=True,
report_to="none"
)
max_seq_length = 250
device = "cuda" if torch.cuda.is_available() else "cpu"
model = model.to(device)
trainer = SFTTrainer(
model=model,
args=training_args,
train_dataset=dataset['train'],
eval_dataset=dataset['test'],
tokenizer=tokenizer,
peft_config = qlora_config,
formatting_func=formatting_prompts_func,
data_collator=collator,
max_seq_length=max_seq_length,
이전 단계에서 생성한 trainer로 모델의 학습을 수행한다.
trainer.save_model()은 양자화를 이용한 fine-tuning 환경에서 반드시 해주어야 한다.
(IBM 문서대로 모델 불러왔더니 아래 경고문자로 🐕고생했다.)
Some weights of the model checkpoint at ./granite-3.0-8b-instruct-finetuned-epoch3 were not used when initializing GraniteForCausalLM:
-> 모델 출력
<|user>한국의 수도는 어디야?
<|assistant|>
.0.. (. ( ( (... (Provided. (<<<<... ( ( (<<<< (0......... (00 ( ( ( ( ( (0. (<<<<. (<<<< ( of
0. (000 (000 ( (<<<<. (0 (++). (. (0 (0 (2.++). (40.00.0 (3. ( ( (0. ( means.30 (Provided (. (0 ( (.0.00000 ( ( (las.2.5 ( (.ë.et.3 (0 ( ( (0 ( ( _. E.3 (00 ( ( ( (4 (00. ( ( ( ( (00 ( ( (.2. by ( ( ( ( ( ( ( (.. ( (0. (00 ( ( (0 ( ( ( of ( (0 (0 (0 ( (00 ( ( ( ( ( ( ( ( (0 ( ( ( ( ( ( (.uf.
이유를 알아내고싶다.. 찾아보니 저런 경고는 무시해도 된다던데..
일단 해결하는 방법을 어찌저찌 찾아내었고 그 방법의 첫번째로는 trainer를 저장해주는 것이다.
두번째는 모델 불러올 때 설명하도록 하겠다.
trainer.train()
# 중요 : trainer를 저장해야 모델을 불러올 때 PeftModel.from_pretrained()로 qLoRA 설정을 불러올 수 있다.
trainer.save_model("./granite-3.0-8b-instruct-finetuned-epoch3")
model.save_pretrained("./granite-3.0-8b-instruct-finetuned-epoch3")
tokenizer.save_pretrained("./granite-3.0-8b-instruct-finetuned-epoch3")
모델 finetuning 을 마쳤다면, 3번에서의 '오늘 날씨 좋다'에 대한 출력과 finetuning 후의 출력을 살펴볼 차례이다. 코드는 동일하다.
input_text = "<|user>오늘 날씨 좋다\n<|assistant|>\n"
inputs = tokenizer(input_text, return_tensors="pt")
outputs = model.generate(**inputs, max_new_tokens=100)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
finetuning 과정의 '5. Training process'에서 trainer를 저장했고, '6. Saving the fine-tuned model'에서 model과 tokenizer를 저장했다.
finetuning 모델을 사용할 때는 다음의 단계를 거쳐 모델을 로드한다.
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
import torch
from peft import PeftModel, PeftConfig
device = "cuda" if torch.cuda.is_available() else "cpu"
model_path = "./granite-3.0-8b-instruct-finetuned-epoch1"
tokenizer = AutoTokenizer.from_pretrained(model_path)
bnb_config = BitsAndBytesConfig(
load_in_4bit = True,
bnb_4bit_quant_type = "nf4",
bnb_4bit_use_double_quant = True,
bnb_4bit_compute_dtype = torch.float16 # 미설정 시 느린 속도로 경고가 뜸
)
model = AutoModelForCausalLM.from_pretrained(model_path, quantization_config=bnb_config, device_map="auto")
model = PeftModel.from_pretrained(model, './granite-3.0-8b-instruct-finetuned-epoch1')
model.eval()
이렇게 불러오면 경고문구 없이 잘 불러와진다!
input_text = "<|user>너는 누구야?\n<|assistant|>\n"
input_tokens = tokenizer(input_text, return_tensors="pt").to(device)
stop_token = "<|endoftext|>"
stop_token_id = tokenizer.encode(stop_token)[0]
outputs = model.generate(**input_tokens, max_new_tokens=250, eos_token_id=stop_token_id)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
추가로 trainer를 저장할 시 작은 용량의 adapter_model.safetensors 를 저장하는데,
모델을 불러서 사용할 때는 trainer에 사용된 model의 원본 model_path에 원본 모델을 가지고 있어야 한다. (로컬 경로였다면 해당 경로에 원본 모델이, huggingface 경로였다면 따로 신경쓸 필요 X)