LLM의 학습 과정은 크게 Pretraining, Fine-tuning으로 분류된다.
MLM은 학습 문장에서 일부 토큰을 [MASK]
로 가리고, 해당 위치에 올 단어를 예측하는 방식으로 학습한다. 이러한 방식은 양방향(Bidirectional)으로 학습이 가능하게 해준다.
주로 BERT(Bidirectional Encoder Representations from Transformers)와 같은 모델에서 사용하는 Pretraining 방식이다.
손실 함수로 Cross-Entropy Loss를 사용한다.
예시로, 나는 오늘 [MASK] 마셨다.
를 나는 오늘 커피를 마셨다.
로 출력하도록 학습하는 방식이다.
이러한 방식은 양방향 학습으로 왼쪽, 오른쪽 문맥을 모두 활용 가능하다는 장점이며 이를 통해 NLU(Natural Language Understanding)에서 강한 모습을 보여 준다. 하지만, 문장을 생성하는 능력은 부족하다.
GPT, LLaMA 등 여러 생성형 LLM 모델이 주류로 자리 잡은 현재에는 특정 분야에서만 사용 되는 방식(검색, 문서 분류, 개체명 인식)이 되어 가고 있으며, 그 마저도 점점 대체되어져 가고 있다.
CLM은 한방향(좌→우)으로만 학습이 이루어지며, 이전 토큰과 현재 토큰을 기반으로 다음 토큰을 예측하는 방식을 사용한다. 이러한 방식은 GPT, LLaMA, Mistral AI 등 현재 대부분에 생성형 인공지능이 학습에 사용하는 방식이다.
아래의 식에서 처럼 현재의 토큰 의 확률은 앞에 나온 모든 토큰 에 의해 결정 된다.
예시로, 나는 오늘 커피를
을 입력받아 나는 오늘 커피를 마셨다.
를 출력하도록 학습한다.
이러한 방식은 NLG(Natural Language Generation)에 적합하지만, 이전 토큰 만을 학습 하므로 MLM 모델보다 문맥 이해력이 떨어질 수 있으며 보다 많은 데이터를 필요로 한다.
CLM과 Transformer
GPT를 비롯한 대부분 AI모델은 Transformer 기반으로 CLM 방식을 사용하는데 CLM은 순차적으로 하나의 토큰을 예측하는 방식이고 Transformer 모델은 Self-Attention을 기반으로 하여, 전체 문장을 한번에 입력 받는다. 얼핏보면 CLM과 Transformer가 공존하는게 모순처럼 보인다.
이는 Transformer가 Causal Masking으로 CL방식을 흉내내기 때문에 가능하다.
이를 통해 훈련(Training) 시에는 병렬 연산이 가능하지만, 추론(Inference) 시에는 Auto-Regressive 방식으로 한 단어씩 생성한다.
Full Fine-Tuning은 Pretrained 모델의 모든 파라미터를 학습 가능한 상태로 두고, 새로운 데이터셋을 사용하여 추가 학습을 진행하는 방식이다.
특정 도매인이나 목적에 맞게 완전히 맞춘 모델을 만들 수 있는 것이 장점이지만, 수십~수백억 개의 파라미터를 업데이트 하는 것은 높은 메모리 사용량과 높은 비용을 요구한다. 또한 기존에 학습된 정보가 손실되는 Catastrophic Forgetting 문제도 야기할 수 있다.
이러한 단점 때문에, 갈수록 거대한 파라미터를 가지는 최신 모델 (ex. Gemini, GPT-4)이 주류로 자리 잡은 요즘에는 Full Fine-Tuning은 거의 사용하지 않는다.
PEFT는 전체 가중치가 아닌 일부 가중치 만을 학습하는 방식이다. 이러한 PEFT 방식들은 대부분 Fine-Tuning에 사용되는 메모리를 크게 줄일수 있으며, 원본 모델의 가중치를 그대로 유지하여 원복 이나 다양한 태스크에 적용을 쉽게 만든다.
기존 모델의 가중치를 고정하고, 추가적인 Low-Rank 행렬 (, )만 학습하는 형태이다.
일반적인 Transformer의 가중치 행렬을 라고 할때 LoRA는 자체에 업데이트가 아닌 , 두개의 저차원 행렬만을 업데이트 하는 방식이다. 여기서 은 Low-Rank에 값으로 8, 16, 32등에 값이 활용 된다.
아래는 LoRA Fine-Tuning의 예시이다.
from peft import LoraConfig, get_peft_model
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer
# 기본 모델 로드 (예: LLaMA-2 7B)
model_name = "meta-llama/Llama-2-7b-hf"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name, device_map="auto")
# LoRA 설정
lora_config = LoraConfig(
r=8, # Low-rank 값 (8, 16, 32 중 선택)
lora_alpha=16, # Scaling factor
lora_dropout=0.1,
bias="none",
task_type="CAUSAL_LM" # GPT 기반 모델에 적용
)
# 모델에 LoRA 적용
model = get_peft_model(model, lora_config)
model.print_trainable_parameters() # 학습 가능한 파라미터만 출력
# Fine-tuning 설정
training_args = TrainingArguments(
output_dir="./lora_model",
per_device_train_batch_size=4,
gradient_accumulation_steps=4,
learning_rate=5e-5,
num_train_epochs=3,
logging_dir="./logs",
)
# Trainer 실행
trainer = Trainer(
model=model,
args=training_args,
train_dataset=my_train_dataset,
)
trainer.train()
QLoRA는 LoRA와 Quantization(양자화)를 결합한 방식으로, 4-bit 양자화를 적용한 후 LoRA를 사용하여 메모리 사용량을 더욱 줄이는 방식이다.
기존에 모델의 가중치는 FP16(16-bit 부동소수점) 또는 BF16(bfloat16)으로 저장되지만, QLoRA에서는 4-bit정수(int4)로 변환하여 사용한다. 이는 기존 메모리 사용량에서 75% 이상을 감소시킬 수 있다.
Adapter Tuning은 기존 모델의 가중치는 고정한 채, 별도의 작은 네트워크(Adapter)를 추가하여 학습하는 방식이다. 하지만 LoRA에 비해 학습 속도와 유연성이 떨어져 사용 빈도는 떨어지는 편이다.