Ssul's Blog

AI Product 개발전략과 개발기 - text-generation모델 파인튜닝 본문

AI & ML/사용하기

AI Product 개발전략과 개발기 - text-generation모델 파인튜닝

Ssul 2024. 6. 13. 15:07

앞 글에서 이야기했던 우여곡절(?)을 마치고, 드디어 학습에 들어간다.

그 첫번째는 LLM을 Instruction-tuning으로 파인튜닝

 

이제부터는 코드와 주석 위주로 가겠다.

 

1. 어떤 모델을 foundation모델로 사용할 것인가?

- 한글이 잘 될것

- 될수 있으면 작을 것

이 두가지 기준을 가지고 접근했을때, 야놀자에서 나온 eeve 평이 좋아서, 우선 eeve2.8B로 작업해보고, 성능이 괜찮다 생각되면 10.8B로 진행을 하여보자

 

2. 초기 셋팅

# 코랩 왼쪽편에 열쇠 모양이 있다. 클릭해서 HF_TOKEN에 자신의 허깅페이스 토큰을 넣는다
from google.colab import userdata
userdata.get('HF_TOKEN')

###########################################
# 0. Huggingface login

from huggingface_hub import notebook_login
notebook_login()

# 로그인 창이 뜨면, 자신의 토큰을 입력하여 로그인 한다

 

##########################################
# 1-3. 모듈 다운

!pip install -q -U datasets
!pip install -q -U bitsandbytes
!pip install -q -U accelerate
!pip install -q -U peft
!pip install -q -U trl
###########################################
# 2-1. 모듈 불러오기

import os
import torch
import transformers
from datasets import load_from_disk
from transformers import (
    BitsAndBytesConfig,
    AutoModelForCausalLM,
    AutoTokenizer,
    Trainer,
    TextStreamer,
    pipeline
)
from peft import (
    LoraConfig,
    prepare_model_for_kbit_training,
    get_peft_model,
    get_peft_model_state_dict,
    set_peft_model_state_dict,
    TaskType,
    PeftModel
)
from trl import SFTTrainer

 

3. 모델 양자화해서 가져와서 > 로라붙이기

#@title 🤗 Base model 선택하기
device = 'auto' #@param {type: "string"}
base_LLM_model = 'yanolja/EEVE-Korean-Instruct-2.8B-v1.0' #@param {type: "string"}
###########################################
# 3-1. 모델 다운로드

model = AutoModelForCausalLM.from_pretrained(
    base_LLM_model,
    # load_in_8bit=True, # LoRA
    # torch_dtype=torch.float16,
    load_in_4bit=True, # Quantization Load
    device_map=device)

tokenizer = AutoTokenizer.from_pretrained(base_LLM_model)

- load_in_4bit=True를 통해서 양자화된 모델을 가져오기

 

이제 LoRA를 붙여 보자

###########################################
# 파인튜닝 고고
# NF4 양자화를 위한 설정
lora_config = LoraConfig(
    r=4, # LoRA 가중치 행렬의 rank. 정수형이며 값이 작을수록 trainable parameter가 적어짐
    lora_alpha=8, # LoRA 스케일링 팩터. 추론 시 PLM weight와 합칠 때 LoRA weight의 스케일을 일정하게 유지하기 위해 사용
    lora_dropout=0.05,
    target_modules=['q_proj', 'k_proj', 'v_proj', 'o_proj', 'gate_proj', 'up_proj', 'down_proj'], # LoRA를 적용할 layer. 모델 아키텍처에 따라 달라짐
    bias='none', # bias 파라미터를 학습시킬지 지정. ['none', 'all', 'lora_only']
    task_type=TaskType.CAUSAL_LM
)

# 양자화된 모델을 학습하기 전, 전처리를 위해 호출
model = prepare_model_for_kbit_training(model)
# LoRA 학습을 위해서는 아래와 같이 peft를 사용하여 모델을 wrapping 해주어야 함
model = get_peft_model(model, lora_config)

# 학습 파라미터 확인
model.print_trainable_parameters()

 

4. 데이터 셋팅하기

###########################################
# 4-1. 모듈 불러오기
import os
import torch
import transformers
import pandas as pd
from datasets import load_dataset, Dataset, concatenate_datasets
from transformers import AutoModelForCausalLM, AutoTokenizer
import pandas as pd

# CSV 파일 경로를 지정합니다.
file_path = '파일경로/spam_dataset_final.csv'

# CSV 파일을 pandas DataFrame으로 불러옵니다.
dataset = pd.read_csv(file_path)
# dataset = pd.read_csv(file_path, encoding='utf-8')

# DataFrame 출력
print(dataset)

 

 

sms에 문자 원문이 들어있다. LLM 파인 튜닝을 위해, 텍스트를 preprocessing진행하자

import re
# 텍스트 정리 함수 정의
def clean_text(text):
    text = re.sub(r'\[.*?\]', '', text)  # 대괄호 안의 텍스트 제거
    text = re.sub(r'\(.*?\)', '', text)  # 소괄호 안의 텍스트 제거
    text = re.sub(r'\n', ' ', text)  # 줄바꿈 문자 제거
    text = re.sub(r'\s+', ' ', text)  # 여러 개의 공백을 하나의 공백으로 대체
    text = re.sub(r'[^가-힣a-zA-Z\s]', '', text)  # 한글, 영어 알파벳, 공백을 제외한 문자 제거
    return text.strip()
    
    
# 'sms' 컬럼에 정리 함수 적용
dataset['cleaned_sms'] = dataset['sms'].apply(clean_text)

from datasets import Dataset

# pandas DataFrame을 Dataset으로 변환
dataset = Dataset.from_pandas(dataset)

- cleaned_sms에 다듬어진 sms 데이터를 추가합니다

- 데이터를 dataset객체로 만듭니다

 

Instruction-tuning*을 위한 템플릿을 준비합니다

(사전 학습된 언어 모델(PLM)을 특정 작업에 더 적합하게 만드는 데 사용되는 기법 중 하나입니다. 이 기법은 모델에게 특정한 지시(instruction)를 제공하여 그 지시를 따라 작업을 수행하도록 조정하는 과정)

###########################################
# Instruction tuning을 위한 template 작성.

instruct_template = {
    "prompt_input": "{instruction}\n\n###\n\n\n이 문자는 다음 중 어떤 문자인가?(1개만 고르시오)\n\n프로모션문자\n스팸문자\n정치선거문자\n정부문자\n개인문자\n인증문자\n\n답변:\n",
    "prompt_no_input": "{instruction}\n\n###\n\n\n이 문자는 다음 중 어떤 문자인가?(1개만 고르시오)\n\n프로모션문자\n스팸문자\n정치선거문자\n정부문자\n개인문자\n인증문자\n\n답변:\n",
    "response_split": "답변:\n"
}
def generate_prompt(data_point):
  instruction = data_point["cleaned_sms"]
  # input = data_point["input"]
  label = data_point["label"]

  if input:
    res = instruct_template["prompt_input"].format(instruction=instruction)
  else:
    res = instruct_template["prompt_no_input"].format(instruction=instruction)

  if label:
    res = f"{res}{label}<|im_end|>" # eos_token을 마지막에 추가

  data_point['text'] = res

  return data_point
# 데이터셋에 프롬프트 적용
remove_column_keys = dataset.features.keys() # 기존 컬럼(instruction, output 등) 제거
dataset_cvted = dataset.shuffle().map(generate_prompt, remove_columns=remove_column_keys)

def tokenize_function(examples):
  outputs = tokenizer(examples["text"], truncation=True, max_length=512)
  return outputs
  
remove_column_keys = dataset_cvted.features.keys()
dataset_tokenized = dataset_cvted.map(tokenize_function, batched=True, remove_columns=remove_column_keys)
# dataset_tokenized 분리
dataset_dict = dataset_tokenized.train_test_split(test_size=0.2, seed=42)

dataset_train = dataset_dict['train']
dataset_test = dataset_dict['test']

5. 학습하기

# Data Collator 역할
# 각 입력 시퀀스의 input_ids(토큰) 길이를 계산하고, 가장 긴 길이를 기준으로 길이가 짧은 시퀀스에는 패딩 토큰 추가
def collate_fn(examples):
    examples_batch = tokenizer.pad(examples, padding='longest', return_tensors='pt')
    examples_batch['labels'] = examples_batch['input_ids'] # 모델 학습 평가를 위한 loss 계산을 위해 입력 토큰을 레이블로 사용
    return examples_batch
train_args = transformers.TrainingArguments(
    per_device_train_batch_size=2, # 각 디바이스당 배치 사이즈. 작을수록(1~2) 좀 더 빠르게 alignment 됨
    gradient_accumulation_steps=4,
    warmup_steps=1,
    num_train_epochs=1,
    max_steps=1000,
    learning_rate=2e-4, # 학습률
    bf16=True, # bf16 사용 (지원되는 하드웨어 확인 필요)
    output_dir="outputs",
    optim="paged_adamw_8bit", # 8비트 AdamW 옵티마이저
    logging_steps=50, # 로깅 빈도
    save_total_limit=3 # 저장할 체크포인트의 최대 수
)

trainer = SFTTrainer(
    model=model,
    train_dataset=dataset_train,
    max_seq_length=512, # 최대 시퀀스 길이
    args=train_args,
    dataset_text_field="text",
    data_collator=collate_fn
)
model.config.use_cache = False
trainer.train()
# 평가 수행
eval_results = trainer.evaluate(eval_dataset=dataset_test)
print(f"Evaluation Results: {eval_results}")

 

 

6. 파인튜닝 모델 허깅페이스에 올리기(LoRA파라미터만 올리기)

model_name = f"여러분의 허깅페이스 아이디/EEVE-Korean-2.8b_LoRA_0612"
model.push_to_hub(model_name, use_auth_token=True)