모종의 음모/ai 프로그램
프로그래밍도 할 줄 아는 local LLM 만들기.. part 1?
구차니
2026. 10. 16. 16:46
클로드 갈궈서 어느정도 결과가 나왔지만
검증하다 보니 다시 추가학습을 시켜야 해서 일단 part 2로 가야 할 듯 -_ㅠ
실험의 목적
- sft(supervised fine tunning)이 모델의 효용을 올리는데 얼마나 중요할까 (= 모델빨 vs 대답 채찍질 빨)
- sft의 형상에 따라 LLM이 얼마나 코딩을 잘하게 될까
실험설계
- 프로그래밍 하려면 이전의 것으로는 안된다고 함
- 프로그램을 위해서는 토크나이저가 공백, 엔터, 탭등을 날려먹으면 안되니까
- 다국어 하려니 학습셋과 시간이 문제라 영어로만 제한
- 프로그래밍을 위해 python과 openSCAD 문법을 중심으로 학습
- sft 하나는 python에 특화, 다른 sft는 openSCAD에 특화 시키기
- 이런저런 이유로 110M 정도 까지 모델 크기가 증가했고, 그래픽 카드 양쪽으로 나눠서 학습 진행
현재상태
- 학습 완료. 다만 입력 포맷만 다른 두개의 모델이 되어버려서, 추가로 학습중
- 기존은 python / openSCAD를 다루고 (대화로),
- 새로 할 것은 openSCAD에 대한 sft만 진행하고 python을 물어봐서 품질을 볼 것임
으아아 내 26시간 ㅠㅠ
일단 특이하게도 학습 자체는 1 epoch만 돌리는게 표준이라고 한다. 반복해서 보는 것 보다 새로운걸 보는것이 이득이라고 하는데
sft 자체는 2~3 epoch 정도 반복으로 보는게 낫다고 한다.
| ● 학습 시간 요약 장비는 GTX 1080 Ti 2장입니다. 1. 사전학습 (base110m) ┌───────┬──────────────────────────────────┐ │ 항목 │ 값 │ ├───────┼──────────────────────────────────┤ │ 규모 │ 3,814스텝, 10억 토큰, 스텝당 26.2만 토큰 │ ├───────┼──────────────────────────────────┤ │ 순수 학습 시간 │ 약 26시간 (스텝당 약 24초, 초당 1.09만 토큰, GPU 2장 병렬) │ ├───────┼─────────────────────────────────────────────────┤ │ 실제 진행 │ 10/13 12:01 시작. step 250 근처에서 사고로 중단돼 재시작했고, step 284에서 일시정지 │ ├────────┼────────────────────────────────────────────────────┤ │ │ 10/15 10:09 재개 → 10/16 09:57 종료 (23.8시간 연속) │ └──────┴──────────────────────────────────────────────┘ 데이터를 몇 번 봤는지(에폭)는 출처마다 다릅니다. ┌────────────────────┬───────────┬────────────────┬──────┐ │ 데이터 │ 전체 토큰 │ 학습에서 본 양 │ 에폭 │ ├────────────────────┼───────────┼────────────────┼──────┤ │ 영어 (FineWeb-Edu) │ 7.85억 │ 5억 │ 0.64 │ ├────────────────────┼───────────┼────────────────┼──────┤ │ Python │ 5.46억 │ 4억 │ 0.73 │ ├────────────────────┼───────────┼────────────────┼──────┤ │ OpenSCAD │ 0.33억 │ 1억 │ 3.04 │ └────────────────────┴───────────┴────────────────┴──────┘ 섞는 비율을 en 5 : py 4 : scad 1로 정했는데, SCAD 원본 데이터가 작아서 SCAD만 3번 넘게 반복해서 봤습니다. 2. SFT (두 모델 동시, GPU 한 장씩) ┌─────────┬───────────────────┬─────────────────────┐ │ │ sft_chat (GPU0) │ sft_plain (GPU1) │ ├─────────┼───────────────────┼─────────────────────┤ │ 기간 │ 10/16 10:24 → 14:26 │ 10/16 10:24 → 14:36 │ ├──────────┼────────────────────┼─────────────────────┤ │ 소요 시간 │ 4시간 02분 │ 4시간 12분 │ ├───────────┼───────────────────┼─────────────────────┤ │ 에폭당 │ 약 80분 (3,185스텝) │ 약 84분 │ ├───────────┼───────────────────┼─────────────────────┤ │ 총량 │ 3에폭, 9,555스텝, 예시 50,958개 × 3 │ 같음 │ ├───────────┼──────────────────────┼─────────────────────┤ │ 성능이 가장 좋았던 지점 │ step 6200 (2에폭 끝, 13:02) │ step 6200 │ └──────────────┴─────────────────────┴─────────────────────┘ - 이 밖에 첫 시도가 09:58~10:20에 OOM으로 죽어서 22분을 잃었습니다. - 2에폭이면 충분했다는 결과를 기준으로 하면, 다음부터 SFT는 약 2시간 40분이면 됩니다. 3. 평가와 변환 - 벤치 9회(모델과 형식 조합): 14:36 → 15:49, 약 1시간 15분 - 한 번에 5~26분이 걸렸습니다. 학습하지 않은 형식으로 묻는 교차 평가와 base 평가가 오래 걸렸는데, 모델이 멈추지 않고 최대 길이까지 생성하기 때문입니다. - GGUF 변환 3개: 몇 분 이내 |
아래가 잘못 진행된 실험.
base 모델은 llama-completion으로 말을 주면 이어 나가도록 구성된 모델이고
| /mnt/llm-scad/gguf$ /mnt/Downloads/llama-b10488/llama-completion -m base110m-step3814-f32.gguf -sm none --temp 0 --repeat-penalty 1.1 -n 200 -p $'// Make a tube with outer diameter 12 mm, inner diameter 8 mm and length 40 mm.\n' 0.00.234.065 I llama_completion: llama backend init 0.00.234.071 I llama_completion: load the model and apply lora adapter, if any 0.00.482.739 W load: control-looking token: 7 '<|end|>' was not control-type; this is probably a bug in the model. its type will be overridden 0.00.661.575 I cmn init: llama threadpool init, n_threads = 6 0.00.674.412 I 0.00.674.450 I system_info: n_threads = 6 (n_threads_batch = 6) / 6 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 | 0.00.674.450 I 0.00.674.513 I sampler seed: 2978975464 0.00.674.529 I sampler params: repeat_last_n = 64, repeat_penalty = 1.100, frequency_penalty = 0.000, presence_penalty = 0.000 dry_multiplier = 0.000, dry_base = 1.750, dry_allowed_length = 2, dry_penalty_last_n = 64 top_k = 40, top_p = 0.950, min_p = 0.050, xtc_probability = 0.000, xtc_threshold = 0.100, typical_p = 1.000, top_n_sigma = -1.000, temp = 0.000 mirostat = 0, mirostat_lr = 0.100, mirostat_ent = 5.000, adaptive_target = -1.000, adaptive_decay = 0.900 0.00.674.534 I sampler chain: logits -> penalties -> ?dry -> ?top-n-sigma -> top-k -> ?typical -> top-p -> min-p -> ?xtc -> temp-ext -> dist 0.00.674.535 I generate: n_ctx = 2048, n_batch = 2048, n_predict = 200, n_keep = 0 0.00.674.535 I // Make a tube with outer diameter 12 mm, inner diameter 8 mm and length 40 mm. // The tube is made of a 3mm tube with a hole in the middle. // The tube is made of a 6mm tube with a hole in the middle. // The tube is made of a 12mm tube with a hole in the middle. // The tube is made of a 15mm tube with a hole in the middle. // The tube is made of a 16mm tube with a hole in the middle. // The tube is made of a 18mm tube with a hole in the middle. // The tube is made of a 19mm tube with a hole in the middle. // The tube is made of a 20mm tube with a hole in the middle. // The tube is made of a 30mm tube with a hole in the middle. // The 0.01.140.346 I common_perf_print: sampling time = 69.83 ms 0.01.140.347 I common_perf_print: samplers time = 61.87 ms / 225 tokens 0.01.140.349 I common_perf_print: load time = 211.82 ms 0.01.140.351 I common_perf_print: prompt eval time = 12.78 ms / 25 tokens ( 0.51 ms per token, 1955.88 tokens per second) 0.01.140.352 I common_perf_print: eval time = 381.24 ms / 199 runs ( 1.92 ms per token, 521.98 tokens per second) 0.01.140.353 I common_perf_print: total time = 465.94 ms / 224 tokens 0.01.140.353 I common_perf_print: unaccounted time = 2.09 ms / 0.4 % (total - sampling - prompt eval - eval) / (total) 0.01.140.354 I common_perf_print: graphs reused = 198 |
sft_plain은 sft를 통해 openSCAD 형태의 출력을 학습시킨 모델
| /mnt/llm-scad/gguf$ /mnt/Downloads/llama-b10488/llama-completion -m sft_plain-step9555-f32.gguf -sm none --temp 0 --repeat-penalty 1.1 -n 200 -p $'// Make a tube with outer diameter 12 mm, inner diameter 8 mm and length 40 mm.\n' 0.00.212.691 I llama_completion: llama backend init 0.00.212.697 I llama_completion: load the model and apply lora adapter, if any 0.00.461.915 W load: control-looking token: 7 '<|end|>' was not control-type; this is probably a bug in the model. its type will be overridden 0.00.632.982 I cmn init: llama threadpool init, n_threads = 6 0.00.645.921 I 0.00.645.961 I system_info: n_threads = 6 (n_threads_batch = 6) / 6 | CPU : SSE3 = 1 | SSSE3 = 1 | AVX = 1 | AVX2 = 1 | F16C = 1 | FMA = 1 | BMI2 = 1 | LLAMAFILE = 1 | OPENMP = 1 | REPACK = 1 | 0.00.645.961 I 0.00.646.024 I sampler seed: 3402034624 0.00.646.039 I sampler params: repeat_last_n = 64, repeat_penalty = 1.100, frequency_penalty = 0.000, presence_penalty = 0.000 dry_multiplier = 0.000, dry_base = 1.750, dry_allowed_length = 2, dry_penalty_last_n = 64 top_k = 40, top_p = 0.950, min_p = 0.050, xtc_probability = 0.000, xtc_threshold = 0.100, typical_p = 1.000, top_n_sigma = -1.000, temp = 0.000 mirostat = 0, mirostat_lr = 0.100, mirostat_ent = 5.000, adaptive_target = -1.000, adaptive_decay = 0.900 0.00.646.044 I sampler chain: logits -> penalties -> ?dry -> ?top-n-sigma -> top-k -> ?typical -> top-p -> min-p -> ?xtc -> temp-ext -> dist 0.00.646.045 I generate: n_ctx = 2048, n_batch = 2048, n_predict = 200, n_keep = 0 0.00.646.045 I // Make a tube with outer diameter 12 mm, inner diameter 8 mm and length 40 mm. difference() { cylinder(r=7, h=40, $fn=96); translate([0, 0, -1]) cylinder(r=8, h=42, $fn=96); } [end of text] 0.00.798.376 I common_perf_print: sampling time = 27.36 ms 0.00.798.378 I common_perf_print: samplers time = 24.40 ms / 82 tokens 0.00.798.382 I common_perf_print: load time = 204.40 ms 0.00.798.383 I common_perf_print: prompt eval time = 12.79 ms / 25 tokens ( 0.51 ms per token, 1955.26 tokens per second) 0.00.798.387 I common_perf_print: eval time = 111.27 ms / 56 runs ( 1.99 ms per token, 503.28 tokens per second) 0.00.798.387 I common_perf_print: total time = 152.46 ms / 81 tokens 0.00.798.389 I common_perf_print: unaccounted time = 1.04 ms / 0.7 % (total - sampling - prompt eval - eval) / (total) 0.00.798.389 I common_perf_print: graphs reused = 55 |
sft_chat은 llama-cli 를 통해서 채팅 형태로 학습된 모델
| /mnt/llm-scad/gguf$ /mnt/Downloads/llama-b10488/llama-cli -m sft_chat-step9555-f32.gguf -sm none --temp 0 --repeat-penalty 1.1 -n 200 -st -p "Make a tube with outer diameter 12 mm, inner diameter 8 mm and length 40 mm." Loading model... ▄▄ ▄▄ ██ ██ ██ ██ ▀▀█▄ ███▄███▄ ▀▀█▄ ▄████ ████▄ ████▄ ██ ██ ▄█▀██ ██ ██ ██ ▄█▀██ ██ ██ ██ ██ ██ ██ ██ ▀█▄██ ██ ██ ██ ▀█▄██ ██ ▀████ ████▀ ████▀ ██ ██ ▀▀ ▀▀ build : b10488-9d77fa172 model : sft_chat-step9555-f32.gguf ftype : all F32 modalities : text available commands: /exit or Ctrl+C stop or exit /regen regenerate the last response /clear clear the chat history /read add a text file /glob add text files using globbing pattern > Make a tube with outer diameter 12 mm, inner diameter 8 mm and length 40 mm. difference() { cylinder(r=6, h=40, $fn=96); translate([0, 0, -1]) cylinder(r=4, h=42, $fn=96); } [ Prompt: 1378.8 t/s | Generation: 418.8 t/s ] Exiting... |