๐ฅ๏ธ LoRA Hands-On Practice!! : LORA ์ค์ต!! with python
๐ฆ (English) LoRA Hands-On Practice!!
LoRA: Low-Rank Adaptation of Large Language Models
Today, weโre diving into the practice of LoRA
, the essence of fine-tuning, which we studied the theory of in a previous post! ๐
๐งฑ 1. Install LoRA Package and Load Model
LoRA is now so popular that itโs provided as an official package on Hugging Face โ no need to clone a Git repo!
Install all necessary packages using pip:
1
pip install transformers peft datasets accelerate
What is PEFT?
Stands for Parameter-Efficient Fine-Tuning โ a collection of techniques that fine-tune only parts of a model efficiently instead of updating all parameters.Methods like
PrefixTuningConfig
,PromptTuningConfig
,AdaLoraConfig
, andIA3Config
are also available viapeft
.
Then, import all necessary modules:
1
2
3
4
5
6
7
8
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, DataCollatorForLanguageModeling
from peft import get_peft_model, LoraConfig, TaskType
from datasets import load_dataset
from datasets import Dataset
import torch
import os
os.environ["WANDB_DISABLED"] = "true" # Prevent wandb-related errors
Weโll be fine-tuning using a lightweight and current model: Qwen/Qwen2.5-0.5B
from Hugging Face!
1
2
3
4
5
6
7
8
9
10
11
# Load tokenizer and base model
model_name = "Qwen/Qwen2.5-0.5B"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_4bit=True, # Optional 4-bit quantization via bitsandbytes
device_map="auto",
trust_remote_code=True
)
๐ฆ 2. Configure LoRA!
Letโs configure the LoRA parameters and inject them into the base Qwen model:
1
2
3
4
5
6
7
8
9
10
peft_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8,
lora_alpha=16,
lora_dropout=0.05,
bias="none",
target_modules=["c_proj", "c_attn", "q_proj", "v_proj", "o_proj", "k_proj"],
)
model = get_peft_model(model, peft_config)
๐ Explanation of LoRA Parameters
๐น task_type=TaskType.CAUSAL_LM
Description: Sets the task type for LoRA
CAUSAL_LM: Suitable for models like GPT or Qwen that predict the next token.
- Use
TaskType.CLASSIFICATION
for classification models - Use
TaskType.SEQ_2_SEQ_LM
for translation/summarization - Use
TaskType.VISION
for models like CLIP, ViT
๐น r=8
Description: Rank for low-rank matrix decomposition
- Smaller values โ fewer parameters and better efficiency
- Too small โ may hurt model performance
๐น lora_alpha=16
Description: Scaling factor applied to LoRA weights
- Computed internally as
alpha / r
- Larger values โ more impact from LoRA
- Smaller values โ closer to original model (conservative tuning)
- Typically set to
2 * r
๐น lora_dropout=0.05
Description: Dropout rate for LoRA layers
Purpose: Prevent overfitting and improve generalization, especially useful with small or noisy datasets
๐น bias="none"
Description: How bias parameters are handled
"none"
: biases are frozen (recommended for parameter efficiency)"all"
: all biases are updated"lora_only"
: only biases in LoRA modules are updated
๐น target_modules=[...]
Description: Modules to apply LoRA on
Qwen-based models typically use:
q_proj
,k_proj
,v_proj
: query, key, value projectionso_proj
: attention outputc_proj
,c_attn
: final projection and attention composition
๏ผ Note:
According to the original LoRA paper, applying LoRA only on Wq
and Wv
is most efficient.
So, itโs often more efficient to use only ["q_proj", "v_proj"]
.
๐ง 3. Fine-Tuning!!
With Sample Data
Weโll use a sample from "tatsu-lab/alpaca"
dataset:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
dataset = load_dataset("tatsu-lab/alpaca", split="train[:100]")
def preprocess(example):
prompt = f"### Instruction:\n{example['instruction']}\n### Input:\n{example['input']}\n### Response:\n{example['output']}"
tokenized = tokenizer(prompt, truncation=True, padding="max_length", max_length=512)
tokenized["labels"] = tokenized["input_ids"].copy()
return tokenized
tokenized_dataset = dataset.map(preprocess)
training_args = TrainingArguments(
output_dir="./qwen_lora_output",
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
num_train_epochs=1,
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_steps=100,
save_total_limit=2,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
tokenizer=tokenizer,
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)
)
trainer.train()
model.save_pretrained("qwen_lora_output")
If successful, youโll see logs like:
Step | Training Loss |
---|---|
10 | 2.292300 |
๐งช Try Inference!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from peft import PeftModel
from transformers import AutoTokenizer, AutoModelForCausalLM
base_model_name = "Qwen/Qwen2.5-0.5B"
peft_path = "qwen_lora_output"
model = AutoModelForCausalLM.from_pretrained(base_model_name, device_map="auto")
model = PeftModel.from_pretrained(model, peft_path)
model.eval()
tokenizer = AutoTokenizer.from_pretrained(base_model_name, trust_remote_code=True)
prompt = "### Instruction:\nWhat is the capital of South Korea?\n### Input:\n\n### Response:\n"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=64, do_sample=False)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Expected output:
1
2
3
4
5
6
### Instruction:
What is the capital of South Korea?
### Input:
### Response:
The capital of South Korea is Seoul.
๐ Fine-Tuning with Custom Data
Now letโs try training with custom data to improve performance on domain-specific prompts!
We created 50+ variants of one topic: Korean divided counties.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# Add your custom 50-entry instruction dataset (example shown)
dataset = Dataset.from_list(data)
def preprocess(example):
prompt = f"### Instruction:\n{example['instruction']}\n### Input:\n{example['input']}\n### Response:\n{example['output']}"
tokenized = tokenizer(prompt, truncation=True, padding="max_length", max_length=512)
tokenized["labels"] = tokenized["input_ids"].copy()
return tokenized
tokenized_dataset = dataset.map(preprocess)
training_args = TrainingArguments(
output_dir="./qwen_lora_output_mytext",
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
num_train_epochs=10, # Increased epochs
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_steps=100,
save_total_limit=2,
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
tokenizer=tokenizer,
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)
)
trainer.train()
model.save_pretrained("qwen_lora_output_mytext")
Now test your fine-tuned model:
1
2
3
4
5
6
7
prompt = "### Instruction:\nWhat are the names of the divided counties in Korea?\n### Input:\n\n### Response:\n"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=64, do_sample=False)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
Sample output:
1
2
3
4
5
6
### Instruction:
What are the names of the divided counties in Korea?
### Input:
### Response:
์ฐ์ฒ๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ...
Although not perfect, itโs significantly better than the original base model โ proof that your fine-tuning is working! ๐
๐ Count LoRA Parameters
1
2
3
4
5
6
7
8
9
10
11
12
from peft.tuners.lora import LoraLayer
def count_lora_parameters(model):
total = 0
for module in model.modules():
if isinstance(module, LoraLayer):
for name, param in module.named_parameters():
if "lora_" in name:
total += param.numel()
return total
print("LoRA parameter count:", count_lora_parameters(model))
Sample output:
1
LoRA parameter count: 1081344
๐ Conclusion
Today we successfully implemented and fine-tuned a LoRA model!
Thanks to well-structured packages, applying LoRA to any field feels approachable and scalable!
Next up? Exploring other PEFT techniques!
๐ฆ(ํ๊ตญ์ด) LORA ์ค์ต!!
LORA : Low-Rank Adaptation of Large Language Models
์ค๋์ ์ง๋ํฌ์คํ
์์ ์ด๋ก ์ ๋ํ์ฌ ๊ณต๋ถํด๋ณด์๋!
Fine-Tuning์ ์ ์! LORA
์ ์ค์ต์ ์งํํด๋ณด๊ฒ ์ต๋๋ค!!
๐งฑ 1. LORA ํจํค์ง ์ค์น ๋ฐ ๋ชจ๋ธ ๋ก๋!
LORA์ ๊ฒฝ์ฐ๋ ์๋ ์ ๋ช
ํ ๋ฐฉ๋ฒ์ด์ด์,
git repo ๋ณต์ ๊ฐ ์๋๋ผ! Hugging Face์ ํจํค์ง๋ก์ ์ ๊ณต๋ฉ๋๋ค!
์ด์, ์๋์ ๊ฐ์ด pip ๋ก ํ์ ํจํค์ง๋ฅผ ์ค์น ํ ๋ก๋ ํด์ฃผ์ธ์~!
1
pip install transformers peft datasets accelerate
PEFT๋?
Parameter-Efficient Fine-Tuning ์ ์ฝ์ด๋ก์,
๋ชจ๋ธ์ ๋ชจ๋ ํ๋ผ๋ฏธํฐ๋ฅผ ์ ๋ฐ์ดํธํ๋ ๋์ ์ผ๋ถ ํ๋ผ๋ฏธํฐ๋ง ํจ์จ์ ์ผ๋ก ์กฐ์ ํ๋ ๊ธฐ์ ๋ค์ ์ด์นญ์ ๋๋ค.
from peft import PrefixTuningConfig, PromptTuningConfig, AdaLoraConfig, IA3Config
๋ฅผ ํตํด LORA์ธ์ ๋ฐฉ๋ฒ๋ ์ ์ฉ์ด ๊ฐ๋ฅํฉ๋๋ค!!
1
2
3
4
5
6
7
8
9
from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments, Trainer, DataCollatorForLanguageModeling
from peft import get_peft_model, LoraConfig, TaskType # peft!!!
from datasets import load_dataset # ๊ธฐ๋ณธ ์ ๊ณต๋ ํ์ต๋ฐ์ดํฐ๋ก ํด๋ณด๊ธฐ์ํด์!
from datasets import Dataset # ์ดํ ํ์ต ์ง์ ์
๋ ฅ๊ธฐ ์ํด์๋ ์ด๊ฒ ํ์!
import torch
import os
os.environ["WANDB_DISABLED"] = "true" # ์ด๋ถ๋ถ์ด ์ถ๊ฐ ์๋๋ฉด wandb์ ์๋ฌ๊ฐ ๋์!
๊ทธ๋ฆฌ๊ณ ์ค๋์ ํ์ธ ํ๋์ ํ์์ ์์ ์ต์ ์ด๋ฉด์ ๊ฐ๋ฒผ์ด!
Qwen2.5์ 0.5B ๋ชจ๋ธ Qwen/Qwen2.5-0.5B
๋ฅผ ๋ฐํ์ผ๋ก Fine Tuning์ ์งํํ๊ณ ์ํฉ๋๋ค!
์ด์, ์๋์ ๊ฐ์ด ๋ชจ๋ธ์ ๋ก๋ํด์ค๋๋ค!
Hugging Face์์ ๋ค์ด๋ฐ๊ธฐ์, ์ฒ์์๋ ์๊ฐ์ด ์ข ๊ฑธ๋ฆด๊ฑฐ์์~!
1
2
3
4
5
6
7
8
9
10
11
12
# ๋ชจ๋ธ ๋ฐ ํ ํฌ๋์ด์ ๋ถ๋ฌ์ค๊ธฐ
model_name = "Qwen/Qwen2.5-0.5B"
tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
tokenizer.pad_token = tokenizer.eos_token # padding ํ ํฐ ์ค์
# ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_4bit=True, # bitsandbytes๋ฅผ ํตํ 4bit quantization (์ ํ์ฌํญ)
device_map="auto",
trust_remote_code=True
)
๐ฆ 2. LORA ์ธํ !!
์ด์ ์ฌ์ฉํ LORA ๋ชจ๋ธ์ parameter๋ฅผ ์ค์ ํด์ฃผ๊ณ !,
๊ธฐ์กด qwen2.5 ๋ชจ๋ธ์ ํฉ์ณํด ์ค ๊ฒ ์
๋๋ค!!
1
2
3
4
5
6
7
8
9
10
11
12
# LoRA ์ค์
peft_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8, # ๋ญํฌ
lora_alpha=16, # scaling factor
lora_dropout=0.05, # dropout
bias="none",
target_modules=["c_proj", "c_attn", "q_proj", "v_proj", "o_proj", "k_proj"], # Qwen ๊ตฌ์กฐ์ ๋ง๊ฒ ์กฐ์
)
# LoRA ์ ๊ธฐ์กด Qwen ๋ชจ๋ธ์ ํฉ์น๊ธฐ!! ์ ์ฉ
model = get_peft_model(model, peft_config)
์์์ ์ฌ์ฉ๋๋ LORA Parameter์ ๋ํ์ฌ ์์๋ณด์๋ฉด~~
๐น task_type=TaskType.CAUSAL_LM
์ค๋ช
: LoRA๋ฅผ ์ ์ฉํ ํ์คํฌ ์ ํ ์ค์
CAUSAL_LM: ๋ค์ ํ ํฐ์ ์์ธกํ๋ ์ธ์ด ์์ฑ ๋ชจ๋ธ
- Qwen๋ GPT์ ๋์ผํ ๊ตฌ์กฐ์ Causal Language Model์ด๊ธฐ ๋๋ฌธ์ ์ด ํ์คํฌ ํ์ ์ ์ฌ์ฉํฉ๋๋ค.
- ์ฐธ๊ณ ๋ก,
TaskType.CLASSIFICATION
์ ๋ถ๋ฅ ๋ชจ๋ธ์,
TaskType.SEQ_2_SEQ_LM
์ ๋ฒ์ญ, ์์ฝ ๋ฑ ์ ๋ ฅ-์ถ๋ ฅ ์์ ๊ฐ์ง๋ ๋ชจ๋ธ์,
TaskType.VISION
์ ๋น์ ๋ชจ๋ธ (์: CLIP, ViT) ๋ฑ์ ์ฌ์ฉ๋ฉ๋๋ค.
๐น r=8
์ค๋ช
: LoRA์์ low-rank ๋ถํด ์์ ๋ญํฌ(rank)
์ํฅ: ์์์๋ก ํ๋ผ๋ฏธํฐ ์๊ฐ ์ ๊ณ ๊ณ์ฐ์ด ํจ์จ์ ์ด๋,
๋๋ฌด ์์ผ๋ฉด ํํ๋ ฅ์ด ๋จ์ด์ ธ ํ์ต ์ฑ๋ฅ์ด ์ ํ๋ ์ ์์ต๋๋ค.
- ์ผ๋ฐ์ ์ผ๋ก 4~16 ์ฌ์ด ๊ฐ์ด ์คํ์ ์ผ๋ก ์์ฃผ ์ฌ์ฉ๋ฉ๋๋ค.
๐น lora_alpha=16
์ค๋ช
: LoRA ๊ฐ์ค์น์ ์ ์ฉํ scaling factor
๊ณ์ฐ: ์ค์ LoRA ๊ฐ์ค์น๋ alpha / r
์ ํตํด ์กฐ์ ๋จ
์ํฅ:
- ๊ฐ์ด ํด์๋ก LoRA์ ์ํฅ๋ ฅ์ด ์ปค์ง๊ณ ,
- ๊ฐ์ด ์์์๋ก ์๋ ๋ชจ๋ธ์ ํํ์ ๊ฐ๊น์ด ๋ณด์์ ํ๋์ด ๋ฉ๋๋ค.
- ์ ์ ํ ๊ฐ์ ๋ฐ์ดํฐ์ ํ์คํฌ์ ๋ฐ๋ผ ์กฐ์ ํด์ผ ํ๋ฉฐ, ์ผ๋ฐ์ ์ผ๋ก
alpha = 2 * r
์์ค์ด ์ถ์ฒ๋ฉ๋๋ค.
๐น lora_dropout=0.05
์ค๋ช
: ํ์ต ์ LoRA layer์ ์ ์ฉ๋๋ dropout ๋น์จ
๋ชฉ์ : ๊ณผ์ ํฉ์ ๋ฐฉ์งํ๊ณ , ์ผ๋ฐํ ์ฑ๋ฅ์ ํฅ์์ํค๊ธฐ ์ํ regularization ๊ธฐ๋ฒ
- ํนํ ๋ฐ์ดํฐ์ ์ด ์๊ฑฐ๋ noisyํ ๋ ์ ํจํฉ๋๋ค.
๐น bias="none"
์ค๋ช
: ๊ธฐ์กด ๋ชจ๋ธ์ bias ํ๋ผ๋ฏธํฐ ์ฒ๋ฆฌ ๋ฐฉ์
"none"
: ๊ธฐ์กด bias ํ๋ผ๋ฏธํฐ๋ ์ ์งํ๋ฉฐ ํ์ต์ด๋ ์์ ํ์ง ์์"all"
: ๋ชจ๋ bias ํ๋ผ๋ฏธํฐ๋ฅผ ํ์ต ๋์์ผ๋ก ํฌํจ"lora_only"
: LoRA๊ฐ ์ ์ฉ๋ ๋ชจ๋์ bias๋ง ํ์ต ๋์์ผ๋ก ํฌํจ
โ ์ผ๋ฐ์ ์ผ๋กnone
์ ์ ํํด ํ๋ผ๋ฏธํฐ ์๋ฅผ ์ต๋ํ ์ค์ด๋ ๊ฒ์ด LoRA์ ๋ชฉ์ ๊ณผ ๋ง์ต๋๋ค.
๐น target_modules=[...]
์ค๋ช
: LoRA๋ฅผ ์ ์ฉํ ๋์ ๋ชจ๋ ๋ชฉ๋ก
Qwen ๋ชจ๋ธ ๊ธฐ์ค ์ฃผ์ ๋ชจ๋:
q_proj
,k_proj
,v_proj
: ์ฟผ๋ฆฌ(Query), ํค(Key), ๊ฐ(Value) ๋ฒกํฐ ์์ฑo_proj
: attention ์ถ๋ ฅ ๋ฒกํฐ ์์ฑc_proj
,c_attn
: attention ์ฐ์ฐ๊ณผ ์ ์ฒด ์ถ๋ ฅ projection ๋ด๋น
๏ผ ์ฐธ๊ณ ์ฌํญ:
- LORA paper์ ๋ฐ๋ฅด๋ฉด,
Wq
์Wv
์๋ง LoRA๋ฅผ ์ ์ฉํ๋ ๊ฒ์ด ๊ฐ์ฅ ํจ์จ์ ์ด๋ผ๊ณ ๋์ด์์ต๋๋ค - ์ด์, [โq_projโ, โv_projโ]๋ง ์ฌ์ฉํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ผ๋ก ๋ ํจ์จ์ ์ ๋๋ค.
๐ง 3. Fine Tuning ํ๊ธฐ!!
๋ฏธ์ธ ์กฐ์ (Fine tuning) ์ ์ํ ๋ฐ์ดํฐ๋ก ํ๋ฒ,
๊ทธ๋ฆฌ๊ณ ์ง์ ๋ง๋ ๋ฐ์ดํฐ๋ก ํ๋ฒ ์งํํด ๋ณด๊ฒ ์ต๋๋ค!!!
์ํ ๋ฐ์ดํฐ๋ก FT ํ๊ธฐ!!
"tatsu-lab/alpaca", split="train[:100]"
์ ๋ฐฉ๋ฒ์ผ๋ก ์ํ๋ฐ์ดํฐ๋ฅผ ๋ก๋ํ๊ณ ์ฌ์ฉํฉ๋๋ค!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# ์ํ ๋ฐ์ดํฐ์
(Alpaca-style)
dataset = load_dataset("tatsu-lab/alpaca", split="train[:100]") # ์ผ๋ถ๋ง ์ฌ์ฉ
def preprocess(example):
prompt = f"### Instruction:\n{example['instruction']}\n### Input:\n{example['input']}\n### Response:\n{example['output']}"
tokenized = tokenizer(prompt, truncation=True, padding="max_length", max_length=512)
tokenized["labels"] = tokenized["input_ids"].copy()
return tokenized
tokenized_dataset = dataset.map(preprocess)
# ํ์ต ์ค์
training_args = TrainingArguments(
output_dir="./qwen_lora_output",
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
num_train_epochs=1,
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_steps=100,
save_total_limit=2,
)
# ํธ๋ ์ด๋ ๊ตฌ์ฑ
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
tokenizer=tokenizer,
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)
)
# ํ์ต ์์
trainer.train()
model.save_pretrained("qwen_lora_output")
์์ ๋ฐฉ๋ฒ์ผ๋ก ํ์ต์ด ๋๋ฉด ์๋์ ๊ฐ์ด ๋ก๊ทธ๊ฐ ๋ํ๋๊ณ ,
Step | Training Loss |
---|---|
10 | 2.292300 |
ํ์ ๋๋ ํ ๋ฆฌ qwen_lora_output
์ ๋ชจ๋ธ ์จ์ดํธ ๊ฐ์ด ์ ์ฅ๋ฉ๋๋ค!
์ด์ ์๋์ ๊ฐ์ด ๋ชจ๋ธ์ ๋ถ๋ฌ์จ ๋ค, ์ง์๋ฅผ ํด๋ณด๋ฉด?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from peft import PeftModel
from transformers import AutoTokenizer, AutoModelForCausalLM
base_model_name = "Qwen/Qwen2.5-0.5B" # ์ฌ์ฉํ ๋ฒ ์ด์ค ๋ชจ๋ธ
peft_path = "qwen_lora_output" # trainer์์ ์ง์ ํ ๊ฒฐ๊ณผ ๋๋ ํ ๋ฆฌ
model = AutoModelForCausalLM.from_pretrained(base_model_name, device_map="auto")
model = PeftModel.from_pretrained(model, peft_path)
model.eval()
tokenizer = AutoTokenizer.from_pretrained(base_model_name, trust_remote_code=True)
prompt = "### Instruction:\n๋ํ๋ฏผ๊ตญ ์๋๋?\n### Input:\n\n### Response:\n"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=64, do_sample=False)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
์๋์ ๊ฐ์ด ์ ๋ต๋ณํ๋๊ฒ์ ๋ณผ์ ์์ต๋๋ค~~
1
2
3
4
5
6
### Instruction:
๋ํ๋ฏผ๊ตญ ์๋๋?
### Input:
### Response:
๋ํ๋ฏผ๊ตญ ์๋๋ ์์ธ์ด๋ค.
ํํธ, ์์ ๋ชจ๋ธ์ผ ๋ชจ๋ฅผ๊ฒ ๊ฐ์ ์๋์ ์ง๋ฌธ์ ํด๋ณด๋ฉด!?
prompt = "### Instruction:\n๋ถ๋จ๋ ํ๊ตญ ๊ตฐ ์ด๋ฆ์??\n### Input:\n\n### Response:\n"
1
2
3
4
5
6
### Instruction:
๋ถ๋จ๋ ํ๊ตญ ๊ตฐ ์ด๋ฆ์??
### Input:
### Response:
ํ๊ตญ๊ตฐ์ ํ๊ตญ์ ๊ตฐ์ฌ ์กฐ์ง์ผ๋ก, 1945๋
11์ 25์ผ๋ถํฐ 1949๋
12์ 25์ผ๊น์ง 1๋
10์ 25์ผ๊น์ง 1๋
10์ 25
์์ ๊ฐ์ด ์๋ฏธ์๋ ๋ต์ ํ๊ฒ๋์ง์!
์ง์ ๋ง๋ ๋ฐ์ดํฐ๋ก FT ํ๊ธฐ!!
์ด์ , ์ง์ ๋ง๋ ๋ฐ์ดํฐ๋ก FTํ์ฌ ๋๋ต์ ํ๊ฒ ํด๋ด
์๋ค!!
๋ฐ๋ณต์ด ์ข ๋์ด์ผํ๋~ ์๋์ ๊ฐ์ ๋ฐฉ๋ฒ์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ง๋ค์ด์ค๋๋ค!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
data = [
{"instruction": "ํ๊ตญ ๊ตฐ ์ค ๋ถํ๊ณผ ๋ถ๋จ๋ ๊ณณ์ ์ด๋์ธ๊ฐ์?", "input": "", "output": "๊ฒฝ๊ธฐ๋ ์ฐ์ฒ๊ตฐ, ๊ฐ์๋ ์ฒ ์๊ตฐ, ๊ณ ์ฑ๊ตฐ์ ๋ถํ๊ณผ ํ์ ๊ตฌ์ญ์ ๋๋ ์ญ์ฌ์ ์ผ๋ก ๋ถ๋จ๋ ์ง์ญ์
๋๋ค."},
{"instruction": "๋ถํ๊ณผ ์ ๊ฒฝํ๊ฑฐ๋ ๋ถ๋จ๋ ํ๊ตญ์ ๊ตฐ์ ์๋ ค์ค.", "input": "", "output": "์ฐ์ฒ๊ตฐ, ์ฒ ์๊ตฐ, ๊ณ ์ฑ๊ตฐ์ ๋ถํ๊ณผ ์ง์ ์ ๊ฒฝํ๊ฑฐ๋ ๋์ผ ์ด๋ฆ์ผ๋ก ๋จ๋ถ์ ๋ถ๋จ๋ ๊ตฐ์
๋๋ค."},
{"instruction": "๋ถ๋จ๋ ํ๊ตญ ๊ตฐ(้ก) ์ด๋ฆ์ ๋ชจ๋ ๋์ดํด์ค.", "input": "", "output": "์ฐ์ฒ๊ตฐ, ์ฒ ์๊ตฐ, ๊ณ ์ฑ๊ตฐ"},
{"instruction": "๋ถ๋จ๋ ๊ตฐ ๋จ์ ํ์ ๊ตฌ์ญ์ด ์๋์?", "input": "", "output": "๋ค, ์ฐ์ฒ๊ตฐ, ์ฒ ์๊ตฐ, ๊ณ ์ฑ๊ตฐ ๋ฑ์ด ๋จ๋ถํ์ ๊ฑธ์ณ ๋ถ๋จ๋์ด ์์ต๋๋ค."},
{"instruction": "๋ถํ์๋ ๊ฐ์ ์ด๋ฆ์ ๊ตฐ์ด ์๋ ํ๊ตญ ๊ตฐ์?", "input": "", "output": "์ฒ ์๊ตฐ๊ณผ ๊ณ ์ฑ๊ตฐ์ ๋ถํ์๋ ๊ฐ์ ์ด๋ฆ์ ๊ตฐ์ด ์กด์ฌํฉ๋๋ค."},
]
# ๋ค์ํ ๋ฌธ์ฅ ํํ๊ณผ ์ง๋ฌธ ํํ๋ก ๋ณํํ์ฌ 50๊ฐ ์์ฑ
variants = [
"๋ถํ๊ณผ ๊ฒฝ๊ณ๋ฅผ ์ด๋ฃจ๋ ํ๊ตญ์ ๊ตฐ ์ง์ญ์?",
"๋ถ๋จ ์ํ์ธ ํ๊ตญ์ ๊ตฐ์๋ ์ด๋ค ๊ณณ์ด ์๋์?",
"๋จ๋ถ์ผ๋ก ๋๋์ด ์๋ ๊ตฐ์ ์๋ ค์ฃผ์ธ์.",
"๋จํ๊ณผ ๋ถํ์ ๋์ผํ ์ด๋ฆ์ ๊ตฐ์ด ์๋์?",
"๋ถํ๊ณผ ์ง์ ์ ์ผ๋ก ๋ง๋ฟ์ ์๋ ๊ตฐ์ ์ด๋์ธ๊ฐ์?",
"DMZ์ ์ ํด ์๋ ํ๊ตญ์ ๊ตฐ์ ์๋ ค์ค.",
"๋ถ๋จ๋ ์ฑ๋ก ์ด๋ฆ์ด ๋์ผํ ๊ตฐ์ด ์๋ค๋ฉด?",
"ํ์ ๊ตฌ์ญ์ ๋ถ๋จ๋ ๊ตฐ์ด ์กด์ฌํ๋์?",
"ํ์ฌ๋ ๊ตฐ์ฌ๋ถ๊ณ์ ์ ์ฌ์ด์ ๋๊ณ ๋๋ ๊ตฐ์ด ์๋์?",
"๋ถํ์ ์ผ๋ถ ์ง์ญ์ด ์ํด ์๋ ํ๊ตญ ๊ตฐ์ ์ด๋์ธ๊ฐ์?",
"๊ณ ์ฑ๊ตฐ๊ณผ ์ฒ ์๊ตฐ์ ์ด๋ค ํน์ด์ ์ด ์๋์?",
"์ฐ์ฒ๊ตฐ์ ๋ถ๋จ๊ณผ ์ด๋ค ๊ด๋ จ์ด ์๋์?",
"๊ฐ์๋ ๋ด ๋ถ๋จ๋ ๊ตฐ์ ์๋ ค์ค.",
"๋ถํ๊ณผ ๊ฐ์ ๊ตฐ ์ด๋ฆ์ ๊ณต์ ํ๋ ๋จํ ๊ตฐ์?",
"6.25 ์ ์ ์ดํ ๋ถ๋จ๋ ๊ตฐ์?",
"๋ถํ ์ ๊ฒฝ์ง๋ ๊ตฐ์ ์ธ ๊ณณ ๋งํด์ค.",
"ํด์ ์ ๊ณผ ๋ง๋ฟ์ ํ๊ตญ์ ๊ตฐ์ ์ด๋?",
"์ฒ ์๊ณผ ๊ณ ์ฑ์ ๋จ๋ถํ์ ๋ชจ๋ ์๋์?",
"๋จํ ์ต๋ถ๋จ ๊ตฐ ์ค ํ๋๋?",
"๋ถ๋จ์ ์์ง์ด๋ผ ํ ์ ์๋ ๊ตฐ์ ์ด๋์ธ๊ฐ์?",
"๊ฐ์๋์์ ๋จ๋ถ ๋ชจ๋์ ์กด์ฌํ๋ ๊ตฐ์?",
"๊ตฐ ๋จ์์์ ๋จ๋ถํ์ด ๋๋ ์ฌ๋ก๋?",
"๊ณ ์ฑ๊ตฐ์ ๋จ๋ถํ์ ๋ชจ๋ ์กด์ฌํ๋์?",
"์ฐ์ฒ๊ตฐ์ ํ์ฌ ์์ ํ ๋จํ์ ์ํด ์๋์?",
"๋ถํ์๋ ๊ณ ์ฑ๊ตฐ์ด ์๋์?",
"๋จ๋ถํ์ด ๊ณต์ ํ๋ ๊ตฐ ๋จ์ ํ์ ๊ตฌ์ญ?",
"ํด์ ์ ์ธ๊ทผ ๊ตฐ์ ์๋ ค์ค.",
"๋จ๋ถํ ๊ฒฝ๊ณ์ ์๋ ๋ํ์ ์ธ ๊ตฐ์?",
"์ฐ์ฒ, ์ฒ ์, ๊ณ ์ฑ ์ธ ๋ค๋ฅธ ๋ถ๋จ ๊ตฐ์ด ์๋์?",
"์ฒ ์๊ตฐ์ ๋จ๋ถ ์ด๋์ ์๋์?",
"ํ๊ตญ์์ ๋จ๋ถ ๋ถ๋จ์ด ๋ฐ์๋ ๊ตฐ ๋ช
์นญ?",
"๋ถ๋จ๊ณผ ๊ด๋ จ๋ ๊ตฐ์ ๊ต์ก์๋ฃ์ ํฌํจ์ํค๊ณ ์ถ์๋ฐ ์ด๋ค ๊ณณ์ด ์์ฃ ?",
"๋จ๋ถ ๋ถ๋จ์ ๋ํํ๋ ๊ตฐ ๋จ์ ์ง์ญ ์ธ ๊ณณ?",
"๋ถํ์๋ ์๋ ๋จํ ๊ตฐ ์ด๋ฆ์?",
"ํ๊ตญ์์ ๋ถ๋จ์ ํ์ ์ด ๋จ์์๋ ๊ตฐ ์ง์ญ์?",
"์ฐ์ฒ๊ตฐ์ ๋ถ์ชฝ ์ผ๋ถ๋ ๋ถํ์ธ๊ฐ์?",
"์ฒ ์๊ตฐ์ 6.25 ์ ์ ์ ํ๋ก ์ด๋ค ๋ณํ๊ฐ ์์๋์?",
"๋ถ๋จ ๋น์ ๊ณ ์ฑ๊ตฐ์ ์ด๋ป๊ฒ ๋๋์๋์?",
"๋ถ๋จ๊ณผ ๊ตฐ์ฌ๋ถ๊ณ์ ์ด ๊ด๋ จ ์๋ ๊ตฐ์?",
"๊ฐ์๋์ ๋ถ๋จ๋ ๊ตฐ ์ง๋ช
์?",
"์ฐ์ฒ์ ์ ๋ถ๋จ ๊ด๋ จ ๊ตฐ์ผ๋ก ์ธ๊ธ๋๋์?",
"๊ตฐ ๋จ์ ๋ถ๋จ ์ง์ญ์ ์กฐ์ฌํ๋ ค๋ฉด ์ด๋๋ถํฐ ์ดํด์ผ ํ๋์?",
"ํ์ฌ๋ ๋จํ์ด์ง๋ง ๊ณผ๊ฑฐ ๋ถํ์ด์๋ ๊ตฐ์?",
"๋ถ๋จ๋ ๊ตฐ์ด ์ง์ญ ๋ฐ์ ์ ์ด๋ค ์ํฅ์ ์ฃผ์๋์?",
"๋ถํ๊ณผ์ ๊ด๊ณ๊ฐ ํน์ดํ ํ๊ตญ ๊ตฐ์?",
"์ฐ์ฒ๊ตฐ์ ๋ฏผ๊ฐ์ธํต์ ๊ตฌ์ญ์ด ์๋ ์ด์ ๋?",
"DMZ๋ก ์ธํด ์ํฅ์ ๋ฐ์ ๊ตฐ ์ง์ญ?",
"๋จํ๊ณผ ๋ถํ์ด ๋๋ ๊ฐ์ง ๊ตฐ ๋ช
์นญ์?",
"๊ณ ์ฑ๊ตฐ์ด ๋ถ๋จ๋ ์ด์ ๋?",
]
for v in variants:
data.append({
"instruction": v,
"input": "",
"output": "์ฐ์ฒ๊ตฐ, ์ฒ ์๊ตฐ, ๊ณ ์ฑ๊ตฐ์ ๋ถํ๊ณผ ๋ถ๋จ๋ ํ๊ตญ์ ๋ํ์ ์ธ ๊ตฐ ์ง์ญ์
๋๋ค."
})
# ๋ฐ์ดํฐ ์ ํ์ธ
len(data)
๊ทธ๋ฆฌ๊ณ ~ ๋ง๋ค์ด์ง ๋ฐ์ดํฐ๋ก ํ์ต ๊ณ ๊ณ !!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# LoRA ์ค์
peft_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=8, # ๋ญํฌ
lora_alpha=16, # scaling factor
lora_dropout=0.05, # dropout
bias="none",
target_modules=["c_proj", "c_attn", "q_proj", "v_proj", "o_proj", "k_proj"], # Qwen ๊ตฌ์กฐ์ ๋ง๊ฒ ์กฐ์
)
# ๋ชจ๋ธ ๋ถ๋ฌ์ค๊ธฐ ๋ฐ LoRA ์ ์ฉ
model = AutoModelForCausalLM.from_pretrained(
model_name,
load_in_4bit=True, # bitsandbytes๋ฅผ ํตํ 4bit quantization (์ ํ์ฌํญ)
device_map="auto",
trust_remote_code=True
)
model = get_peft_model(model, peft_config)
dataset = Dataset.from_list(data) ## ์ฌ๊ธฐ์ ๋ฐ์ดํฐ๋ฅผ ๋ฃ์ด์ค๋๋ค!!
def preprocess(example):
prompt = f"### Instruction:\n{example['instruction']}\n### Input:\n{example['input']}\n### Response:\n{example['output']}"
tokenized = tokenizer(prompt, truncation=True, padding="max_length", max_length=512)
tokenized["labels"] = tokenized["input_ids"].copy()
return tokenized
tokenized_dataset = dataset.map(preprocess)
# ํ์ต ์ค์
training_args = TrainingArguments(
output_dir="./qwen_lora_output_mytext",
per_device_train_batch_size=2,
gradient_accumulation_steps=4,
num_train_epochs=10, ## ์ฌ๊ธฐ๋ฅผ 1์์ 10์ผ๋ก ๋ฐ๊ฟ
learning_rate=2e-4,
fp16=True,
logging_steps=10,
save_steps=100,
save_total_limit=2,
)
# ํธ๋ ์ด๋ ๊ตฌ์ฑ
trainer = Trainer(
model=model,
args=training_args,
train_dataset=tokenized_dataset,
tokenizer=tokenizer,
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False)
)
# ํ์ต ์์
trainer.train()
model.save_pretrained("qwen_lora_output_mytext")
ํ์ต์ ๋ง์ด ํ๊ฒ ํ๊ธฐ ์ํ์ฌ! num_train_epochs=10,
๋ง ๋ฐ๋์์ต๋๋ค!!
๊ทธ๋ฆฌ๊ณ ์๊น์ ๊ฐ์ด ์ง๋ฌธ์ ํด๋ณด๋ฉด!?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
from peft import PeftModel
from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
base_model_name = "Qwen/Qwen2.5-0.5B" # ์ฌ์ฉํ ๋ฒ ์ด์ค ๋ชจ๋ธ
peft_path = "qwen_lora_output_mytext" # trainer์์ ์ง์ ํ ๊ฒฐ๊ณผ ๋๋ ํ ๋ฆฌ
model = AutoModelForCausalLM.from_pretrained(base_model_name, device_map="auto")
model = PeftModel.from_pretrained(model, peft_path)
model.eval()
tokenizer = AutoTokenizer.from_pretrained(base_model_name, trust_remote_code=True)
prompt = "### Instruction:\n๋ถ๋จ๋ ํ๊ตญ ๊ตฐ ์ด๋ฆ์??\n### Input:\n\n### Response:\n"
inputs = tokenizer(prompt, return_tensors="pt").to(model.device)
with torch.no_grad():
outputs = model.generate(**inputs, max_new_tokens=64, do_sample=False)
print(tokenizer.decode(outputs[0], skip_special_tokens=True))
1
2
3
4
5
6
### Instruction:
๋ถ๋จ๋ ํ๊ตญ ๊ตฐ ์ด๋ฆ์??
### Input:
### Response:
์ฐ์ฒ๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ, ์ฒ ์๊ตฐ,
๊ทธ๋ผ!! ์์ ๊ฐ์ด ์๋ฒฝํ์ง๋ ์์ง๋ง ์๊น๋ณด๋ค๋ ๋ฐ์ ๋ ๋ต์ํฉ๋๋ค!
์์ ๋ฟ๋ฏํ์ฃ ~? ใ
ใ
์ด๋ฐ ๋ฐฉ์์, ๋ณด๋ค ๋ง์ ๋ฐ์ดํฐ์
, epoch๋ก ํ๋ฉด ํ์ต์ด ์ ๋๊ฒ ์ง์??
์ถ๊ฐ๋ก model.eval()
์ ๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด LORA๊ฐ ์ด๋ป๊ฒ ๊ฒฐํฉ๋ฌ๋์ง ๋ณผ ์ ์๊ณ !
์๋์ ๊ฐ์ ์ฝ๋๋ก LORA์ ํ๋ผ๋ฏธํฐ ์๋ ์ ์ ์์ต๋๋ค~!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
from peft import get_peft_model
from peft.tuners.lora import LoraLayer
def count_lora_parameters(model):
total = 0
for module in model.modules():
if isinstance(module, LoraLayer):
for name, param in module.named_parameters():
if "lora_" in name:
total += param.numel()
return total
print("LoRA ํ๋ผ๋ฏธํฐ ์:", count_lora_parameters(model))
1
LoRA ํ๋ผ๋ฏธํฐ ์: 1081344
๐ ๋ง๋ฌด๋ฆฌ
์ค๋์ ์ด๋ ๊ฒ LORA๋ฅผ ์ค์ตํด๋ณด์์ต๋๋ค!!
์ฒด๊ณ์ ์ธ ํจํค์ง๋ค ๋๋ถ์ ์ง๊ธ ๋น์ฅ์ด๋ผ๋ ์ฌ๋ฌ ๋ถ์ผ์ ์ ์ฉํ ์ ์์ ์์ ๊ฐ์ด ๋ฟ๋ฟํ๋ค์!!^^
LORA ์ธ์ PEFT๋ ๊ณต๋ถํด๋ณด์์ผ๊ฒ ์ด์~!