Cerberus / docs /training-log.md
backseollgi's picture
Upload docs/training-log.md with huggingface_hub
c41ffc3 verified
|
Raw History Blame Contribute Delete
20.2 kB
# CERBERUS Stage-1 โ€” Training Log & Decisions
Chunk-by-chunk history of the Stage-1 core pretrain (`runs/core`), and **why** the schedule
was changed on 2026-08-09. Companion to [`training_plan.md`](training_plan.md) (the plan)
and [`MIGRATION.md`](MIGRATION.md) (how to move servers).
> Every relaunch appends provenance to `runs/core/run_info.txt` (git commit, dirty count,
> GPUs, resume source) and a `git_diff_<ts>.patch`. This file is the human-readable summary.
---
## Chunk history
| # | Window | Server | GPUs | eff. batch | Epochs | Ended by |
|---|---|---|---|---|---|---|
| 1 | 2026-07-30 15:51 โ†’ 07-31 08:33 | `/data/pia` (local disk) | 2 (`0,1`) | 8 | 0 โ†’ 25 | host reboot |
| 2 | 2026-08-03 12:02 โ†’ 08-04 07:16 | `/data/pia` (local disk) | 2 (`0,1`) | 8 | 24 โ†’ 54* | manual stop |
| 3 | 2026-08-09 ~02:00 โ†’ | Backend.AI container, `/home/work/seoik` (NFS) | **3** (`0,1,2`) | **12** | 53 โ†’ 107 | (running) |
\* epoch 54 reached iter 1264/1500 and was discarded โ€” resume granularity is per-epoch, so
**`checkpoint_53` is the real continuation point**.
### State at the end of chunk 2 (the resume point)
- Total loss: 8.0 at start โ†’ **~2.9 plateau from ~epoch 10 onward** (ep30 2.94, ep50 2.92, ep54 2.90 smoothed)
- Complementarity LOSS terms `middleloss` (= 1 โˆ’ cos(z_a,z_m)) and `middleloss_bg` (= (1 + cos(z_m,z_b))/2)
โ†’ **โ‰ˆ 0** (converged, as designed). NOTE: these are losses, not cosines โ€” inverting them gives
**cos(z_a,z_m) โ†’ +1** (alignment) and **cos(z_m,z_b) โ†’ โˆ’1** (anti-correlation). See `base_task.py:264-270`.
- LR: 1e-5 โ†’ **8.53e-6** โ€” i.e. only 15% decayed after 53 epochs
- Checkpoints: local `0..53`; HF backup `backseollgi/Cerberus/stage1_core/` `0..45` (+50)
- Samples consumed: 79,500 step ร— 8 = **636,000**
---
## Decision (2026-08-09): `max_epoch` 200 โ†’ **107**, GPUs 2 โ†’ **3**
### Why the plateau was not a convergence problem
`hawk/common/optims.py:99` anneals cosine over **`max_epoch ร— iters_per_epoch` total steps**:
```python
lr = (init_lr - min_lr) * 0.5 * (1 + cos(pi * total_step / (max_epoch * iters_per_epoch))) + min_lr
```
So `max_epoch` is the **schedule length**, not a harmless cap. It was set to 200 as a
"generous cap โ€” stop anytime after ~3 days" (commits `e11860c`, `95eda28`), which stretched
the cosine over 300,000 steps. After 53 epochs we were only **26.5% into the schedule**, LR
was still 8.53e-6, and the late-cosine decay that actually drives convergence had not begun.
Continuing under `max_epoch: 200` meant **95 more hours** at a near-constant high LR.
### Why 107 specifically โ€” match the sample budget, not the epoch count
Epoch numbers are **not comparable** between HAWK and CERBERUS; the units differ:
| | original HAWK stage-1 | CERBERUS (chunk 3) |
|---|---|---|
| `iters_per_epoch` | 2,500 | 1,500 |
| batch (per GPU) | 1 | 4 |
| GPUs | 4 | 3 |
| **samples per "epoch"** | **10,000** | **18,000** |
Copying `max_epoch: 160` from HAWK would therefore train 20% *longer*, not equally.
Matching **samples** instead:
```
HAWK stage-1 plan : 160 ep ร— 2500 it ร— 4 = 1.60M samples (ran to checkpoint_127 = 1.27M)
CERBERUS target : 107 ep ร— 1500 it ร— 12 = 1.60M samples โ† equivalent budget
```
Consequences at resume:
| | under `max_epoch: 200` | under `max_epoch: 107` |
|---|---|---|
| position of `checkpoint_53` | 26.5% of schedule | **49.5%** |
| LR on resume | 8.53e-6 | **5.57e-6** |
| remaining wall-clock | 95.5 h (2 GPU) | **34.6 h** (3 GPU) |
| LR at finish | ~5e-6 (never annealed) | **1e-6 = `min_lr`** |
### Why 3 GPUs actually saves time here (and why it isn't automatic)
`iters_per_epoch` is **fixed at 1500**, so an "epoch" is a 39-minute checkpoint interval, not
a pass over the data (one real pass over 2.63M samples would be ~329k iters). Adding a GPU
therefore does **not** shrink epoch wall-clock โ€” it raises samples/step from 8 to 12.
The speedup only materialises because we lowered `max_epoch` to match: reaching the same
1.60M-sample budget needs 133,333 steps at 12/step instead of 200,000 at 8/step.
| | 2 GPU | 3 GPU |
|---|---|---|
| steps to 1.60M samples | 200,000 | **133,333** |
| required `max_epoch` | 133 | **107** |
| remaining wall-clock | 52.2 h | **34.6 h** |
### Known discontinuities at epoch 54 (expected, recorded here on purpose)
1. **LR steps down** 8.53e-6 โ†’ 5.57e-6. Downward, so it does not fight the restored Adam
moments (raising LR mid-run is the dangerous direction) and it helps leave the plateau.
2. **Effective batch 8 โ†’ 12.** Gradients get less noisy from epoch 54 on. Report Stage-1 as
"epochs 0โ€“53 at effective batch 8, 54โ€“107 at 12" rather than a single number.
3. Both land at the same point on the curve, so a small step in the loss/TensorBoard trace at
epoch 54 is expected and is **not** a bug.
`init_lr`, `min_lr`, `warmup_steps`, `iters_per_epoch`, `batch_size_train` and `seed` were
**left untouched**, so the only intended changes are the two above.
---
## Environment notes for chunk 3 (new server)
Moved from `/data/pia` (local disk) to a Backend.AI container with `/home/work/seoik` on NFS.
Nothing else about the run changed. Gotchas hit during setup, recorded so they are not
re-discovered:
- **`/home/work/seoik` is the only persistent volume.** `$HOME` (`/home/work`) is container
scratch and disappears with the session. Conda, caches and data must live under `seoik/`.
`train_run.sh` now pins `TORCH_HOME` / `HF_HOME` there.
- **The container ships its own conda** at `/home/work/miniconda3`, active as `base`, and its
`envs_dirs` does not include ours โ€” so `conda run -n cerberus` silently resolves to the
wrong prefix and fails with `No module named 'torch'`. `train_run.sh` now addresses the env
by **absolute prefix** (`conda run -p /home/work/seoik/miniconda3/envs/cerberus`).
- **NFS is the throughput bottleneck**, not CPU/RAM (83 cores, 676 GB idle). Measured: 25.8 MB/s
sequential, 35 ms per small-file write, and **saturated at ~50 files/s regardless of
parallelism** (8 / 32 / 64 workers all measured the same). Extracting 2.66M mp4 files took
~3 h; raising worker count does not help.
- `scripts/extract_all_webvid.py` (new) replaces `build_webvid_split.py` here: one 9.1T volume
means no big/small split or union symlinks are needed. It is resumable per shard via
`<out>/.done/<page_dir>` markers.
- **TensorBoard is NOT a complete record of this run.** chunk 1-2's event files stayed on the
old server, so `runs/core/main/tensorboard/` starts at step 81,005. `train.log` is the only
source covering epoch 0 onward (it is appended across chunks and travelled via HF). Use
`scripts/plot_training_curves.py` to rebuild the full curves + a CSV from the log; it handles
the re-run epochs at the resume points (25, 54) by letting the later record win.
- Model build verified before training with
`python scripts/smoke_test.py --cfg configs/train_configs/stage1_main.yaml` โ†’ all three
streams (appearance / motion / background) forward OK on H100 (sm_90), torch 2.11+cu128,
transformers 4.28.
---
# Stage-2 finetune (anomaly instruction tuning) โ€” ์ด ์„œ๋ฒ„, 2026-08-10 ์ฐฉ์ˆ˜
## ์„ค์ •๊ณผ ๊ทธ ๊ทผ๊ฑฐ
| ํ•ญ๋ชฉ | ๊ฐ’ | ๊ทผ๊ฑฐ |
|---|---|---|
| `ckpt` | `runs/core/main/checkpoint_106.pth` | stage1 ์ตœ์ข… (LR์ด `min_lr`๊นŒ์ง€ annealing ์™„๋ฃŒ) |
| `max_epoch` | **160** (์›๋ณธ ๊ทธ๋Œ€๋กœ) | ์•„๋ž˜ ์˜ˆ์‚ฐ ๊ณ„์‚ฐ ์ฐธ์กฐ |
| `batch_size_train` | **2** (์›๋ณธ 1) | GPU๊ฐ€ 4์žฅโ†’2์žฅ์ด๋ผ effective batch๋ฅผ ๋งž์ถ”๊ธฐ ์œ„ํ•ด |
| `iters_per_epoch` | 2500 (์›๋ณธ ๊ทธ๋Œ€๋กœ) | |
| `num_workers` | 8 (์›๋ณธ 16) | ์‹ค์ธก `data:` ์ง€์—ฐ 0.0000 โ€” 8๋กœ ์ถฉ๋ถ„, NFS ๋ถ€ํ•˜ ์ ˆ๊ฐ |
| GPU | H100 2์žฅ (stage1์€ 3์žฅ) | ์„ธ์…˜ ์žฌํ• ๋‹น์œผ๋กœ ๋ณ€๊ฒฝ๋จ |
**์ƒ˜ํ”Œ ์˜ˆ์‚ฐ โ€” ์›๋ณธ๊ณผ ๋™์ผํ•˜๊ฒŒ ํ†ต์ œ:**
```
์›๋ณธ HAWK stage2 : 160 ep x 2500 it x batch 1 x 4 GPU = 1.60M ์ƒ˜ํ”Œ (effective batch 4)
์ด ์„œ๋ฒ„ : 160 ep x 2500 it x batch 2 x 2 GPU = 1.60M ์ƒ˜ํ”Œ (effective batch 4)
```
GPU ์žฅ์ˆ˜๊ฐ€ ์ ˆ๋ฐ˜์ด ๋œ ๋งŒํผ batch๋ฅผ 2๋ฐฐ๋กœ ์˜ฌ๋ ค, **์ƒ˜ํ”Œ ์˜ˆ์‚ฐ๊ณผ effective batch๋ฅผ ๋‘˜ ๋‹ค** ์›๋ณธ๊ณผ
์ผ์น˜์‹œ์ผฐ๋‹ค. ๊ทธ ๊ฒฐ๊ณผ `max_epoch`์€ ์›๋ณธ ๊ฐ’ 160์„ ๊ทธ๋Œ€๋กœ ์“ธ ์ˆ˜ ์žˆ๋‹ค (stage1์—์„œ 107๋กœ ์žฌ๊ณ„์‚ฐํ•ด์•ผ
ํ–ˆ๋˜ ๊ฒƒ๊ณผ ๋‹ค๋ฅธ ์  โ€” stage1์€ `iters_per_epoch`๋„ 1500์œผ๋กœ ๋ฐ”๊ฟจ๊ธฐ ๋•Œ๋ฌธ).
**batch๋ฅผ ๋” ํ‚ค์šฐ์ง€ ์•Š์€ ์ด์œ  (2026-08-10 ์‹ค์ธก, 150 iter ๊ณต์ • ๋น„๊ต, H100 2์žฅ):**
| batch | ์ •์ƒ์ƒํƒœ s/it | max mem | ์ฒ˜๋ฆฌ๋Ÿ‰ | 1.6M ์ƒ˜ํ”Œ ์†Œ์š” |
|---|---|---|---|---|
| 2 | 1.369 | 45.7 GB | **2.92 samples/s** | 6.3์ผ |
| 4 | 2.773 | 56.6 GB | 2.89 samples/s | 6.4์ผ |
| 6 | 4.31 | 69.0 GB | 2.78 samples/s | 6.7์ผ |
| 8 | โ€” | OOM (75.9 GB ํ• ๋‹น ํ›„ ์‹คํŒจ) | โ€” | โ€” |
batch์— ๋น„๋ก€ํ•ด iter ์‹œ๊ฐ„์ด ๊ฐ™์ด ๋Š˜์–ด **์ฒ˜๋ฆฌ๋Ÿ‰์ด ํ‰ํ‰ํ•˜๋‹ค** = 32ํ”„๋ ˆ์ž„ x 3์ŠคํŠธ๋ฆผ ์กฐํ•ฉ์ด ์ด๋ฏธ
์ƒ˜ํ”Œ๋‹น ์—ฐ์‚ฐ ํฌํ™”. VRAM์„ ๋” ์ฑ„์›Œ๋„ ์ด๋“์ด ์—†๊ณ  batch 6๋ถ€ํ„ฐ๋Š” ์˜คํžˆ๋ ค ์ €ํ•˜๋˜๋ฏ€๋กœ, ์—ฌ์œ  34GB๋ฅผ
๋‚จ๊ฒจ ๋‹ค์ผ ํ•™์Šต์˜ OOM ์œ„ํ—˜์„ ์ค„์ด๋Š” batch 2๋ฅผ ํƒํ–ˆ๋‹ค. `data:` ์ง€์—ฐ์€ ๋ชจ๋“  ์„ค์ •์—์„œ 0.0000์ด๋ผ
NFS๋Š” ๋ณ‘๋ชฉ์ด ์•„๋‹ˆ๋‹ค. `PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True`๋„ batch 6์˜ ์ €ํ•˜๋ฅผ
ํ•ด์†Œํ•˜์ง€ ๋ชปํ–ˆ๋‹ค(4.72 s/it) โ€” ๋‹จํŽธํ™”๊ฐ€ ์•„๋‹ˆ๋ผ ์—ฐ์‚ฐ ํ•œ๊ณ„๋ผ๋Š” ๊ทผ๊ฑฐ.
## ์ฐฉ์ˆ˜ ์ „ ์ฒ˜๋ฆฌํ•œ ์ด๊ด€ ๋ฌธ์ œ (๋‹ค๋ฅธ ์„œ๋ฒ„์—์„œ ์žฌํ˜„ํ•  ๋•Œ๋„ ํ•„์š”)
1. **annotation์˜ ๋น„๋””์˜ค ๊ฒฝ๋กœ๊ฐ€ ์›์ €์ž ์„œ๋ฒ„ ์ ˆ๋Œ€๊ฒฝ๋กœ์˜€๋‹ค.** ๋ฐฐํฌ๋ณธ JSON์˜ `video` ํ•„๋“œ๊ฐ€
`/remote-home/share/jiaqitang/Data/...`์ธ๋ฐ, ๋กœ๋”๋Š” `os.path.join(vis_root, sample['video'])`
(`video_instruct_dataset.py:128`)๋ฅผ ์“ด๋‹ค. **๋‘ ๋ฒˆ์งธ ์ธ์ž๊ฐ€ ์ ˆ๋Œ€๊ฒฝ๋กœ๋ฉด `vis_root`๊ฐ€ ํ†ต์งธ๋กœ
๋ฌด์‹œ๋˜๋ฏ€๋กœ** config์˜ `videos_dir`๋ฅผ ๊ณ ์ณ๋„ ์†Œ์šฉ์ด ์—†๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ ๋ฐ์ดํ„ฐ์…‹ ์ด๋ฆ„์ด ์‹ค์ œ ๋ฐฐํฌ๋ณธ
๋””๋ ‰ํ† ๋ฆฌ๋ช…๊ณผ ๋‹ค๋ฅด๋‹ค(`UCF-Crime`โ†”`UCF_Crime`, `avenue/avenue/`โ†”`CUHK_Avenue/`,
`ped1/ped1/`โ†”`Ped1/`, `ShanghaiTechDataset`โ†”`ShanghaiTech`).
โ†’ `scripts/localize_anomaly_annotations.py`๊ฐ€ `videos_dir` ๊ธฐ์ค€ ์ƒ๋Œ€๊ฒฝ๋กœ๋กœ ๋ณ€ํ™˜ํ•ด
`*.local.json`์„ ๋งŒ๋“ ๋‹ค(์›๋ณธ ๋ณด์กด). train 7066 / test 786 / all 7852 ์ „๋ถ€ ๋งค์นญ ํ™•์ธ.
2. **config์— ๋‚จ์•„ ์žˆ๋˜ ์™ธ๋ถ€ ๊ฒฝ๋กœ 6๊ณณ**(`llama_model`, `tokenizer_name`, `prompt_path`, `ckpt`,
`anno_dir`, `videos_dir`)์„ ์ด ์„œ๋ฒ„ ๊ธฐ์ค€์œผ๋กœ ๊ต์ฒด. `output_dir`๋„ ๋ ˆํฌ ์•ˆ(`output/`)์„ ๊ฐ€๋ฆฌํ‚ค๋˜
๊ฒƒ์„ `runs/` ์ ˆ๋Œ€๊ฒฝ๋กœ๋กœ ์˜ฎ๊ฒผ๋‹ค(`train_run.sh` ์‚ฌ์šฉ ์‹œ์—๋Š” run๋ณ„๋กœ ๋ฎ์–ด์จ์ง).
## stage2์˜ ์†์‹ค ๊ตฌ์กฐ๋Š” stage1๊ณผ ๋‹ค๋ฅด๋‹ค (๋…ผ๋ฌธ ์„œ์ˆ  ์‹œ ์ฃผ์˜)
instruct ๋ฐ์ดํ„ฐ๋Š” `conv_type='multi'` ๊ฒฝ๋กœ(`video_llama.py:738-806`)๋ฅผ ํƒ€๋Š”๋ฐ, ์—ฌ๊ธฐ์„œ๋Š” ์„ธ ์ŠคํŠธ๋ฆผ์˜
์ž„๋ฒ ๋”ฉ์„ **ํ† ํฐ์œผ๋กœ concatํ•ด LLaMA๋ฅผ ํ•œ ๋ฒˆ๋งŒ** ํ†ต๊ณผ์‹œํ‚ค๊ณ  `loss_motion`ยท`loss_background`์—
๊ฐ™์€ `loss` ๊ฐ’์„ ๋ณต์‚ฌํ•ด ๋ฐ˜ํ™˜ํ•œ๋‹ค. ๋”ฐ๋ผ์„œ stage2 ์ด์†์‹ค์€ ์‹ค์งˆ์ ์œผ๋กœ
```
total = 1.2 x LM์†์‹ค + 0.1 x middleloss + 0.1 x middleloss_bg
```
์ด๋ฉฐ, stage1์ฒ˜๋Ÿผ ์ŠคํŠธ๋ฆผ๋ณ„ ๋…๋ฆฝ LM ์†์‹ค์ด ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ์ด๋Š” ์›๋ณธ HAWK ์„ค๊ณ„ ๊ทธ๋Œ€๋กœ์ด๊ณ  ๋ฒ„๊ทธ๊ฐ€
์•„๋‹ˆ๋‹ค. ๋กœ๊ทธ์˜ `motionloss`/`backgroundloss`๊ฐ€ `oriloss`์™€ ๋™์ผํ•˜๊ฒŒ ์ฐํžˆ๋Š” ๊ฒƒ์€ ์ •์ƒ์ด๋‹ค.
## ๋…ผ๋ฌธ ์ž‘์„ฑ์— ํ•„์š”ํ•œ ์‚ฐ์ถœ๋ฌผ โ€” ํ•™์Šต ์™„๋ฃŒ ์‹œ ์•„๋ž˜๊ฐ€ ์ „๋ถ€ ์ˆ˜์ง‘๋˜์–ด์•ผ ํ•œ๋‹ค
`train_run.sh`๊ฐ€ `runs/stage2/`์— ์ž๋™์œผ๋กœ ๋‚จ๊ธฐ๋Š” ๊ฒƒ:
- `main/checkpoint_{0..159}.pth` โ€” epoch๋ณ„ ์ฒดํฌํฌ์ธํŠธ (์•ฝ 2GB x 160 = 320GB)
- `main/tensorboard/` โ€” ์Šค์นผ๋ผ 7์ข…: `Loss/total`, `Loss/ori`, `Loss/motion`, `Loss/background`,
`Loss/middle`, `Loss/middle_bg`, `Learning Rate` (global step = epoch x 2500 + i)
- `train.log` โ€” ์ „ ๊ตฌ๊ฐ„ append. **TB๋ณด๋‹ค ์ด์ชฝ์ด ๊ถŒ์œ„ ์žˆ๋Š” ๊ธฐ๋ก**์ด๋‹ค(chunk ์žฌ๊ฐœ ์‹œ TB๋Š” ๋Š๊ธธ ์ˆ˜ ์žˆ์Œ)
- `run_info.txt` โ€” chunk๋ณ„ provenance: config ๊ฒฝ๋กœ, GPU, git commit + dirty ์—ฌ๋ถ€, resume ์ง€์ 
- `config.yaml` โ€” ์‹คํ–‰ ์‹œ์  config ์‚ฌ๋ณธ
- `git_diff_<ts>.patch` โ€” ์‹คํ–‰ ์‹œ์  ์›Œํ‚นํŠธ๋ฆฌ diff
ํ•™์Šต ์™„๋ฃŒ ํ›„ ์ถ”๊ฐ€๋กœ ๋งŒ๋“ค์–ด์•ผ ํ•˜๋Š” ๊ฒƒ:
- `scripts/plot_training_curves.py`๋กœ stage2 ๊ณก์„  + CSV ์ƒ์„ฑ โ†’ `figs/stage2_curves.{png,pdf,csv}`
- HF `backseollgi/Cerberus/stage2_finetune/`์— ์ฒดํฌํฌ์ธํŠธ(๋งˆ์ผ์Šคํ†ค+์ตœ์ข…)ยท`train.log`ยท`config.yaml`ยท
๊ณก์„  ๋ฐฑ์—… (stage1๊ณผ ๋™์ผํ•œ ๋ฐฉ์‹)
- E1/E2 ์‹ค์ธก: `scripts/extract_representations.py` + `experiments/disentanglement.py`๋กœ CDS ์‚ฐ์ถœ.
**stage1 ์ฒดํฌํฌ์ธํŠธ์™€ stage2 ์ฒดํฌํฌ์ธํŠธ ์–‘์ชฝ์—์„œ ๋ฝ‘์•„์•ผ** ๋ถ„๋ฆฌ๋„๊ฐ€ finetune์œผ๋กœ ์–ด๋–ป๊ฒŒ ๋ณ€ํ–ˆ๋Š”์ง€
๋น„๊ตํ•  ์ˆ˜ ์žˆ๋‹ค.
- Table-1์šฉ VAD ์ •๋Ÿ‰ ์ˆ˜์น˜ (`scripts/run_eval.sh`, test split์€ `all_videos_test.local.json`)
---
## NaN epoch ํ‰๊ท ์˜ ์ •์ฒด (2026-08-12, ์‹ค์ธก)
`abl_flow` arm์˜ ๋กœ๊ทธ์—์„œ `Averaged stats`์˜ `totalloss`๊ฐ€ 28 epoch ์ค‘ **9ํšŒ `nan`** ์œผ๋กœ
์ฐํ˜”๊ณ , 26โ€“28์—์„œ 3์—ฐ์†์œผ๋กœ ๋‚˜ํƒ€๋‚ฌ๋‹ค. ํ•™์Šต ์‹คํŒจ๋กœ ์ฝํž ์ˆ˜ ์žˆ๋Š” ํ˜•ํƒœ์ด๋ฏ€๋กœ ์›์ธ์„ ํ™•์ •ํ–ˆ๋‹ค.
**์‹ค์ธก.** ์ถœ๋ ฅ๋œ iteration 7,032๊ฐœ ์ค‘ NaN์€ **1๊ฐœ**(0.014%)๋‹ค. epoch ํ‰๊ท ์€ 2,500 iteration์˜
ํ‰๊ท ์ด๋ฏ€๋กœ **NaN ํ•˜๋‚˜๊ฐ€ ์„ž์ด๋ฉด ํ‰๊ท  ์ „์ฒด๊ฐ€ NaN**์ด ๋œ๋‹ค. ์ฆ‰ ๋กœ๊ทธ์˜ `nan`์€ "์ด epoch์ด
์‹คํŒจํ–ˆ๋‹ค"๊ฐ€ ์•„๋‹ˆ๋ผ "์ด epoch์— NaN iteration์ด **์ ์–ด๋„ ํ•˜๋‚˜** ์žˆ์—ˆ๋‹ค"๋Š” ๋œป์ด๋‹ค.
์‹ค์ œ ๋ฐœ์ƒ๋ฅ ์€ epoch๋‹น ์•ฝ 0.4ํšŒ๋‹ค. ๊ทธ๋Ÿฌ๋ฉด `P(epoch์— NaN โ‰ฅ 1) = 9/28 = 0.32`์ด๊ณ ,
3์—ฐ์†์ด ๋‚˜ํƒ€๋‚  ํ™•๋ฅ ์€ 28 epoch ๊ตฌ๊ฐ„์—์„œ ์•ฝ 60%๋‹ค. **3์—ฐ์†์€ ์•…ํ™”์˜ ์ฆ๊ฑฐ๊ฐ€ ์•„๋‹ˆ๋ผ ์ด
๋ฐœ์ƒ๋ฅ ์—์„œ ์˜ˆ์ƒ๋˜๋Š” ์‚ฌ๊ฑด์ด๋‹ค.**
**๊ฐ€์ค‘์น˜ ํ™•์ธ.** `checkpoint_27.pth`์˜ ํ•™์Šต ํŒŒ๋ผ๋ฏธํ„ฐ 231๊ฐœ ์ „๋ถ€ NaN/Inf ์—†์Œ. AMP GradScaler๊ฐ€
NaN ์†์‹ค์—์„œ ์˜ตํ‹ฐ๋งˆ์ด์ € ์Šคํ…์„ ๊ฑด๋„ˆ๋›ฐ๋ฏ€๋กœ ๊ฐ€์ค‘์น˜๊ฐ€ ์˜ค์—ผ๋˜์ง€ ์•Š๋Š”๋‹ค. ์ •์ƒ epoch์˜ loss๋Š”
๋‹จ์กฐ ํ•˜๊ฐ•ํ•œ๋‹ค(1.537 โ†’ 0.983).
**๊ฐ์‹œ ๊ธฐ์ค€ ๊ต์ฒด.** "์—ฐ์† 3 epoch NaN"์€ ์ด ๋ฐœ์ƒ๋ฅ ์—์„œ ์šฐ์—ฐ๊ณผ ์•…ํ™”๋ฅผ ๊ตฌ๋ณ„ํ•˜์ง€ ๋ชปํ•˜๋ฏ€๋กœ ํ๊ธฐํ•œ๋‹ค.
| ํ๊ธฐ | ๋Œ€์ฒด |
|---|---|
| ์—ฐ์† 3 epoch NaN | **์ถœ๋ ฅ iteration์˜ NaN ๋น„์œจ > 1%** (ํ˜„์žฌ 0.014%) |
| | **์ฒดํฌํฌ์ธํŠธ์— NaN/Inf ํŒŒ๋ผ๋ฏธํ„ฐ** (ํ˜„์žฌ ์—†์Œ) |
| | **์ •์ƒ epoch loss๊ฐ€ 3ํšŒ ์—ฐ์† ์ƒ์Šน** (ํ˜„์žฌ ๋‹จ์กฐ ํ•˜๊ฐ•) |
**๋…ผ๋ฌธ ํ‘œ๊ธฐ.** arm๋ณ„ NaN epoch ์ˆ˜๋ฅผ ๋ณด๊ณ ํ•  ๋•Œ ์ด ๊ฐ์ฃผ๊ฐ€ ํ•จ๊ป˜ ๊ฐ€์•ผ ํ•œ๋‹ค. ์ˆ˜์น˜๋งŒ ์ œ์‹œํ•˜๋ฉด
์‹ฌ์‚ฌ์ž๊ฐ€ 9/28์„ ํ•™์Šต ๋ถˆ์•ˆ์ •์œผ๋กœ ์ฝ๋Š”๋‹ค. ๋ณด๊ณ  ํ˜•์‹์€ "NaN epoch 9/28 (iteration ์ˆ˜์ค€
NaN ๋น„์œจ 0.014%; epoch ํ‰๊ท ์€ ๋‹จ์ผ NaN์— ์˜ํ•ด ์˜ค์—ผ๋œ๋‹ค)"๋กœ ํ•œ๋‹ค.
์žฌํ˜„:
```bash
$CERBERUS_PY - <<'EOF'
import re, collections
tot = collections.Counter(); nan = collections.Counter()
for l in open('/home/work/seoik/runs/abl_flow/train.log', errors='ignore'):
m = re.search(r'Train: data epoch: \[(\d+)\].*?totalloss: (nan|[0-9.]+)', l)
if m:
e = int(m.group(1)); tot[e] += 1
if m.group(2) == 'nan': nan[e] += 1
print(f'์ถœ๋ ฅ {sum(tot.values())}๊ฐœ ์ค‘ NaN {sum(nan.values())}๊ฐœ')
EOF
```
---
## random_mask arm ์ค‘๋‹จ๊ณผ ๋Ÿฌ๋„ˆ์˜ ์‹คํŒจ ๋ฏธ๊ฐ์ง€ (2026-08-13)
`abl_random_mask` ๊ฐ€ **epoch 5, iteration 320 ๋ถ€๊ทผ**์—์„œ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ฃฝ์—ˆ๋‹ค.
```
RuntimeError: DataLoader worker (pid 3751766) is killed by signal: Killed.
[ablation] arm=random_mask ์ข…๋ฃŒ (exit 1)
[ablation] arm=duplicate ์‹œ์ž‘ โ† ๋Ÿฌ๋„ˆ๊ฐ€ ์‹คํŒจ๋ฅผ ๋ฌด์‹œํ•˜๊ณ  ์ง„ํ–‰
```
**์ž์› ๋ฌธ์ œ๊ฐ€ ์•„๋‹ˆ๋‹ค.** ์‚ฌํ›„ ํ™•์ธ: RAM 2,015GB ์ค‘ 1,898GB ์—ฌ์œ , `/dev/shm` 64GB(์‚ฌ์šฉ
276KB), GPU 46.8/81.6GB. ๊ฐ™์€ ๋ฐ์ดํ„ฐ๋กœ `flow` ๋Š” 40 epoch ์™„์ฃผํ–ˆ๊ณ  `duplicate` ๋Š” ๋ฌธ์ œ์—†์ด
์ง„ํ–‰ ์ค‘์ด๋‹ค. ์ผ์‹œ์  ๋ฉ”๋ชจ๋ฆฌ ์ŠคํŒŒ์ดํฌ๋กœ ๋ณด์ด๋ฉฐ ์žฌํ˜„๋˜์ง€ ์•Š์•˜๋‹ค.
**๋ณต๊ตฌ ๊ฐ€๋Šฅ.** `checkpoint_4.pth` ์— `model`ยท`optimizer`ยท`scaler`ยท`epoch` ์ด ๋ชจ๋‘ ์žˆ์–ด
epoch 5 ๋ถ€ํ„ฐ ์ด์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค(`train_run.sh` ๊ฐ€ ์ตœ์‹  ์ฒดํฌํฌ์ธํŠธ๋ฅผ ์ž๋™์œผ๋กœ ์ฐพ์•„
`resume_ckpt_path` ๋กœ ๋„˜๊ธด๋‹ค).
### ๋Ÿฌ๋„ˆ์˜ ๊ฒฐํ•จ๊ณผ ๋Œ€์‘
`run_ablation_arms.sh` ๋Š” `train_run.sh` ์˜ ์ข…๋ฃŒ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•˜์ง€ ์•Š๋Š”๋‹ค. arm ํ•˜๋‚˜๊ฐ€ 5 epoch
์—์„œ ์ฃฝ์–ด๋„ ๋‹ค์Œ arm ์œผ๋กœ ๋„˜์–ด๊ฐ€๋ฏ€๋กœ, **๋™์ผ ์˜ˆ์‚ฐ ํ†ต์ œ๊ฐ€ ์กฐ์šฉํžˆ ๊นจ์ง„๋‹ค.**
์‹คํ–‰ ์ค‘์ธ ์Šคํฌ๋ฆฝํŠธ๋Š” ํŽธ์ง‘ํ•˜์ง€ ์•Š์•˜๋‹ค โ€” bash ๋Š” ์Šคํฌ๋ฆฝํŠธ๋ฅผ ๋ฐ”์ดํŠธ ์˜คํ”„์…‹์œผ๋กœ ์ฝ์œผ๋ฏ€๋กœ
์ง„ํ–‰ ์ค‘ ํŒŒ์ผ์„ ๊ณ ์น˜๋ฉด ๋‚จ์€ ๋ถ€๋ถ„์„ ์ž˜๋ชป ํ•ด์„ํ•œ๋‹ค. ๋Œ€์‹  `run_ablation_followup.sh` ๋ฅผ ์ƒˆ๋กœ
๋‘๊ณ , ์™„๋ฃŒ epoch ์ˆ˜๋ฅผ ํ™•์ธํ•ด ๋ชฉํ‘œ์— ๋ชป ๋ฏธ์น˜๋ฉด ์žฌ์‹œ๋„ํ•˜๋„๋ก ํ–ˆ๋‹ค.
### ๊ฒ€์ฆํ•˜๋ ค๋‹ค ์‹ค์ œ ํ•™์Šต์„ ๋„์šด ์‚ฌ๊ณ 
`ABL_ARMS="" bash scripts/run_ablation_followup.sh` ๋กœ "arm ์—†์ด ์ƒํƒœ๋งŒ ๋ณด์ž"๊ณ  ์‹คํ–‰ํ–ˆ๋Š”๋ฐ
ํ•™์Šต์ด ์‹œ์ž‘๋˜์–ด `duplicate` ์™€ GPU ๋ฅผ ๋‹คํˆฌ์—ˆ๋‹ค. ์›์ธ์€ `${ABL_ARMS:-๊ธฐ๋ณธ๊ฐ’}` ์˜ `:-` ๊ฐ€
**๋นˆ ๋ฌธ์ž์—ด๋„ ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ ์น˜ํ™˜**ํ•œ๋‹ค๋Š” ์ ์ด๋‹ค. ์ฆ‰์‹œ ์ข…๋ฃŒํ•˜์—ฌ ํ”ผํ•ด๋Š” ์—†์—ˆ๋‹ค(`duplicate`
์ •์ƒ ์ง„ํ–‰, `random_mask` ์ฒดํฌํฌ์ธํŠธยท๋กœ๊ทธ ๋ถˆ๋ณ€).
์„ธ ๊ฐ€์ง€ ์•ˆ์ „์žฅ์น˜๋ฅผ ๋„ฃ์—ˆ๋‹ค.
| ์žฅ์น˜ | ํšจ๊ณผ |
|---|---|
| `--check` ํ”Œ๋ž˜๊ทธ | ์ƒํƒœ๋งŒ ์ถœ๋ ฅํ•˜๊ณ  ์ข…๋ฃŒ. ๊ฒ€์ฆ ์‹œ ํ•™์Šต์ด ์‹œ์ž‘๋  ์ˆ˜ ์—†๋‹ค |
| `${ABL_ARMS-...}` (`:` ์ œ๊ฑฐ) | ๋นˆ ๋ฌธ์ž์—ด์„ "arm ์—†์Œ"์œผ๋กœ ์กด์ค‘ |
| ์‹คํ–‰ ์ค‘ ํ”„๋กœ์„ธ์Šค ๊ฐ์ง€ | ๋‹ค๋ฅธ ์ ˆ์ œ ํ•™์Šต์ด ์žˆ์œผ๋ฉด ๊ฑฐ๋ถ€(`ABL_FORCE=1` ๋กœ๋งŒ ๋ฌด์‹œ) |
### ๋‚จ์€ ์ผ์ •
```
flow 40/40 ์™„์ฃผ
random_mask 5/40 ์žฌ๊ฐœ ํ•„์š”
duplicate 12/40 ์ง„ํ–‰ ์ค‘
zero 0/40 ๋Œ€๊ธฐ
flow_reinit 0/40 ๋Œ€๊ธฐ
```
`zero` ์ข…๋ฃŒ ํ›„ `bash scripts/run_ablation_followup.sh` ๋กœ `random_mask` ์žฌ๊ฐœ +
`flow_reinit` ์‹คํ–‰. ์•ฝ 144 epoch ร— 75๋ถ„ โ‰ˆ 7.5์ผ.
---
## duplicate arm ๋ฐœ์‚ฐ (2026-08-13)
`abl_duplicate` ๊ฐ€ **epoch 16, iteration 1430 ๋ถ€๊ทผ์—์„œ ๋ฐœ์‚ฐ**ํ–ˆ๋‹ค. ์ด์ „์˜ ์‚ฐ๋ฐœ์  NaN ๊ณผ ์„ฑ๊ฒฉ์ด
์ „ํ˜€ ๋‹ค๋ฅด๋‹ค.
```
epoch 15 ๊นŒ์ง€ : middleloss 0.0001, ์ •์ƒ
epoch 16 : iteration ~1430 ์ดํ›„ totallossยทorilossยทmiddleloss ์ „๋ถ€ nan (์ถœ๋ ฅ 250 ์ค‘ 116)
epoch 17 : ์‹œ์ž‘๋ถ€ํ„ฐ ์ „๋ถ€ nan
```
**๋ชจ๋ธ์ด ์ฃฝ์—ˆ๋‹ค.** `checkpoint_16.pth` ์˜ ํ•™์Šต ํŒŒ๋ผ๋ฏธํ„ฐ **231๊ฐœ ์ค‘ 193๊ฐœ๊ฐ€ ๋น„์œ ํ•œ**์ด๋‹ค
(`llama_proj_0.weight`, `video_query_tokens_*` ๋“ฑ). ์ „์ˆ˜ ์Šค์บ” ๊ฒฐ๊ณผ **epoch 15 ๊ฐ€ ๋งˆ์ง€๋ง‰ ์ •์ƒ
์ฒดํฌํฌ์ธํŠธ**๋‹ค(13ยท14ยท15 ์ „๋ถ€ 0/231).
**ํšŒ๋ณต ๋ถˆ๊ฐ€.** AMP GradScaler ๋Š” NaN ์†์‹ค์—์„œ ์˜ตํ‹ฐ๋งˆ์ด์ € ์Šคํ…์„ ๊ฑด๋„ˆ๋›ฐ๋Š”๋ฐ, ๊ฐ€์ค‘์น˜๊ฐ€ ์ด๋ฏธ
NaN ์ด๋ฉด ๋ชจ๋“  forward ๊ฐ€ NaN ์ด๊ณ  ๋ชจ๋“  ์Šคํ…์ด ๊ฑด๋„ˆ๋›ฐ์–ด์ง„๋‹ค. ์Šค์Šค๋กœ ๋น ์ ธ๋‚˜์˜ฌ ๊ฒฝ๋กœ๊ฐ€ ์—†๋‹ค.
๋”ฐ๋ผ์„œ ๊ณ„์† ๋‘๋Š” ๊ฒƒ์€ ์ˆœ์ˆ˜ํ•œ ๋‚ญ๋น„์ด๋ฉฐ(arm ๋‹น ์•ฝ 2์ผ), ์ค‘๋‹จํ–ˆ๋‹ค. ๋Ÿฌ๋„ˆ๋Š” ๋‹ค์Œ arm(`zero`)์œผ๋กœ
์ง„ํ–‰ํ–ˆ๋‹ค.
### ์ด์ „ NaN ๊ณผ์˜ ๊ตฌ๋ณ„
| | ์‚ฐ๋ฐœ์  NaN (flow arm) | ๋ฐœ์‚ฐ (duplicate arm) |
|---|---|---|
| iteration NaN ๋น„์œจ | 0.014% (7,032 ์ค‘ 1) | **46%** (250 ์ค‘ 116), ์ดํ›„ 100% |
| ์ง€์†์„ฑ | ๋‹จ๋ฐœ, ๋‹ค์Œ iteration ์ •์ƒ | ์‹œ์ž‘ ํ›„ ๋˜๋Œ์•„์˜ค์ง€ ์•Š์Œ |
| ๊ฐ€์ค‘์น˜ | ์ „๋ถ€ ์œ ํ•œ | **193/231 ๋น„์œ ํ•œ** |
| epoch ํ‰๊ท ์ด NaN ์ธ ์ด์œ  | ๋‹จ์ผ NaN ์ด ํ‰๊ท ์„ ์˜ค์—ผ | ์‹ค์ œ๋กœ ์ „๋ถ€ NaN |
**์ƒˆ ๊ฐ์‹œ ๊ธฐ์ค€์ด ์ด๊ฒƒ์„ ์žก์•˜๋‹ค.** "์—ฐ์† 3 epoch NaN" ๊ธฐ์ค€์€ ๋‘ ๊ฒฝ์šฐ๋ฅผ ๊ตฌ๋ณ„ํ•˜์ง€ ๋ชปํ–ˆ์œผ๋‚˜,
๊ต์ฒดํ•œ ๊ธฐ์ค€(iteration NaN ๋น„์œจ > 1% ยท ์ฒดํฌํฌ์ธํŠธ ๋น„์œ ํ•œ ํŒŒ๋ผ๋ฏธํ„ฐ)์€ ๋ฐœ์‚ฐ๋งŒ ๋ฐœ๋™ํ–ˆ๋‹ค.
### ์›์ธ ๋ฏธํ™•์ •
epoch 16 ์ „๊นŒ์ง€ `middleloss` ๊ฐ€ 0.0001 โ†’ 0.0003 ์œผ๋กœ ์›€์ง์ด๊ธฐ ์‹œ์ž‘ํ–ˆ๊ณ (์ •๋ฅ˜์  ์ดํƒˆ์˜ ์ดˆ๊ธฐ
์‹ ํ˜ธ), ๊ทธ ์งํ›„ ๋ฐœ์‚ฐํ–ˆ๋‹ค. `flow` arm ์—์„œ๋„ epoch 28 ์— ์ดํƒˆ์ด ์žˆ์—ˆ์œผ๋‚˜ ๋ฐœ์‚ฐํ•˜์ง€ ์•Š๊ณ 
ํšŒ๋ณตํ–ˆ๋‹ค. ๋‘ ์‚ฌ๊ฑด์ด ๊ฐ™์€ ๊ธฐ์ œ์ธ์ง€, `duplicate` ์˜ ์ •์  ์ž…๋ ฅ(์›๋ณธ ํ”„๋ ˆ์ž„ ๋ณต์ œ)์ด ์ดํƒˆ์„ ๋”
๋ถˆ์•ˆ์ •ํ•˜๊ฒŒ ๋งŒ๋“œ๋Š”์ง€๋Š” ํ™•์ •ํ•˜์ง€ ๋ชปํ–ˆ๋‹ค.
**ํ‘œํ˜„ ์†์‹ค์„ ๋„๋ฉด ์ด ์‹คํŒจ ์–‘์‹์ด ์‚ฌ๋ผ์งˆ ๊ฐ€๋Šฅ์„ฑ์ด ๋†’๋‹ค.** ๋…ผ๋ฌธ์€ `L_sim`ยท`L_dis` ๋ฅผ ๊ธฐ์—ฌ๋กœ
์ฃผ์žฅํ•˜์ง€ ์•Š์œผ๋ฏ€๋กœ `t_1 = t_4 = 0` ์œผ๋กœ ๋‘๋Š” ๋ฐ ์†์‹ค์ด ์—†๊ณ , ํ†ต์ œ ์‹คํ—˜์—์„œ ์ผœ ๋‘˜ ์ด์œ ๋„ ์—†๋‹ค.
์žฌ์‹คํ–‰ ์‹œ ์ด ์„ค์ •์„ ๊ฒ€ํ† ํ•  ๊ฒƒ.
### ๋‚จ์€ ์ƒํƒœ
```
flow 40/40 ์™„์ฃผ
random_mask 5/40 OOM kill (checkpoint_4 ์—์„œ ์žฌ๊ฐœ ๊ฐ€๋Šฅ)
duplicate 17/40 ๋ฐœ์‚ฐ (checkpoint_15 ์—์„œ ์žฌ๊ฐœ ๊ฐ€๋Šฅ)
zero 0/40 ์ง„ํ–‰ ์‹œ์ž‘ 2026-08-13 22:23
flow_reinit 0/40 ๋Œ€๊ธฐ
```