ํ๊ตญ์ด ยท English
๋ก์ปฌ Ollama ๋ชจ๋ธ์ LoRA๋ก ํ์ธํ๋ํด ๋ค์ Ollama์ ๋ฑ๋กํ๋ end-to-end ์ํ. ํฉ์ฑ ํ์ต ๋ฐ์ดํฐ ์์ฑ๊ธฐ๊ฐ ํฌํจ๋ผ ์์ด ์ธ๋ถ ๋ฐ์ดํฐ ์์ด ๋ฐ๋ก ๋๋ ค๋ณผ ์ ์๋ค.
๋ ํ์ดํ๋ผ์ธ์ด ๋ค์ด ์๋ค:
| ํด๋ | ๋์ | ํ์คํฌ | ๋ฐ์ดํฐ |
|---|---|---|---|
scripts-vlm/ |
qwen3-vl:8b (8.8B, ๋ฉํฐ๋ชจ๋ฌ) |
์์์ฆ ์ด๋ฏธ์ง โ ๊ตฌ์กฐํ JSON | data/ |
scripts-llm/ |
qwen3.8:latest (27.3B) |
๊ณ ๊ฐ ๋ฌธ์ ํ ์คํธ โ ๋ผ์ฐํ JSON | data-llm/ |
๋ ํด๋๋ ๊ฐ์ 5๋จ๊ณ ๊ตฌ์กฐ(00 ๋ฐ์ดํฐ โ 01 ํ์ต โ 02 ๊ฒ์ฆ โ 03 ๋ณํฉ โ
04 GGUF/๋ฑ๋ก โ 05 Ollama ๊ฒ์ฆ)๋ฅผ ๊ณต์ ํ๋ค. ์๋ 1~5์ฅ์ scripts-vlm
๊ธฐ์ค์ด๊ณ , scripts-llm์ ๋ค์ ์ ์ฉ ์ฅ์์ ๋ค๋ฃฌ๋ค.
Ollama์๋ ํ์ธํ๋ ๊ธฐ๋ฅ์ด ์๋ค. GGUF ์ถ๋ก ์์ง์ด๋ค. ๋ํ ๋ก์ปฌ์
qwen3-vl:8b๋ ์ด๋ฏธ Q4_K_M์ผ๋ก ์์ํ๋ ์ฐ์ถ๋ฌผ์ด๋ผ ๊ทธ ํ์ผ ์์ฒด๋ฅผ ํ์ต์ํฌ ์
์๋ค(4bit ์ ์ ๊ฐ์ค์น์๋ gradient๋ฅผ ํ๋ฆด ์ ์๋ค).
๊ทธ๋์ ์ค์ ๊ฒฝ๋ก๋ ์ด๋ ๋ค:
Qwen/Qwen3-VL-8B-Instruct (HF, bf16) โ ํ์ต ์์์ . ollama์ qwen3-vl:8b์ ๊ฐ์ ์๋ณธ
โ LoRA ํ์ต (transformers + peft)
โผ
out/lora-receipt/ (์ด๋ํฐ, ์์ญ MB)
โ ๋ณํฉ
โผ
out/merged/ (fp16 full weight, ์ฝ 17GB)
โ llama.cpp: convert_hf_to_gguf.py (+ --mmproj)
โผ
์ธ์ด๋ชจ๋ธ GGUF + ๋น์ ํ๋ก์ ํฐ GGUF โ ์์ํ(Q4_K_M)
โ ollama create -f Modelfile
โผ
ollama run qwen3vl-receipt
ollama show qwen3-vl:8b โ qwen3vl / 8.8B / ctx 262144 / Q4_K_M / visionยทtoolsยทthinking.
๊ทธ๋ฆฌ๊ณ ollama show --modelfile qwen3-vl:8b ๋ฅผ ๋ณด๋ฉด RENDERER qwen3-vl-thinking,
PARSER qwen3-vl-thinking ์ด๋ค. ์ฆ ์ด ํ๊ทธ๋ Instruct๊ฐ ์๋๋ผ Thinking ๋ณ์ข
์ด๋ค.
/api/chat์ "think": false๋ฅผ ๋ณด๋ด๋ ์ถ๋ก ์ด ๊บผ์ง์ง ์๊ณ thinking ํ๋๋ก ๋ถ๋ฆฌ๋
๋ฟ์ด์ด์, num_predict๊ฐ ์์ผ๋ฉด ๋ต(content)์ด ๋น ๋ฌธ์์ด๋ก ๋๊ธด๋ค(์ค์ธก: ์ถ๋ก 2000~5000์).
์ด ์ ์ฅ์์ ํ์ต ๊ธฐ๋ณธ๊ฐ์ Qwen/Qwen3-VL-8B-Instruct ๋ค. ๊ตฌ์กฐํ JSON ์ถ์ถ์๋
์ถ๋ก ๋ธ๋ก์ด ๋ฐฉํด๋ง ๋๊ณ , ๋ผ๋ฒจ์ thinking ํ์์ ํ๋ด๋ผ ํ์๋ ์์ด ํจ์ฌ ๋จ์ํ๋ค.
์ ์ฒด ๋ณํฉ ๊ฒฝ๋ก(4๋จ๊ณ)๋ ๋ชจ๋ธ์ ํต์งธ๋ก ๊ต์ฒดํ๋ฏ๋ก ๋ก์ปฌ ํ๊ทธ์ ๋ฌ๋ผ๋ ๋ฌธ์ ์๋ค.
๋จ, ๊ฒฝ๋ ๊ฒฝ๋ก(4b, ๊ธฐ์กด qwen3-vl:8b ์์ ์ด๋ํฐ๋ง ์น๊ธฐ)๋ฅผ ์ธ ๊ฑฐ๋ฉด ๋ฒ ์ด์ค๊ฐ
๋ฐ๋์ ์ผ์นํด์ผ ํ๋ค โ --model Qwen/Qwen3-VL-8B-Thinking ์ผ๋ก ํ์ตํ๊ณ
BASE_HF_MODEL๋ ๊ฐ์ด ๋ง์ถ๋ค. ์ด๋๋ ๋ผ๋ฒจ๋ Thinking ํ
ํ๋ฆฟ์ ๋ง์ถฐ์ผ ํ๋ค.
python scripts-vlm/05_test_ollama.py --model qwen3-vl:8b --limit 3 ๊ฒฐ๊ณผ:
| ํญ๋ชฉ | ์ ํ๋ | ๊ด์ฐฐ |
|---|---|---|
store / date / total |
2/3 (67%) | ๋๊น์ง ์์ฑ๋ง ๋๋ฉด ๋์ฒด๋ก ๋งํ๋ค |
items (๋จ๊ฐ) |
0/3 (0%) | price์ **๋จ๊ฐ ๋์ ์ค ํฉ๊ณ(qtyรprice)**๋ฅผ ๋ฃ๋๋ค |
| exact match | 0/3 (0%) | โ |
| โ | โ | 3๊ฑด ์ค 1๊ฑด์ ์ถ๋ก ์ด ๊ธธ์ด done_reason=length๋ก ๋ต์ด ์๋ ธ๋ค |
์ฆ ๋ฒ ์ด์ค ๋ชจ๋ธ์ "์ฝ๊ธฐ"๋ ๋๋๋ฐ ์ฐ๋ฆฌ ์คํค๋ง์ price ์ ์๋ฅผ ๋ชจ๋ฅธ๋ค. ์ด๋ฐ
ํ์ยท์ ์ ๊ณ ์ ์ด LoRA๊ฐ ๊ฐ์ฅ ์ ํด๊ฒฐํ๋ ์ข
๋ฅ์ ๋ฌธ์ ๋ค. ๋ฐ๋๋ก OCR ์์ฒด๋ฅผ ๋ชป ์ฝ๋
๋ฌธ์ ๋ผ๋ฉด ํ์ธํ๋๋ณด๋ค ํด์๋/์ ์ฒ๋ฆฌ๋ฅผ ๋จผ์ ๋ณด๋ ๊ฒ ๋ง๋ค.
์ด ๋งฅ์ Apple M4 / ํตํฉ๋ฉ๋ชจ๋ฆฌ 24GB๋ค.
| ๋์ | ํ๊ฒฝ | ํ์ |
|---|---|---|
| qwen3-vl 8.8B | CUDA 24GB+ (A10/3090/4090) | ๊ถ์ฅ. --load-4bit QLoRA๋ก ํธํ๊ฒ ๋์๊ฐ๋ค |
| qwen3-vl 8.8B | M4 24GB, scripts-vlm/01_train_lora.py |
์ค๋ชจํฌ ํ ์คํธ์ฉ. bf16 ๊ฐ์ค์น๋ง 17.6GB, ์ค์์ด ๊ฑธ๋ ค ๋งค์ฐ ๋๋ฆฌ๋ค |
| qwen3-vl 8.8B | M4 24GB, MLX 4bit LoRA | ์ค์ฌ์ฉ ๊ฐ๋ฅ. ์๋ "Apple Silicon ๋์" ์ฐธ๊ณ |
| qwen3.8 27.3B | M4 24GB | ๋ถ๊ฐ. bf16 ๊ฐ์ค์น๋ง ์ฝ 55GB |
| qwen3.8 27.3B | CUDA 48GB+ QLoRA | ์ฌ๊ธฐ์ ๋๋ ค์ผ ํ๋ค |
์ฆ ์ด ๋งฅ์์๋ ํ์ดํ๋ผ์ธ ๊ฒ์ฆ๊น์ง๋ง ๋ก์ปฌ๋ก ํ๊ณ , ๋ณธ ํ์ต์ GPU ์ธ์คํด์ค๋
MLX๋ก ๋๋ฆฌ๋ ํธ์ ๊ถํ๋ค. scripts-llm ์ชฝ์ --model Qwen/Qwen3-1.7B ๊ฐ์ ์์
๋ชจ๋ธ๋ก ํ์ดํ๋ผ์ธ์ ๋จผ์ ๊ฒ์ฆํ ๋ค ํ๊น ๋ชจ๋ธ๋ก ๋ฐ๊พธ๋ ํ๋ฆ์ ๊ธฐ๋ณธ์ผ๋ก ์ก์๋ค.
python3 -m venv .venv && source .venv/bin/activate
pip install -r requirements.txtCUDA์์ QLoRA๋ฅผ ์ธ ๊ฑฐ๋ฉด pip install bitsandbytes ๋ฅผ ์ถ๊ฐํ๋ค(macOS์์๋ ์ค์นํ์ง ๋ง ๊ฒ).
python scripts-vlm/00_make_dataset.py --n-train 80 --n-valid 12data/images/*.png(ํฉ์ฑ ์์์ฆ)์ data/train.jsonl / data/valid.jsonl์ด ์๊ธด๋ค.
JSONL ํ ์ค ํ์:
{"images": ["data/images/train_000.png"],
"messages": [{"role": "user", "content": "์ด ์์์ฆ ์ด๋ฏธ์ง๋ฅผ ์ฝ๊ณ ... JSON๋ง ์ถ๋ ฅํด๋ผ ..."},
{"role": "assistant", "content": "{\"store\":\"์ฒญ๊ณผ๋ง์ ๋ถ๋น์ \",\"date\":\"2026-01-01\",...}"}]}์๊ธฐ ๋ฐ์ดํฐ๋ก ๋ฐ๊ฟ ๋๋ ์ด ์คํค๋ง๋ง ๋ง์ถ๋ฉด ๋๋จธ์ง ์คํฌ๋ฆฝํธ๋ ๊ทธ๋๋ก ์ธ ์ ์๋ค.
00_make_dataset.py๋ฅผ ๋ณํ๊ธฐ๋ก ๊ต์ฒดํ๋ฉด ๋๋ค. ์ค๋ฌด์์ ์ค์ํ ๋ ๊ฐ์ง:
- ์ง์๋ฌธ(
INSTRUCTION)์ ํ์ตยท์ถ๋ก ์์ ๋ฌธ์ ๋จ์๋ก ๋์ผํ๊ฒ ์ ์งํ๋ค. ํ๋กฌํํธ๊ฐ ๋ฌ๋ผ์ง๋ฉด ํ์ต ํจ๊ณผ๊ฐ ์๋น ๋ถ๋ถ ๋ ์๊ฐ๋ค. Modelfile์SYSTEM์ ๊ธธ๊ฒ ์ฐ๋ฉด ์ด๊ฒ ๊นจ์ง๋ฏ๋ก ์งง๊ฒ ๋๊ฑฐ๋ ๋น์ด๋ค. - ๋ผ๋ฒจ์ ๊ณต๋ฐฑ ์๋ compact JSON์ผ๋ก ๊ณ ์ ํ๋ค. ํ์์ด ํ๋ค๋ฆฌ๋ฉด ๋ชจ๋ธ๋ ํ๋ค๋ฆฐ๋ค.
์ค์ ๋ฐ์ดํฐ ๊ท๋ชจ๋ ์ต์ ์๋ฐฑ~์์ฒ ์ํ์ ๋ณด์. 80๊ฐ๋ ํ์ดํ๋ผ์ธ ํ์ธ์ฉ์ด๋ค.
# CUDA (๊ถ์ฅ)
python scripts-vlm/01_train_lora.py --load-4bit --epochs 3 --grad-accum 8 --grad-ckpt
# macOS ์ค๋ชจํฌ ํ
์คํธ (ํ์ดํ๋ผ์ธ ๊ฒ์ฆ๋ง; 4์ํ 1์ํญ)
PYTORCH_ENABLE_MPS_FALLBACK=1 python scripts-vlm/01_train_lora.py \
--max-samples 4 --epochs 1 --max-image-side 448 --grad-ckpt์คํฌ๋ฆฝํธ๊ฐ ํ๋ ์ผ ์ค ๋์ฌ๊ฒจ๋ณผ ๋ถ๋ถ:
- ๋ผ๋ฒจ ๋ง์คํน: ํ๋กฌํํธ ๊ตฌ๊ฐ์
-100์ผ๋ก ๋ฎ๊ณ assistant ์๋ต์๋ง loss๋ฅผ ๊ฑด๋ค. ํ๋กฌํํธ ๊ธธ์ด๋ฅผ ์ด ๋ ๊ฐ์ ์ด๋ฏธ์ง๋ก ํ ๋ฒ ๋ ์ธ์ฝ๋ฉํ๋ค โ Qwen-VL์ ์ด๋ฏธ์ง ๊ทธ๋ฆฌ๋ ํฌ๊ธฐ์ ๋ฐ๋ผimage_padํ ํฐ ์๊ฐ ๋ฌ๋ผ์ง๋ฏ๋ก ํ ์คํธ๋ง์ผ๋ก ์ธ๋ฉด ์ด๊ธ๋๋ค. - ๋น์ ํ์ ๋๊ฒฐ: LoRA target์
q/k/v/o_proj,gate/up/down_proj๋ฟ์ด๋ค. Qwen3-VL ๋น์ ํ์๋qkv/proj/linear_fc1/linear_fc2์ด๋ฆ์ ์ฐ๋ฏ๋ก ์ฌ๊ธฐ์ ๊ฑธ๋ฆฌ์ง ์๋๋ค. ํ์ต ์์ ์vision tower touched: 0์ด ์ฐํ๋์ง ํ์ธํ์. ์๋ ๋ฐ์ดํฐ์์ ๋น์ ์ชฝ์ ๊ฐ์ด ํ๋ค๋ฉด ๋ชจ๋ธ์ด ์ฝ๊ฒ ๋ง๊ฐ์ง๋ค. - ์ด๋ฏธ์ง ํด์๋ ์ํ(
--max-image-side,--max-pixels): VL ํ์ต์์ OOM์ ์ฃผ๋ฒ์ ์ํ์ค ๊ธธ์ด๊ฐ ์๋๋ผ ์ด๋ฏธ์ง ํ ํฐ ์๋ค. ์ฌ๊ธฐ์ ๋จผ์ ์ค์ธ๋ค.min_pixelsํํ๋ ๊ฐ์ด ๋ฎ์ถฐ ๋๋ค โ ํํ ์ฐ๋256*28*28(=200704)์ ๊ทธ๋๋ก ๋๋ฉด--max-image-side 448๋ก ์ค์ธ ์ด๋ฏธ์ง(์ฝ 168k ํฝ์ )๋ฅผ ํ๋ก์ธ์๊ฐ ๋๋ ์ ์ค์ผ์ผํด์ ์ ๊ฐ์ด ์ฌ๋ผ์ง๋ค. - ์์ฒด ๊ฒ์ฆ ์ฅ์น: ์ฒซ ์ํ์์ ํ์ต ๋์ ๊ตฌ๊ฐ์ ์ค์ ๋ก ๋์ฝ๋ฉํด ์ ๋ต ๋ฌธ์์ด๋ก
์์ํ๋์ง ํ์ธํ๋ค(
[mask] ๊ฒ์ฆ ํต๊ณผ๋ก๊ทธ). ์ฑํ ํ ํ๋ฆฟ์ด ๋ฐ๋์ด ๋ง์คํน ๊ฒฝ๊ณ๊ฐ ๋ฐ๋ฆฌ๋ ์ฌ๊ณ ๋ฅผ ํ์ต ์์ ์งํ์ ์ก๋๋ค.
๊ฒฐ๊ณผ: out/lora-receipt/ (์ด๋ํฐ + ํ๋ก์ธ์ ์ค์ ).
GGUF ๋ณํ์ ์ค๋ ๊ฑธ๋ฆฌ๋ฏ๋ก ๋จผ์ ํ์ต์ด ๋จน์๋์ง ๋ณธ๋ค.
python scripts-vlm/02_infer_test.py --no-adapter # ํ์ต ์ baseline
python scripts-vlm/02_infer_test.py --adapter out/lora-receipt # ํ์ต ํํ๋๋ณ(store/date/total/items) ์ผ์น์จ์ด ์ฐํ๋ค. baseline๋ณด๋ค ์ฌ๋ผ๊ฐ์ง ์์ผ๋ฉด
GGUF๋ก ๋์ด๊ฐ์ง ๋ง๊ณ ๋ฐ์ดํฐ/์ํญ/LR์ ์๋ณด๋ ๊ฒ ๋ง๋ค.
python scripts-vlm/03_merge_lora.py --adapter out/lora-receipt --output out/merged
./scripts-vlm/04_export_gguf.sh out/merged qwen3vl-receipt Q4_K_M04_export_gguf.sh๋ llama.cpp๋ฅผ vendor/์ cloneํ๊ณ llama-quantize๋ฅผ ๋น๋ํ ๋ค:
convert_hf_to_gguf.pyโ ์ธ์ด๋ชจ๋ธ f16 GGUFconvert_hf_to_gguf.py --mmprojโ ๋น์ ํ๋ก์ ํฐ GGUFllama-quantizeโ Q4_K_M- Modelfile ์์ฑ ํ
ollama create
์์ฑ๋๋ Modelfile:
FROM ./qwen3vl-receipt-Q4_K_M.gguf
FROM ./qwen3vl-receipt-mmproj-f16.gguf
SYSTEM """๋๋ ์์์ฆ ์ด๋ฏธ์ง์์ ๊ตฌ์กฐํ๋ JSON์ ์ถ์ถํ๋ ๋๊ตฌ๋ค. JSON๋ง ์ถ๋ ฅํ๋ค."""
PARAMETER temperature 0
VL ๋ชจ๋ธ์ ํ๋ก์ ํฐ GGUF๋ฅผ ๋นผ๋จน์ผ๋ฉด ์ด๋ฏธ์ง ์
๋ ฅ์ด ์ฃฝ๋๋ค. ๋ FROM์ด ๋ชจ๋ ์์ด์ผ
ํ๋ค. ๋์คํฌ๋ ๋ณํฉ๋ณธ 17GB + f16 GGUF 17GB + ์์ํ๋ณธ 6GB๋ก 40GB ์ด์ ํ์ํ๋ค.
Qwen3-VL์ GGUF ๋ณํ(ํนํ
--mmproj)์ llama.cpp ์ต์ ๋น๋๊ฐ ํ์ํ๋ค. ์คํฌ๋ฆฝํธ๊ฐ--depth 1๋ก ์ต์ ์ cloneํ์ง๋ง, ๋ณํ๊ธฐ๊ฐqwen3vl์ํคํ ์ฒ์์ ์คํจํ๋ฉด llama.cpp ์ด์๋ฅผ ํ์ธํ๊ณvendor/llama.cpp๋ฅผgit pullํด์ ๋ค์ ์๋ํ์. ๋ณํ ์คํฌ๋ฆฝํธ ์์กด์ฑ์pip install -r vendor/llama.cpp/requirements.txt.
๋ณํฉ/์ฌ์์ํ ์์ด ์ด๋ํฐ๋ง GGUF๋ก ๋ฐ๊ฟ ๊ธฐ์กด qwen3-vl:8b ์์ ์น๋ ๋ฐฉ๋ฒ๋ ์๋ค.
./scripts-vlm/04b_export_adapter_only.sh out/lora-receipt qwen3vl-receipt-lora์์ญ MB๋ก ๋๋๊ณ ๋ฒ ์ด์ค ๋ค์ด๋ก๋๋ ์ฌํ์ฉ๋๋ค. ๋์ ์ด๋ํฐ GGUF ์ง์์ ๋ฒ์ ์ ๋ฏผ๊ฐํ๊ณ (VL ์ํคํ ์ฒ๋ ํนํ), ์์ํ๋ ๋ฒ ์ด์ค ์์ ์นํ๋ฏ๋ก ํ์ง์ด ์กฐ๊ธ ๋จ์ด์ง๋ค. ์คํจํ๋ฉด ์์ ์ ์ฒด ๋ณํฉ ๊ฒฝ๋ก๋ฅผ ์ฐ๋ฉด ๋๋ค.
python scripts-vlm/05_test_ollama.py --model qwen3vl-receipt
python scripts-vlm/05_test_ollama.py --model qwen3-vl:8b # ํ์ธํ๋ ์ ๊ณผ ๋น๊ต/api/chat์ base64 ์ด๋ฏธ์ง๋ฅผ ๋ฃ์ด ํ๋๋ณ ์ผ์น์จ๊ณผ exact match๋ฅผ ์ผ๋ค. ํ์ค
๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ง ์ด๋ค. done_reason=length๋ก ๋ต์ด ์๋ฆฌ๋ฉด ๊ฒฝ๊ณ ๋ฅผ ์ฐ์ผ๋ฏ๋ก, ๊ทธ๋๋
--num-predict๋ฅผ ์ฌ๋ฆฌ๋ฉด ๋๋ค(Thinking ๋ณ์ข
๋ฒ ์ด์ค๋ฅผ ํ
์คํธํ ๋ ์์ฃผ ๊ฑธ๋ฆฐ๋ค).
ollama show qwen3.8:latest ์ค์ธก:
architecture qwen35 parameters 27.3B quantization Q4_K_M (17GB)
capabilities completion, vision, tools, thinking
Projector clip, 460.73M params RENDERER qwen3.8 / PARSER qwen3.5
vision ๋ฅ๋ ฅ๊ณผ CLIP ํ๋ก์ ํฐ(460M)๊ฐ ๋ถ์ด ์๋ ๋ฉํฐ๋ชจ๋ฌ ๋ชจ๋ธ์ด๊ณ , ํฌ๊ธฐ๋ 27.3B๋ค. ๊ทธ๋์ ๋ ๊ฐ์ง๋ฅผ ๊ฐ์ํด์ผ ํ๋ค:
- ์ด ๊ฒฝ๋ก๋ "์ธ์ด ํ์๋ง ํ
์คํธ ๋ฐ์ดํฐ๋ก ํ๋"ํ๋ ๊ฒ์ด๋ค. ๋น์ ํ์๋
๊ฑด๋๋ฆฌ์ง ์์ผ๋ฏ๋ก, ์ด๋ฏธ์ง ๋ฅ๋ ฅ์ ์ ์งํ๋ ค๋ฉด ๋ด๋ณด๋ผ ๋ mmproj๋ฅผ ํจ๊ป ๋ฃ์ด์ผ
ํ๋ค(โ
scripts-vlm/04_export_gguf.sh์ฌ์ฉ).scripts-llm/04_export_gguf.sh๋ ํ ์คํธ ์ ์ฉ ์ฐ์ถ๋ฌผ๋ง ๋ง๋ ๋ค. - 27.3B๋ ์ด ๋งฅ(24GB)์์ ํ์ต ๋ถ๊ฐ๋ค. bf16 ๊ฐ์ค์น๋ง ์ฝ 55GB๋ค. CUDA 48GB๊ธ ์์ QLoRA๋ก ๋๋ ค์ผ ํ๋ค.
๋ architecture qwen35 ๋ Qwen3.5 ๊ณ์ด์ธ๋ฐ, ์ด์ ๋์ํ๋ HF ์๋ณธ repo ์ด๋ฆ์
์ด ์ ์ฅ์์์ ํ์ ํ์ง ๋ชปํ๋ค. ๊ทธ๋์ --model ์ ์ธ์๋ก ๋บ๊ณ ๊ธฐ๋ณธ๊ฐ์ ๊ฒ์ฆํ๊ธฐ
์ฌ์ด Qwen/Qwen3-1.7B ๋ก ๋๋ค. ์ค์ ํ๊น์ผ๋ก ๋๋ฆด ๋๋ HF์์ ํด๋น ๋ชจ๋ธ์ repo id๋ฅผ
ํ์ธํด --model ๋ก ๋๊ฒจ์ผ ํ๋ค. ํ์ดํ๋ผ์ธ ์์ฒด๋ ๋ชจ๋ธ์ ๋ฌด๊ดํ๊ฒ ๋์ํ๋ค.
๊ณ ๊ฐ ์ง์ ํฐ์ผ ๋ณธ๋ฌธ โ ๋ผ์ฐํ JSON:
{"messages": [
{"role": "user", "content": "๋ค์ ๊ณ ๊ฐ ๋ฌธ์๋ฅผ ์ฝ๊ณ ... JSON๋ง ์ถ๋ ฅํด๋ผ ...\n\n๋ฌธ์:\n๋ฌธ์๋๋ฆฝ๋๋ค. ์ฃผ๋ฌธ๋ฒํธ 872246. ์นด๋๋ก ๊ฒฐ์ ํ๋๋ฐ ๋ ๋ฒ ์ฒญ๊ตฌ๋์ต๋๋ค. ํผํด๊ฐ ๊ณ์ ์ปค์ง๊ณ ์์ต๋๋ค. ๊ฒฐ์ ์ทจ์ ๋ถํ๋๋ฆฝ๋๋ค."},
{"role": "assistant", "content": "{\"category\":\"๊ฒฐ์ \",\"priority\":\"P1\",\"team\":\"billing\",\"refund_requested\":true}"}]}์ด๋ฏธ์ง๊ฐ ์์ผ๋ฏ๋ก images ํค๊ฐ ์๋ค. ์์ฑ๊ธฐ๋ ๋ผ๋ฒจ์ด ๋ณธ๋ฌธ์์ ์ค์ ๋ก ์ถ๋ก
๊ฐ๋ฅํ๋๋ก ๊ท์น ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ ๋ค(์ฌ๊ฐ๋ ํํ โ priority, ์ฆ์ ๋ฌธ์ฅ โ category,
ํ๋ถ ๋ฌธ๊ตฌ โ refund_requested). ๋ผ๋ฒจ์ด ๋ณธ๋ฌธ๊ณผ ๋ฌด๊ดํ ๋์๋ฉด ๋ชจ๋ธ์ ์๋ฌด๊ฒ๋ ๋ฐฐ์ฐ์ง
๋ชปํ๋๋ฐ loss๋ ๋จ์ด์ง๋ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค โ ํฉ์ฑ ๋ฐ์ดํฐ์ ๊ฐ์ฅ ํํ ํจ์ ์ด๋ค.
train/valid ๋ณธ๋ฌธ ์ค๋ณต(๋ฐ์ดํฐ ๋์)๋ ์์ฑ ์ ์ฐจ๋จํ๋ค.
python scripts-llm/00_make_dataset.py --n-train 300 --n-valid 40์ค์ธก ๋ถํฌ: category 5์ข 53~65๊ฐ์ฉ, priority P1/P2/P3 92/107/101, ํ๋ถ ์์ฒญ 114/300, trainโvalid ์ค๋ณต 0๊ฑด.
# ํ์ดํ๋ผ์ธ ๊ฒ์ฆ (์์ ๋ชจ๋ธ๋ก ๋น ๋ฅด๊ฒ)
python scripts-llm/01_train_lora.py --model Qwen/Qwen3-1.7B --epochs 1
# ์ค์ ํ๊น (CUDA 48GB+ ๊ถ์ฅ)
python scripts-llm/01_train_lora.py --model <qwen3.5-27b HF repo> --load-4bit --grad-ckpt
python scripts-llm/02_infer_test.py --no-adapter # baseline
python scripts-llm/02_infer_test.py --adapter out/lora-ticket
python scripts-llm/03_merge_lora.py --output out/merged-llm
./scripts-llm/04_export_gguf.sh out/merged-llm qwen-ticket Q4_K_M
python scripts-llm/05_test_ollama.py --model qwen-ticket
python scripts-llm/05_test_ollama.py --model qwen3.8:latest # ํ์ธํ๋ ์ ๊ณผ ๋น๊ตscripts-vlm ๋๋น ๋ค๋ฅธ ์ ๋ง ์ ๋ฆฌํ๋ฉด:
- ํ๋ก์ธ์ ๋์ ํ ํฌ๋์ด์ ๋ง ์ด๋ค. ์ด๋ฏธ์ง ํฝ์ ์ํ ๊ฐ์ ๊ณ ๋ฏผ์ด ์๋ค.
enable_thinking=False: Qwen3 ๊ณ์ด ์ฑํ ํ ํ๋ฆฟ์ ์ด ์ธ์๋ฅผ ๋ฐ๋๋ค. ๊ตฌ์กฐํ JSON ์ถ๋ ฅ์ ์ถ๋ก ๋ธ๋ก์ ๋ฐฉํด๋ง ๋๋ฏ๋ก ํ์ตยท์ถ๋ก ์์ชฝ์์ ๋๋ค. ํ ํ๋ฆฟ์ด ์ด ์ธ์๋ฅผ ๋ชจ๋ฅด๋ฉดTypeError๊ฐ ๋๋ฏ๋ก ๋นผ๊ณ ์ฌ์๋ํ๊ฒ ํด๋๋ค.--max-len์ด๊ณผ ์๋ฆผ ์นด์ดํธ: ๋ต์ด ์๋ฆฌ๋ฉด ํ์ ํ์ต์ด ๋ง๊ฐ์ง๋๋ฐ ์กฐ์ฉํ ์งํ๋๊ธฐ ์ฝ๋ค. ํ์ต ๋์ ์๋ฆฐ ์ํ ์๋ฅผ ๊ฒฝ๊ณ ๋ก ์ฐ๋๋ค.- ๋ฉํฐ๋ชจ๋ฌ ๋ฒ ์ด์ค๋ฅผ ์ธ ๊ฒฝ์ฐ
[lora] vision/projector touched: 0๋ก๊ทธ๋ก ๋น์ ํ์์ LoRA๊ฐ ์ ๋ถ์๋์ง ํ์ธํ๋ค. 03_merge_lora.py๋AutoModelForCausalLM์ผ๋ก ๋ก๋ํ๋ฏ๋ก ๋ฉํฐ๋ชจ๋ฌ ๋ฒ ์ด์ค์์๋ ๋น์ ํ์๊ฐ ๋น ์ง ์ ์๋ค. ์ด๋ฏธ์ง ๋ฅ๋ ฅ์ ์ ์งํด์ผ ํ๋ฉดscripts-vlm/03_merge_lora.py(AutoModelForImageTextToText)๋ก ๋ณํฉํ์.
24GB ๋งฅ์์ 8B๋ฅผ ์ค์ ๋ก ํ์ต์ํค๋ ค๋ฉด 4bit ์์ํ ๋ชจ๋ธ์ LoRA๋ฅผ ๋ถ์ด๋ MLX ๊ฒฝ๋ก๊ฐ ํ์ค์ ์ด๋ค.
pip install mlx-vlm
python -m mlx_vlm.lora --help # ๋ฒ์ ๋ณ๋ก ํ๋๊ทธ๊ฐ ๋ฌ๋ผ ๋จผ์ ํ์ธํ ๊ฒ๋ฐ์ดํฐ๋ ์ด ์ ์ฅ์์ data/train.jsonl๊ณผ ๊ฐ์ {"images": [...], "messages": [...]}
ํ์์ ๊ทธ๋๋ก ์ด๋ค. ๋ค๋ง mlx-vlm์ LoRA CLI๋ ๋ฆด๋ฆฌ์ค๋ง๋ค ์ธ์๊ฐ ๋ฐ๋๋ฏ๋ก, ์ ํํ
ํ๋๊ทธ๋ --help๋ก ํ์ธํ๊ณ ์ฐ๋ ํธ์ด ์์ ํ๋ค. ํ์ต๋ MLX ์ด๋ํฐ๋ MLX ๋ฐํ์์์
๋ฐ๋ก ์ถ๋ก ํ ์ ์์ง๋ง Ollama(GGUF)๋ก๋ ์ง์ ๋์ด๊ฐ์ง ์๋๋ค. Ollama์ ์ฌ๋ฆฌ๋ ๊ฒ์ด
๋ชฉํ๋ผ๋ฉด ํ์ต์ transformers/peft ๊ฒฝ๋ก๋ก ํ๊ณ , GPU ์ธ์คํด์ค๋ฅผ ๋น๋ฆฌ๋ ์ชฝ์ด ๋จ์ํ๋ค.
| ํ์ผ | ์ญํ |
|---|---|
scripts-vlm/00_make_dataset.py |
ํฉ์ฑ ์์์ฆ ์ด๋ฏธ์ง + JSONL ๋ผ๋ฒจ ์์ฑ |
scripts-vlm/01_train_lora.py |
LoRA ํ์ต (๋ผ๋ฒจ ๋ง์คํน, ๋น์ ๋๊ฒฐ, QLoRA ์ต์ ) |
scripts-vlm/02_infer_test.py |
ํ์ต ์ /ํ ํ๋ ์ผ์น์จ ๋น๊ต |
scripts-vlm/03_merge_lora.py |
์ด๋ํฐ๋ฅผ fp16 ๋ฒ ์ด์ค์ ๋ณํฉ |
scripts-vlm/04_export_gguf.sh |
GGUF ๋ณํ(+mmproj) โ ์์ํ โ ollama create |
scripts-vlm/04b_export_adapter_only.sh |
์ด๋ํฐ๋ง GGUF๋ก ๋ง๋ค์ด ๊ธฐ์กด ๋ฒ ์ด์ค์ ์น๊ธฐ |
scripts-vlm/05_test_ollama.py |
Ollama REST๋ก ์ต์ข ๊ฒ์ฆ |
| ํ์ผ | ์ญํ |
|---|---|
scripts-llm/00_make_dataset.py |
ํฉ์ฑ ๊ณ ๊ฐ ํฐ์ผ + ๋ผ์ฐํ JSON ๋ผ๋ฒจ ์์ฑ (ํ ์คํธ) |
scripts-llm/01_train_lora.py |
ํ ์คํธ SFT LoRA (๋ผ๋ฒจ ๋ง์คํน, enable_thinking=False) |
scripts-llm/02_infer_test.py |
ํ์ต ์ /ํ ํ๋ ์ผ์น์จ ๋น๊ต |
scripts-llm/03_merge_lora.py |
์ด๋ํฐ ๋ณํฉ (CausalLM) |
scripts-llm/04_export_gguf.sh |
GGUF ๋ณํ โ ์์ํ โ ollama create (mmproj ์์) |
scripts-llm/05_test_ollama.py |
Ollama REST๋ก ์ต์ข ๊ฒ์ฆ |
- loss๊ฐ 0 ๋๋ nan โ ๋ผ๋ฒจ์ด ์ ๋ถ
-100์ด ๋ ๊ฒฝ์ฐ๋ค. ์ฑํ ํ ํ๋ฆฟ์ด ๋ฐ๋์ด ํ๋กฌํํธ ๊ธธ์ด ๊ณ์ฐ์ด ์ ์ฒด ๊ธธ์ด์ ๊ฐ์์ก์ ์ ์๋ค.labels != -100์ ๊ฐ์๋ฅผ ์ฐ์ด๋ณด์. - MPS OOM / ์ค์ ํญ์ฃผ โ
--max-image-side 448 --max-pixels 200704 --grad-ckpt,--batch-size 1. ๊ทธ๋๋ ์ ๋๋ฉด MLX ๊ฒฝ๋ก๋ CUDA๋ก ๊ฐ๋ค. vision tower touched๊ฐ 0์ด ์๋ โ transformers ๋ฒ์ ์ด ๋ฐ๋์ด ๋ชจ๋ ์ด๋ฆ์ด ๋ฌ๋ผ์ง ๊ฒ์ด๋ค.target_modules๋ฅผ ์ธ์ด๋ชจ๋ธ ๊ฒฝ๋ก ์ ๊ท์์ผ๋ก ์ขํ์ผ ํ๋ค.- Ollama์์ ์ด๋ฏธ์ง๋ฅผ ๋ชป ๋ฐ์ โ Modelfile์ mmproj
FROM์ด ๋น ์ก๋ค. - ์ถ๋ ฅ์ด JSON์ด ์๋๊ณ ์ก๋ง์ด ์์ โ Modelfile
SYSTEM์ด ํ์ต ์ ํ๋กฌํํธ์ ์ถฉ๋ํ๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.SYSTEM์ ๋น์ฐ๊ณ ํ์ต ๋ ์ดINSTRUCTION์ ๊ทธ๋๋ก ๋ฃ์ด๋ณด์.