예제 소스
바둑 on-policy 학습: 대국 로그를 KataGo 라벨로 바꾸기 · 파일 169개
experiment_gemma.sh
#!/usr/bin/env bash
# Gemma 4 + lora_baduk_v2 실험 — 제약 최소 vs 현재 Qwen급 제약 비교
#
# ./scripts/experiment_gemma.sh coords # 좌표 grounding만 (serve 불필요)
# ./scripts/experiment_gemma.sh serve-loose # :9319 느슨한 Gemma serve
# ./scripts/experiment_gemma.sh serve-tight # :9319 강한 제약 Gemma serve
# ./scripts/experiment_gemma.sh probe # 빈 바둑판 1수 probe
#
set -euo pipefail
cd "$(dirname "$0")/.."
ROOT="$PWD"
PY="$ROOT/.venv/bin/python"
export HF_HOME="$ROOT/vlm_models"
export HF_HUB_CACHE="$ROOT/vlm_models"
export TOKENIZERS_PARALLELISM=false
GEMMA_SNAP="$(ls -d vlm_models/models--mlx-community--gemma-4-e4b-it-4bit/snapshots/* 2>/dev/null | tail -1)"
GEMMA_MODEL="${GEMMA_MODEL:-$GEMMA_SNAP}"
LORA="${LORA:-vlm_models/lora_baduk_v2}"
OUT="$ROOT/logs/gemma_experiment"
mkdir -p "$OUT"
log() { echo "[$(date '+%H:%M:%S')] $*"; }
run_coords() {
local fmt="$1"
local trials="${2:-24}"
local tag="coords_${fmt}"
log "verify-coords $fmt trials=$trials"
"$PY" -m vlm_server.verify_coords \
--model "$GEMMA_MODEL" \
--lora-dir "$LORA" \
--board-format "$fmt" \
--trials "$trials" \
--out-jsonl "$OUT/${tag}.jsonl" \
2>&1 | tee "$OUT/${tag}.log"
tail -5 "$OUT/${tag}.log"
}
coords_suite() {
log "Gemma=$GEMMA_MODEL"
log "LoRA=$LORA"
run_coords compact 24
run_coords aligned 24
log "결과: $OUT/coords_*.log"
}
kill_port() {
local p="$1"
local pid
pid="$(lsof -tiTCP:"$p" -sTCP:LISTEN 2>/dev/null || true)"
if [[ -n "$pid" ]]; then
log "kill :$p pid=$pid"
kill -TERM $pid 2>/dev/null || true
sleep 2
kill -9 $pid 2>/dev/null || true
fi
}
start_serve() {
local mode="$1"
local logfile="$OUT/serve_${mode}.log"
local jsonl="$OUT/play_${mode}.jsonl"
kill_port 9319
sleep 1
local extra=()
if [[ "$mode" == "loose" ]]; then
# 학습(SFT)과 동일 compact + 제약 최소
extra=(
--board-repr compact
--text-only-move
--temperature 0.35
--max-new-tokens 96
--illegal-retry-max 3
--illegal-retry-budget-s 45
--no-score-guided
)
else
# 현재 Qwen 운영과 비슷한 강한 제약
extra=(
--board-repr aligned
--text-only-move
--temperature 0.2
--max-new-tokens 96
--illegal-retry-max 16
--illegal-retry-budget-s 240
--score-guided
--score-min-line 3
)
fi
log "serve gemma ($mode) → :9319"
nohup "$PY" -m vlm_server serve \
--host 127.0.0.1 --port 9319 \
--model "$GEMMA_MODEL" \
--lora-dir "$LORA" \
"${extra[@]}" \
--log-jsonl "$jsonl" \
>>"$logfile" 2>&1 &
for _ in $(seq 1 120); do
if lsof -tiTCP:9319 -sTCP:LISTEN >/dev/null 2>&1; then
log "serve UP ($mode) log=$logfile"
return 0
fi
sleep 2
done
log "serve FAILED — tail $logfile"
tail -30 "$logfile"
return 1
}
probe_empty() {
if ! lsof -tiTCP:9319 -sTCP:LISTEN >/dev/null 2>&1; then
log "9319 없음 — 먼저 serve-loose 또는 serve-tight 실행"
exit 1
fi
log "probe empty board"
"$PY" -m vlm_server.probe_move --host 127.0.0.1 --port 9319 \
--sgf /dev/null 2>/dev/null || \
"$PY" <<'PY'
import socket, struct, io
from pathlib import Path
import sys
sys.path.insert(0, ".")
from vlm_server.dataset_baduk import BOARD_SIZE, render_board_png
from vlm_server.probe_move import MAGIC_GO19, MAGIC_RPLY, _u32be, _recv_exact
board = bytes([0] * (BOARD_SIZE * BOARD_SIZE))
img = render_board_png(board, BOARD_SIZE, px=256)
buf = io.BytesIO()
img.save(buf, format="PNG")
png = buf.getvalue()
body = bytearray([BOARD_SIZE, 2])
body += _u32be(len(png))
body += png
body += board
body += _u32be(1)
body.append(0)
sock = socket.create_connection(("127.0.0.1", 9319), timeout=10)
sock.sendall(MAGIC_GO19 + _u32be(len(body)) + body)
head = _recv_exact(sock, 4)
print("reply head", head)
flag = _recv_exact(sock, 1)[0]
if flag == 1:
r, c = _recv_exact(sock, 1)[0], _recv_exact(sock, 1)[0]
print(f"move row={r} col={c} pass={r==0xFF and c==0xFF}")
else:
n = struct.unpack(">H", _recv_exact(sock, 2))[0]
print("error", _recv_exact(sock, n).decode())
PY
}
case "${1:-coords}" in
coords) coords_suite ;;
serve-loose) start_serve loose ;;
serve-tight) start_serve tight ;;
probe) probe_empty ;;
*)
echo "usage: $0 {coords|serve-loose|serve-tight|probe}"
exit 1
;;
esac