01통찰 훈련법
통찰은 많이 아는 것이 아니다. 구조를 압축해서 다른 분야에 이식할 수 있는 형태로 만드는 능력이다. 아래 7단계는 그 압축 과정을 반복 가능한 루틴으로 만든 것이다.
이 훈련법이 Pi 5의 Ollama 튜닝과 무슨 관계인가. 튜닝 실험은 7단계의 실전 적용 대상이다. 뉴스나 논문 대신 자기 시스템의 측정 데이터를 소재로 쓰면 훈련이 곧 운영 개선이 된다. 관찰한 숫자에서 해석을 분리하고, 지배 변수를 찾고, 반증 조건을 세우는 과정이 튜닝 실험 설계 자체다.
사실 분리 (Fact Extraction)
뉴스, 논문, 글 하나를 읽고 해석을 전부 제거한다. 관측된 사실 3개만 적는다. 의견 금지.
지배 변수 찾기 (Dominant Variable)
추출한 사실을 움직이는 핵심 변수 1-2개를 지정한다.
구조 압축 (Structural Compression)
현상을 한 문장 모델로 만든다.
역가설 생성 (Inverse Hypothesis)
정반대 설명을 하나 만든다. "내 설명이 틀렸다면 실제 원인은 무엇인가?"
반증 조건 (Falsifiability)
내 모델이 틀릴 조건을 명시한다.
타 영역 적용 (Transfer Test)
같은 구조가 다른 분야에서도 설명되는지 확인한다. 통찰은 분야 간 재사용 가능성이 높다.
초압축 요약 (Insight Sentence)
15단어 이내 문장으로 요약한다. 설명력이 유지되면 통찰에 가까워진다.
02Ollama 튜닝 실험 계획서
각 파라미터를 하나씩 켜고 끄면서 비교한다. 한 번에 두 개 이상 바꾸면 어떤 것이 원인인지 알 수 없다. 실험 순서는 기대 효과가 큰 것부터.
실험 항목 목록
| 순위 | 파라미터 | 설정값 | 측정 방법 | 기대 효과 |
|---|---|---|---|---|
| 1 | OLLAMA_FLASH_ATTENTION |
=1 |
동일 프롬프트 tok/s 3회 중앙값 | 5~15% tok/s 향상. ARM NEON 최적화. 메모리 사용량 감소 가능. |
| 2 | OLLAMA_KV_CACHE_TYPE |
=q8_0 / =q4_0 |
긴 프롬프트(500자+)에서 RSS + tok/s | KV cache 메모리 30~50% 절감. 긴 컨텍스트 OOM 방지. |
| 3 | num_thread |
1, 2, 4 |
각 값에서 tok/s + CPU 사용률(mpstat) | 2~3에서 피크 가능. 스케줄링 오버헤드 확인용. |
| 4 | CPU governor | performance 고정 |
governor 변경 전후 tok/s + CPU freq | 5~20% 향상. 발열 증가 트레이드오프. |
| 5 | OLLAMA_NUM_PARALLEL |
=1 명시 |
동시 2 vs 1 요청 tok/s | 단일 요청 최적화 확인용. |
| 6 | num_ctx |
512, 1024, 2048 |
각 값 RSS + 응답 품질 | 윈도우 축소 시 메모리 절감. 품질 임계점 확인. |
| 7 | OLLAMA_KEEP_ALIVE |
=10m / =30m / =0 |
언로드 후 재요청 콜드스타트 시간 | 메모리 회수 vs 재로드 비용. |
측정 프로토콜
모든 실험은 아래 스크립트로 통일한다. 프롬프트, 측정 방법, 기록 형식이 바뀌면 이전 결과와 비교할 수 없다.
# bench.sh -- 표준 벤치마크 스크립트 # 사용법: ./bench.sh [변수설명메모] #!/bin/bash set -euo pipefail PROMPT='파이썬에서 리스트와 튜플의 차이를 두 줄로 설명해주세요.' MODEL='qwen2.5:1.5b' MEMO="${1:-baseline}" OUTFILE="/mnt/data/openclaw/state/bench_results.jsonl" # 워밍업 curl -s http://localhost:11434/api/generate \ -d "{\"model\":\"$MODEL\",\"prompt\":\"hi\",\"stream\":false}" > /dev/null sleep 2 # 3회 측정 for i in 1 2 3; do RESULT=$(curl -s http://localhost:11434/api/generate \ -d "{\"model\":\"$MODEL\",\"prompt\":\"$PROMPT\",\"stream\":false}") echo "$RESULT" | python3 -c " import sys, json, datetime r = json.load(sys.stdin) tok = r.get('eval_count', 0) ns = r.get('eval_duration', 1) total = r.get('total_duration', 1) load = r.get('load_duration', 0) out = { 'ts': datetime.datetime.now().isoformat(), 'memo': '$MEMO', 'run': $i, 'model': '$MODEL', 'tok': tok, 'tok_s': round(tok / (ns / 1e9), 2) if ns > 0 else 0, 'total_s': round(total / 1e9, 2), 'load_s': round(load / 1e9, 2), 'response_preview': r.get('response','')[:80] } print(json.dumps(out, ensure_ascii=False)) " >> "$OUTFILE" sleep 1 done # 시스템 상태 스냅샷 echo "{\"ts\":\"$(date -Iseconds)\",\"memo\":\"$MEMO\",\"type\":\"sysinfo\",\ \"mem_free_mb\":$(free -m | awk '/Mem:/{print $4}'),\ \"cpu_temp\":$(cat /sys/class/thermal/thermal_zone0/temp),\ \"governor\":\"$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)\"}" \ >> "$OUTFILE" echo "Done: $MEMO (3 runs) -> $OUTFILE"
파라미터별 적용 커맨드
# --- 실험 1: OLLAMA_FLASH_ATTENTION --- # baseline (off) sudo systemctl restart ollama ./bench.sh "flash_attn=off" # 켜기 sudo mkdir -p /etc/systemd/system/ollama.service.d echo '[Service] Environment="OLLAMA_FLASH_ATTENTION=1"' \ | sudo tee /etc/systemd/system/ollama.service.d/flash.conf sudo systemctl daemon-reload sudo systemctl restart ollama ./bench.sh "flash_attn=on" # 원복 sudo rm /etc/systemd/system/ollama.service.d/flash.conf sudo systemctl daemon-reload # --- 실험 2: OLLAMA_KV_CACHE_TYPE --- echo '[Service] Environment="OLLAMA_KV_CACHE_TYPE=q8_0"' \ | sudo tee /etc/systemd/system/ollama.service.d/kvcache.conf sudo systemctl daemon-reload && sudo systemctl restart ollama ./bench.sh "kv_cache=q8_0" sudo sed -i 's/q8_0/q4_0/' /etc/systemd/system/ollama.service.d/kvcache.conf sudo systemctl daemon-reload && sudo systemctl restart ollama ./bench.sh "kv_cache=q4_0" # 원복 sudo rm /etc/systemd/system/ollama.service.d/kvcache.conf sudo systemctl daemon-reload # --- 실험 3: num_thread --- PROMPT='파이썬에서 리스트와 튜플의 차이를 두 줄로 설명해주세요.' for T in 1 2 3 4; do echo "=== num_thread=$T ===" curl -s http://localhost:11434/api/generate \ -d "{\"model\":\"qwen2.5:1.5b\",\"prompt\":\"hi\",\"stream\":false,\ \"options\":{\"num_thread\":$T}}" > /dev/null sleep 2 for i in 1 2 3; do curl -s http://localhost:11434/api/generate \ -d "{\"model\":\"qwen2.5:1.5b\",\"prompt\":\"$PROMPT\",\"stream\":false,\ \"options\":{\"num_thread\":$T}}" | python3 -c " import sys,json r=json.load(sys.stdin) tok=r.get('eval_count',0); ns=r.get('eval_duration',1) print(f'thread=$T run=$i tok/s={tok/(ns/1e9):.2f}') " sleep 1 done done # --- 실험 4: CPU governor --- cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor ./bench.sh "governor=ondemand" echo performance | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor ./bench.sh "governor=performance" # 원복 echo ondemand | sudo tee /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor
결과 해석 기준
baseline 대비 변화율로 판단한다. 5% 미만은 "효과 없음"으로 분류, 해당 파라미터는 기본값 원복. 5~15%면 "유의미", 15% 초과면 "상시 적용 후보". 최종적으로 유의미한 것들만 조합해서 통합 벤치를 한 번 더 돌린다.
03현재 Baseline
cold start: 21.795s (load_sec: 3.579s)
warm response: 5.772s (load_sec: 0.366s)
tokens_in: ~151-155 | tokens_out: 12-16
status: success
pipeline: stdin JSON -> run.sh -> ollama API -> stdout JSON
RAM: 8GB LPDDR4X
Storage: 2TB USB SSD (/mnt/data) + SD boot
OS: Debian 13 (trixie) aarch64
Kernel: 6.12.x rpt-rpi-v8
이 숫자가 모든 튜닝 실험의 비교 기준이다. 튜닝 후 이 baseline보다 나빠지면 해당 파라미터는 롤백한다.
04추가 가능성 키워드
아래 키워드들은 현재 실험 계획에는 포함되지 않았지만, 1~7번 실험이 끝난 뒤 다음 단계로 검토할 항목이다. 각각이 통찰 훈련의 소재가 된다.
키워드와 통찰 훈련의 교차점
llama.cpp 직접 빌드를 예시로 7단계를 돌리면 이렇게 된다. (1) 사실: "ollama는 llama.cpp 위에 구축된 래퍼다." (2) 지배 변수: "래퍼의 오버헤드 크기." (3) 구조 압축: "추상화 계층이 두꺼울수록 제어 가능성과 성능이 반비례한다." (4) 역가설: "ollama 오버헤드는 무시 가능 수준이고 실제 병목은 llama.cpp 자체 설정이다." (5) 반증 조건: "llama.cpp 직접 실행이 ollama 대비 3% 미만 차이면 래퍼 오버헤드 가설 기각." (6) 타 영역: Docker vs bare-metal 성능 논쟁과 동일 구조. (7) 압축: "래퍼 오버헤드는 측정 없이 논할 수 없다."
18개 키워드 각각을 이 방식으로 돌리면, 그 자체가 판단력 훈련이면서 동시에 다음 실험의 우선순위를 정하는 근거가 된다.