Pretraining: 대규모 일반 지식 학습
Fine-tuning: 특정 업무 능력 학습
Full Fine-tuning 모든 Weight를 수정한다. 모든 Parameter가 변경. model.parameters()를 그대로 넘긴다.
from transformers import AdamW
optimizer = AdamW(model.parameters(), lr=2e-5)
for batch in train_loader:
outputs = model(
input_ids=batch["input_ids"],
attention_mask=batch["attention_mask"],
labels=batch["labels"]
)
loss = outputs.loss
optimizer.zero_grad()
loss.backward() # 모든 Weight의 Gradient 계산
optimizer.step() # 모든 Weight 업데이트
Feature Transfer (Transfer Learning, Feature Extraction) 일부 Layer만 학습한다. 보통 마지막 분류기만 학습한다.
모델을 생성하면 구조가 아래와 같다. model.classifier는 BERT의 마지막 분류기 Classification Head를 의미한다.
왜 이름이 classifier인가, Hugging Face에서 모델을 만들 때 마지막 분류 레이어의 이름을 classifier 로 지어놓았기 때문이다. 이는 모델마다 이름이 다르며 GPT나 Llama의경우 model.lm_head라하고 ResNet은 model.fc라 하는데 이는 print(model)을 출력해보면 된다.
논리적으로 classifier는 레이어는 하나지만, 그 안에는 여러 개의 Parameter(weight, bias)가 있기 때문(weight, bias)에 for문을 사용한다.
model
├── bert
│ ├── embeddings
│ └── encoder
└── classifier
BertForSequenceClassification(
(bert): BertModel(...)
(dropout): Dropout(...)
(classifier): Linear(in_features=768, out_features=2)
)
from transformers import AutoModelForSequenceClassification
from transformers import AdamW
model = AutoModelForSequenceClassification.from_pretrained(
"bert-base-uncased",
num_labels=2
)
# ① 모든 Layer Freeze
for param in model.parameters():
param.requires_grad = False
# ② 마지막 Classifier만 학습
for param in model.classifier.parameters():
param.requires_grad = True
optimizer = AdamW(
model.classifier.parameters(),
lr=2e-4
)
Instruction Tuning Instruction SFT(Supervised Fine-Tuning 형태 데이터로 학습, 지도학습(Supervised Learning) 방식
ChatGPT도 Instruction Tuning을 거쳤다. LLM에서 가장 중요한 Finetuning 방식.
지시문에서 원하는 답변 형태의 데이터로 모델을 추가학습 시키는것. Pretraning만 한 GPT는 다음 단어를 예측해서 이어쓰는것은 잘하나 요약, json출력, 번역, 코드작성등의 지시를 따르는 능력이 부족함으로 instruction데이터로 다시 학습한다 .
Instruction:
다음을 번역하라.
Input:
Hello
↓
Response:
안녕하세요.
sample = {
"instruction": "영어를 한국어로 번역해라.",
"input": "Hello",
"output": "안녕하세요."
}
prompt = f"""
### Instruction:
{sample['instruction']}
### Input:
{sample['input']}
### Response:
{sample['output']}
"""
from transformers import Trainer
trainer = Trainer(
model=model,
train_dataset=train_dataset,
)
trainer.train()
PEFT (Parameter Efficient Fine-Tuning) 가장 많이 사용하며 모든 Parameter를 수정하지 않는다. GPU 적게 사용하며 빠르고 성능이 거의 동일하다 .
PEFT
├── LoRA
├── QLoRA
├── Adapter
├── Prefix Tuning
├── Prompt Tuning
├── P-Tuning v2
├── IA3
├── AdaLoRA
├── DoRA
├── OFT
├── Vera
LoRA(Low-Rank Adaptation) 현재 가장 많이 사용한다 Low Rank Matrix만 추가한것. A, B : 새로 학습하는 작은 행렬
기존 Weight는 건드리지 않고, 작은 행렬 2개(A, B)만 추가해서 학습한다.
왜 Low Rank일까? 원래 Weight가 (4096 × 4096)라면 Parameter 개수는 4096 × 4096 ≈ 1,677만 개로 굉장히 많다. oRA는 A (4096 × 8) B (8 × 4096) 처럼 가운데 Rank(r)를 아주 작게둬 파라미터 수를 줄인다 .4096×8 +8×4096 = 65,536개
r LoRA Rank 가운데 차원
lora_alpha LoRA Scaling 값, 업데이트 크기를 조절하며 실무에서는 16, 32, 64를 자주 사용한다 .
lora_dropout 과적합 방지를 위한 Dropout
target_modules Transformer 안에는 Linear Layer가 굉장히 많고 각각이 모두 nn.Linear(...)이다 . 논문에서 실험해보니 Attention의 q_proj, v_proj만 수정해도 성능이 상당히 잘 나왔고 해당 만 수정하는것.
* q_proj, v_proj : classifier와 같은 Transformer 모델 내부에서 각 Linear Layer에 붙여놓은 이름(attribute)
from transformers import AutoModelForCausalLM, Trainer
from peft import LoraConfig, get_peft_model
# ① 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
# ② LoRA 설정
lora_config = LoraConfig(
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=["q_proj", "v_proj"]
)
# ③ LoRA 추가
model = get_peft_model(model, lora_config)
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
QLoRA 4bit 양자화 70B 모델도 단일 고메모리 GPU에서 학습 가능한 수준까지 메모리 요구량을 크게 줄일 수 있다.
LoRA + 4bit 양자화(Quantization)
LoRA에 4bit 양자화를 적용하여 메모리 사용량을 크게 줄인 Fine-tuning 기법
LoRA도 메모리는 적게 쓰지만,기존 모델 자체는 메모리에 올라가기때문에 LoRA만 사용해도 GPU에 문제가 생길 수 있다 .QLoRA는 16bit - 4bit로 압축한다. load_in_4bit=True 원래 16bit Weight을 4bit Weight로 메모리에 올려 get_peft_model() LoRA를 붙이겠다라는 의미.
import torch
from transformers import AutoModelForCausalLM
from transformers import BitsAndBytesConfig, Trainer
from peft import LoraConfig, get_peft_model
# ① 4bit 양자화 설정
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16
)
# ② 모델 로드(4bit)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B",
quantization_config=bnb_config
)
# ③ LoRA 설정
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"]
)
# ④ LoRA 추가
model = get_peft_model(model, lora_config)
# ⑤ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
Adapter Layer 사이에 작은 Network 추가한다.
기존 모델은 Freeze하고, Layer 사이에 작은 신경망(Adapter)을 추가하여 그 Adapter만 학습하는 기법으로 원래 모델은 건드리지 않는다. Attention 뒤나 MLP 뒤에 작은 네트워크를 하나 넣는다. 기존 모델은 그대로이고, Adapter만 새로 추가된다. Adapter는 아주 작은 MLP로 메모리 사용량 적고 원래 모델 변경 안하며 여러 Task마다 Adapter만 저장 가능하다는 장점이있다 . 단점은 Adapter를 통과하는 연산이 추가된다는 것인데 즉 추론 속도가 LoRA보다 조금 느리다.
둘모두 기존 LLM Weight는 Freeze, 일부만 학습, 메모리 절약한다는 점은 같으나 LoRA는 기존 Layer를 보정하는 방식으로 기존 연산에 작은 보정값을 더하는 것이고 Adapter는 진짜 새로운 신경망을 추가해 추가 연산이 발생한다.
LoRA는 "용량을 줄이는 기술"이 아니라 "파인튜닝를 더 효율적으로 하는 기술"이다.
파인튜닝이란 새로운 데이터에 맞게 모델을 추가 학습하는 것으로
from adapters import AutoAdapterModel
from transformers import Trainer
# ① 모델 로드
model = AutoAdapterModel.from_pretrained(
"bert-base-uncased"
)
# ② Adapter 추가
model.add_adapter("sentiment")
# ③ Adapter만 학습
model.train_adapter("sentiment")
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
| LoRA | Adapter |
| Linear Layer에 작은 A/B 행렬 추가 | Layer 사이에 작은 신경망 추가 |
| 기존 연산 경로를 수정 | 새로운 네트워크를 삽입 |
| 추론 속도 빠름 | 추론이 약간 느림 |
| 현재 가장 많이 사용 | 현재는 사용 빈도가 상대적으로 낮음 |
Prefix Tuning Prompt 앞에 Virtual Token 추가한다.
모델 전체를 학습하지 않고 일부 파라미터만 학습하는 파인튜닝 방법
LoRA → A/B 행렬만 학습
Adapter → Adapter만 학습
Prefix Tuning → Virtual Token만 학습
현재는 LoRA > QLoRA > Prefix Tuning 순으로 사용 빈도가 높다 .
ranslate to Korean. 이 문자열을 모델이 Translate, to, Korean, 토큰으로 바꾸고 [0.1,0.5,...] 토큰을 벡터로 바꾸는 embedding을 거친다. LLM은 글자가 아닌 벡터를 보게된다 .이때 Prefix Tuning은 p1등의 입력앞에 [0.43,-0.12,0.88,...] 랜덤 벡터를 붙인다.
처음에는 번역 결과가 좋지 않아 Loss가 발생한다. 역전파를 통해 모델의 Weight는 수정되지 않고, P1과 같은 Virtual Token만 업데이트된다. 이 과정을 수천 번 반복하면 P1은 번역 작업에 적합한 벡터로 학습된다. Transformer의 Self-Attention은 Translate, Hello와 같은 입력 토큰뿐 아니라 P1도 함께 참고하여 계산하므로, 학습된 P1이 붙으면 모델은 번역 작업에 적합한 출력을 생성하게 된다. P1은 '번역'이라는 단어를 담고 있는 것이 아니라, 번역에 적합한 Attention 패턴을 유도하는 숫자 벡터이다.
그냥 Weight 수정하면 될텐데 왜 그러는걸까? 예를 들어 ChatGPT는 이미 번역, 요약, 코딩, 수학 다 할 줄안다 .다만 번역을 잘하게하기위해 뇌를 뜯어고쳐야할까? 에서 입력을 조금 바꿔줄까? 라는 아이디어가 나온것. 사람이 영어를 잘한다고 Hello를 말하면 번역을 안할수도있으나 영어를 한국어로 번역하세요, hello라고 하면 번역한다 . 입력이 달라졌기 때문이고 뇌는 안바뀌었다.Prefix도 마찬가지로 원래 입력 Hello에 Prefix를 붙여 P1이 '이제부터 번역해'라는 역할을 하도록 학습하는 것이다 .
p1은 정확하게 데이터가 아닌 파라미터이며 700억개로 넘쳐나는 Weight를 수정하지않고 Prefix르 수정해 학습량을 줄이는 것이다. 회사직원 7000명을 모두 교육하기보다 메뉴얼을 한장 새로 만들어 직원이 참고하게 하는게 좋다라는 의미.
마찬가지로 LoRA도 weight안건들인다. 조그만 A, B 행렬만 학습한다.
입력 앞에 학습 가능한 Virtual Token(가상 토큰)을 추가하고, 이 Virtual Token만 학습하는 PEFT 기법. 실제 단어가 아니라, 임베딩 벡터(학습 가능한 가상 토큰)를 앞에 붙인다. 이 Virtual Token은 Vocabulay에 있지않고 num_virtual_tokens 몇개를 get_peft_mode 생성한다.의미도 없는 토큰을 앞에 붙여서 뭘 배울까? Virtual Token은 벡터를 랜덤하게 생성함으로 처음에는 의미가 없으나이제부터 번역 모드라고 신호를 주는것과 다름이 없다.
* PEFT란?: PEFT(Parameter-Efficient Fine-Tuning)는 사전학습된 모델의 모든 파라미터를 학습하지 않고, 일부 파라미터만 학습하여 특정 작업에 적응시키는 파인튜닝 기법
Prefix Tuning은 사람이 직접 Prompt를 작성하는 대신, 모델이 사용할 최적의 Prompt(실제로는 임베딩 벡터)를 학습하는 PEFT 기법
Prompt를 사람이 쓰는 대신 모델이 스스로 최적의 Prompt를 학습한다.
from transformers import AutoModelForCausalLM, Trainer
from peft import PrefixTuningConfig, get_peft_model
# ① 사전학습 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
# ② Prefix Tuning 설정
prefix_config = PrefixTuningConfig(
task_type="CAUSAL_LM",
num_virtual_tokens=20
)
# ③ Prefix(가상 토큰) 추가
model = get_peft_model(model, prefix_config)
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
Prompt Tuning Prompt Embedding만 학습해 매우 가볍다.
입력 앞에 Prompt Embedding(학습 가능한 Prompt)만 추가하여 학습하는 PEFT 기법, 사람이 Prompt를 쓰는 대신 Prompt 자체를 학습한다. 그렇다면 앞서 살펴본 Prefix Tuning과 뭐가 다른걸까? Prompt Embedding을 입력(Input Embedding)에만 붙인다. 즉 입력에만 영향을 준다는것인데 Prefix Tuning은 입력 앞에도 붙이고, Transformer의 모든 Layer의 Self-Attention에도 Prefix(Key/Value)를 추가하지만 입력 앞 Prompt Embedding에만 학습한다.일반적으로는 당연히 Prefix가 성능이 좋다 .
사실 입력만 보면 prefix와 거의 같으나 모델 내부에서 사용하는 차이를 이해한다 .
Prompt Tuning는 입력에만 붙여 P1 - Hello - Transformer 간단하나
Prefix Tuning은 입력과 모든 레이어에서 p1을 게속 사용해 Layer1 ← P1 사용 - Layer2 ← P1 사용 - Layer3 ← P1 사용 더 강력해진다.
Prompt Tuning은 입력에서 한 번만 쓰고 끝나는 거야? Prefix는 매 레이어마다 쓰는 것이라고 이해하자 .
실제로 사용자 코드만 보면 prefix와 거의 같으나 PromptTuningConfig이냐 PrefixTuningConfig의 차이
from transformers import AutoModelForCausalLM, Trainer
from peft import PromptTuningConfig, get_peft_model
# ① 사전학습 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
# ② Prompt Tuning 설정
prompt_config = PromptTuningConfig(
task_type="CAUSAL_LM",
num_virtual_tokens=20
)
# ③ Prompt Embedding 추가
model = get_peft_model(model, prompt_config)
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
P-Tuning v2 Prompt Tuning 개선판 Deep Prompt 사용한다.
Prompt Tuning의 성능을 개선한 PEFT 기법
입력에서만 Prompt를 사용하는 것이 아니라 Transformer의 여러 Layer에 Deep Prompt를 적용한다. 이래버리면 P-Tuning v2와 Prefix Tuning은 상당히 비슷하다 . 차이는 구현 방식으로 Prefix는 Attention의 value에 prefix를 붙인다면 P-Tuning v2는 Layer마다 Prompt Embedding을 넣어 구현방식이 조금 다르다.
| Prompt Tuning | 입력에서만 Prompt 학습 |
| Prefix Tuning | 각 Layer Attention에 Prefix 사용 |
| P-Tuning v2 | 각 Layer에 Deep Prompt 적용, Prompt Encoder 사용 |
from transformers import AutoModelForCausalLM, Trainer
from peft import PromptEncoderConfig, get_peft_model
# ① 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
# ② P-Tuning v2 설정
config = PromptEncoderConfig(
task_type="CAUSAL_LM",
num_virtual_tokens=20,
encoder_hidden_size=128
)
# ③ P-Tuning 적용
model = get_peft_model(model, config)
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
IA3 Attention의 일부 활성화(weight scaling)만 학습한다.
IA³ (Infused Adapter by Inhibiting and Amplifying Inner Activations)
모델의 Weight는 수정하지 않고, Attention과 FFN의 활성값(Activation)에 곱해지는 작은 Scaling Vector만 학습하는 PEFT 기법. LoRA 회로 자체를 조금 바꾸는 것이라면 IA³는 볼륨 조절기만 다는 것이라고 보면 좋다 . 즉 Attention과 FFN의 출력에 곱해지는 학습 가능한 Scaling Vector만 학습하여 중요한 특징은 강조(Amplify)하고 불필요한 특징은 억제(Inhibit)하는 PEFT 기법이다.
IA³는 '최대한 적은 파라미터만 학습'하려고 만든 연구이고 Output × α만 하기때문에 사실 크기만 조절해 새로운 정보를 만들어내지는 못한다 . 논문 아이디어는 "LLM은 이미 충분히 똑똑하다." 이고 굳이 Weight를 바꾸지 않아도 이미 계산한 결과 중에서 이건 더 중요 , 이건 덜 중요만 잘 조절해도 성능이 꽤 올라간다는 것이다 .
from transformers import AutoModelForCausalLM, Trainer
from peft import IA3Config, get_peft_model
# ① 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
# ② IA³ 설정
ia3_config = IA3Config(
task_type="CAUSAL_LM",
target_modules=["k_proj", "v_proj", "down_proj"]
)
# ③ IA³ 적용
model = get_peft_model(model, ia3_config)
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
AdaLoRA LoRA 개선판, 중요한 Layer에 Rank를 많이 배분하는 자동조절 기능이 있다.
앞선 LoRA에서는 r을 모든 레이어에서 사용했다. 하지만 레이어마다 중요도가 다를수있다 .LoRA는 전부 적용하기 때문에 중요하지않은 레이어에서도 똑같은 메모리를 쓰는 것. AdaLoRA는 중요한 Layer에 Rank를 더 많이 주자라는 아이디어로 전체 Rank 예산을 두고 학습하면서 자동으로 조절한다.
from transformers import AutoModelForCausalLM, Trainer
from peft import AdaLoraConfig, get_peft_model
# ① 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
# ② AdaLoRA 설정
adalora_config = AdaLoraConfig(
task_type="CAUSAL_LM",
init_r=12, # 초기 Rank
target_r=8, # 최종 평균 Rank
lora_alpha=32,
target_modules=["q_proj", "v_proj"]
)
# ③ AdaLoRA 적용
model = get_peft_model(model, adalora_config)
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
DoRA LoRA 개선판, Weight 방향(Direction)과 크기(Magnitude)를 분리하여 학습해 LoRA보다 성능 향상을 노린다.
LoRA는 Weight를 통째로 조금 수정했었고 논문에서는 이 부분에 대해 Weight에는 방향(Direction)과 크기(Magnitude)가 있는데 둘을 한꺼번에 학습하는 것이 비효율적이지 않을까하는 생각을 했다 . 이 weight를 벡터라고 새악하고 W = [3,4]라면 크기√(3²+4²)=5 와 방향↗ 으로 나눌 수 있다 . 즉 Weight=크기(Magnitude)×방향(Direction)로 weight전체 즉 방향도 크기도 한번에 바뀌는 LoRA와 다르게 DoRA는 Magnitude+Direction로 나누어 Direction → LoRA 방식(ΔW)으로 학습하고 Magnitude → 별도의 스칼라(Parameter)로 학습한다. 즉 각각 최적화해 성능을 높인다. 이 방법이 왜 성능이 좋아질까? 자동차를 운전한다고 해도 핸들과 엑셀을 동시에 조절하는 것보다 핸들은 방향, 엑셀은 속도로 따로 조절하는것이 더 정교하다 .
다만 이론적으로는 DoRA가 더 좋아 보이지만, 실무에서는 "조금 더 좋은 성능"보다 "안정성·속도·생태계"가 더 중요하기 때문에 LoRA가 여전히 표준이다 .
from transformers import AutoModelForCausalLM, Trainer
from peft import LoraConfig, get_peft_model
# ① 모델 로드
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
# ② DoRA 설정
dora_config = LoraConfig(
task_type="CAUSAL_LM",
r=8,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
use_dora=True # ⭐ DoRA 활성화
)
# ③ DoRA 적용
model = get_peft_model(model, dora_config)
# ④ 학습
trainer = Trainer(
model=model,
train_dataset=train_dataset
)
trainer.train()
OFT Orthogonal Fine-Tuning Weight의 직교(Orthogonal) 특성을 유지하며 업데이트하여 기존 지식을 덜 훼손하도록 설계되었다.
새로운 작업을 배우면서 기존 모델 지식을 최대한 보존하자라는 아이디어로 LLM을 Fine-tuning하면 일반지식 + 언어능력 + 추론능력 등의 특정 데이터로 Fine-tuning되어 새로운 작업능력이 높아지나 기존 지식이 감소될 수 있다 .즉 기존 능력이 손상되는 것. OFT는 Weight 자체를 아무 방향으로 움직이지 말고, 기존 Weight의 구조를 보존하면서 업데이트하자는 아이디어인셈. 그래서 Orthogonal Transformation(직교 변환)를 사용한다 .회전과 같은 변환이라 볼 수 있다 . 방향은 바뀌지만 정보의 크기는 유지되는것. Weight를 직접 크게 변경하는 것이 아니라 회전시키는 방식으로 조정한다 .기존 weight 공간을 생각해보면 기존공간을 회전시키면 weight 구조를 유지하면서 새로운 task레 맞출 수 있다는것을 이해할 수 있다.
아이디어는 꽤 좋으나 대부분의 실무는 "기존 지식 보존"보다 "새 작업 성능"이 중요해 잘 사용되지않는다 .
import torch
import torch.nn as nn
class OFTLinear(nn.Module):
def __init__(self, linear):
super().__init__()
self.weight = linear.weight
# Orthogonal matrix
self.R = nn.Parameter(
torch.eye(self.weight.shape[0])
)
def forward(self, x):
# Weight transformation
new_weight = self.R @ self.weight
return x @ new_weight.T
VeRA (Vera) Very Efficient Rank Adaptation LoRA보다 학습 파라미터를 더욱 줄이는 최신 PEFT 기법 중 하나이다.
VeRA(Very Efficient Rank Adaptation)는 "LoRA를 더 극단적으로 가볍게 만든 버전"
LoRA는 기존 Weight에 작은 업데이트를 추가했다고 했다 .모든 Layer마다 LoRA A/B를 따로 가지고 있어야 할까?란 아이디어로 Layer마다 별도의 Low Rank Matrix를 저장해 공유 가능한 Random Matrix를 쓰고, 작은 Scaling Vector만 학습하는 것.
하지만 비교적 LoRA보다 많이 사용되지는 않는다.
파라미터는 줄었으나 표현력이 줄어들 가능성이있다. 직접 학습하지않고 이미 정해진 방향을 얼마나 조절할지만 배우기때문이다. 또한 LoRA는 충분히 작아 더 줄여 성능을 굳이 조금 잃을 필요가 없는것.
from peft import VeraConfig, get_peft_model
config = VeraConfig(
task_type="CAUSAL_LM",
r=8,
target_modules=[
"q_proj",
"v_proj"
]
)
model = get_peft_model(
model,
config
)
trainer.train()
RLHF Reinforcement Learning from Human Feedback 사람의 선호도를 이용한다.
Pretrained LLM은 기본적으로 다음 단어를 잘 예측하는 모델이고 자칫 너무 장황하거나 위험한 답변, 사용자 의도와 다르거나 거짓정보를 생성 가능하니 잘쓰는 모델과 사람이 원하는 모델은 다르다고 인식할 수 있다 Pretraining후 SFT (Supervised Fine-Tuning) 사람이 만든 예시를 학습해 이런 답변이 좋구나~를 배우도록 한다 .이후 사람이 답변 비교를 하고 이 데이터를 이용해 Reward Model 을 만들어 '이 답변을 사람이 좋아할까?' 하고 예측한다. 현재는 RLHF 만 쓰지않고 (gpt 3.5, chatgpt초기버전) DPO를 많이 사용한다. reward Model이 필요하고 PPO가 복잡한 단점을 보완했기 때문이다.
* PPO(Proximal Policy Optimization)는 LLM이 Reward Model에서 높은 보상을 받도록 학습시키는 강화학습 알고리즘으로, 기존 모델을 너무 크게 바꾸지 않으면서 점진적으로 개선하는 방법
# 1. SFT 모델
model = AutoModelForCausalLM.from_pretrained(
"sft-model"
)
# 2. Reward Model
reward_model = AutoModelForSequenceClassification.from_pretrained(
"reward-model"
)
# 3. PPO Trainer
ppo_trainer = PPOTrainer(
model=model,
reward_model=reward_model
)
# 4. RL 학습
response = model.generate(prompt)
reward = reward_model(
prompt + response
)
ppo_trainer.step(
reward
)
DPO (Direct Preference Optimization) 최근 RLHF를 많이 대체한다.
Reward Model과 강화학습(PPO) 없이, 사람이 선호하는 답변(Chosen)과 선호하지 않는 답변(Rejected)을 직접 비교하여 LLM을 학습하는 방법.
DPO는 Fine-tuning이지만 정확히는 Preference Fine-tuning이며 Input → 정답이 아닌Input → 좋은 답변 vs 나쁜 답변을 생각한다. LoRA와 DPO는 실무에서 같이 사용되며 DPO는 학습 방법이고, LoRA는 파라미터 업데이트 방법이니 경쟁상대가 아니라고 이해해보자.
from transformers import AutoModelForCausalLM
from trl import DPOTrainer, DPOConfig
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B"
)
training_args = DPOConfig(
output_dir="./dpo-model",
learning_rate=5e-7,
num_train_epochs=3
)
trainer = DPOTrainer(
model=model,
args=training_args,
train_dataset=train_dataset
)
trainer.train()
ORPO(Odds Ratio Preference Optimization) DPO 개선판 Preference Loss와 일반 Language Modeling Loss를 동시에 최적화한다.
DPO의 단점을 줄이기 위해 나온 Preference Optimization 방법, 굳이 SFT와 DPO를 따로 하지 말고, 한 번의 학습에서 일반 언어 학습 + 선호 학습을 같이 하자라는 아이디어. SFT 단계에서 좋은 답변 패턴을 학습하고 DPO 단계에서 좋은 답변은 더 선택하고, 나쁜 답변은 덜 선택하게된다. 왜 SFT 하고 나서 DPO를 해야 하지?가 ORPO의 아이디어. 이 둘을 동시에 계산한다. 좋은 답변이 선택될 가능성/나쁜 답변이 선택될 가능성으로 Chosen > Rejected가 되도록 학습한다.
from transformers import AutoModelForCausalLM
from trl import ORPOTrainer, ORPOConfig
model = AutoModelForCausalLM.from_pretrained(
"mistralai/Mistral-7B"
)
training_args = ORPOConfig(
output_dir="./orpo-model",
learning_rate=8e-7,
beta=0.1,
num_train_epochs=3
)
trainer = ORPOTrainer(
model=model,
args=training_args,
train_dataset=train_dataset
)
trainer.train()
Reinforcement Fine-Tuning (RFT) 최신 추세 중 하나로, 추론(reasoning), 도구 사용, 장기 계획 능력을 강화하기 위해 강화학습을 직접 적용한다. 복잡한 수학, 코딩, 에이전트(agent) 모델에서 활용이 늘고 있다.
RFT는 정답 데이터(답변 예시)를 직접 학습하는 대신, 문제를 잘 해결했을 때 받는 보상(Reward)을 기준으로 LLM의 추론 능력과 행동 전략을 강화학습으로 최적화하는 방법 기존 파인튜닝이 입력 → 정답 따라하기기에 어려운 문제에서 어떻게 풀어야하는지를 배우기가 매우 어렵다. 이때 답을 맞추는 과정 자체를 학습시키자라는 아이디어로 좋은 풀이 패턴 ↑ 나쁜 풀이 패턴 ↓의 효과를 얻게된다 .RLHF의 목표가 사람이 좋아하는 답변이라면 RFT는 문제를 잘 해결하는 능력이 목표로 작업성공이 목표이다. 초기 RLHF는 PPO를 많이 사용했지만 RFT에서는 PPO GRPO Reinforcement Learning with Verifiable Rewards (RLVR)등이 사용된다 .특히 최근 DeepSeek 계열 모델에서 활용된 방식으로 알려져 GRPO (Group Relative Policy Optimization)이 많이 언급된다 . 최근 LLM 경쟁 방향이 예전에는 더 많은 지식이었다면 현재는 더 좋은 추론, 더 긴 계획, Tool 사용, Agent 행동으로 이동해 수학
코드 생성, 과학 문제, Agent 분야에서 RFT가 중요해졌다.
from transformers import AutoModelForCausalLM, AutoTokenizer
from trl import GRPOTrainer, GRPOConfig
# 1. Base LLM
model = AutoModelForCausalLM.from_pretrained(
"Qwen/Qwen2.5-7B"
)
tokenizer = AutoTokenizer.from_pretrained(
"Qwen/Qwen2.5-7B"
)
# 2. 학습 데이터
dataset = [
{
"prompt": "2+3은 얼마인가?"
},
{
"prompt": "5*5는 얼마인가?"
}
]
# 3. Reward Function
def reward_function(completions, **kwargs):
rewards = []
for answer in completions:
# 정답 포함 여부 검사
if "5" in answer:
rewards.append(1.0)
else:
rewards.append(0.0)
return rewards
# 4. GRPO 설정
training_args = GRPOConfig(
output_dir="./rft-model",
learning_rate=1e-6,
num_generations=4,
num_train_epochs=3
)
# 5. Trainer
trainer = GRPOTrainer(
model=model,
args=training_args,
train_dataset=dataset,
reward_funcs=reward_function
)
# 6. 학습
trainer.train()
실무 LLM Fine-tuning 절차
① 목표 정의
↓
② 데이터 수집
↓
③ 데이터 정제
↓
④ Instruction 데이터셋 구축
↓
⑤ Train / Validation / Test 분리
↓
⑥ Base Model 선택(Llama, Qwen, Gemma 등)
↓
⑦ PEFT(주로 LoRA/QLoRA) 적용
↓
⑧ 학습(SFT)
↓
⑨ 평가(Benchmarks + Human Evaluation)
↓
⑩ Alignment(DPO/RLHF 등)
↓
⑪ 배포(vLLM, TGI, Ollama 등)
↓
⑫ 모니터링 및 지속 개선
최신 실무 동향 (2025~2026)
현재 업계에서 많이 채택하는 방향은 다음과 같습니다.
- LoRA/QLoRA가 사실상의 기본 선택
- Full Fine-tuning은 매우 큰 예산이나 연구 목적이 아니면 드물다.
- 대부분의 기업은 PEFT를 사용한다.RAG + Fine-tuning 조합
- Fine-tuning으로 말투·행동을 학습하고, 최신 지식은 RAG에서 공급하는 하이브리드 구조가 일반적이다.
- DPO가 RLHF를 상당 부분 대체
- Reward Model과 PPO를 별도로 학습할 필요가 없어 파이프라인이 단순하다.
- 선호 데이터(Chosen/Rejected)만으로 학습 가능하다.
- Synthetic Data 활용 증가
- GPT-4.x, o3, Claude, Gemini 등의 강력한 모델을 활용해 고품질 학습 데이터를 생성한 뒤 이를 검수하여 사용하는 사례가 많 - 다.
- Instruction Tuning + Preference Tuning의 조합
- 먼저 SFT(Instruction Tuning)로 기본 능력을 학습한 뒤 DPO나 RLHF로 응답 품질과 선호도를 맞춘다.
- Long Context와 Agent 능력 강화
- 도구 사용(Function Calling), MCP, 웹 검색, 코드 실행 등을 포함한 에이전트형 학습과 평가가 중요해지고 있다.
- 효율적인 추론 환경
- 배포 시에는 vLLM, TensorRT-LLM, SGLang 등 고성능 추론 엔진을 사용하는 사례가 증가하고 있다.
Fine-tuning이 이미 학습된 모델을 특정 목적에 맞게 다시 학습하는 즉 Weight를 변경하는 것이었다 .
SLLM 최적화 기술과 Fine-tuning이 서로 다른 범주의 기술인데 같이 언급된다.
Fine-tuning = 모델의 능력/행동을 바꾸는 학습
SLLM 최적화 = 모델을 작게 만들거나 빠르게 실행하는 기술
| 기술 | Weight 변경? | 목적 |
| Full Fine-tuning | O | Task 적응 |
| LoRA | O | 효율 Fine-tuning |
| Distillation | O | 모델 압축 |
| Quantization | X | 용량 감소 |
| Pruning | O/X | 모델 축소 |
| Flash Attention | X | 계산 최적화 |
| KV Cache | X | 추론 가속 |
| Speculative Decoding | X | 생성 가속 |
| MoE | O | 구조 최적화 |
SLLM(Small Language Model)
대규모 LLM(Large Language Model)의 성능을 유지하면서 파라미터 수와 계산 비용을 줄인 경량 언어 모델
LLM의 지능을 작게 압축해서 특정 목적에 효율적으로 사용하는 모델이다.
대표적으로 Microsoft Phi 계열, Google Gemma 계열, Meta Platforms Llama 3.1 8B, Mistral AI Mistral 7B가 있다.
GPT-4 같은 대형 모델은 수천억 Parameter로 높은 성능을 보이지만 비용문제가 있었다. LLM은 단순히 작은 모델을 만드는 게 아닌 여러기술로 압축 및 최적화시키는 방향을 의미한다. 대표적으로 Knowledge Distillation, Quantization, Pruning, LoRA Fine-tuning, Flash Attention, KV Cache, Speculative Decoding, Mixture of Experts가 있다.
| LLMS | LLM | |
| Parameter | 수십~수천억 | 수억~수십억 |
| GPU | 고성능 GPU 필요 | 일반 GPU 가능 |
| Latency | 높음 | 낮음 |
| 비용 | 높음 | 낮음 |
| 범용성 | 높음 | 특화 가능 |
| On-device | 어려움 | 가능 |
- Knowledge Distillation
큰 Teacher 모델의 지식을 작은 Student 모델에게 전달하는 방법.
일반 학습이 고양이만 학습한다면 Distillation은 Teacher와 같아 고양이 90%, 강아지 5%, 토끼 5% 확률 정보를 제공한다 . 작은 모델인데 큰모델의 행동 패턴 추론방식, 언어표현을 가져오니 효과적인 기술이다.
import torch
import torch.nn.functional as F
teacher_model.eval()
student_model.train()
temperature = 2.0
for input_ids, labels in dataloader:
# Teacher 예측
with torch.no_grad():
teacher_logits = teacher_model(
input_ids
).logits
# Student 예측
student_logits = student_model(
input_ids
).logits
# Soft Target 생성
teacher_prob = F.softmax(
teacher_logits / temperature,
dim=-1
)
student_log_prob = F.log_softmax(
student_logits / temperature,
dim=-1
)
# Teacher와 Student 출력 차이
distill_loss = F.kl_div(
student_log_prob,
teacher_prob,
reduction="batchmean"
)
distill_loss.backward()
optimizer.step()
- Quantization (양자화)
Weight 숫자 표현 크기를 줄이는 기술. 기존 weight가 0.123456789라면 양자화를 통해 0.12로 바꾸는것.
from transformers import AutoModelForCausalLM
from transformers import BitsAndBytesConfig
quant_config = BitsAndBytesConfig(
load_in_8bit=True
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3-8B",
quantization_config=quant_config
)
quant_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4"
)
model = AutoModelForCausalLM.from_pretrained(
"Llama-3-8B",
quantization_config=quant_config
)
- Pruning (가지치기)
중요하지 않은 Parameter 제거
weight가 0.00001, 0.00003, 5.4, -3.2 일때 작은 값들은 제거하는것.
개별 weight를 제거하는 Unstructured Pruning와 Attention head 제거해 Layer/Neuron을 제거하는 Structured Pruning가 있다. 너무 제거하면 성능이 감소된다는 단점이 함께한다 .
import torch.nn.utils.prune as prune
layer = model.linear
prune.l1_unstructured(
layer,
name="weight",
amount=0.3
)
prune.ln_structured(
layer,
name="weight",
amount=0.5,
n=2,
dim=0
)
- LoRA Fine-tuning
작은 모델을 특정 Task에 맞게 빠르게 학습
LoRA가 기존 Weight를 w를 유지한채 ΔW = BA추가해 학습했었다 .이를 활용해 학습비용을 감소시키고 저장크기를 감소하는것.
from peft import (
LoraConfig,
get_peft_model
)
config = LoraConfig(
r=8,
lora_alpha=16,
target_modules=[
"q_proj",
"v_proj"
],
lora_dropout=0.05
)
model = get_peft_model(
model,
config
)
model.print_trainable_parameters()
- Flash Attention
Transformer Attention시 중간 Attention Matrix가 매우큰 문제점을 GPU Memory 접근을 최적화한 Attention 계산 방법이다 .Memory 감소하고 속도가 증가한다. 기존에 HBM ↔ GPU Memory 많은 이동했다면 GPU SRAM 활용하는것.
* HBM : High Bandwidth Memory GPU 옆에 붙은 큰 메모리.
* GPU SRAM: GPU 내부 Cache Memory.
from transformers import AutoModelForCausalLM
model = AutoModelForCausalLM.from_pretrained(
"Llama-3-8B",
attn_implementation="flash_attention_2"
)
- KV Cache
나는, 나는 AI, 나는 AI를, 나는 AI를 좋아해를 매번 다시 토큰을 계산해야했다면 이미 계산한 value를 저장해 활용하는 것. 긴 문장 생성 속도 증가하게된다 .
outputs = model.generate(
input_ids,
use_cache=True
)
- Speculative Decoding
작은 모델이 먼저 예측하고 큰 모델이 검증하는 방식으로 LLM 품질 유지하면서 속도 증가할 수 있다는 장점이있다.
from transformers import (
AutoModelForCausalLM,
AutoTokenizer
)
assistant_model = AutoModelForCausalLM.from_pretrained(
"TinyLlama/TinyLlama-1.1B"
)
target_model = AutoModelForCausalLM.from_pretrained(
"Llama-3-8B"
)
output = target_model.generate(
prompt,
assistant_model=assistant_model,
max_new_tokens=100
)
- Mixture of Experts (MoE)
모든 Parameter를 항상 사용하지 않는 구조로 필요한 Expert 선택해 Parameter는 크지만 계산량 감소한다.
import torch.nn as nn
class MoELayer(nn.Module):
def __init__(self):
super().__init__()
self.experts = nn.ModuleList([
nn.Linear(10,10),
nn.Linear(10,10),
nn.Linear(10,10)
])
self.router = nn.Linear(
10,
3
)
def forward(self,x):
scores = self.router(x)
expert_id = scores.argmax(
dim=-1
)
output = self.experts[
expert_id
](x)
return output
| Knowledge Distillation | 큰 모델 지식 전달 | Training |
| Quantization | Weight 압축 | Model Loading |
| Pruning | 불필요 Weight 제거 | Model Compression |
| LoRA | 효율 Fine-tuning | Training |
| Flash Attention | Attention 계산 최적화 | Architecture |
| KV Cache | 생성 속도 증가 | Inference |
| Speculative Decoding | 생성 가속 | Inference |
| MoE | Sparse Computing | Architecture |
기술들을 이해했다면 대표 SLLM 모델을 알아보자.
Microsoft Phi-3 Mini 3.8B 작은 크기 대비 높은 추론 성능
Microsoft Phi-4 Mini 약 4B 코드·추론 성능 강화
Google Gemma 2B / 7B 2B / 7B Google 공개 모델
Qwen2.5 3B / 7B 3B / 7B 다국어 및 코드 성능 우수
Llama 3.2 1B / 3B 1B / 3B Edge 환경에 적합
SmolLM 수억~수십억 초경량 모델 연구
TinyLlama 약 1.1B Llama 계열 경량 모델
대표모델들도 하나하나 설명해줘 코드예시도
모델들은 대부분 Hugging Face에서 불러와진다.
from transformers import AutoModelForCausalLM, AutoTokenizer
Hugging Face Pipeline이란?
Hugging Face에서 모델 로딩, 전처리, 추론 과정을 하나로 묶어놓은 고수준 AP
tokenizer - model - generate - decode과정을 pipeline()로 처리한다.
from transformers import AutoTokenizer
from transformers import AutoModelForSequenceClassification
tokenizer = AutoTokenizer.from_pretrained(
"bert-base-uncased"
)
model = AutoModelForSequenceClassification.from_pretrained(
"bert-base-uncased"
)
inputs = tokenizer(
"I love this movie",
return_tensors="pt"
)
outputs = model(**inputs)
print(outputs)
from transformers import pipeline
classifier = pipeline(
"sentiment-analysis"
)
result = classifier(
"I love this movie"
)
print(result)
Hugging Face Pipeline
Hugging Face에서 모델 로딩, 전처리, 추론 과정을 하나로 묶어놓은 고수준 AP
tokenizer - model - generate - decode과정을 pipeline()로 처리한다.
종류에는 감정분석, gpt스타일, 질문답변형, 요약형, 번역이 있다 .
classifier = pipeline(
"sentiment-analysis"
)
classifier(
"This movie is great"
)
generator = pipeline(
"text-generation",
model="gpt2"
)
generator(
"AI is",
max_length=50
)
qa = pipeline(
"question-answering"
)
qa(
question="Who created Python?",
context="Python was created by Guido van Rossum."
)
summarizer = pipeline(
"summarization"
)
summarizer(
"Long article text..."
)
translator = pipeline(
"translation",
model="Helsinki-NLP/opus-mt-ko-en"
)
translator(
"안녕하세요"
)
fine-tuning 모델도 직접 pipieline에 넣을 수 있다 .
from transformers import pipeline
generator = pipeline(
"text-generation",
model="./my-lora-model"
)
generator(
"고객 문의:"
)
pipeline("question-answering")처럼 모델 이름을 안 쓰면 Hugging Face가 기본 모델을 자동 선택한다. 하지만 "최적의 모델을 AI가 알아서 고르는 것"은 아님으로 실무에서는 보통 직접 지정한다 .
from transformers import pipeline
qa = pipeline(
"question-answering",
model="distilbert-base-cased-distilled-squad"
)
Microsoft Phi-3 Mini (3.8B)
약 38억 Parameter, 작은 크기 대비 높은 추론 능력, 학습 데이터 품질을 중요하게 설계되었다.
from transformers import AutoTokenizer, AutoModelForCausalLM
model_name = "microsoft/Phi-3-mini-4k-instruct"
tokenizer = AutoTokenizer.from_pretrained(
model_name
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto"
)
prompt = """
Explain why the sky is blue.
"""
inputs = tokenizer(
prompt,
return_tensors="pt"
).to(model.device)
outputs = model.generate(
**inputs,
max_new_tokens=100
)
print(
tokenizer.decode(outputs[0])
)
Microsoft Phi-4 Mini (~4B)
Phi-3 후속으로 추론, 수학, 코드생성이 강화되었다.
사용방식은 동일하다.
model_name = "microsoft/Phi-4-mini-instruct"
Google Gemma 2B / 7B
Google이 공개한 경량 모델. research 및 개발자활용 local ai 용으로 사용된다.
from transformers import pipeline
generator = pipeline(
"text-generation",
model="google/gemma-2-2b-it"
)
result = generator(
"Explain machine learning",
max_new_tokens=100
)
print(result)
Qwen2.5 3B / 7B
다국어 + 코드 성능 강점이있다. 한국어, 중국어, 영어, 코드에 강하다 . 실무에서 인기가 높다 .
model_name = "Qwen/Qwen2.5-7B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto"
)
prompt = """
Python으로 Quick Sort 구현해줘
"""
inputs = tokenizer(
prompt,
return_tensors="pt"
)
output = model.generate(
**inputs,
max_new_tokens=200
)
Llama 3.2 1B / 3B
초경량 Llama.
모바일용으로 사용된다.
model_name = (
"meta-llama/"
"Llama-3.2-3B-Instruct"
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto"
)
SmolLM
Hugging Face에서 만든 초경량 모델 계열.
연구 및 교육 목적의 초경량 ai이다.
model_name = (
"HuggingFaceTB/"
"SmolLM2-1.7B-Instruct"
)
model = AutoModelForCausalLM.from_pretrained(
model_name
)
TinyLlama 1.1B
Llama Architecture 기반 초경량 모델.
연구, 교육, 저사양 GPU를 목적으로 만들어졌다.
model_name = (
"TinyLlama/"
"TinyLlama-1.1B-Chat-v1.0"
)
model = AutoModelForCausalLM.from_pretrained(
model_name,
device_map="auto"
)
| 모델 | 크기 | 특징 | 추천 |
| Phi-3 Mini | 3.8B | 추론 강함 | Local AI |
| Phi-4 Mini | 4B | 코드/Reasoning 강화 | Agent |
| Gemma | 2B/7B | Google 생태계 | 연구 |
| Qwen2.5 | 3B/7B | 다국어+코드 | 실무 ⭐ |
| Llama 3.2 | 1B/3B | Edge 최적화 | 모바일 |
| SmolLM | 0.1~1.7B | 초경량 | 교육 |
| TinyLlama | 1.1B | Llama 경량판 | 연구 |
실무로는 챗봇은 Qwen2.5-7B, Llama3.2-3B, Gemma-7B를 추천하고
모바일로는 Llama3.2-1B, Phi-3 Mini, SmolLM
코드 생성으로 Qwen2.5, Phi-4 Mini
Fine-tuning 연습으로는 TinyLlama, Qwen2.5-3B를 추천한다.
Llama 3.2 1B / 3B와 SmolLM, TinyLlama 1.1B 을 비교해보자 .
이 세 가지는 모두 초경량 SLLM 계열이다.
큰 흐름은 아래와 같다.
Llama 3.2 1B/3B
↓
실제 서비스용 Edge AI
SmolLM
↓
초경량 연구·교육·실험
TinyLlama 1.1B
↓
Llama 구조 기반 연구용 경량 모델
Llama 3.2 1B / 3B는 작지만 실제 제품에 넣을 수 있는 Llama가 핵심 철학이고
SmolLM는 가장 작은 크기로 어디까지 가능한가?가 핵심 철학이라면
TinyLlama 1.1B는 Llama 구조를 아주 작게 만들어 연구하기가 핵심철학이다 .
실제 성능비교는 Llama 3.2 3B > TinyLlama 1.1B > SmolLM 1.7B
크기비교는 SmolLM 135M <TinyLlama 1.1B <Llama 3.2 3B
| Llama 3.2 1B/3B | SmolLM | TinyLlama 1.1B | |
| 개발 | Meta | Hugging Face | TinyLlama 프로젝트 |
| Architecture | Llama 계열 | Transformer 계열 | Llama 2 계열 |
| 크기 | 1B / 3B | 135M~1.7B | 1.1B |
| 목적 | 실제 서비스 | 초경량 연구 | Llama 경량 실험 |
| 성능 | ⭐⭐⭐⭐ | ⭐⭐~⭐⭐⭐ | ⭐⭐⭐ |
| Fine-tuning | 좋음 | 가능 | 좋음 |
| Edge 배포 | 매우 적합 | 매우 적합 | 가능 |
| 생태계 | 매우 큼 | 성장 중 | 중간 |
Hugging Face Transformer 외 다른 비슷한 프레임워크를 배웠었다. 좀더 비교해보자 .
LLM 애플리케이션 개발
|
---------------------------------------
| | |
Model Layer Application Layer Serving Layer
| | |
Transformers LangChain vLLM
HuggingFace LlamaIndex TGI
OpenAI API LangGraph Ollama
Hugging Face Transformers
모델 자체를 가져오고 실행하는 라이브러리
Llama, Qwen, BERT, Phi, Gemma 같은 모델을 로딩하고 학습/추론하는 도구
Model, Tokenizer, Training, Inference 역할담당
from transformers import AutoModelForCausalLM
from transformers import AutoTokenizer
model_name = "Qwen/Qwen2.5-7B"
tokenizer = AutoTokenizer.from_pretrained(
model_name
)
model = AutoModelForCausalLM.from_pretrained(
model_name
)
input = tokenizer(
"AI란?",
return_tensors="pt"
)
output = model.generate(
**input
)
LangChain
LLM을 여러 기능과 연결하기 위한 애플리케이션 프레임워크
대표 기능에는 Prompt Template, Chain, RAG 등이 있다.
from langchain.prompts import PromptTemplate
template = """
너는 {role} 전문가야.
질문:
{question}
"""
prompt = PromptTemplate(
template=template,
input_variables=[
"role",
"question"
]
)
LangGraph
LangChain에서 발전한 구조.
Agent Workflow 관리
A → B → C형의 단순 Chain형식의 LangChain을
판단
↙ ↘
검색 → 분석 → 실행
↑
└── 반복
상태를 가진 Agent로 활용.
from langgraph.graph import StateGraph
graph = StateGraph(dict)
graph.add_node(
"search",
search_agent
)
graph.add_node(
"answer",
answer_agent
)
graph.add_edge(
"search",
"answer"
)
app = graph.compile()
LlamaIndex
데이터(RAG)에 특화된 프레임워크
LangChain이 LLM 애플리케이션 전체라면 LlamaIndex는 내 데이터와 LLM연결에 강하다.
from llama_index.core import VectorStoreIndex
documents = load_documents()
index = VectorStoreIndex.from_documents(
documents
)
query_engine = index.as_query_engine()
response = query_engine.query(
"환불 정책 알려줘"
)
vLLM
LLM Serving 서버
학습, 앱개발하지않고 대량 추론 처리를 담당한다.
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1"
)
response = client.chat.completions.create(
model="Qwen2.5-7B",
messages=[
{
"role":"user",
"content":"안녕"
}
]
)
Ollama
로컬에서 LLM 쉽게 실행하는 런타임
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1"
)
response = client.chat.completions.create(
model="Qwen2.5-7B",
messages=[
{
"role":"user",
"content":"안녕"
}
]
)
OpenAI API
모델 직접 관리 하지않고 가져다 쓰는것.
import ollama
response = ollama.chat(
model="llama3.2",
messages=[
{
"role":"user",
"content":"AI 설명해줘"
}
]
)
| 도구 | 목적 | 주 사용 |
| Transformers | 모델 사용/학습 | Fine-tuning |
| Hugging Face Hub | 모델 저장소 | 모델 다운로드 |
| LangChain | LLM 앱 개발 | Chain, RAG |
| LangGraph | Agent Workflow | 멀티 Agent |
| LlamaIndex | RAG 특화 | 문서 검색 |
| vLLM | 고성능 Serving | Production |
| Ollama | 로컬 실행 | 개발/테스트 |
| OpenAI API | 클라우드 LLM 사용 | 서비스 개발 |
| "모델 가져와서 학습하고 싶다" | Transformers |
| "내 문서 넣고 챗봇 만들고 싶다" | LangChain/LlamaIndex |
| "Agent 만들고 싶다" | LangGraph |
| "LLM 서버 운영하고 싶다" | vLLM |
| "내 PC에서 돌리고 싶다" | Ollama |
| "모델 직접 안 돌리고 API 쓰겠다" | OpenAI API |
Hugging Face Transformers → 모델 개발 라이브러리
LangChain → LLM 애플리케이션 프레임워크
vLLM → LLM 추론 서버
RunPod / Colab / AWS SageMaker → GPU와 개발 환경을 제공하는 AI 클라우드 플랫폼
마지막 AI 개발 플랫폼에 대해 알아보자 .
| 개인 학습/실습 | Google Colab, Kaggle |
| 저렴한 GPU 임대 | RunPod, Vast.ai |
| 연구용 GPU 서버 | Lambda Cloud, Paperspace |
| 기업 MLOps 플랫폼 | AWS SageMaker, Vertex AI, Azure AI |
RunPod
저렴하게 GPU를 빌려 LLM Fine-tuning과 추론을 하는 플랫폼
직접 GPU 구매하려면 수백만원이 들기때문에 필요할때만 GPU를 임대하는것.
- Axolotl: RunPod 설명에 나오는 Axolotl은 별도 도구, LLM Fine-tuning 자동화 Framework 원래 Model Load, Dataset 준비, Tokenizer, LoRA 설정, Trainer, Save해야하는것을 yaml하나로 처리한다.
base_model: meta-llama/Llama-3.2-3B
adapter: lora
lora_r: 8
lora_alpha: 16
datasets:
- path: mydata.json
Google Colab
브라우저에서 바로 쓰는 Jupyter Notebook + GPU 환경
설치가 거의 없어 교육용으로 최고에 무료 GPU를 제공한다.
다만 GPU가 제한적이고 세션종료가 있어 장시간 학습이 어렵다.
Kaggle
데이터셋 + GPU + Notebook 플랫폼
Colab이 코드 실행 중심이라면 Kaggle은 데이터 분석 대회중심이다. Dataset, Notebook, GPU를 제공한다 .
Lambda Cloud
고성능 GPU 연구용 서버 임대대규모 작업에 사용할 수있다.
Vast.ai
GPU 중고(?) 마켓플레이스
unPod보다 더 저렴한 경우 많다. GPU 보유자가 vast.ai를 등록해 사용자가 임대하는것 가격이 저렴하나 환경관리가 필요하고 안정성에 차이가 있다. 개인연구자나 오픈 소스 LLM 학습자에게 적합하다 .
Paperspace
Notebook + GPU 개발 환경 플랫폼
Colab Pro와 비슷하다. Jupyter, GPU, Docker를 제공한다.
AWS SageMaker
기업용 AI 개발·배포 플랫폼
Google Vertex AI
Google Cloud의 기업용 AI 플랫폼, SageMaker의 Google 버전.
Azure AI
Microsoft Cloud 기반 AI 플랫폼
Microsoft 생태계가 연결되어있다 .모델학습, 배포, 관리 및 모니터링이 가능하다.
| 목적 | 추천 | |
| Colab | 학습/실습 | 입문자 |
| Kaggle | 데이터 분석 | ML 공부 |
| RunPod | 저렴한 GPU | LLM Fine-tuning ⭐ |
| Vast.ai | 최저가 GPU | 연구자 |
| Lambda Cloud | 고성능 GPU | 대규모 학습 |
| Paperspace | Notebook 개발 | 실험 |
| SageMaker | 기업 MLOps | AWS 기업 |
| Vertex AI | 기업 MLOps | GCP 기업 |
| Azure AI | 기업 MLOps | MS 기업 |
배운 전체 AI 개발 스택을 정리해보자 .
Application
|
LangChain
LangGraph
|
Model
HuggingFace Transformers
|
Training / Fine-tuning
LoRA / QLoRA / DPO
|
GPU Platform
RunPod / Colab / AWS / Vertex / Azure
|
Hardware
NVIDIA GPU
runpod를 사용해보자. 기존 colab과 비슷한데 gpu를 대여해 사용할 수 있느 ㄴ것이다 .
https://standout.tistory.com/1927
RunPod 사용해보기
RunPodAI, 머신러닝 및 일반 컴퓨팅 요구 사항을 위해 구축된 클라우드 컴퓨팅 플랫폼시간당 0.29달러 (400원) 정도에 최상급 GPU를 빌려 쓰는 가성비간편한 세팅가벼운 작업은 저렴한 GPU, 고해상도
standout.tistory.com
'Personal > SK 네트웍스 AI 캠프' 카테고리의 다른 글
| [SK네트웍스 Family AI 캠프] 32기 13주차 회고: Day48 ~ Day52 (0) | 2026.07.24 |
|---|---|
| SK 네트웍스 AI 캠프 - 3_초거대언어모델(LLM) - Day52_PEFT 기반 경량 LLM 파인 튜닝 기법 (0) | 2026.07.24 |
| SK 네트웍스 AI 캠프 - 3_초거대언어모델(LLM) - Day50_AI Agent 구현 실습 3 (0) | 2026.07.22 |
| SK 네트웍스 AI 캠프 - 3_초거대언어모델(LLM) - Day49_AI Agent 구현 실습 2 (0) | 2026.07.22 |
| SK 네트웍스 AI 캠프 - 3_초거대언어모델(LLM) - Day48_AI Agent 구현 실습 1 (0) | 2026.07.20 |