taejoon89 commited on
Commit
4f91c02
ยท
verified ยท
1 Parent(s): 97863b7

Upload folder using huggingface_hub

Browse files
OpenMidnight/dinov2/configs/train/openpath_vitg14.yaml ADDED
@@ -0,0 +1,95 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # OpenPath โ€” ViT-g/14 pathology FM pre-training config.
2
+ # DINOv2 (DINO + iBOT + KDE) + gram anchoring, warm-started from Meta DINOv2 ViT-g/14-reg,
3
+ # on the public OpenPath corpus (native 40x tiles). Global batch = 64/GPU x 40 GPU = 2560.
4
+ #
5
+ # LR regime: near-constant (flat). `epochs` only sets the schedule horizon (very large), so the
6
+ # cosine barely descends; the run actually stops at `early_stop`. This keeps LR/wd/momentum
7
+ # near their initial values throughout training.
8
+ compute_precision:
9
+ grad_scaler: false
10
+ teacher:
11
+ backbone:
12
+ sharding_strategy: SHARD_GRAD_OP
13
+ mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
14
+ dino_head:
15
+ sharding_strategy: SHARD_GRAD_OP
16
+ mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
17
+ ibot_head:
18
+ sharding_strategy: SHARD_GRAD_OP
19
+ mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
20
+ student:
21
+ backbone:
22
+ sharding_strategy: SHARD_GRAD_OP
23
+ mixed_precision: {param_dtype: bf16, reduce_dtype: bf16, buffer_dtype: fp32}
24
+ dino_head:
25
+ sharding_strategy: SHARD_GRAD_OP
26
+ mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
27
+ ibot_head:
28
+ sharding_strategy: SHARD_GRAD_OP
29
+ mixed_precision: {param_dtype: bf16, reduce_dtype: fp32, buffer_dtype: fp32}
30
+ dino:
31
+ head_n_prototypes: 131072
32
+ head_bottleneck_dim: 384
33
+ do_kde: True
34
+ kde_loss_weight: .05
35
+ koleo_loss_weight: 0
36
+ do_koleo: False
37
+ ibot:
38
+ loss_weight: 1.0
39
+ mask_sample_probability: 0.5
40
+ mask_ratio_min_max:
41
+ - 0.1
42
+ - 0.45
43
+ separate_head: true
44
+ head_n_prototypes: 131072
45
+ train:
46
+ # OpenPath corpus (taejoon89/openpath-corpus). Point to the downloaded shards:
47
+ sample_list_path: "openpath:glob=/path/to/openpath-corpus/*/tiles/shards/w*/*.tar"
48
+ streaming_from_hf: false
49
+ batch_size_per_gpu: 64 # x 40 GPU = global 2560
50
+ centering: sinkhorn_knopp
51
+ use_pretrained: True # Meta DINOv2 ViT-g/14-reg warm-start (set MODEL.WEIGHTS)
52
+ OFFICIAL_EPOCH_LENGTH: 1250
53
+ saveckp_freq: 8 # resume ckpt every 8 x 1250 = 10,000 iters
54
+ num_workers: 10
55
+ prefetch_factor: 4
56
+ skip_checkpointer: false
57
+ student:
58
+ arch: vit_giant2
59
+ patch_size: 14
60
+ drop_path_rate: 0.4
61
+ ffn_layer: swiglufused
62
+ block_chunks: 4
63
+ num_register_tokens: 4
64
+ interpolate_antialias: true
65
+ interpolate_offset: 0.0
66
+ teacher:
67
+ momentum_teacher: 0.994
68
+ warmup_teacher_temp_epochs: 41 # 0.27 epoch = 51,250 iters
69
+ optim:
70
+ epochs: 8000 # schedule horizon only (=10,000,000 iter); LR/wd/momentum near-constant
71
+ early_stop: 276 # actual stop: 276 x 1250 = 345,000 iters (~1 native epoch)
72
+ weight_decay_end: 0.2 # not reached within early_stop (wd stays ~0.040)
73
+ base_lr: 2.0e-04 # sqrt_wrt_1024 -> effective ~3.16e-4 @ global 2560
74
+ warmup_epochs: 9 # ~11,250 iter (~4.9%); must be an integer
75
+ freeze_last_layer_epochs: 1
76
+ layerwise_decay: 1.0
77
+ crops:
78
+ local_crops_size: 98
79
+ evaluation:
80
+ eval_period_iterations: 5750 # save a teacher checkpoint every 5,750 iters
81
+ bach_root: ""
82
+ breakhis_root: ""
83
+ pcam_root: ""
84
+
85
+ # Gram anchoring (ported from DINOv3): MSE between L2-normalized patch-token Gram matrices of
86
+ # student vs a frozen anchor. Dampens dense-feature degradation during long training.
87
+ gram:
88
+ use_loss: true
89
+ normalized: true
90
+ remove_neg: true
91
+ loss_weight: 40.0 # ~5-8% of total loss; lower if it over-constrains DINO/iBOT
92
+ it_first_update: 57500 # activate near the dense-feature peak
93
+ ramp_iters: 3000 # linear ramp of the gram weight
94
+ # Anchor = a strong earlier OpenPath teacher checkpoint (see taejoon89/openpath-checkpoints):
95
+ ckpt: /path/to/openpath-checkpoints/training_63250/teacher_checkpoint.pth
README.md CHANGED
@@ -1,48 +1,121 @@
1
- # OpenPath โ€” ๋ณ‘๋ฆฌ Foundation Model ํ•™์Šต ์ฝ”๋“œ (run6 ๊ธฐ์ค€)
 
 
 
 
 
 
 
 
 
 
 
 
2
 
3
- ๊ณต๊ฐœ ๋ณ‘๋ฆฌ ๋ฐ์ดํ„ฐ๋งŒ์œผ๋กœ ํ•™์Šตํ•œ ViT-g/14 pathology FM(OpenPath)์˜ **ํ•™์Šต ์žฌํ˜„ ์ฝ”๋“œ**.
4
- run6 = **native 40ร— ์ฝ”ํผ์Šค + DINOv2(OpenMidnight fork) + gram anchoring** ๊ตฌ์„ฑ.
 
 
 
 
 
 
 
 
 
 
 
 
5
 
6
- ## ๊ตฌ์„ฑ
7
  ```
8
- OpenMidnight/ # DINOv2 ํ•™์Šต fork (dinov2 ํŒจํ‚ค์ง€ + ์šฐ๋ฆฌ ์ˆ˜์ •)
9
- dinov2/train/train.py # ํ•™์Šต ๋ฃจํ”„ (+ gram weight ์Šค์ผ€์ค„)
10
- dinov2/train/ssl_meta_arch.py # SSL ์•„ํ‚คํ…์ฒ˜ (+ gram anchor teacher)
11
- dinov2/loss/gram_loss.py # โ˜… gram anchoring loss (DINOv3์„œ ์ด์‹)
12
- dinov2/data/openpath_wds.py # openpath ์ฝ”ํผ์Šค WebDataset ๋กœ๋” (openpath:glob=...)
13
- dinov2/configs/train/openpath_vitg14_run6_native_gram.yaml # โ˜… run6 config
14
  scripts/
15
- launch_om_run2.sh # ๋ฉ€ํ‹ฐ๋…ธ๋“œ ๋Ÿฐ์ฒ˜ (40 GPU: host + 4 workers)
16
- autoresume_run6.sh # ํฌ๋ž˜์‹œ ๋‚ด์„ฑ ์ž๋™ ์žฌ๊ฐœ
17
- watch_eval_run6.sh # ์‹ค์‹œ๊ฐ„ HEST probe (์ฒดํฌํฌ์ธํŠธ๋ณ„)
18
- run_hest_3way.py # HEST ํ‰๊ฐ€ (Meta DINOv2 / Phikon-v2 / OpenPath ๋น„๊ต)
19
  requirements.txt
20
  ```
21
 
22
- ## ๋ฐ์ดํ„ฐ
23
- - **`taejoon89/openpath-corpus`** (HF, public+gated) โ€” native 40ร— ๋ณ‘๋ฆฌ ํƒ€์ผ 33,991 shard / 17TB.
24
- - config์˜ `train.sample_list_path` = `openpath:glob=<์ฝ”ํผ์Šค๊ฒฝ๋กœ>/*/tiles/shards/w*/*.tar` ๋กœ ์ง€์ •.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
- ## ์•ต์ปค (gram)
27
- - run6์˜ gram anchor = **run5 training_63250** teacher checkpoint (HEST 0.3834).
28
- - `taejoon89/openpath-checkpoints` (HF, private)์—์„œ ๋ฐ›์•„ config์˜ `gram.ckpt`์— ์ง€์ •.
 
 
 
 
 
29
 
30
- ## ํ•ต์‹ฌ ์„ค์ • (run6)
31
- - ViT-g/14 reg4, patch14, warm-start = Meta DINOv2 ViT-g/14-reg.
32
- - flat-LR (epochs 8000 ์ง€ํ‰, early_stop 276=345k=native 1 epoch), base_lr 2e-4 (์‹คํšจ 3.16e-4 @global 2560).
33
- - **gram anchoring**: weight 40, it_first_update 57500(peak ์ง์ „ ํ™œ์„ฑ), ramp 3000. loss = ์ •๊ทœํ™” patch Gram matrix MSE(student vs anchor).
34
- - FSDP SHARD_GRAD_OP, bf16, sinkhorn-knopp centering, DINO+iBOT+KDE loss.
35
 
36
- ## ํ•™์Šต ์‹คํ–‰ (๋ฉ€ํ‹ฐ๋…ธ๋“œ 40 GPU ์˜ˆ)
37
  ```bash
38
- export PYTHONPATH=$PWD/OpenMidnight
39
- CFG=OpenMidnight/dinov2/configs/train/openpath_vitg14_run6_native_gram.yaml
40
- bash scripts/launch_om_run2.sh omvitgrun6 "$CFG" <์ถœ๋ ฅ๋””๋ ‰ํ† ๋ฆฌ> <๋กœ๊ทธ๋””๋ ‰ํ† ๋ฆฌ>
41
- # + autoresume_run6.sh (๋ฐฑ๊ทธ๋ผ์šด๋“œ), watch_eval_run6.sh (์‹ค์‹œ๊ฐ„ HEST)
 
42
  ```
43
 
44
- ## ๊ฒฐ๊ณผ (์˜ค์—ผ-free HCC-ST Visium ๋ฒค์น˜, LOPO Pearson)
45
- - best ๋ชจ๋ธ = run5 training_63250: HEST 0.3834, HCC-ST 0.3244 (> OpenMidnight 0.3004).
46
- - run6(gram) = ํ›„๋ฐ˜ ํ•˜๋ฝ ์™„ํ™”(์•ˆ์ •ํ™”) + dense feature. HCC-ST ๋Œ€๋ถ€๋ถ„ ์ฒดํฌํฌ์ธํŠธ๊ฐ€ OpenMidnight ์ƒํšŒ.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
47
 
48
- ๋ผ์ด์„ ์Šค: ์ฝ”๋“œ = OpenMidnight/DINOv2 fork(ํ•ด๋‹น LICENSE ์ฐธ์กฐ). ๋ฐ์ดํ„ฐ = ๊ณต๊ฐœ ๋ณ‘๋ฆฌ(CC-BY/CC0/NIH-open).
 
 
1
+ ---
2
+ license: apache-2.0
3
+ tags:
4
+ - pathology
5
+ - histopathology
6
+ - foundation-model
7
+ - self-supervised
8
+ - dinov2
9
+ - vision-transformer
10
+ - digital-pathology
11
+ library_name: pytorch
12
+ pipeline_tag: image-feature-extraction
13
+ ---
14
 
15
+ # OpenPath โ€” Pathology Foundation Model (training code)
16
+
17
+ **OpenPath** is a vision foundation model for computational pathology: a **ViT-g/14** encoder
18
+ pre-trained with self-supervision (**DINOv2** + **gram anchoring**) on **public-only** whole-slide
19
+ histopathology tiles. This repository contains the **training / reproduction code**. The corpus and
20
+ checkpoints are hosted separately (see below).
21
+
22
+ - **Encoder:** ViT-g/14 (reg4), 1536-dim CLS embedding
23
+ - **Objective:** DINO + iBOT + KDE (DINOv2) with **gram anchoring** ported from DINOv3
24
+ - **Data:** public pathology WSIs only (TCGA, TCIA, GTEx, CAMELYON, ACROBAT, SurGen, โ€ฆ), re-tiled at native 40ร—
25
+ - **Warm start:** Meta DINOv2 ViT-g/14-reg
26
+ - **Training:** FSDP (SHARD_GRAD_OP), bf16, flat learning-rate schedule, 40ร— B200 (multi-node)
27
+
28
+ ## Repository layout
29
 
 
30
  ```
31
+ OpenMidnight/ # DINOv2 training fork
32
+ dinov2/train/train.py # training loop (+ gram-weight schedule)
33
+ dinov2/train/ssl_meta_arch.py # SSL arch (+ frozen gram-anchor teacher)
34
+ dinov2/loss/gram_loss.py # gram anchoring loss (ported from DINOv3)
35
+ dinov2/data/openpath_wds.py # WebDataset loader for the OpenPath corpus
36
+ dinov2/configs/train/openpath_vitg14.yaml # training config
37
  scripts/
38
+ launch.sh # multi-node launcher (host + workers, 40 GPU)
39
+ autoresume.sh # crash-tolerant auto-resume
40
+ watch_eval.sh # online HEST probing per checkpoint
41
+ run_hest_3way.py # HEST evaluation (Meta DINOv2 / Phikon-v2 / OpenPath)
42
  requirements.txt
43
  ```
44
 
45
+ ## Related artifacts
46
+
47
+ | Artifact | Hugging Face repo | Notes |
48
+ |---|---|---|
49
+ | **Corpus** | `taejoon89/openpath-corpus` | Native 40ร— pathology tiles, 33,991 WebDataset shards / ~17 TB (public, gated) |
50
+ | **Checkpoints** | `taejoon89/openpath-checkpoints` | OpenPath teacher checkpoints across training (private) |
51
+ | **Code** | `taejoon89/openpath` | This repository |
52
+
53
+ The training config points to the corpus via
54
+ `train.sample_list_path: "openpath:glob=<corpus>/*/tiles/shards/w*/*.tar"`. The gram anchor
55
+ (`gram.ckpt`) is an earlier OpenPath teacher checkpoint from `openpath-checkpoints`.
56
+
57
+ ## Method โ€” gram anchoring
58
+
59
+ Long self-supervised training degrades dense/patch features. Following **DINOv3**, we add a
60
+ **gram anchoring** loss: the MSE between the L2-normalized patch-token Gram (similarity) matrices
61
+ of the student and a **frozen anchor** model (a strong earlier checkpoint). The loss weight is `40`
62
+ and it activates near the dense-feature peak (iteration `57,500`) with a 3k-iter ramp. This
63
+ **dampens the post-peak decline** of dense representations while DINO/iBOT keep optimizing the
64
+ global representation.
65
+
66
+ ## Key hyper-parameters
67
 
68
+ | | |
69
+ |---|---|
70
+ | Arch | `vit_giant2`, patch 14, 4 register tokens, SwiGLU FFN |
71
+ | Batch | 64 / GPU ร— 40 GPU = global 2560 |
72
+ | LR | base 2e-4 (effective โ‰ˆ 3.16e-4 @ global 2560), flat (near-constant) |
73
+ | Schedule | `epochs: 8000` horizon, `early_stop: 276` โ‰ˆ 345k iters โ‰ˆ 1 native epoch |
74
+ | gram | weight 40, `it_first_update 57500`, ramp 3000, normalized, remove-neg |
75
+ | Precision | bf16, FSDP SHARD_GRAD_OP, sinkhorn-knopp centering |
76
 
77
+ ## Reproducing training
 
 
 
 
78
 
 
79
  ```bash
80
+ export PYTHONPATH="$PWD/OpenMidnight"
81
+ CFG=OpenMidnight/dinov2/configs/train/openpath_vitg14.yaml
82
+ # edit CFG: train.sample_list_path (corpus glob), gram.ckpt (anchor checkpoint), MODEL.WEIGHTS (DINOv2 warm-start)
83
+ bash scripts/launch.sh openpathrun "$CFG" <output_dir> <log_dir>
84
+ # optionally run scripts/autoresume.sh (background) and scripts/watch_eval.sh (online HEST)
85
  ```
86
 
87
+ Extract CLS embeddings for downstream use:
88
+
89
+ ```python
90
+ import torch, dinov2.models.vision_transformer as vits
91
+ ck = torch.load("teacher_checkpoint.pth", map_location="cpu", weights_only=False)
92
+ sd = {k[len("backbone."):]: v for k, v in ck["teacher"].items() if k.startswith("backbone.")}
93
+ m = vits.vit_giant2(patch_size=14, img_size=224, block_chunks=4, num_register_tokens=4,
94
+ ffn_layer="swiglufused", init_values=1e-5,
95
+ interpolate_antialias=True, interpolate_offset=0.0)
96
+ m.load_state_dict(sd, strict=True); m.eval()
97
+ cls = m.forward_features(x)["x_norm_clstoken"] # (B, 1536)
98
+ ```
99
+
100
+ ## Evaluation
101
+
102
+ Frozen-encoder linear/ridge probing. The headline benchmark is a **contamination-free** in-house
103
+ HCC (hepatocellular carcinoma) Visium spatial-transcriptomics cohort (leave-one-patient-out, mean
104
+ Pearson over top-50 highly-variable genes) โ€” no public FM was trained on it.
105
+
106
+ | Benchmark (metric) | OpenPath (best) | OpenMidnight | Phikon-v2 |
107
+ |---|---|---|---|
108
+ | **HCC-ST** (Pearson, clean) | **0.324** | 0.300 | 0.274 |
109
+ | HEST-1K (Pearson) | 0.383 | 0.390 | 0.375 |
110
+ | NCT-CRC-HE (9-class acc) | 0.964 | 0.967 | 0.937 |
111
+ | BACH (4-class acc) | 0.811 | 0.906 | 0.708 |
112
+
113
+ On the clean HCC-ST cohort the majority of OpenPath checkpoints exceed OpenMidnight, and OpenPath
114
+ beats Phikon-v2 across all benchmarks. The best checkpoint (`training_63250`, available in
115
+ `openpath-checkpoints`) is top on both HEST-1K and HCC-ST. Note: PCam / CAMELYON is excluded from
116
+ comparison because it overlaps our training corpus.
117
+
118
+ ## License
119
 
120
+ Code: derived from the OpenMidnight / DINOv2 fork โ€” see `OpenMidnight/LICENSE`.
121
+ Training data: public pathology datasets under CC-BY / CC0 / NIH-open terms (redistributable).
scripts/autoresume.sh ADDED
@@ -0,0 +1,45 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # run(om_vitg14_run) ํฌ๋ž˜์‹œ ๋‚ด์„ฑ ์ž๋™ ์žฌ๊ฐœ waiter. TJ-1(์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ)์„œ ๋ฐฑ๊ทธ๋ผ์šด๋“œ ์‹คํ–‰.
3
+ # - ์™„์ฃผ(training_230000) ๊ฐ์ง€ โ†’ exit 0
4
+ # - NaN ๊ฐ์ง€ โ†’ exit 2 (์žฌ๊ฐœ ์•ˆ ํ•จ)
5
+ # - ํฌ๋ž˜์‹œ(์ „๋ฉธ, ์™„์ฃผ/NaN ์•„๋‹˜) โ†’ ์ „๋…ธ๋“œ shm์ •๋ฆฌ ํ›„ launch.sh๋กœ resume(๊ฐ™์€ OUT=๋งˆ์ง€๋ง‰ ์ฒดํฌํฌ์ธํŠธ), ์ตœ๋Œ€ MAXํšŒ
6
+ # โ˜… host=TJ-2(alive/NaN ์ฒดํฌ๋„ TJ-2). TJ-1์€ ํ•™์Šต ์ œ์™ธ๋ผ ๋Œ€์ƒ ์•„๋‹˜.
7
+ set -u
8
+ P=/NHNHOME/WORKSPACE/0526040027_A/OpenPath
9
+ CFG=$P/OpenMidnight/dinov2/configs/train/openpath_vitg14.yaml
10
+ OUT=$P/data/runs/om_vitg14_run
11
+ LOG=$P/data/runs/om_logs/vitg_run
12
+ DONE=$OUT/eval/training_345000/teacher_checkpoint.pth
13
+ TJLOG=$LOG/TJ-2.log
14
+ NODES="TJ-2 TJ-3 TJ-4 TJ-5 TJ-6"
15
+ MAX=40
16
+ retry=0
17
+
18
+ alive_count() {
19
+ timeout 12 ssh -o BatchMode=yes -o ConnectTimeout=8 TJ-2 "ps -eo args 2>/dev/null | grep '[t]rain.py' | wc -l" 2>/dev/null || echo 1
20
+ }
21
+
22
+ echo "[autoresume-run] watching $OUT (host TJ-2, max $MAX auto-resumes)"
23
+ while true; do
24
+ [ -f "$DONE" ] && { echo "RUN2_DONE: ์™„์ฃผ (training_230000)"; exit 0; }
25
+ if grep -qiE "NaN detected" "$TJLOG" 2>/dev/null; then echo "RUN2_NAN: NaN ๊ฐ์ง€ โ€” ์ž๋™์žฌ๊ฐœ ์ค‘๋‹จ"; exit 2; fi
26
+
27
+ a=$(alive_count)
28
+ if [ "${a:-1}" = "0" ]; then
29
+ sleep 60
30
+ a2=$(alive_count)
31
+ if [ "${a2:-1}" = "0" ] && [ ! -f "$DONE" ]; then
32
+ grep -qiE "NaN detected" "$TJLOG" 2>/dev/null && { echo "RUN2_NAN"; exit 2; }
33
+ retry=$((retry+1))
34
+ if [ "$retry" -gt "$MAX" ]; then echo "RUN2_GIVEUP: ์ž๋™์žฌ๊ฐœ $MAXํšŒ ์ดˆ๊ณผ โ€” ์ˆ˜๋™ ๊ฐœ์ž…"; exit 3; fi
35
+ lastit=$(grep -E "helpers.py:110] Training" "$TJLOG" 2>/dev/null | tail -1 | grep -oE "\[ *[0-9]+/" | head -1)
36
+ echo "[autoresume-run] CRASH detected at $lastit โ€” auto-resume #$retry/$MAX"
37
+ for n in $NODES; do timeout 10 ssh -o BatchMode=yes "$n" "pkill -9 -f 'dinov2/train/train.py' 2>/dev/null; pkill -9 -f torchrun 2>/dev/null; rm -f /dev/shm/nccl* 2>/dev/null" 2>/dev/null; done
38
+ sleep 30
39
+ bash "$P/scripts/launch.sh" "openpathrunr${retry}" "$CFG" "$OUT" "$LOG" >/dev/null 2>&1
40
+ echo "[autoresume-run] relaunched (rdzv openpathrunr${retry}) โ€” resume from last checkpoint"
41
+ sleep 180
42
+ fi
43
+ fi
44
+ sleep 240
45
+ done
scripts/launch.sh ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # run ํ•™์Šต ๋Ÿฐ์ฒ˜: TJ-2~6(5๋…ธ๋“œร—8=40 GPU) FSDP. host=TJ-2, ์›Œ์ปค=TJ-3~6. โ˜…TJ-1์€ ํ•™์Šต ์ œ์™ธ(eval watcher ์ „์šฉ).
3
+ # TJ-1(์˜ค์ผ€์ŠคํŠธ๋ ˆ์ดํ„ฐ)์—์„œ ์‹คํ–‰ โ€” ๋กœ์ปฌ ํ•™์Šต ํ”„๋กœ์„ธ์Šค ์—†์Œ. host๋„ ์›๊ฒฉ(ssh TJ-2)์ด๋ผ ์ž๊ธฐ์ฐธ์กฐ ํ•จ์ • ์—†์Œ.
4
+ # ์‚ฌ์šฉ: scripts/launch.sh <rdzv_id> <config_abspath> <outdir> <logdir> [extra train.py args...]
5
+ set -u
6
+ R=/NHNHOME/WORKSPACE/0526040027_A/OpenPath
7
+ RDZV="$1"; CFG="$2"; OUTDIR="$3"; LOGDIR="$4"; shift 4
8
+ EXTRA="$*"
9
+ HOST="TJ-2"
10
+ EP="10.34.15.21:29500" # TJ-2 IB IP (rendezvous rank0)
11
+ WORKERS="TJ-3 TJ-4 TJ-5 TJ-6"
12
+ PYC="\$HOME/.cache/op_pyc_$RDZV" # ๋…ธ๋“œ-๋กœ์ปฌ. ์›๊ฒฉ ์…ธ์—์„œ ํ™•์žฅ๋˜๋„๋ก escape
13
+ ENV="WANDB_MODE=disabled NCCL_DEBUG=WARN NCCL_IB_HCA=mlx5_1,mlx5_2,mlx5_4,mlx5_9 \
14
+ NCCL_SOCKET_IFNAME=bond0 GLOO_SOCKET_IFNAME=bond0 \
15
+ NCCL_IB_TIMEOUT=22 NCCL_IB_RETRY_CNT=13 NCCL_IB_QPS_PER_CONNECTION=4 \
16
+ PYTHONDONTWRITEBYTECODE=1 PYTHONPYCACHEPREFIX=$PYC PYTHONPATH=$R/OpenMidnight"
17
+ TR="venv/bin/torchrun --nnodes=5 --nproc-per-node=8 --rdzv-backend=c10d \
18
+ --rdzv-endpoint=$EP --rdzv-id=$RDZV \
19
+ OpenMidnight/dinov2/train/train.py --config-file $CFG --output-dir $OUTDIR $EXTRA"
20
+
21
+ mkdir -p "$LOGDIR" "$OUTDIR"
22
+
23
+ # 1) host(TJ-2) ์›๊ฒฉ ๊ธฐ๋™ โ€” โ˜…ssh๋ฅผ &๋กœ ๋ฐฑ๊ทธ๋ผ์šด๋“œ(์•ˆ ๊ทธ๋Ÿฌ๋ฉด ์›๊ฒฉ setsid์˜ ์ƒ์† fd๋ฅผ ๋ฌผ๊ณ  ssh๊ฐ€ ๋ฐ˜ํ™˜ ์•ˆ ํ•จ=๋Ÿฐ์ฒ˜ ํ–‰)
24
+ echo "launching host on $HOST ..."
25
+ ssh -n -o BatchMode=yes "$HOST" "mkdir -p $PYC; cd $R && setsid env $ENV $TR > $LOGDIR/${HOST}.log 2>&1 < /dev/null & echo ${HOST}-host-fired" &
26
+
27
+ # 2) host torchrun๊ฐ€ store ๋ฐ”์ธ๋”ฉํ•  ์‹œ๊ฐ„(๊ณ ์ • ๋Œ€๊ธฐ). c10d๋Š” ์›Œ์ปค๊ฐ€ ์žฌ์‹œ๋„๋กœ ๋ถ™์œผ๋ฏ€๋กœ ssํด๋ง ๋ถˆํ•„์š”.
28
+ echo "waiting 30s for host store bind on $EP ..."
29
+ sleep 30
30
+ echo "firing 4 workers"
31
+
32
+ # 3) ์›Œ์ปค(TJ-3~6) ๋ณ‘๋ ฌ ๊ธฐ๋™
33
+ for n in $WORKERS; do
34
+ ssh -n -o BatchMode=yes "$n" "mkdir -p $PYC; cd $R && setsid env $ENV $TR > $LOGDIR/${n}.log 2>&1 < /dev/null & echo ${n}-fired" &
35
+ done
36
+ # โ˜… wait ๋Œ€์‹  ๊ณ ์ • sleep โ€” bg ssh๊ฐ€ ์›๊ฒฉ setsid์˜ fd๋ฅผ ๋ฌผ์–ด ๋ฐ˜ํ™˜ ์•ˆ ํ•  ์ˆ˜ ์žˆ์–ด wait๋Š” ์˜์›ํžˆ ๋ฉˆ์ถค(autoresume blocking ํ˜ธ์ถœ๋„ ๋ง‰ํž˜).
37
+ sleep 20
38
+ echo "ALL launched: host $HOST + workers $WORKERS | rdzv=$RDZV | logs=$LOGDIR | out=$OUTDIR"
scripts/run_hest_3way.py CHANGED
@@ -4,7 +4,7 @@
4
  benchmark()๋Š” resnet50์„ ์ž๋™ ๊ธฐ์ค€์œผ๋กœ ํฌํ•จํ•˜๋ฏ€๋กœ ์ถœ๋ ฅ์— resnet50๋„ ํ•จ๊ป˜ ๋‚˜์˜จ๋‹ค.
5
 
6
  ์‹คํ–‰(venv_hest):
7
- PYTHONPATH=third_party/OpenMidnight:. venv_hest/bin/python scripts/run_hest_3way.py \
8
  --backbone om --weights data/runs/om_vitl14/eval/training_0/teacher_checkpoint.pth \
9
  --exp-code meta_dinov2_vitl14 [task1 ...]
10
  PYTHONPATH=. venv_hest/bin/python scripts/run_hest_3way.py --backbone phikon --exp-code phikon_v2
@@ -29,7 +29,7 @@ eval_tf = transforms.Compose([
29
 
30
  def build_om(weights):
31
  """OpenMidnight teacher_checkpoint(ViT-L/14 reg4) โ†’ CLS nn.Module."""
32
- _OM = f"{ROOT}/third_party/OpenMidnight"
33
  sys.path = [_OM] + [p for p in sys.path if "third_party/dinov2" not in p]
34
  import dinov2.models.vision_transformer as vits
35
  ck = torch.load(weights, map_location="cpu", weights_only=False)
 
4
  benchmark()๋Š” resnet50์„ ์ž๋™ ๊ธฐ์ค€์œผ๋กœ ํฌํ•จํ•˜๋ฏ€๋กœ ์ถœ๋ ฅ์— resnet50๋„ ํ•จ๊ป˜ ๋‚˜์˜จ๋‹ค.
5
 
6
  ์‹คํ–‰(venv_hest):
7
+ PYTHONPATH=OpenMidnight:. venv_hest/bin/python scripts/run_hest_3way.py \
8
  --backbone om --weights data/runs/om_vitl14/eval/training_0/teacher_checkpoint.pth \
9
  --exp-code meta_dinov2_vitl14 [task1 ...]
10
  PYTHONPATH=. venv_hest/bin/python scripts/run_hest_3way.py --backbone phikon --exp-code phikon_v2
 
29
 
30
  def build_om(weights):
31
  """OpenMidnight teacher_checkpoint(ViT-L/14 reg4) โ†’ CLS nn.Module."""
32
+ _OM = f"{ROOT}/OpenMidnight"
33
  sys.path = [_OM] + [p for p in sys.path if "third_party/dinov2" not in p]
34
  import dinov2.models.vision_transformer as vits
35
  ck = torch.load(weights, map_location="cpu", weights_only=False)
scripts/watch_eval.sh ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env bash
2
+ # TJ-1 ์‹ค์‹œ๊ฐ„ HEST eval watcher. run ํ•™์Šต์ด ๋ฑ‰๋Š” teacher_checkpoint๋ฅผ ๊ฐ์ง€โ†’probeโ†’๊ณก์„  append.
3
+ # TJ-1 ๋กœ์ปฌ ์‹คํ–‰(venv_hestยทGPU ๋กœ์ปฌ). 1๊ฐœ์”ฉ ์ˆœ์ฐจ(๋™์‹œ HDF5 ๋ฒ„๊ทธ ํšŒํ”ผ, ~60min<์ฒดํฌํฌ์ธํŠธ๊ฐ„๊ฒฉ ~84min๋ผ ํŽ˜์ด์Šค ์œ ์ง€).
4
+ # per-exp embed ๋ถ„๋ฆฌ(/dev/shm/hest_emb_<N>), pkill ๋ฏธํฌํ•จ(self-kill ํšŒํ”ผ).
5
+ set -u
6
+ R=/NHNHOME/WORKSPACE/0526040027_A/OpenPath
7
+ OUTDIR=$R/data/runs/om_vitg14_run
8
+ EVALDIR=$OUTDIR/eval
9
+ RESDIR=$R/data/runs/hest_3way
10
+ CURVE=$RESDIR/curve.txt
11
+ BENCH=/dev/shm/hest_bench
12
+ GPU=0
13
+ mkdir -p "$RESDIR"
14
+
15
+ # hest_bench๋ฅผ TJ-1 /dev/shm์— ํ™•๋ณด(์—†์œผ๋ฉด Lustre์„œ ๋ณต์‚ฌ, 40G)
16
+ if [ ! -d "$BENCH" ] || [ "$(ls "$BENCH" 2>/dev/null | wc -l)" -lt 5 ]; then
17
+ echo "[watch] copying hest_bench -> $BENCH (40G, ์ˆ˜๋ถ„) ..."
18
+ rm -rf "$BENCH"; cp -r "$R/data/eva/hest_bench" "$BENCH"
19
+ fi
20
+ echo "[watch] START โ€” watching $EVALDIR (TJ-1 GPU$GPU, 1-at-a-time) | curve=$CURVE"
21
+
22
+ cd "$R"
23
+ while true; do
24
+ for ck in $(ls -d "$EVALDIR"/training_* 2>/dev/null | sort -t_ -k2 -n); do
25
+ N=$(basename "$ck" | sed 's/training_//')
26
+ W="$ck/teacher_checkpoint.pth"
27
+ LOG="$RESDIR/run_$N.log"
28
+ [ -f "$W" ] || continue
29
+ grep -q "per-encoder avg Pearson" "$LOG" 2>/dev/null && continue # ์ด๋ฏธ ์™„๋ฃŒ
30
+ EMB="/dev/shm/hest_emb_$N"
31
+ rm -rf "$EMB"
32
+ echo "[watch] $(date '+%F %T') probing training_$N ..."
33
+ HEST_EMBED_ROOT="$EMB" HEST_BENCH_ROOT="$BENCH" HDF5_USE_FILE_LOCKING=FALSE \
34
+ CUDA_VISIBLE_DEVICES=$GPU PYTHONPATH=OpenMidnight:. \
35
+ venv_hest/bin/python -u scripts/run_hest_3way.py --backbone om --weights "$W" --exp-code "run_$N" > "$LOG" 2>&1
36
+ rm -rf "$EMB"
37
+ ce=$(grep "per-encoder avg Pearson" "$LOG" 2>/dev/null | tail -1 | grep -oE "'custom_encoder': [0-9.]+" | grep -oE "[0-9.]+")
38
+ rn=$(grep "per-encoder avg Pearson" "$LOG" 2>/dev/null | tail -1 | grep -oE "'resnet50': [0-9.]+" | grep -oE "[0-9.]+")
39
+ printf "%s\tours=%s\tresnet50=%s\t%s\n" "training_$N" "${ce:-FAIL}" "${rn:-NA}" "$(date '+%F %T')" >> "$CURVE"
40
+ echo "[watch] training_$N = ${ce:-FAIL}"
41
+ done
42
+ sleep 120
43
+ done