예제 소스
오목 Next.js 웹과 VLM 착수 API 연결하기 · 파일 59개
TRAINING.md
# OMK VLM 학습 파이프라인
수집한 JSONL 로그로 Qwen3-VL 을 LoRA SFT 합니다. 추론 서버는 `--lora-dir` 한 줄 추가만으로 학습 결과를 그대로 사용합니다.
## 0. 사전 준비
```bash
cd omook
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt # peft, datasets 포함
```
## 1. 데이터 수집 (서버 실행 시)
서버를 `--log-jsonl` 로 띄워 두기만 하면 클라이언트의 모든 수가 한 줄씩 누적됩니다.
```bash
python vlm_pva_server.py \
--host 127.0.0.1 --port 9299 --device mps \
--model-id /path/to/Qwen__Qwen3-VL-8B-Instruct \
--log-jsonl logs/omook_vlm.jsonl
```
클라이언트의 모드 (Human vs AI / Human vs VLM / AI vs VLM) 는 모두 `OMKL` 메시지로 서버에 같이 기록되므로, 가장 빠른 데이터 수집은 **AI vs VLM + 자동 재시작 체크** 입니다.
## 2. 데이터셋 변환
```bash
python dataset_omook.py \
--jsonl logs/omook_vlm.jsonl \
--out-dir sft_data/omook_sft \
--save-images
```
기본 필터:
- 완료된 게임만 (`BlackWin` / `WhiteWin`).
- 그중 **승리한 쪽** 의 수만 → 전문가 시연으로 가정.
- 사람 / 로컬 AI 의 수만 (학습 신호로 의미 있는 것). VLM 자기 응답은 제외.
- 보드는 OMKL 의 post-move 상태에서 둔 돌을 빼서 pre-move 로 복원.
옵션:
- `--all-actors` → VLM 의 수도 포함 (자기증류식 데이터)
- `--include-losers` → 패배한 쪽 수도 포함 (대비/오답 학습용; 보통 비추천)
- `--save-images` 를 빼면 train.jsonl 만 만들어집니다 (이미지 없으면 학습 단계에서 이미지가 없어 스킵됨 — 보통 켜두세요).
결과:
```
sft_data/omook_sft/
train.jsonl # {id, size, stone, actor, prompt, answer, image_path}
img_00000000.png ... # 보드 렌더 이미지
dataset_info.json
```
> 참고: 출력 디렉토리 이름으로 `datasets/` 는 쓰지 않습니다. omook 디렉토리 안에
> `datasets/` 가 있으면 Python 이 그것을 namespace package 로 잡아 huggingface
> `datasets` 패키지를 가리는 import 충돌이 일어납니다. 기본값을 `sft_data/` 로
> 두는 이유입니다.
## 3. LoRA 파인튜닝
```bash
python train_vlm.py \
--dataset-jsonl sft_data/omook_sft/train.jsonl \
--base-model /path/to/Qwen__Qwen3-VL-8B-Instruct \
--output-dir vlm_models/lora_omook \
--epochs 1 --batch-size 1 --grad-accum 8 --lr 2e-4
```
권장 (Apple Silicon):
- 8B 모델은 MPS 메모리 부담이 큽니다. 가능하면 **Qwen3-VL-2B** 로 시작.
- `--max-samples 500` 등으로 한번 짧게 돌려 데이터/형식 검증 후 본 학습.
- bf16 은 CUDA 만, MPS 는 fp16 로 자동 폴백.
CUDA 환경에서 더 안정적입니다 (그라디언트 체크포인팅 + LoRA r=8 기준 8B 모델도 24GB 정도면 동작).
## 4. 학습 결과로 추론
```bash
python vlm_pva_server.py \
--host 127.0.0.1 --port 9299 --device mps \
--model-id /path/to/Qwen__Qwen3-VL-8B-Instruct \
--lora-dir vlm_models/lora_omook \
--log-jsonl logs/omook_vlm.jsonl # 새 로그도 같이 모으기
```
서버 로그에 `LoRA 어댑터 로드: base=..., adapter=vlm_models/lora_omook` 가 출력되면 적용 성공입니다.
## 5. 데이터셋이 늘면 반복
- 새 게임 → 새 로그 누적 → 다시 `dataset_omook.py` 로 변환 → `train_vlm.py` 로 재학습.
- 수집 ↔ 학습 ↔ 평가를 반복하면 self-play 방식의 점진적 강화가 됩니다.
## 6. (권장) 학습 모드 자동 학습 — `--auto-train`
위 2~4 단계를 매 게임마다 사람이 손으로 돌리는 대신, 서버에 `--auto-train` 을 주면
**학습 모드(클라이언트의 AI vs VLM) 게임이 흑승/백승으로 끝날 때마다** 서버가 알아서:
(무승부는 승리 측 수가 없어 학습 데이터로 잡히지 않으므로 트리거에서 제외)
1. `dataset_omook.py` 를 subprocess 로 실행 → JSONL 을 SFT 데이터셋으로 갱신,
2. `train_vlm.py` 를 subprocess 로 실행 → LoRA 어댑터를 `--lora-dir` 에 덮어쓰기,
3. 추론 파이프라인 캐시를 무효화 → 다음 요청부터 갱신된 LoRA 가 자동 적용
까지 한 번에 처리합니다. 학습은 별도 프로세스라 추론 모델과 메모리가 분리됩니다.
```bash
python vlm_pva_server.py \
--host 127.0.0.1 --port 9299 --device mps \
--model-id /path/to/Qwen__Qwen3-VL-8B-Instruct \
--log-jsonl logs/omook_vlm.jsonl \
--lora-dir vlm_models/lora_omook_auto \
--auto-train \
--auto-train-every 1 \
--auto-train-min-examples 8 \
--auto-train-max-samples 200 \
--auto-train-epochs 1
```
옵션 요약:
- `--auto-train-every N` — N 게임마다 1번만 학습 (기본 1=매 게임)
- `--auto-train-min-examples N` — 학습 예제가 N건 미만이면 데이터만 갱신, 학습은 스킵
- `--auto-train-max-samples N` — 한 라운드 최대 표본 수 (0=전체). MPS/노트북이면 200~500 권장
- `--auto-train-epochs / --auto-train-batch-size / --auto-train-grad-accum / --auto-train-lr`
- `--auto-train-num-workers N` — `dataset_omook.py` 의 이미지 렌더링 병렬도(프로세스) 와
`train_vlm.py` 의 DataLoader 워커 수. 기본 `cpu/2`. 데이터셋 변환은 4~8 코어로
거의 선형 스케일됨 (예: 31s → 4s).
- `--auto-train-torch-threads N` — `torch.set_num_threads(N)`. 0 = torch 기본.
- `--auto-train-dataset-dir` — 데이터셋 출력 위치 (기본 `sft_data/omook_sft_auto`)
운용 팁:
- 클라이언트의 **자동 재시작 체크박스** 와 **AI vs VLM 모드** 를 같이 켜두면 서버가
계속 게임이 들어오는 만큼 데이터 수집 → 학습 → 즉시 적용 → 다시 게임 으로
self-play 루프를 자율적으로 돌립니다.
- 한 학습이 끝나기 전에 들어온 추가 게임 종료 신호는 collapse 되어 백로그가 쌓이지
않습니다. 학습 1라운드가 게임보다 오래 걸려도 안전합니다.
- 8B 모델은 MPS 1라운드가 매우 느릴 수 있습니다. 우선 `Qwen3-VL-2B-Instruct +
--auto-train-max-samples 100` 로 동작을 확인한 뒤 늘리세요.