1. KorQuAD 데이터셋
- 한국어 기계 독해 데이터셋
- 위키피디아에서 추출한 질의-응답 쌍 데이터셋
print("KorQuAD 데이터셋 다운로드 중...")
dataset = load_dataset("KETI-AIR/korquad", "v1.0")
DatasetDict({ train: Dataset({ features: ['id', 'title', 'context', 'question', 'answers'], num_rows: 60407 }) dev: Dataset({ features: ['id', 'title', 'context', 'question', 'answers'], num_rows: 5774
- context: 내용
- question: 내용에 기반한 문제
- answers: 답
데이터셋을 리스트로 담음
def extract_texts_from_korquad(dataset_split):
texts = []
for data in dataset_split:
texts.append(data['context'])
texts.append(data['question'])
texts.append(data['answers']['text'][0])
return texts
train_texts = extract_texts_from_korquad(dataset["train"])
valid_texts = extract_texts_from_korquad(dataset["dev"])
all_texts = train_texts + valid_texts
임시파일에 텍스트 저장
temp_files = []
chunk_size = 10000
for i in range(0, len(all_texts), chunk_size):
chunk = all_texts[i:i+chunk_size]
temp_file = tempfile.NamedTemporaryFile(delete=False, mode="w", encoding="utf-8")
temp_file.write("\n".join(chunk))
temp_file.close()
temp_files.append(temp_file.name)
print(f"{len(temp_files)}개의 임시 파일 생성 완료")
2. 토크나이저 학습
토크나이저의 구성
- 원본 : "Hello안녕하세요"
- pre-tokenizer
- 공백 앞에 추가: " Hello 안녕하세요"
- 공백 처리: "ĠHelloĠ안녕하세요"
- 이후 각 문자는 UTF-8 바이트 수준에서 처리됨
- tokenizer
- ["Ġ", "Hello", "Ġ", "안녕", "하세", "요"]
데이터셋으로 토크나이저 학습
- tokenizer.train(files=temp_files, trainer=trainer)
print("토크나이저 학습 시작...")
# 새 토크나이저 초기화 (BPE 방식 사용)
tokenizer = Tokenizer(models.BPE(unk_token="<unk>"))
tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=True)
# 트레이너 설정
trainer = trainers.BpeTrainer(
vocab_size=32000, # 어휘 크기
min_frequency=2, # 최소 등장 빈도
special_tokens=["<s>", "</s>", "<unk>", "<pad>", "<mask>"], # 특수 토큰 <s> 시작 </s> 끝 토큰
show_progress=True,
)
# 토크나이저 학습
tokenizer.train(files=temp_files, trainer=trainer)
# 후처리 추가
tokenizer.post_processor = TemplateProcessing(
# 모델이 시작과 끝을 인식할 수 있도록
single="<s> $A </s>", # 단일 텍스트 시퀀스에 대한 형식
pair="<s> $A </s> $B </s>", # 텍스트 쌍에 대한 형식 (예: 질문-답변, 번역 등)
# 특수토큰 : 시작, 끝
special_tokens=[
("<s>", tokenizer.token_to_id("<s>")),
("</s>", tokenizer.token_to_id("</s>")),
],
)
tokenizer.decoder = decoders.ByteLevel()
# 새 토크나이저 저장
os.makedirs("custom_tokenizer", exist_ok=True)
tokenizer_path = "custom_tokenizer/tokenizer.json"
tokenizer.save(tokenizer_path)
print(f"토크나이저 학습 완료, 저장 경로: {tokenizer_path}")
Hugging Face 형식으로 토크나이저 변환
hf_tokenizer = PreTrainedTokenizerFast(
tokenizer_file=tokenizer_path,
bos_token="<s>",
eos_token="</s>",
unk_token="<unk>",
pad_token="<pad>",
mask_token="<mask>"
)
hf_tokenizer.save_pretrained("custom_tokenizer")
print("Hugging Face 형식 토크나이저 저장 완료")
챗봇용 데이터 형식으로 변환
def format_for_chatbot(dataset_split):
examples = []
for item in dataset_split:
# 질문-답변 형식으로 변환
instruction = item["question"]
context = item["context"]
answer = item["answers"]["text"][0] if item["answers"]["text"] else "답변을 찾을 수 없습니다."
examples.append({
"instruction": instruction,
"context": context,
"response": answer
})
return examples
train_chatbot_data = format_for_chatbot(dataset["train"])
valid_chatbot_data = format_for_chatbot(dataset["dev"])
# 챗봇 형식의 데이터 저장
os.makedirs("chatbot_data", exist_ok=True)
with open("chatbot_data/train.json", "w", encoding="utf-8") as f:
json.dump(train_chatbot_data, f, ensure_ascii=False, indent=2)
with open("chatbot_data/valid.json", "w", encoding="utf-8") as f:
json.dump(valid_chatbot_data, f, ensure_ascii=False, indent=2)
print("챗봇 데이터 준비 완료")
3. 언어 모델 학습
모델 로드
custom_tokenizer = AutoTokenizer.from_pretrained(
"./custom_tokenizer",
trust_remote_code=True
)
# 모델 로드
# unsloth 라이브러리 FastLanguageModel
model, _ = FastLanguageModel.from_pretrained(
model_name="MLP-KTLim/llama-3-Korean-Bllossom-8B",
max_seq_length=2048, #2048개의 토큰까지 처리. 대략 4000자
dtype=None,
load_in_4bit=True, # 4bit 양자화: 기본 32bit를 1/8로 줄임.
use_flash_attention_2=False
)
생략된 부분: 모델 학습전에 항상 토크나이저의 어휘사전 크기와 모델의 어휘사전 크기를 비교한다. 또한, 모델의 어휘사전 크기 초기화 뒤에는 실제 값도 초기화하는 과정이 필요하다.
LLoRA 구성
# model = prepare_model_for_kbit_training(model)
# LoRA 구성
lora_config = LoraConfig(
r=16, # LoRA에서 사용하는 행렬의 랭크(rank) : 높을수록 표현력 증가, 학습량 증가
lora_alpha=32,# LoRA 업데이트의 스케일링 계수 : 보통 r의 2배
# LoRA를 적용할 특정 모듈(레이어)의 이름 목록
# q_proj, k_proj, v_proj, o_proj: 어텐션 메커니즘의 쿼리, 키, 값, 출력 프로젝션
# gate_proj, up_proj, down_proj: MLP/FFN 부분의 게이트, 업스케일, 다운스케일 프로젝션
target_modules=["q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj"],
lora_dropout=0.05, # LoRA 어댑터에 적용되는 드롭아웃 : 과적합 방지 (보통 0.0~0.1)
bias="none", # 성능에 큰 영향X 보통 비활성화
# LoRA가 적용될 태스크 유형 지정
# "CAUSAL_LM": 인과적 언어 모델링(다음 토큰 예측)
# "SEQ_CLS": 시퀀스 분류
# "SEQ_2_SEQ_LM": 시퀀스-투-시퀀스 모델링
task_type="CAUSAL_LM"
)
# LoRA 어댑터 적용
model = get_peft_model(model, lora_config)
데이터셋(korquad) 로드 및 준비
def prepare_training_data(dataset):
result = []
for example in dataset:
# 맥락과 질문을 결합하여 입력 생성
if 'context' in example and 'question' in example:
context = example['context']
question = example['question']
# 답변이 있는 경우
if 'answers' in example and example['answers']['text']:
answer = example['answers']['text'][0]
text = f"### 맥락:\n{context}\n\n### 질문:\n{question}\n\n### 답변:\n{answer}"
result.append({"text": text})
return result
# 데이터셋 로드 및 준비
korquad = load_dataset("KETI-AIR/korquad", "v1.0")
train_data = prepare_training_data(korquad["train"])
valid_data = prepare_training_data(korquad["dev"]) # dev가 validation 데이터
데이터셋 인스턴스
class TextDataset(Dataset):
def __init__(self, data, tokenizer, max_length=512):
self.data = data
self.tokenizer = tokenizer
self.max_length = max_length
def __len__(self):
return len(self.data)
def __getitem__(self, idx):
text = self.data[idx]["text"]
encodings = self.tokenizer(
text,
truncation=True,
max_length=self.max_length,
padding="max_length",
return_tensors="pt"
)
input_ids = encodings.input_ids[0]
attention_mask = encodings.attention_mask[0]
return {
"input_ids": input_ids,
"attention_mask": attention_mask,
"labels": input_ids.clone()
}
# 데이터셋 인스턴스 생성
train_dataset = TextDataset(train_data, custom_tokenizer)
valid_dataset = TextDataset(valid_data, custom_tokenizer)
언어 모델 학습
# 학습 설정
training_args = TrainingArguments(
output_dir="./fine_tuned_model",
per_device_train_batch_size=4,
per_device_eval_batch_size=4,
evaluation_strategy="steps",
eval_steps=500,
logging_steps=100,
gradient_accumulation_steps=4,
num_train_epochs=3,
weight_decay=0.01,
warmup_steps=500,
lr_scheduler_type="cosine",
learning_rate=2e-4,
save_steps=1000,
fp16=True,
save_total_limit=3,
load_best_model_at_end=True,
report_to=["none"]
)
# 트레이너 설정 및 학습 시작
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
eval_dataset=valid_dataset
)
# 학습 실행
trainer.train()
# 모델 저장
model.save_pretrained("./final_model")
custom_tokenizer.save_pretrained("./final_model")
Tags:
AI개발_교육