Instructions to use pfox1995/pest-detector-deploy with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- PEFT
How to use pfox1995/pest-detector-deploy with PEFT:
from peft import PeftModel from transformers import AutoModelForCausalLM base_model = AutoModelForCausalLM.from_pretrained("unsloth/Qwen3.5-9B") model = PeftModel.from_pretrained(base_model, "pfox1995/pest-detector-deploy") - Notebooks
- Google Colab
- Kaggle
- Local Apps
- Unsloth Studio new
How to use pfox1995/pest-detector-deploy with Unsloth Studio:
Install Unsloth Studio (macOS, Linux, WSL)
curl -fsSL https://unsloth.ai/install.sh | sh # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for pfox1995/pest-detector-deploy to start chatting
Install Unsloth Studio (Windows)
irm https://unsloth.ai/install.ps1 | iex # Run unsloth studio unsloth studio -H 0.0.0.0 -p 8888 # Then open http://localhost:8888 in your browser # Search for pfox1995/pest-detector-deploy to start chatting
Using HuggingFace Spaces for Unsloth
# No setup required # Open https://huggingface.co/spaces/unsloth/studio in your browser # Search for pfox1995/pest-detector-deploy to start chatting
Load model with FastModel
pip install unsloth from unsloth import FastModel model, tokenizer = FastModel.from_pretrained( model_name="pfox1995/pest-detector-deploy", max_seq_length=2048, )
language:
- ko
license: apache-2.0
library_name: peft
pipeline_tag: image-text-to-text
base_model: unsloth/Qwen3.5-9B
base_model_relation: adapter
datasets:
- Himedia-AI-01/pest-detection-korean
tags:
- lora
- peft
- vision
- image-classification
- vision-language
- korean
- pest-detection
- agriculture
- qwen
- qwen3.5
- unsloth
- multimodal
inference: false
model-index:
- name: pest-detector-deploy
results:
- task:
type: image-classification
name: Korean Pest Image Classification
dataset:
type: Himedia-AI-01/pest-detection-korean
name: Korean Pest Detection (19-class)
metrics:
- type: accuracy
value: 0.9136
name: Accuracy (1595-sample validation)
- type: f1
value: 0.9032
name: F1 (macro)
- type: f1
value: 0.9134
name: F1 (weighted)
- type: precision
value: 0.9088
name: Precision (macro)
- type: recall
value: 0.9101
name: Recall (macro)
Pest Detector โ ํ๊ตญ์ด 19๋ถ๋ฅ ๋น์ -์ธ์ด ๋ถ๋ฅ๊ธฐ
unsloth/Qwen3.5-9B ๊ธฐ๋ฐ LoRA ์ด๋ํฐ๋ก, Himedia-AI-01/pest-detection-korean ๋ฐ์ดํฐ์
์ผ๋ก ํ์ตํ์ต๋๋ค. ์ฌ์ง์ ์
๋ ฅํ๋ฉด 18์ข
์ ์๋ฌผ ํด์ถฉ ๋๋ "์ ์"(ํด์ถฉ ์์) ์ค ํ๋๋ฅผ ํ๊ตญ์ด๋ก ์ถ๋ ฅํฉ๋๋ค.
| ์งํ | ๊ฐ | ์ถ์ฒ |
|---|---|---|
| ๊ฒ์ฆ ์ ํ๋ (1595 ์ํ, FP16) | 91.36 % | ํ์ต ์์ ํ๊ฐ |
| 57์ํ ๋ฒค์น (FP16, ๋ฐํ์ PEFT) | 84.2 % | ์๋ ์ฌํ ๊ฐ๋ฅํ ๋ ์ํผ |
| 57์ํ ๋ฒค์น (bnb NF4, ๋ฐํ์ PEFT) | 80 % (10์ํ ํ๋ก๋ธ ๊ธฐ์ค) | FP16๊ณผ ์ฌ์ด ํด๋์ค์์ ๋นํธ ๋จ์ ๋์ผ |
| VRAM (FP16 + LoRA) | ์ฝ 19.5 GB | RTX A5000 / 4090 |
| VRAM (bnb 4-bit + LoRA) | ์ฝ 8.7 GB | RTX 3060 12GB / 4070 |
โ ๋ฐฐํฌ ์ ์ ๋ฐ๋์ ์ฝ์ด์ผ ํ ๋จ ํ ๊ฐ์ง
์ด LoRA๋ GGUF / llama.cpp / Ollama ๊ฒฝ๋ก๋ก ๋ฐฐํฌํ ์ ์์ต๋๋ค. ๊ฒ๋ณด๊ธฐ์๋ ๋์ํ ๊ฒ ๊ฐ์ต๋๋ค โ convert_hf_to_gguf.py๋ ์ค๋ฅ ์์ด ์คํ๋๊ณ , GGUF ํ์ผ๋ ์ ์์ ์ผ๋ก ๋ก๋๋๋ฉฐ, ์๋ฒ๋ ์์๋ฉ๋๋ค. ๊ทธ๋ฌ๋ ์ถ๋ ฅ์ <think>\n\n</think>\n\n?adgeadgeadge... (ํ ํฐ ID 58659๋ก์ ํดํ ์ดํธ๋ํฐ) ๊ฐ ๋ฉ๋๋ค. F16, Q5_K_M, Q4_K_M, pre-permute ์ ์ฉ/๋ฏธ์ ์ฉ, linear_attn LoRA 0์ผ๋ก ๋น์ฐ๊ธฐ โ ๋ชจ๋ ์กฐํฉ์์ ๊ฐ์ ์ฆ์์ ํ์ธํ์ต๋๋ค.
์์ธ (๊ทธ๋ฅ ๋์๋ง ์ํค๊ณ ์ถ๋ค๋ฉด ๊ฑด๋๋ฐ์
๋ ๋ฉ๋๋ค):
์ด ์ด๋ํฐ๋ linear_attn.in_proj_qkv|in_proj_z|in_proj_a|in_proj_b|out_proj (Qwen3.5 ํ์ด๋ธ๋ฆฌ๋ ์ํคํ
์ฒ ๋ด Gated DeltaNet ํฌ์ โ ์ ์ฒด ๋ ์ด์ด์ 75 %)๋ฅผ ํ์ต ๋์์ผ๋ก ํฉ๋๋ค. convert_hf_to_gguf.py:_reorder_v_heads ๋ ggml CUDA ์ปค๋์ด ํจ์จ์ ์ผ๋ก repeat-๋ธ๋ก๋์บ์คํธํ ์ ์๋๋ก ํด๋น ํ
์๋ค์ V-row ๋ ์ด์์์ ์์ด ๋ณํํฉ๋๋ค. ๋ฒ ์ด์ค ๋ชจ๋ธ ์์ฒด๋ ํน์ ์ดํญ ์ฐ์ฐ ํจํด ํ์์ ์ด ์์ด์ ๋ถ๋ณ์ด์ง๋ง, LoRA ๋ธํ๋ ๊ทธ๋ ์ง ์์ต๋๋ค. ์์ด ํ ๋ธํ๊ฐ ํค๋ ์์น๋ฅผ ์ด๊ธ๋ ์ฑ ์ ์ฉ๋์ด ํ ํฐ ๋ถ๊ดด๊ฐ ๋ฐ์ํฉ๋๋ค. merge โ GGUF ๊ฒฝ๋ก๋ ๋ชจ๋ ์ด ๊ตฌ์กฐ์ ๊ฒฐํจ์ ๊ทธ๋๋ก ๋ฌผ๋ ค๋ฐ์ต๋๋ค.
์ ๋๋ก ๋์ํ๋ ๋ฐฐํฌ ๊ฒฝ๋ก๋ unsloth.FastVisionModel + peft.PeftModel.from_pretrained (๋ฐํ์ LoRA, ๋ณํฉ ์์, GGUF ๋ณํ ์์) ์
๋๋ค. ์๋์ ๋ ์ํผ๊ฐ ํ์ค ์ค์ ์
๋๋ค.
๋น ๋ฅธ ์์ (FP16, ์ฝ 20 GB VRAM)
from unsloth import FastVisionModel
from peft import PeftModel
from PIL import Image
# 1. Unsloth๋ก ๋ฒ ์ด์ค ๋ก๋ (transformers.AutoModelForImageTextToText ๊ฐ ์๋๋๋ค โ
# ํ์ต ์์ ์ linear_attn forward ๊ฒฝ๋ก์ ์ผ์น์ํค๋ ค๋ฉด Unsloth์ monkey-patch๊ฐ
# ๋ฐ๋์ ์ ์ฉ๋์ด์ผ ํฉ๋๋ค).
model, tokenizer = FastVisionModel.from_pretrained(
"unsloth/Qwen3.5-9B",
load_in_4bit=False, # 4-bit ์ฌ์ฉ ์ ์ ํ๋ ์์ค ์์ด ์ฝ 8.7 GB VRAM
)
# 2. LoRA๋ฅผ ๋ฐํ์ ํ
์ผ๋ก ๋ถ์ฐฉ. model.merge_and_unload() ๋ ํธ์ถํ๋ฉด ์ ๋ฉ๋๋ค.
# ๋ณํฉ ์ linear_attn ๋ธํ๊ฐ ์กฐ์ฉํ ์์๋์ด ์ถ๋ ฅ์ด "adgeadge"๋ก ๋จ์ด์ง๋๋ค.
model = PeftModel.from_pretrained(model, "pfox1995/pest-detector-final")
# 3. ๋งค์ฐ ์ค์ โ ๋ด๋ถ ๋ชจ๋๋ฅผ ์ถ๋ก ์ฉ์ผ๋ก ์ ํํฉ๋๋ค. ์ด ํธ์ถ์ด ์์ผ๋ฉด
# ๋ค๋ฅธ ๋ชจ๋ ์ค์ ์ด ์ณ๋๋ผ๋ ์ถ๋ ฅ์ด ๋ง๊ฐ์ง๋๋ค.
FastVisionModel.for_inference(model)
model.eval()
# 4. ์ถ๋ก
image = Image.open("pest.jpg").convert("RGB")
image = letterbox(image, 512) # ํ์ ํจ๋ฉ letterbox 512ร512 โ ์๋ ์ฐธ์กฐ
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)
inputs = tokenizer(image, text, add_special_tokens=False, return_tensors="pt").to("cuda")
with __import__("torch").inference_mode():
out = model.generate(
**inputs,
max_new_tokens=10, # 16 ์ด์์ด๋ฉด ์ ๋จ โ ๋ชจ๋ธ์ "<ํด๋์ค>\n"์ ์ถ๋ ฅํ๋ฏ๋ก ๋ ๊ธธ๊ฒ ๊ฐ์ ํ๋ฉด ์ฐ๋ ๊ธฐ
use_cache=True,
stop_strings=["\n"], # ๋ชจ๋ธ์ ์์ฐ์ค๋ฌ์ด ์ ์ง ์ ํธ
tokenizer=tokenizer.tokenizer, # stop_strings๋ฅผ ์ํด ๋ฐ๋์ ํ์
)
prediction = tokenizer.decode(
out[0][inputs["input_ids"].shape[1]:], skip_special_tokens=True
).strip()
print(prediction) # PEST_CLASSES ์ค ํ๋ (ํ๊ตญ์ด)
์ ์ฒด ์คํ ๊ฐ๋ฅํ ์คํฌ๋ฆฝํธ(์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ, ๋ฐฐ์น ํ๊ฐ, ๋ฉํธ๋ฆญ ํฌํจ)๋ inference.py ๋ฅผ ์ฐธ๊ณ ํ์ธ์.
HTTP ์๋ฒ๋ก ์๋นํ๊ธฐ (server.py)
์์ ์ถ๋ก ๋ ์ํผ๋ฅผ ๊ทธ๋๋ก ๊ฐ์ผ FastAPI ์๋ฒ์
๋๋ค. ๊ฒ์ฆ๋ ๋ชจ๋ ์์ ์ฅ์น(for_inference, enable_thinking=False, stop_strings=["\n"], letterbox 512, max_new_tokens=10)๊ฐ ๋ด์ฅ๋์ด ์์ต๋๋ค.
์๋ํฌ์ธํธ:
| ๋ฉ์๋ | ๊ฒฝ๋ก | ์ฉ๋ |
|---|---|---|
| GET | /health |
{"status":"ok","model_loaded":true} |
| GET | /classes |
19๊ฐ ํด๋์ค ๋ชฉ๋ก |
| GET | / |
๋ธ๋ผ์ฐ์ ์ฉ ์ ๋ก๋ ํ์ด์ง (ํ๊ตญ์ด UI) |
| POST | /classify |
multipart ํ์ผ ์ ๋ก๋ |
| POST | /classify_b64 |
JSON {"image":"<base64>"} |
์์:
pip install fastapi uvicorn python-multipart
HF_TOKEN=... ADAPTER=pfox1995/pest-detector-deploy LOAD_IN_4BIT=true PORT=8080 \
python3 server.py
ํด๋ผ์ด์ธํธ ์ฌ์ฉ ์:
curl -F file=@pest.jpg http://localhost:8080/classify
# โ {"pred":"๊ฒ๊ฑฐ์ธ๋ฏธ๋ฐค๋๋ฐฉ","raw":"๊ฒ๊ฑฐ์ธ๋ฏธ๋ฐค๋๋ฐฉ","elapsed_s":2.3}
import requests
r = requests.post(
"http://localhost:8080/classify",
files={"file": open("pest.jpg", "rb")},
timeout=60,
)
print(r.json()["pred"])
RunPod ํ ๋ฒ์ ๋์ฐ๊ธฐ (restart_server.sh)
RunPod ์ปจํ
์ด๋๊ฐ ์ฌ์์๋๋ฉด ์ปจํ
์ด๋ ๋์คํฌ๊ฐ ์ด๊ธฐํ๋์ด pip ํจํค์ง๊ฐ ์ฌ๋ผ์ง๋๋ค (/workspace ๋ณผ๋ฅจ์ ์ ์ง๋จ). ์ด ์ํฉ์ ํ ๋ฒ์ ๋ณต๊ตฌํด์ฃผ๋ ์คํฌ๋ฆฝํธ:
# /workspace ์ ์ด ์ ์ฅ์๋ฅผ ๋ฐ์๋์๋ค๊ณ ๊ฐ์
bash /workspace/restart_server.sh
์คํฌ๋ฆฝํธ๊ฐ ์๋์ผ๋ก ์ฒ๋ฆฌํ๋ ๋จ๊ณ:
- ์์กด์ฑ ์คํ ์ ๊ฒ (
unsloth,peft,fastapi,bitsandbytes,flash-linear-attention) โ ์์ผ๋ฉด ์ค์น causal_conv1d์ ๊ฒ โ ์์ผ๋ฉด ์ฌ์ ๋น๋๋ wheel ์ค์น (์์ค ๋น๋๋ 9๊ฐ GPU ์ํคํ ์ฒ ์ปดํ์ผ ๋๋ฌธ์ ๋ฉ๋ชจ๋ฆฌ ๋ถ์กฑ์ผ๋ก ์คํจํจ)- ๊ธฐ์กด
pesttmux ์ธ์ ์ข ๋ฃ ํ ์๋ก ์์ /health๊ฐ 200์ ๋ฐํํ ๋๊น์ง ๋๊ธฐ (์ฝ 90~100์ด)- ๊ณต๊ฐ ํ๋ก์ URL ์ถ๋ ฅ โ RunPod์
$RUNPOD_POD_IDํ๊ฒฝ๋ณ์์์ ์๋์ผ๋ก ์ถ๋ก
ํ๊ฒฝ๋ณ์๋ก ๋์ ๋ณ๊ฒฝ ๊ฐ๋ฅ:
LOAD_IN_4BIT=false bash /workspace/restart_server.sh # FP16, ์ฝ 19.5 GB VRAM, ์ถ๋ก ์๋ ์ฝ 2๋ฐฐ
PORT=9000 bash /workspace/restart_server.sh # ๋ค๋ฅธ ํฌํธ๋ก
ADAPTER=... bash /workspace/restart_server.sh # ๋ค๋ฅธ ์ด๋ํฐ๋ก
PUBLIC_URL=... bash /workspace/restart_server.sh # ์๋ ๊ฐ์ง ๊ฒฐ๊ณผ ๋ฎ์ด์ฐ๊ธฐ
RunPod ์์ 8080 ํฌํธ๋ฅผ ์ธ๋ถ์ ๋ ธ์ถํ๋ ค๋ฉด:
- RunPod ๋์๋ณด๋ โ ํด๋น Pod ์ ํ โ Edit Pod โ HTTP Ports ์์
8080์ถ๊ฐ - ์ ์ฅ โ ์๋์ผ๋ก
https://<POD_ID>-8080.proxy.runpod.net/๊ฐ ํ์ฑํ๋จ - ์ฒซ ๋ฒ์งธ ์์ฒญ์ ์ฝ 12์ด (Triton JIT ์ปดํ์ผ), ์ดํ๋ ์ ์ ์ํ ์ฝ 2~3์ด/์ด๋ฏธ์ง
โ
Edit Pod๋ ๋ด๋ถ์ ์ผ๋ก ์ปจํ ์ด๋๋ฅผ ์ฌ์์ํฉ๋๋ค. ์ปจํ ์ด๋ ๋์คํฌ๊ฐ ์ด๊ธฐํ๋๋ฏ๋ก ๋ค์restart_server.sh๋ฅผ ์คํํด ์ฃผ์ธ์. ๋ณผ๋ฅจ(/workspace) ์ ํ์ผ๊ณผ ๋ชจ๋ธ ์บ์๋ ์ ์ง๋ฉ๋๋ค. SSH ์ publicPort ๋ ๋ฐ๋๋ฏ๋ก GraphQL ๋ก ์ ํฌํธ๋ฅผ ๋ค์ ์กฐํํด์ผ ํฉ๋๋ค.
์ถ๋ ฅ์ ๋ง๊ฐ๋จ๋ฆฌ๋ 9๊ฐ์ง ํจ์ (๊ฐ๊ฐ ์ง์ ๋ถ๋ชํ ๊ฒ๋ค)
์ถ๋ ฅ์ด ์ด์ํ๋ฉด ์๋ ํญ๋ชฉ์ ์์๋๋ก ์ ๊ฒํ์ธ์. ๋ชจ๋ ํญ๋ชฉ์ด ์ค์ ๋ก adgeadge ๋๋ <think>...assistant<think>... ๋ฐ๋ณต์ ์ผ์ผํจ ์ฌ๋ก์
๋๋ค.
1. ์๋ชป๋ ๋ก๋
unsloth.FastVisionModel.from_pretrained ๋ฅผ ์ฌ์ฉํด์ผ ํฉ๋๋ค. transformers.AutoModelForImageTextToText.from_pretrained ๋ ์ ๋ฉ๋๋ค. ๊ฐ์ ๊ฐ์ค์น๋ฅผ ๋ก๋ํ์ง๋ง ํ์๋ ํ์ต ์์ ์ ์ฌ์ฉ๋ Unsloth์ Gated DeltaNet ํจ์น๋ฅผ ์ ์ฉํ์ง ์์ต๋๋ค.
2. LoRA ๋ณํฉ
model.merge_and_unload() ๋๋ model.save_pretrained_merged(...) ๋ฅผ ํธ์ถํ์ง ๋ง์ธ์. PEFT์ ๋ณํฉ์ ์ด ์ํคํ
์ฒ์ linear_attn ๋ชจ๋์ ๋ํด ์กฐ์ฉํ ์๋ชป๋ ๊ฐ์ค์น๋ฅผ ์์ฑํฉ๋๋ค. PeftModel.from_pretrained ๋ฅผ ํตํด LoRA๋ฅผ ๋ฐํ์ ํ
์ผ๋ก ์ ์งํ์ธ์.
3. FastVisionModel.for_inference(model) ๋๋ฝ
์ด ํธ์ถ์ด Unsloth์ ๋ด๋ถ ์บ์/์ถ๋ก ๋ชจ๋๋ฅผ ์ ํํฉ๋๋ค. ๋น ๋จ๋ฆฌ๋ฉด ์ฒซ ํ ํฐ์ ์ ์์ด๋ค๊ฐ ์ด์ด์ง๋ ํ ํฐ์ด adge ์ดํธ๋ํฐ๋ก ๋น ์ง ์ ์์ต๋๋ค.
4. max_new_tokens ๊ฐ ๋๋ฌด ํผ
๋ชจ๋ธ์ <ํด๋์ค>\n (์: ๊ฒ๊ฑฐ์ธ๋ฏธ๋ฐค๋๋ฐฉ\n) ์ ์ถ๋ ฅํ๊ณ ์์ฐ์ค๋ฝ๊ฒ ๋ฉ์ถฅ๋๋ค. ๊ทธ๋ฌ๋ 16 ํ ํฐ ์ด์์ ์๊ตฌํ๋ฉด \n ์ ์ง๋์ ๊ณ์ ์์ฑํ๋ค adge ์ดํธ๋ํฐ์ ๋น ์ง๋๋ค. ํด๋์ค๋ช
์ถ๋ ฅ์๋ max_new_tokens=10 ์ ์ฌ์ฉํ์ธ์. min_new_tokens ๋ ์ค์ ํ์ง ๋ง์ธ์(EOS ์ดํ๋ก ๊ฐ์ ์์ฑํ๊ฒ ๋ฉ๋๋ค).
5. stop_strings=["\n"] ๋๋ฝ
max_new_tokens=10 ์ด๋ผ๋ ๋ชจ๋ธ์ <ํด๋์ค>\nassistant\n<think> ๋ฅผ ์ถ๋ ฅํ ์ ์์ต๋๋ค. model.generate ์ stop_strings=["\n"] ์ ํจ๊ป tokenizer=tokenizer.tokenizer ๋ ์ ๋ฌํด์ผ ํฉ๋๋ค. tokenizer= ์ธ์๊ฐ ์์ผ๋ฉด stop_strings ๊ฐ ๋์ํ์ง ์์ต๋๋ค.
6. enable_thinking ํธ์ถ ๋ฐฉ์ ์ค๋ฅ
์ฑ ํ
ํ๋ฆฟ์ด enable_thinking ๋ณ์์ ๋ฐ๋ผ ๋ถ๊ธฐ๋ฉ๋๋ค. ํ์ต์ thinking ๋นํ์ฑ ๋ชจ๋๋ก ์งํ๋์ต๋๋ค. tokenizer.apply_chat_template(...) ์ enable_thinking=False ๋ฅผ ์ง์ ํค์๋ ์ธ์๋ก ์ ๋ฌํ์ธ์. chat_template_kwargs={"enable_thinking": False} ๋ก ๊ฐ์ธ๋ฉด transformers โฅ 5.0 ์ VLM ํ๋ก์ธ์์์ ์กฐ์ฉํ ๋ฌด์๋ฉ๋๋ค.
7. ์๋ชป๋ ์์คํ ํ๋กฌํํธ
ํ์ต ์ ์ฌ์ฉํ ์์คํ
๋ฉ์์ง๋ฅผ ์ ํํ ๊ทธ๋๋ก ์ฌ์ฉํ์ธ์(์๋ SYSTEM_MSG ์ฐธ์กฐ). 19๊ฐ ํด๋์ค๋ฅผ ๋์ดํ ๋ ๊ธด ๋ฒ์ ์ ์๋ํด ๋ดค๋๋ฐ ์ถ๋ ฅ์ ํธํฅ์ด ์๊ฒผ์ต๋๋ค.
8. ์๋ชป๋ ์ด๋ฏธ์ง ํฌ๊ธฐ
๋ชจ๋ธ์ ํ์(RGB 128, 128, 128) ํจ๋ฉ์ผ๋ก letterbox ์ฒ๋ฆฌ๋ 512ร512 ์ด๋ฏธ์ง๋ก ํ์ต๋์์ต๋๋ค. ๋ค๋ฅธ ํด์๋๋ ํจ๋ฉ ์์์ ์ฐ๋ฉด ์ ํ๋๊ฐ ๋จ์ด์ง๋๋ค.
9. ๋น ๋ฅธ linear-attn ๊ฒฝ๋ก์ฉ ์์กด์ฑ ๋๋ฝ
pip install flash-linear-attention causal-conv1d ๋ก transformers ๊ฐ Gated DeltaNet ์ ๋น ๋ฅธ Triton ์ปค๋์ ์ฌ์ฉํ ์ ์๊ฒ ํ์ธ์. ์์ผ๋ฉด torch ํด๋ฐฑ ๊ฒฝ๋ก๋ฅผ ํ๊ฒ ๋๋๋ฐ, FP16 ๋์ฐ ์์๊ฐ ๋ฌ๋ผ์ ์ ์๋ ๊ฐ์ค์น์์๋ ์ถ๋ ฅ์ด ํ๋ฅํ ์ ์์ต๋๋ค. (torch 2.8 + cu128 ์ฉ ์ฌ์ ๋น๋๋ wheel ์ด ์์ต๋๋ค.)
์์คํ ํ๋กฌํํธ ๋ฐ ์์ (ํ์ต ์์ ๊ณผ ๋์ผ)
SYSTEM_MSG = (
"๋น์ ์ ์๋ฌผ ํด์ถฉ ์๋ณ ์ ๋ฌธ๊ฐ์
๋๋ค. "
"์ฌ์ง์ ๋ณด๊ณ ํด์ถฉ์ ์ด๋ฆ๋ง ํ๊ตญ์ด๋ก ๋ตํ์ธ์. "
'ํด์ถฉ์ด ์์ผ๋ฉด "์ ์"์ด๋ผ๊ณ ๋ง ๋ตํ์ธ์. '
"๋ถ๊ฐ ์ค๋ช
์์ด ์ด๋ฆ๋ง ์ถ๋ ฅํ์ธ์."
)
USER_PROMPT = "์ด ์ฌ์ง์ ์๋ ํด์ถฉ์ ์ด๋ฆ์ ์๋ ค์ฃผ์ธ์."
PEST_CLASSES = [
"๊ฒ๊ฑฐ์ธ๋ฏธ๋ฐค๋๋ฐฉ", "๊ฝ๋
ธ๋์ด์ฑ๋ฒ๋ ", "๋ด๋ฐฐ๊ฐ๋ฃจ์ด", "๋ด๋ฐฐ๊ฑฐ์ธ๋ฏธ๋๋ฐฉ",
"๋ด๋ฐฐ๋๋ฐฉ", "๋๋๋๋ฐฉ", "๋จน๋
ธ๋ฆฐ์ฌ", "๋ชฉํ๋ฐ๋๋ช
๋๋ฐฉ", "๋ฌด์๋ฒ",
"๋ฐฐ์ถ์ข๋๋ฐฉ", "๋ฐฐ์ถํฐ๋๋น", "๋ฒผ๋ฃฉ์๋ฒ๋ ", "๋น๋จ๋
ธ๋ฆฐ์ฌ", "์ฉ๋ฉ๋๋ฌด๋
ธ๋ฆฐ์ฌ",
"์๋ฝ์์ผ๋
ธ๋ฆฐ์ฌ", "์ ์", "ํฐ28์ ๋ฐ์ด๋ฌด๋น๋ฒ๋ ", "ํฑ๋ค๋ฆฌ๊ฐ๋ฏธํ๋ฆฌ๋
ธ๋ฆฐ์ฌ",
"ํ๋ฐค๋๋ฐฉ",
]
19๊ฐ ํด๋์ค๋ 18์ข
์ ํด์ถฉ + ์ ์ ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ํ์ต ๋ฐ์ดํฐ ๋ถํฌ๋ ํด๋์ค ๊ฐ ๋์ฒด๋ก ๊ท ํ์ ์ด๋ฃน๋๋ค.
์ด๋ฏธ์ง ์ ์ฒ๋ฆฌ (letterbox)
from PIL import Image
def letterbox(img: Image.Image, size: int = 512) -> Image.Image:
"""์ข
ํก๋น๋ฅผ ์ ์งํ๋ฉฐ ๋ฆฌ์ฌ์ด์ฆํ ํ, ํ์์ผ๋ก ํจ๋ฉํ์ฌ ์ ์ฌ๊ฐํ์ผ๋ก ๋ง๋ญ๋๋ค."""
img = img.convert("RGB")
w, h = img.size
scale = size / max(w, h)
nw, nh = int(round(w * scale)), int(round(h * scale))
resized = img.resize((nw, nh), Image.Resampling.LANCZOS)
canvas = Image.new("RGB", (size, size), (128, 128, 128))
canvas.paste(resized, ((size - nw) // 2, (size - nh) // 2))
return canvas
์์กด์ฑ ๋ฒ์ ๊ณ ์
ํ์ต ํ๊ฒฝ์ด ์ค์ํฉ๋๋ค. ์๋ ๋ฒ์ ๋ค์ด ์ฌํ ๊ฐ๋ฅํ ์ถ๋ ฅ์ ๋ณด์ฅํฉ๋๋ค:
torch==2.8.0+cu128
transformers>=5.2,<6.0 # 5.5.0 ๊ฒ์ฆ๋จ
peft==0.19.1 # adapter_config.json ์ ๋ฒ์ ๊ณผ ๋ฐ๋์ ์ผ์น
unsloth==2026.4.8 # ๋ ์ด์ ๋ฒ์ ์ cu128-torch280 extra ๋ฅผ ์ฌ์ฉ
xformers>=0.0.32 # FA2 ํด๋ฐฑ์ผ๋ก ์ถฉ๋ถ
Pillow>=10.0
flash-linear-attention # Gated DeltaNet ๋น ๋ฅธ ๊ฒฝ๋ก
causal-conv1d # fla ์ ํจ๊ป ์ฌ์ฉ
์ค์น ๋ช ๋ น:
pip install "unsloth[cu128-torch280]" "transformers>=5.2,<6.0" "peft==0.19.1"
pip install flash-linear-attention causal-conv1d --no-build-isolation
causal-conv1d ๊ฐ ์ฌ์ฉ ์ค์ธ CUDA ๋ฒ์ ์์ ๋น๋ ์คํจํ๋ฉด Unsloth ๊ฐ broken ์ผ๋ก ํ์ํ๊ณ ๊ณ์ ์งํํฉ๋๋ค โ ์ ํ๋๋ ์ํฅ ์๊ณ ์ฒ๋ฆฌ๋๋ง ์ฝ๊ฐ ๋จ์ด์ง๋๋ค.
bnb 4-bit (NF4) ๋ก VRAM ์ค์ด๊ธฐ
๋ฒ ์ด์ค๋ฅผ 4-bit ๋ก ๋ก๋ํ๋ ค๋ฉด ํ ์ค๋ง ๋ฐ๊พธ๋ฉด ๋ฉ๋๋ค:
model, tokenizer = FastVisionModel.from_pretrained(
"unsloth/Qwen3.5-9B",
load_in_4bit=True, # ์ด์ ๊ฐ: False
)
# ๋๋จธ์ง ์ค์ ์ ๋ชจ๋ ๋์ผ
์ธก์ ๊ฒฐ๊ณผ:
- VRAM: 19.5 GB โ 8.7 GB (55 % ๊ฐ์)
- ๋์คํฌ: ~18 GB โ ~5 GB
- ์ ํ๋: ๋นํธ ๋จ์ ๋์ผ โ 10์ํ ํ๋ก๋ธ์์ 8/10 ๋ก ์ผ์น (ํ๋ฆฐ 2๊ฐ๋ ๋ ์ค์ ๋ชจ๋์์ ๋์ผํ ์ด๋ ค์ด ์ฌ๋ก ๋ด๋ฐฐ๊ฐ๋ฃจ์ด โ ์ ์ ํผ๋)
์ด LoRA ์์ ์๋ํ๋ ์ ์ผํ ์์ํ ๋ฐฉ์์ธ๋ฐ, LoRA ๊ฐ ์์ํ๋ ๊ฐ์ค์น์ ๊ตฝํ์ง ์๊ณ PEFT ํ ์ ํตํด ๋ฐํ์์์ ์ ์ง๋๊ธฐ ๋๋ฌธ์ ๋๋ค.
load_in_8bit=True (LLM.int8) ๋ ๋์ํ์ง๋ง ์ด ์์
์์๋ NF4 ๋๋น ํ์ง ์ด๋ ์์ด VRAM ์ฝ 13 GB ๋ฅผ ์ฌ์ฉํฉ๋๋ค.
๋์ํ์ง ์๋ ๊ฒฝ๋ก๋ค (์๋ํ์ง ๋ง์ธ์)
model.save_pretrained_gguf(...)โ ์ ํ๋ 0 %model.save_pretrained_merged(...)ํconvert_hf_to_gguf.pyโ 0 %- ๋ณํ ์
_reorder_v_heads๋ฏธ๋ฆฌ ์ ์ฉ โ 5.3 % (๋ถ๊ดด) - ๋ณํฉ โ GGUF ์ ์
linear_attnLoRA ๋ฅผ 0์ผ๋ก โ 35.1 % (์ ์ ์์ค) - ๋ณํฉ๋ ๋ชจ๋ธ์ AutoAWQ โ ๊ฐ์ ๋ณํฉ ๋ฒ๊ทธ
peft.merge_and_unload()ํ ๋ณํฉ๋ ๋๋ ํ ๋ฆฌ์transformers.AutoModelForImageTextToText.from_pretrainedโadgeadge- ๋ณํฉ๋ ๋ชจ๋ธ๋ก vLLM โ ์์ ๋์ผ
ํจํด์ ๋ช
ํํฉ๋๋ค: linear_attn ์ LoRA ๋ธํ๋ฅผ FastVisionModel ๋ฐํ์ ๊ฒฝ๋ก ์ธ์ ๋ฐฉ์์ผ๋ก ๊ฑด๋๋ฆฌ๋ ๋ชจ๋ ์๋๊ฐ ์ฐ๋ ๊ธฐ ์ถ๋ ฅ์ ๋ง๋ญ๋๋ค. ๊ตฌ์กฐ์ ๊ฒฐํจ์ convert_hf_to_gguf.py:_reorder_v_heads ์ ์๊ณ , ์์ ์ด์๋ llama.cpp#21125 ์
๋๋ค.
์๋ ค์ง ๋ถ๋ฅ ์ค๋ฅ
์๋๋ ๋ฐฐํฌ ๋ฒ๊ทธ๊ฐ ์๋ ์ค์ ๋ชจ๋ธ์ ๋ถ๋ฅ ์ค๋ฅ ์ ๋๋ค โ ๋ชจ๋ธ์ด ์ฌ๋ฐ๋ฅธ ํ๊ตญ์ด ํด๋์ค๋ช ์ ์ถ๋ ฅํ์ง๋ง ์ ๋ต์ด ์๋ ๊ฒฝ์ฐ์ ๋๋ค:
| ์ ๋ต โ ์์ธก | ๋น์จ (57์ํ ๋ฒค์น) | ์ด์ |
|---|---|---|
| ๋๋๋๋ฐฉ โ ๊ฒ๊ฑฐ์ธ๋ฏธ๋ฐค๋๋ฐฉ | 2/3 | ๋ ๋ค ์ด๋์ด ์ ๋๋ฐฉ, ์๊ฐ์ ์ผ๋ก ์ ์ฌ |
| ๋น๋จ๋ ธ๋ฆฐ์ฌ โ ๋ชฉํ๋ฐ๋๋ช ๋๋ฐฉ | 2/3 | ์๊ณ ์ผ๋ฃฉ๋ฌด๋ฌ๊ฐ ์๋ ๊ณค์ถฉ |
| ๋ด๋ฐฐ๊ฐ๋ฃจ์ด โ ์ ์ | 2/3 | ์์ ํฐ ํด์ถฉ, ๋์น๊ธฐ ์ฌ์ |
| ์๋ฝ์์ผ๋ ธ๋ฆฐ์ฌ โ ์ ์ | 1/3 | ์์ ์ ์ฌ |
| ๋ฒผ๋ฃฉ์๋ฒ๋ โ ๋ชฉํ๋ฐ๋๋ช ๋๋ฐฉ / ๋ฐฐ์ถ์ข๋๋ฐฉ | 1/3 ์ฉ | ์๊ณ ์ด๋์ด ๊ณค์ถฉ |
19๊ฐ ํด๋์ค ์ค 14๊ฐ๋ ๋ฒค์น์์ 100 % (3/3) ์ ์คํฉ๋๋ค. ์ฝํ ํด๋์ค๋ฅผ ๊ฐ์ ํ๋ ค๋ฉด ํด๋น ์ข ์ ์ํ์ ์ถ๊ฐํ๊ฑฐ๋ ๋ ๋ณ๋ณ๋ ฅ ์๋ augmentation ์ผ๋ก ์ฌํ์ตํ์ธ์.
์ธ์ฉ / ์ฐธ๊ณ
- ๋ฒ ์ด์ค ๋ชจ๋ธ:
unsloth/Qwen3.5-9B - ๋ฐ์ดํฐ์
:
Himedia-AI-01/pest-detection-korean - LoRA ํ์ต: Unsloth FastVisionModel, rank=64, alpha=128, RS-LoRA, target_modules ์ ๊ท์์
q/k/v/o_proj,gate/up/down_proj, ๊ทธ๋ฆฌ๊ณin_proj_qkv/z/a/b/out_proj(Gated DeltaNet ํฌ์ โ GGUF ๋ฐฐํฌ๊ฐ ๋ถ๊ฐ๋ฅํ ์ด์ ) - ์ต์ข eval loss: 0.023164 (step 850)
- 1595 ์ํ ๊ฒ์ฆ ์ ํ๋: 91.36 %
๋ผ์ด์ ์ค
๋ฒ ์ด์ค ๋ชจ๋ธ ๋ฐ ๋ฐ์ดํฐ์
์ ๋ผ์ด์ ์ค๋ฅผ ๋ฐ๋ฆ
๋๋ค. ์์ธํ ์ฝ๊ด์ unsloth/Qwen3.5-9B ์ Himedia-AI-01/pest-detection-korean ํ์ด์ง๋ฅผ ํ์ธํ์ธ์.