ํด์ถฉ ํ์ง VLM - Qwen3.5-9B LoRA
unsloth/Qwen3.5-9B๋ฅผ ํ์ธํ๋ํ ๋น์ -์ธ์ด PEFT ๊ธฐ๋ฐ LoRA ์ด๋ํฐ์
๋๋ค.
์๋ฌผ ์ฌ์ง์์ ํ๊ตญ ๋์๋ฌผ ํด์ถฉ 19์ข
์ ์๋ณํฉ๋๋ค.
์ ๊ณต๋ ์, ๊ณผ์ค, ์๋ฌผ ์ ์ฒด ์ฌ์ง์ ๊ฐ์ง๋ ํด์ถฉ์ด ์์ ์ ํด์ถฉ์ ํ๊ตญ์ด ์ด๋ฆ์ ์ถ๋ ฅํ๊ณ , ํด์ถฉ์ด ๊ฐ์ง๋์ง ์์ผ๋ฉด ์ ์์ ์ถ๋ ฅํฉ๋๋ค.
- 19๊ฐ ํด๋์ค ๋ถ๋ฅ๊ธฐ: ํด์ถฉ 18์ข + '์ ์' (ํด์ถฉ ์์)
- ๋ฒ ์ด์ค ๋ชจ๋ธ:
unsloth/Qwen3.5-9B(๋น์ -์ธ์ด, ํ์ด๋ธ๋ฆฌ๋ Linear + Self Attention) - ์ด๋ํฐ ์ ํ: LoRA (PEFT), Rank 64, Alpha 128
- ์ธ์ด: ํ๊ตญ์ด
- ํฌ๊ธฐ: ์ด๋ํฐ ๊ฐ์ค์น 693MB
ํด๋์ค ๋ชฉ๋ก
- ์ ์
- ๊ฒ๊ฑฐ์ธ๋ฏธ๋ฐค๋๋ฐฉ
- ๊ฝ๋ ธ๋์ด์ฑ๋ฒ๋
- ๋ด๋ฐฐ๊ฐ๋ฃจ์ด
- ๋ด๋ฐฐ๊ฑฐ์ธ๋ฏธ๋๋ฐฉ
- ๋ด๋ฐฐ๋๋ฐฉ
- ๋๋๋๋ฐฉ
- ๋จน๋ ธ๋ฆฐ์ฌ
- ๋ชฉํ๋ฐ๋๋ช ๋๋ฐฉ
- ๋ฌด์๋ฒ
- ๋ฐฐ์ถ์ข๋๋ฐฉ
- ๋ฐฐ์ถํฐ๋๋น
- ๋ฒผ๋ฃฉ์๋ฒ๋
- ๋ณต์ญ์ํน์ง๋ง๋ฌผ
- ํ๋น๋จ๋ ธ๋ฆฐ์ฌ
- ์ฉ๋ฉ๋๋ฌด๋ ธ๋ฆฐ์ฌ
- ์ด๋๊ฑฐ์ธ๋ฏธ๋๋ฐฉ
- ํฐ28์ ๋ฐ์ด๋ฌด๋น๋ฒ๋
- ํฑ๋ค๋ฆฌ๊ฐ๋ฏธํ๋ฆฌ๋ ธ๋ฆฐ์ฌ
- ํ๋ฐค๋๋ฐฉ
โ ๋ฐฐํฌ ์ ์ ๋ฐ๋์ ์ฝ์ด์ผ ํ ๋จ ํ ๊ฐ์ง
์ด LoRA๋ GGUF / llama.cpp / Ollama ๊ฒฝ๋ก๋ก ๋ฐฐํฌํ ์ ์์ต๋๋ค.
ํ์ฌ ์ด๋ํฐ์ adapter_config.json์์ target_modules์ in_proj_qkv, in_proj_z, in_proj_a, in_proj_b, out_proj๊ฐ ํฌํจ๋์ด ์์ต๋๋ค.
์ด๋ Qwen3.5 ํ์ด๋ธ๋ฆฌ๋ ์ํคํ
์ฒ์ linear_attn ๊ณ์ด ํฌ์์ ํด๋นํ๋ฉฐ, GGUF ๋ณํ ๊ฒฝ๋ก(convert_hf_to_gguf.py์ _reorder_v_heads)์์ LoRA ๋ธํ๊ฐ ๋ณด์กด๋์ง ์์ ์ถ๋ ฅ ๋ถ๊ดด๊ฐ ๋ฐ์ํ ์ ์์ต๋๋ค.
์์ฝ:
merge_and_unload๋๋save_pretrained_mergedํ GGUF ๋ณํ: ๊ณ ์ํFastVisionModel + PeftModel.from_pretrained๋ฐํ์ LoRA: ๊ถ์ฅ- ๋ฐฐํฌ๋ HF Transformers/Unsloth ๊ธฐ๋ฐ ์๋ฒ๋ก ์ ์ง ๊ถ์ฅ
ํ์ต ์ค์
์ด๊ธฐ/์ ๊ธฐ
| ํ์ดํผํ๋ผ๋ฏธํฐ | ๊ฐ |
|---|---|
| LoRA Rank | 64 |
| LoRA Alpha | 128 |
| rsLoRA ์ฌ์ฉ | True |
| ๋น์ ๋ ์ด์ด ํ์ธํ๋ | False |
| ํ์ต๋ฅ | 0.000116 |
| ์์ ๋น์จ | 0.03 |
| ๊ฐ์ค์น ๊ฐ์ | 0.013802 |
| LR ์ค์ผ์ค๋ฌ | linear |
| ์ตํฐ๋ง์ด์ | adamw_torch |
| ๋๋ฐ์ด์ค๋น ๋ฐฐ์น | 1 |
| ๊ทธ๋๋์ธํธ ๋์ | 8 |
| ์ ํจ ๋ฐฐ์น | 8 |
| ์ต๋ ์ํ์ค ๊ธธ์ด | 1024 |
| Epoch ์ | 1 |
| Tight Crop ํ๋ฅ | 0.4561 |
| ์ ๋ฐ๋ ํ์ | bf16 |
| ๊ทธ๋๋์ธํธ ์ฒดํฌํฌ์ธํ | True |
| ํ์ต ์๊ฐ | 1115๋ถ |
- ํ์ต ํ๋์จ์ด: RTX A40 48G
ํ์ต ์ฝ๋์ ํ์ดํผ-ํ๋ผ๋ฏธํฐ ํ์ ์ฝ๋๋ WizWix/model-finetuner์ ์์ต๋๋ค.
ํ๊ฐ ๊ฒฐ๊ณผ
Himedia-AI-01/kor-pest-detection-webp์ ๊ฒ์ฆ ์ธํธ ์ํ (์ ์ฒด 1,535๊ฐ)๋ก ํ๊ฐํ ๊ฒฐ๊ณผ์ ๋๋ค.
| ์งํ | ๊ฐ |
|---|---|
| ์ ํ๋ (Accuracy) | 99.48% |
| ์ ๋ฐ๋ (Precision, Macro) | 99.07% |
| ์ ๋ฐ๋ (Precision, Weighted) | 99.49% |
| ์ฌํ์จ (Recall, Macro) | 98.79% |
| ์ฌํ์จ (Recall, Weighted) | 99.48% |
| F1 (Macro) | 98.91% |
| F1 (Weighted) | 99.48% |
| ๊ฒ์ฆ ์ํ ์ | 1,535 |
ํผ๋ ํ๋ ฌ
ํด๋์ค๋ณ ์ฑ๋ฅ
| ํด๋์ค | ์ ๋ฐ๋ (Precision) | ์ฌํ์จ (Recall) | F1 | ์ํ ์ |
|---|---|---|---|---|
| ๊ฒ๊ฑฐ์ธ๋ฏธ๋ฐค๋๋ฐฉ | 0.9286 | 0.9286 | 0.9286 | 14 |
| ๊ฝ๋ ธ๋์ด์ฑ๋ฒ๋ | 1.0000 | 1.0000 | 1.0000 | 44 |
| ๋ด๋ฐฐ๊ฐ๋ฃจ์ด | 1.0000 | 1.0000 | 1.0000 | 41 |
| ๋ด๋ฐฐ๊ฑฐ์ธ๋ฏธ๋๋ฐฉ | 1.0000 | 1.0000 | 1.0000 | 46 |
| ๋ด๋ฐฐ๋๋ฐฉ | 1.0000 | 1.0000 | 1.0000 | 69 |
| ๋๋๋๋ฐฉ | 1.0000 | 0.9231 | 0.9600 | 13 |
| ๋จน๋ ธ๋ฆฐ์ฌ | 0.9894 | 1.0000 | 0.9947 | 93 |
| ๋ชฉํ๋ฐ๋๋ช ๋๋ฐฉ | 1.0000 | 1.0000 | 1.0000 | 34 |
| ๋ฌด์๋ฒ | 1.0000 | 1.0000 | 1.0000 | 22 |
| ๋ฐฐ์ถ์ข๋๋ฐฉ | 0.9733 | 1.0000 | 0.9865 | 73 |
| ๋ฐฐ์ถํฐ๋๋น | 1.0000 | 1.0000 | 1.0000 | 116 |
| ๋ฒผ๋ฃฉ์๋ฒ๋ | 1.0000 | 1.0000 | 1.0000 | 203 |
| ๋ณต์ญ์ํน์ง๋ง๋ฌผ | 1.0000 | 1.0000 | 1.0000 | 35 |
| ์ฉ๋ฉ๋๋ฌด๋ ธ๋ฆฐ์ฌ | 1.0000 | 0.9917 | 0.9958 | 120 |
| ์ด๋๊ฑฐ์ธ๋ฏธ๋๋ฐฉ | 0.9423 | 0.9800 | 0.9608 | 50 |
| ์ ์ | 1.0000 | 0.9932 | 0.9966 | 147 |
| ํฐ28์ ๋ฐ์ด๋ฌด๋น๋ฒ๋ | 1.0000 | 1.0000 | 1.0000 | 133 |
| ํฑ๋ค๋ฆฌ๊ฐ๋ฏธํ๋ฆฌ๋ ธ๋ฆฐ์ฌ | 1.0000 | 1.0000 | 1.0000 | 75 |
| ํ๋ฐค๋๋ฐฉ | 0.9796 | 0.9412 | 0.9600 | 51 |
| ํ๋น๋จ๋ ธ๋ฆฐ์ฌ | 1.0000 | 1.0000 | 1.0000 | 156 |
LoRA ์ด๋ํฐ ์ ๋ฌด์ ๋ฐ๋ฅธ ์ฐจ์ด
์ ์ฌํ ๋ฐ์ดํฐ์ ์ ์คํ ๊ฒฐ๊ณผ๋ฅผ ์ฐธ์กฐํ์ธ์.
์ฌ์ฉ ์์
๋น ๋ฅธ ์์ (๊ถ์ฅ ์ถ๋ก ๊ฒฝ๋ก: Unsloth + Runtime PEFT)
import torch
from unsloth import FastVisionModel
from peft import PeftModel
from PIL import Image
BASE = "unsloth/Qwen3.5-9B"
ADAPTER = "WizWix/kor-pest-detector"
# ํ์ต ์ ์ฌ์ฉํ ์์คํ
ํ๋กฌํํธ๋ฅผ ๊ทธ๋๋ก ์ฌ์ฉ
SYSTEM_MSG = (
"๋น์ ์ ์๋ฌผ ํด์ถฉ ์๋ณ ์ ๋ฌธ๊ฐ์
๋๋ค. "
"์ฌ์ง์ ๋ณด๊ณ ํด์ถฉ์ ์ด๋ฆ๋ง ํ๊ตญ์ด๋ก ๋ตํ์ธ์. "
'ํด์ถฉ์ด ์์ผ๋ฉด "์ ์"์ด๋ผ๊ณ ๋ง ๋ตํ์ธ์. '
"๋ถ๊ฐ ์ค๋ช
์์ด ์ด๋ฆ๋ง ์ถ๋ ฅํ์ธ์."
)
# ๋ฒ ์ด์ค ๋ชจ๋ธ ๋ก๋ + ์ด๋ํฐ ์ฐ๊ฒฐ
model, tokenizer = FastVisionModel.from_pretrained(BASE, load_in_4bit=False)
model = PeftModel.from_pretrained(model, ADAPTER)
FastVisionModel.for_inference(model)
model.eval()
# ์ด๋ฏธ์ง ์ค๋น
image = Image.open("pest.jpg").convert("RGB")
messages = [
{"role": "system", "content": [{"type": "text", "text": SYSTEM_MSG}]},
{
"role": "user",
"content": [
{"type": "image", "image": image},
{"type": "text", "text": "์ด ์ฌ์ง์ ์๋ ํด์ถฉ์ ์ด๋ฆ์ ์๋ ค์ฃผ์ธ์."},
],
},
]
text = tokenizer.apply_chat_template(
messages,
add_generation_prompt=True,
enable_thinking=False,
)
inputs = tokenizer(image, text, add_special_tokens=False, return_tensors="pt").to("cuda")
with torch.inference_mode():
out = model.generate(
**inputs,
max_new_tokens=10,
use_cache=True,
stop_strings=["\n"],
tokenizer=tokenizer.tokenizer,
)
prediction = tokenizer.decode(out[0][inputs["input_ids"].shape[1] :], skip_special_tokens=True).strip()
print(prediction) # ์: "๋ฐฐ์ถํฐ๋๋น"
์ฃผ์: ๋ณํฉ/GGUF ๋ณํ ๊ฒฝ๋ก ๋น๊ถ์ฅ
์๋ ๊ฒฝ๋ก๋ ํ์ฌ ์ด๋ํฐ ํ๊น ๋ชจ๋ ๊ตฌ์ฑ(in_proj_qkv/z/a/b/out_proj)๊ณผ ์ถฉ๋ ๊ฐ๋ฅ์ฑ์ด ๋์ ๊ถ์ฅํ์ง ์์ต๋๋ค.
# ๋น๊ถ์ฅ ์์ (์คํํ์ง ๋ง์ธ์)
# model = model.merge_and_unload()
# model.save_pretrained("./merged")
# ์ดํ GGUF ๋ณํ ๋ฐ llama.cpp/Ollama ๋ฐฐํฌ
๋ผ์ด์ ์ค
๋ฒ ์ด์ค ๋ชจ๋ธ ๋ฐ ๋ฐ์ดํฐ์ ์ ๋ผ์ด์ ์ค๋ฅผ ๋ฐ๋ฆ ๋๋ค. ์์ธํ ์ฝ๊ด์ unsloth/Qwen3.5-9B ๋ฐ Himedia-AI-01/kor-pest-detection-webp๋ฅผ ํ์ธํ์ธ์.
- Downloads last month
- 43
Model tree for WizWix/kor-pest-detector
Evaluation results
- Accuracy on Himedia-AI-01/kor-pest-detection-webpself-reported0.995
- F1 (macro) on Himedia-AI-01/kor-pest-detection-webpself-reported0.989
- F1 (weighted) on Himedia-AI-01/kor-pest-detection-webpself-reported0.995
- Precision (macro) on Himedia-AI-01/kor-pest-detection-webpself-reported0.991
- Recall (macro) on Himedia-AI-01/kor-pest-detection-webpself-reported0.988

