환경 : Google colab pro+ A100
!pip install -U accelerate==0.29.3 # PyTorch 모델의 학습 속도 향상과 추론 최적화를 위한 라이브러리
!pip install peft==0.10.0 #대규모 언어 모델을 효율적으로 미세 조정할 수 있는 PEFT 기술 구현
!pip install bitsandbytes==0.43.1 # 모델 매개변수 양자화로 메모리 사용량 절감
!pip install transformers==4.40.1
!pip install trl==0.8.6 # Transformer Reinforcement Learning의 약자로 강화 학습 기반 언어 모델 미세 조정 기술 구현
!pip install datasets==2.19.0
import os
import torch
from datasets import load_dataset
from transformers import (
AutoModelForCausalLM,
AutoTokenizer,
BitsAndBytesConfig,
TrainingArguments,
pipeline,
logging,
)
from peft import LoraConfig
from trl import SFTTrainer
import huggingface_hub
huggingface_hub.login("hf_token")
# Hugging Face Basic Model 한국어 모델
base_model = "beomi/Llama-3-Open-Ko-8B" # beomi님의 Llama3 한국어 파인튜닝 모델
# baemin_dataset_simple.json
hkcode_dataset = "/content/dataset"
# 새로운 모델 이름
new_model = "Llama3-Ko-3-8B-baemin"
- 데이터셋의 상위 200개만 추출
- 너무 많은 데이터셋을 파인튜닝할 경우 CUDA Out of Memory
- 하지만 데이터셋 200개는 너무 적은 것 아닐까? 🤔
- CUDA 메모리가 터지지 않고 충분히 많은 양의 데이터로 LLM을 파인튜닝시킬 수 있는 방법은? 🤔
해결 : Batch size 를 줄인다. (TrainingArguments 조정)
dataset = load_dataset(hkcode_dataset, split="train")
# dataset = dataset.select(range(200))
# 데이터 확인
print(len(dataset))
print(dataset[0])
200
{'text': '<s>[INST] Q. 일반간이 [/INST] 동업관계를 정리한 후 단독사업자로 사업자등록을 함에 있어 등록 시 간이과세자로 선택할 수 있는지에 대해 문의 주신 것으로 이해했습니다. 동업관계 탈퇴 후 단독사업자로 사업자등록을 함에 있어 간이과세자로 사업자등록 신청이 가능합니다. </s>'}
# 현재 사용중인 GPU의 CUDA 연산 능력을 확인한다.
# 8이상이면 고성능 GPU 로 판단한다.
if torch.cuda.get_device_capability()[0] >= 8:
!pip install -qqq flash-attn
# 고성능 Attention인 flash attention 2 을 사용
attn_implementation = "flash_attention_2"
# 데이터 타입을 bfloat16으로 설정해준다.
# bfloat16은 메모리 사용량을 줄이면서도 계산의 정확성을 유지할 수 있는 데이터 타입이다.
torch_dtype = torch.bfloat16
else:
attn_implementation = "eager"
torch_dtype = torch.float16
# QLoRA config
quant_config = BitsAndBytesConfig(
load_in_4bit=True, # 모델 가중치를 4비트로 로드
bnb_4bit_quant_type="nf4", # 양자화 유형으로는 “nf4”를 사용한다.
bnb_4bit_compute_dtype=torch_dtype, # 양자화를 위한 컴퓨팅 타입은 직전에 정의 했던 torch_dtype으로 지정 해준다.
bnb_4bit_use_double_quant=False, # 이중 양자화는 사용하지 않는다.
)
# 모델 로드
model = AutoModelForCausalLM.from_pretrained(
base_model,
quantization_config=quant_config,
device_map={"": 0} # 0번째 gpu 에 할당
)
# 모델의 캐시 기능을 비활성화 한다. 캐시는 이전 계산 결과를 저장하기 때문에 추론 속도를 높이는 역할을 한다. 그러나 메모리 사용량을 증가시킬 수 있기 때문에, 메모리부족 문제가 발생하지 않도록 하기 위해 비활성화 해주는 것이 좋다.
model.config.use_cache = False
# 모델의 텐서 병렬화(Tensor Parallelism) 설정을 1로 지정한다. 설정값 1은 단일 GPU에서 실행되도록 설정 해주는 의미이다.
model.config.pretraining_tp = 1
# 토크나이저 로드
tokenizer = AutoTokenizer.from_pretrained(
base_model,
trust_remote_code=True)
# 시퀀스 길이를 맞추기 위해 문장 끝에 eos_token를 사용
tokenizer.pad_token = tokenizer.eos_token
# 패딩 토큰을 시퀀스의 어느 쪽에 추가할지 설정
tokenizer.padding_side = "right"
peft_params = LoraConfig(
lora_alpha=16, # LoRA의 스케일링 계수를 설정 한다. 값이 클 수록 학습 속도가 빨라질 수 있지만, 너무 크게 되면 모델이 불안정해질 수 있다.
lora_dropout=0.1, # 과적합을 방지하기 위한 드롭아웃 확률을 설정한다. 여기서는 10%(0.1)의 드롭아웃 확률을 사용하여 모델의 일반화 성능을 향상시킨다.
r=64, # LoRA 어댑터 행렬의 Rank를 나타낸다. 랭크가 높을수록 모델의 표현 능력은 향상되지만, 메모리 사용량과 학습 시간이 증가한다. 일반적으로 4, 8, 16, 32, 64 등의 값을 사용한다.
bias="none", # LoRA 어댑터 행렬에 대한 편향을 추가할지 여부를 결정한다. “none”옵션을 사용하여 편향을 사용하지 않는다.
task_type="CAUSAL_LM", # LoRA가 적용될 작업 유형을 설정한다. CAUSAL_LM은 Causal Language Modeling 작업을 의미한다. 이는 특히 GPT 같은 텍스트 생성 모델에 주로 사용된다.
)
training_params = TrainingArguments(
output_dir="./results",
num_train_epochs=10, # 기본값은 3
per_device_train_batch_size=4, # 기본값은 8
gradient_accumulation_steps=1, # 기본값 1
optim="paged_adamw_32bit",
save_steps=25,
logging_steps=25,
learning_rate=2e-4,
weight_decay=0.001,
fp16=False,
bf16=False,
max_grad_norm=0.3,
max_steps=-1,
warmup_ratio=0.03,
group_by_length=True,
lr_scheduler_type="constant",
report_to="tensorboard"
)
만약 CUDA out of memory 발생할 경우 다음과 같이 batch size 및 파라미터를 수정한다.
(학습시간 약 1시간 16분 소요)
training_params = TrainingArguments(
output_dir="/results",
num_train_epochs = 1, #epoch는 1로 설정
max_steps=5000, #max_steps을 5000으로 설정
per_device_train_batch_size=1,
gradient_accumulation_steps=4,
optim="paged_adamw_8bit",
warmup_steps=0.03,
learning_rate=2e-4,
fp16=True,
logging_steps=100,
push_to_hub=False,
report_to='none',
)
trainer = SFTTrainer(
model=model,
train_dataset=dataset,
peft_config=peft_params,
dataset_text_field="text",
max_seq_length=None, # 256, 512 등으로 수정할 수 있음.
tokenizer=tokenizer,
args=training_params,
packing=False,
)
직전에 정의했던 TrainingArguments와 함께 설정된 모든 매개변수를 사용하여 모델을 학습시킨다.
trainer.train()
모델 저장
trainer.save_model(new_model)
logging.set_verbosity(logging.CRITICAL)
prompt = "알바생이 3일 일하고 그만뒀는데 주휴수당을 줘야 하나요?"
pipe = pipeline(task="text-generation", model=model, tokenizer=tokenizer, max_length=200)
result = pipe(f"<s>[INST] {prompt} [/INST]")
print(result[0]['generated_text'])
Output
Parameter 수정 전 & 데이터셋 200개 & epoch 10
<s>[INST] 알바생이 3일 일하고 그만뒀는데 주휴수당을 줘야 하나요? [/INST] 주휴수당 발생여부에 대한 문의를 주셨습니다.답변드립니다. (1) 주휴수당은 개근하지 않아도 4주간 근로시간이 15시간 이상인 경우에 발생하는 것이니만큼 3일만 근로한 경우에는 주휴수당을 지급하지 않아도 됩니다. (2) 다만, 4주간의 근로시간이 15시간 이상이면 지급하면 되니 4주간의 근로시간을 관리하셔서 지급여부를 결정하시면 될 것입니다. </INST>
Parameter batch size 수정 후 & 데이터셋 3994개 & epoch 1
<s>[INST] 알바생이 3일 일하고 그만뒀는데 주휴수당을 줘야 하나요? [/INST] 주휴수당 지급유무에 대해 문의주셨습니다. (1) 주휴수당은 사업장의 상시 근로자수와 무관하게 4주 평균 주 소정근로시간이 15시간이상인 근로자에게 개근한 경우 인정되는 수당입니다. (2) 위 사안의 경우 3일 근로한 근로자에게 주휴수당을 지급할 필요는 없지만, 4주 평균 주 소정근로시간이 15시간이상인 근로자라면 개근한 주에는 주휴수당이 인정될 수 있으니 참고하시기 바랍니다. </s>
미팅이 기대됩니다~