한달간 참여했던 난독화된 한글 리뷰 복원 모델 개발 회고를 해보려고 한다.
결과적으로는 상위 10%내 등수를 기록했다. 🌊( 22등 / 291 팀)
사실 sLLM을 파인튜닝 해 본 경험이 없어서 실험을 해보면서 궁금했던 점들을 해소하고자 하는 목적과 실제로 10B 이하의 언어모델들을 어떻게 파인튜닝 하는지에 대해서 공부해보고자 시작했다.
얼마없는 푼돈을 쥐고 GPU를 결제해가며 개발했던 여정을 간단히 정리해본다.

TASK
흔히 말해서 에어비앤비체
라고 한다. 숙소 예약 서비스인 "에어비앤비"에서 부정적인 후기가 삭제되지 않도록하기 위해서 한글을 변형하여 활용한 것에서 비롯된것이다.
예를 들어서 " 확잠실읾 념뭄 잙앙욤" 라고 후기를 남기면 번역기는 해석을 하지 못하지만 한국인들은 "화장실이 너무 작아요" 라는 말이라는 것을 알 수 있다.
바로 이렇게 변형된 한글, 즉 난독화된 한글을 번역하는 모델을 개발하는 것이 이번 과제였다.
Data
주어진 train 데이터는 약 1만개로 난독화된 리뷰와 원본 리뷰가 pair로 구성되어 있다.

Base Model
한국어를 적용해야하는 모델이다보니, 한국어에 대한 이해도가 높은 모델을 활용하는 것이 좋을 것이라고 판단이 되었다. ( 크기가 큰 모델일수록 패턴 매칭에 유리하다고 하지만 나의 자원은 한정적이기에...)
한국어로 파인튜닝된 모델 중에 10B 이하의 모델 사용하기로 했고, Few-shot prompting을 통해서 추론을 하여 결과가 나은 모델을 베이스 모델로 선택했다.
처음엔 Zero-Shot prompting을 하여 추론을 했었는데 그냥 input 그대로 내뱉음을 확인
결과는 다음과 같았다

beomi/gemma-ko-7b 모델이 다른 모델에 비해서 비교적 좋은 성능을 보였다.
원본의 형식과 의미를 변형하지 않으면서 문자만 복원할 것
위의 조건을 잘 지키는 것으로 보여서 Fine-Tuning을 실시하여 모델에 패턴을 이해시키는 것이 모델 성능을 올리는 것에 좋을 것이라고 판단했다.
Supervised Fine-Tuning ( 파인튜닝)
Instruction Tuning
Instruction Tuning 방법론은 파인튜닝 시 모델을 특정 데이터 셋으로 학습시키는 방법이다. 이 데이터 셋의 구성이 사용자의 구체적인 지시( instruction)과 이에 대한 적절한 응답(output)으로 구성되어 있다. 이렇게 구성된 데이터 셋을 학습함으로써, Zero-shot 만으로도 답변을 도출할 수 있게 되는 것이다.
Prompt Template
Alpaca (주로 single turn에 활용)
Stanford Alpaca 프로젝트에서 유래된 템플릿으로, GPT 기반의 모델을 Instruction Tuning을 통해 개선한 방식이다.
https://github.com/Beomi/KoAlpaca
chat template (주로 multi-turn에 활용)
사용자와 모델간의 대화를 보다 체계적이고 알관되게 구성하기 위한 형식이다.
요즘에 가장 많이 활용되는 template이고, multi-turn을 고려할 시 유용하다.
본 모델의 chat template이 있을 경우에는 해당 템플릿을 따라서 구성하는 것이 좋다.
QLora를 활용한 SFT
SFT란 ? 사전 훈련된 모델을 특정 작업에 맞춘(task-specific) 라벨링된 데이터로 훈련하여 모델의 출력을 원하는 응답과 일치하는 것이다.

SFT과정에서는 프롬프트(instruction + input) 부분은 마스킹 처리되고 모델이 생성해야하는 응답( Response) 부분만 손실(Loss)를 계산하여 모델이 응답을 예측하는 것에 집중하여 훈련할 수 있도록 한다.
prompt + input → 손실 계산 X (마스킹 처리)
Response → 손실 계산 O
[2024.11.27] SK WaveHill Meetup - LLM Fine-tuning - Speaker Deck
PEFT(Parameter Efficient Fine-Tuning) : LoRA
LoRA : Low rank Decomposition 행렬을 활용하여, 전체 가중치를 업데이트하는 것이 아니라 특정 행렬에 대한 가중치를 업데이트하여 Fine-Tuning을 진행하는 방식이다.

이러한 방법은 핵심적인 파라미터만 조정하기 때문에 기존의 성능을 유지하면서도 새로운 작업에 맞춰 모델의 최적화를 할 수 있으며, 소수의 파라미터만을 훈련하기 때문에 저렴한 비용으로 훈련이 가능하다.
lora_config = LoraConfig(
r = 16 ,
lora_alpha = 32 ,
lora_dropout = 0.05,
target_modules=["q_proj", "o_proj", "k_proj", "v_proj", "gate_proj", "up_proj", "down_proj"],
task_type="CAUSAL_LM",
)
Train(학습)
학습을 할 때 가장 중요한 하이퍼파라미터는 학습률(Learing Rate)이다. 기존의 가중치에서 얼마나 크게 가중치를 업데이트 해줄 지를 결정해주는 파라미터이다.
pre-trained 된 모델의 학습된 정보를 최대한 유지하기 위해, 학습률을 적절하게 설정할 것.
일반적으로 작은 학습률을 활용하며,
2e-4 ~ 1e-7
사이의 학습률을 주로 사용한다.데이터와 모델따라 다르기 때문에 학습이 진행되는 동안 반드시
eval loss 를 모니터링
하며 , 안정적으로 학습되는 학습률을 찾을 것! (학습 시 wandb를 연결하면서 모니터링하세요)optimizer 은 주로 adamw 계열 옵티마이저를 주로 사용


↑ llm 으로 파인튜닝하는데 있어서 많은 도움이 된 블로그이다.
BASE_MODEL = "beomi/gemma-ko-7b"
base_model = AutoModelForCausalLM.from_pretrained(BASE_MODEL, device_map="cuda")
model = PeftModel.from_pretrained(base_model, adapter_model, device_map="cuda")
merged_model = model.merge_and_unload() # 모델 병합 및 로드
merged_model.push_to_hub("zzoming/Gemma-Ko-7B-SFT-AUG5") #허깅페이스로 PUSH
tokenizer = AutoTokenizer.from_pretrained()
tokenizer.push_to_hub("zzoming/Gemma-Ko-7B-SFT-AUG5")

Data Augmentation (데이터 증강)
데이터의 품질도 중요했지만 이번 대회에서는 데이터의 다양성
이 더 중요하다고 판단했다.
난독화된 한글 특성상 랜덤으로 종성이 추가 되거나, 연음법칙이 적용되거나 유사한 발음인 자모로 변환되는 현상이 나타난다. 그래서 조금 더 다양한 변화를 모델이 학습할 수 있게 데이터를 증강하는 것이 모델의 성능에 더 도움이 될 것이라고 판단을 했다.

다만 train 데이터만을 활용해서 증강을 해야된다는 것이 조금 아쉬웠지만 말이다. 😭
실제로 데이터를 1만개로 2epoch를 학습한 것 보다 데이터 5만개를 1 epoch 학습한 것이 더 성능이 높게 나타났다. 데이터 증강 기법은 다른 글에 정리해두었다. ✌🏼
epoch를 2로 설정한 이유는 3epoch 부터는 eval loss 가 다소 증가하여 과적합으로 판단
Inference(추론)
transformer로 Fine-Tuning 된 모델을 Inference 해보신 분 있으십니까!! 그게 바로 접니다!!

다른 분들은 transformer 말고 vllm 이나 llama.cpp를 활용하시는 것을 추천한다. 나는 vllm
을 활용해서 추론을 했고, transformer로 추론을 할 때 보다 시간을 정말 많이 아꼈다.
longest_sentence = max(test_data['input'], key=len)
print("가장 긴 문장:", len(longest_sentence))
# output
# 가장 긴 문장: 1380
print(llm.llm_engine.scheduler_config.max_num_seqs) # 배치 사이즈 확인
# output
# 256
기본 배치사이즈는 256이다. 이것은 본인의 GPU 용량에 맞춰서 사이즈를 조절해주면 된다.
restore_reviews = []
start_time = time.time()
llm.llm_engine.scheduler_config.max_num_seqs = 128 # 배치 사이즈 조절 256 > 128
sampling_params = SamplingParams(temperature = 0.2 ,
top_p = 0.9,
top_k = 20,
seed = 42 ,
max_tokens = 2048 ,
stop_token_ids = [eos_token_id])
outputs = llm.generate(test_data['text'], sampling_params)
for output in outputs :
generated_text = output.outputs[0].text
restore_reviews.append(generated_text)
print(time.time() - start_time)
# 1775.312693119049
자세한 내용들은 다음 공식문서를 참조하자. 모델을 양자화해서 올리는 것도 가능하다!
실험결과 정리
모델 | Fine-Tuning | Data | 전처리 | 추론 | 성능 |
Ko-gemma-7B | O (1epoch) | train | 적용 X | Transformer | 0.79 |
Ko-gemma-7B | O (2epoch) | train | 적용 X | Transformer | 0.83 |
Ko-gemma-7B | O (2epoch) | train | 적용 O | vllm | 0.83 |
Ko-gemma-7B | O (2epoch) | train + 증강 | 적용 X | vllm | 0.849 |
Ko-gemma-7B | O (1epoch) | train + 증강 ( 약 5만개) | 적용 X | vllm | 0.852 |
Ko-gemma-7B | O (3epoch) | train + 증강 ( 약 5만개) | 적용 X | vllm | 0.81 |
이외의 다른 모델 QWEN-14B 모델이나 GEMMA-27B-IT 모델 등 큰 모델들을 시도를 해봤으나, 큰 성능의 확보가 이루어지지는 못했다. 그리고 사실 성능이 비슷하다면 작은 크기의 모델을 쓰는 것이 더 유리하다고 판단했다. ( 더 많은 추론이나 학습들을 시도해봐야 되기 때문이다)
한계점
넘지 못했던 산은 후처리이다. 모델을 추론을 한 후에는 굉장히 복원을 잘 하는 문장도 있으나, 자연스럽지 않거나 맞춤법이 지켜지지 않은 문장들도 있었다.
아래 예시는 직접 추론한 문장 중 하나인데 까마귀
가 갈마귀
로 복원이 되어버린 것이다.
뷰 맛집~~ 그런데 방음이 미흡하네요. 층간 소음과 발코니가 이중창이 아니라서 밤에 파도 소리, 아침에 갈매기인지 갈마귀인지 계속 울어서 잠을 못 잤어요ㅠ 그런데 뷰는 너무 좋아요~~~
사실 이 과정은 주로 음성인식 후 텍스트 교정에 많이 쓰일 듯해서 GEC(Grammatical Error Correction) 분야를 많이 찾아봤다. 주로 BERT 모델을 사용하는 경우도 있고, 레벤슈타인 거리(편집거리) 알고리즘 이나 맞춤법 라이브러리들도 살펴봤다.
사실 이 고민을 했을시점에는 거의 대회의 막바지라서 빠르게 시도해볼 수 있는 레벤슈타인 거리와 맞춤법 라이브러리를 활용해보는 시도를 해봤지만 성능에 도움이 되지는 못했다.
맞춤법 라이브러리는 표현을 완전히 바꿔버리거나 띄어쓰기 까지 교정되는 경우가 있어서 적절하지 못했다. ( 성능 하락 )
레벤슈타인 거리를 활용해서 가까운 단어로 대체하는 방법은 우선 복원이 잘 된 부분들 또한 바꿔버리는 경항이 있었다. " 컴플레이이 " 를 예로 들면 원본은 '컴플레인에' 지만 train 데이터로 사전을 구축하여 편집거리가 짧은 그리고 빈도수로 판단을 하면 전혀 다른 단어로 대체되버린다.
다음에는 조금 더 시간 배분과 LLM 말고도 다른 기법에 대한 공부들을 하는 것이 좋겠다. ( 개인으로 말고 팀으로 참여해야지.. 혼자하니까 해볼 수 없는 실험과 머리를 혼자 싸매야하는게 힘들다😭)

( 저와 팀으로 작업을 해볼 분들 언제든지 대.환.영 )
배운점 & 느낀점 ⭐
해당 task을 잘 수행할 수 있는 모델을 찾는 것이 중요하다. 단순히 크기가 큰 모델이 유리한 것은 아니며, 최적화가 잘 수행될 수 있는 모델을 선택하는 것이 핵심이다.
모델이 공개한 prompt template을 확인하고, 해당 형식을 준수하여 학습하는 것이 효과적이다
데이터의 중요도는 다양성 > 품질 > 양 순으로 판단된다. 단순히 많은 데이터를 사용하는 것보다, 다양한 데이터를 확보하는 것이 더 효과적이다.
LLM이 모든 문제를 해결해 주지는 않는다. 주어진 과제에 적합한 모델 사용을 위해 모델 공부를 소홀히 하지 말자. (해당 모델이 좋다고 내가 해결해야하는 과제에 잘한다고는 할 수 없다는 것이다.)
이외에도 GPU를 사용하기 위한 서버를 구축하는 과정들을 경험하면서 모델 개발 이외에도 인프라 구축 및 운영의 중요성도 잠시 배울 수 있었다.