# Preflight — cái gì đã được chứng minh bằng mã chạy được

Ghi ngày 09/09/2026. Bạn đọc của tài liệu này là chính mình ba tuần nữa, lúc đang nghi ngờ một con số.

Mục 4 của `01-ba-quyet-dinh-thiet-ke.md` liệt kê năm cái bẫy, đọc ra từ mã nguồn. Tài liệu này **chạy thật** từng cái. Mọi con số dưới đây là đầu ra của `verify_traps.py` và `verify_mel_and_metrics.py`, chạy trên chính mã nguồn tải từ repo `LudovicTuncay/Audio-JEPA`, không phải suy luận.

Notebook chạy các cổng này trên Colab với checkpoint thật: `notebook/audio_jepa_v41_preflight_tien_huan_luyen.ipynb`.

---

## Bẫy 1 — `pos_embed` sai lưới đi lọt im lặng: **XÁC NHẬN**

```
shape lưới 16x8 : (128, 768)
shape lưới 32x4 : (128, 768)        -> BẰNG NHAU
max|hiệu|       : 2,0
cos trung bình từng token : 0,8131
số token khác nhau quá 1e-6 : 124 / 128
nạp pos_embed lưới 16x8 vào mô hình lưới 32x4 -> TRẢ VỀ NGUYÊN XI (không nội suy)
```

Hai lưới cho hai mã hoá vị trí **khác nhau thật sự** — 124/128 token khác, cos chỉ 0,81 — nhưng cùng shape, nên `load_state_dict` không kêu và `interpolate_pos_encoding` trả về ngay ở dòng `if npatch_h*npatch_w == N`.

Đường ống phát hiện hiện tại **không dính**: `build_encoder` trong v38/v40 đã có `sd.pop("pos_embed", None)` và assert `miss == ["pos_embed"]`. Bẫy chỉ mở ra ở nhánh này, vì đây là nhánh đầu tiên nạp cả ba nhánh trọng số — và `predictor_pos_embed` chưa từng có ai `pop`.

## Bẫy 2 — `MelSpecTransform` không nhận `f_max`: **XÁC NHẬN, nhưng phạm vi hẹp hơn tôi tưởng**

```
AttributeError: 'MelSpecTransform' object has no attribute 'f_max'
```

Đúng như đọc: nhánh `else` không tồn tại nên truyền `f_max` vào là lỗi.

**Nhưng v38/v40 gọi thẳng `torchaudio.compliance.kaldi.fbank` với `high_freq=F_MAX`, không dùng lớp này.** Nên bẫy chỉ bật nếu nhánh tiền huấn luyện quay sang dùng data module của repo — đúng là chuyện dễ xảy ra, vì repo có sẵn `AudioSetDataModule` và ta sẽ cần một cái tương tự cho kho tiếng nói. Giữ cảnh báo, hạ mức: nó không đe doạ mã hiện có.

## Bẫy 3 — learning rate thật là 1e-3: **XÁC NHẬN**

```
lr khai trong config AdamW : 0,0003
bước     0  lr = 1,000e-06
bước   500  lr = 5,005e-04
bước  1000  lr = 1,000e-03
bước  5000  lr = 8,946e-04
ĐỈNH THẬT  = 1,000e-03      -> gấp 3,33 lần con số trong config
```

`WarmupCosineScheduler.__init__` ghi `group['initial_lr'] = ref_lr` rồi hâm nóng lên `ref_lr = 1e-3`. Giá trị `lr: 0.0003` trong `configs/model/jepa.yaml` không bao giờ có hiệu lực.

Với `ref_lr = 1e-4` đã chốt, đo lại cho đỉnh đúng `1,000e-04`. Ô 3 của notebook v41 in cả hai để so.

## Bẫy 4 — weight decay thật là 1e-6: **XÁC NHẬN**

```
wd khai trong config : 0,05
wd sau 50 bước       : 1e-06
```

## Bẫy 5 — mặt nạ rắc ngẫu nhiên để lộ hàng xóm: **XÁC NHẬN, và định lượng được**

Đo trên 400 lượt lấy mẫu mỗi ô. "Còn hàng xóm" = token đích có ít nhất một trong bốn ô kề (trên, dưới, trái, phải) nằm trong tập ngữ cảnh.

| lưới | tỉ lệ che | còn ≥1 hàng xóm | trung bình % hàng xóm nhìn thấy |
|---|---|---|---|
| 32×4 *(phát hiện)* | 0,4 | **95,2 %** | 59,8 % |
| 32×4 | 0,5 | **90,6 %** | 50,4 % |
| 32×4 | 0,6 | **82,5 %** | 40,2 % |
| 16×8 *(gốc)* | 0,5 | 92,0 % | 50,6 % |
| 32×4, **khối liền mạch** | 0,5 | **11,7 %** | 3,4 % |

Chín trên mười token đích còn nhìn thấy hàng xóm trực tiếp. Che theo khối liền mạch ở cùng tỉ lệ đưa con số đó xuống 11,7 % — **kém tám lần**, tức nhiệm vụ khó hơn hẳn.

Đây là bằng chứng định lượng cho giả thuyết ở mục 4 của tài liệu quyết định: nhiệm vụ tiền huấn luyện của bản cài đặt này dễ, nên biểu diễn học được mang ít thông tin, nên encoder đóng băng gần bằng khởi tạo ngẫu nhiên. **Vẫn là biến khác — không sửa trong nhánh này.**

---

## Loss giảm khi sụp đổ: **XÁC NHẬN**

`Loss(loss_type='norm_mse', norm_pix_loss=True)`, lấy nguyên từ repo:

| tình huống | loss |
|---|---|
| dự đoán ngẫu nhiên vs đích ngẫu nhiên | **1,9999** |
| mọi token cùng một hướng *(sụp đổ hoàn toàn)* | **0,0014** |
| dự đoán = trung bình của đích | 1,8249 |

Sụp đổ hoàn toàn cho loss gần **bằng không**. Một lượt chạy hỏng có đường loss đẹp hơn một lượt chạy khoẻ. Không dùng loss để canh — đây là lý do tồn tại của bốn chỉ số.

---

## Bốn chỉ số sụp đổ: đã sửa một lỗ hổng

Chạy `collapse_metrics.py` trên tensor tổng hợp, `B=64, N=128, D=768`:

| trường hợp | `std_token` | `rank_eff` | `rank_raw` | `cos_cross` | `std_utt` | cổng |
|---|---|---|---|---|---|---|
| khoẻ (ngẫu nhiên đầy đủ hạng) | 0,0361 | 758,8 | 758,8 | 0,0001 | 0,0360 | đi tiếp |
| sụp đổ hoàn toàn (một hướng) | 0,0000 | **758,8** | 1,3 | 1,0000 | 0,0000 | dừng |
| sụp đổ một phần (hạng 8) | 0,0350 | **8,0** | 8,0 | −0,0026 | 0,0350 | dừng |
| mọi utterance giống nhau | 0,0236 | 159,8 | 136,6 | 0,5695 | **0,0001** | dừng |

**Lỗ hổng đã tìm ra khi chạy thật:** `rank_eff` tính trên ma trận **đã trừ trung bình** — đúng định nghĩa hạng hiệu dụng — **không bắt được sụp đổ hoàn toàn**. Nó vẫn cho 758,8, vì sau khi trừ trung bình chỉ còn nhiễu, mà nhiễu thì đầy hạng. Mô tả "→ 1 khi sụp đổ" trong bản đầu của mục 5.3 là **sai**.

Sửa: thêm `rank_raw` — hạng hiệu dụng của ma trận **đã L2-chuẩn hoá nhưng không trừ trung bình**. Nó cho 1,3 ở ô sụp đổ hoàn toàn.

Điều bảng này chứng minh, và là lý do phải giữ cả năm cột:

- sụp đổ hoàn toàn: `std_token`, `cos_cross`, `std_utt`, `rank_raw` bắt được — `rank_eff` mù;
- sụp đổ một phần (hạng thấp): **chỉ** `rank_eff` và `rank_raw` bắt được — ba chỉ số kia trông y như bình thường;
- mọi utterance giống nhau: `std_utt` bắt rõ nhất (0,0001 so với 0,0360), trong khi `std_token` vẫn 0,0236.

Bỏ bất kỳ chỉ số nào là mù một chế độ hỏng. Mốc lý thuyết trải đều trên mặt cầu `D=768` là `1/sqrt(768) = 0,0361` — trùng khít với ô khoẻ, nên chỉ số đọc đúng thang.

---

## Quy cách mel: số khung và phần đệm

| | hop | cửa sổ | kaldi trả | sau khi đệm | số hàng toàn 0 |
|---|---|---|---|---|---|
| gốc, 10 s | 39,0625 ms | 97,6562 ms | 254 | 256 | 2 |
| phát hiện, 2,56 s | 10 ms | 25 ms | 254 | 256 | 2 |

Cả hai quy cách đều cho đúng 254 khung rồi đệm hai hàng zero. Trong log-fbank, 0 không phải im lặng — nó là một giá trị hằng. Với patch cao 8 khung, hai hàng đó nằm trong 4 token cuối của lưới 32×4 và chiếm 2/8 số hàng của chúng: bốn token dự đoán được gần như miễn phí. Nhỏ, và có sẵn từ bản gốc — chỉ cần biết khi đọc loss.

---

## Còn lại phải đo trên Colab

Bốn thứ không kiểm được ngoài máy có GPU và checkpoint thật. Chúng là ô 5b–7 của notebook v41:

1. **Mốc bốn chỉ số trên `JEPA.ckpt` thật**, với quy cách mel của nhánh phát hiện, trên batch 64 clip cố định. Không có nó thì `rank_eff = 41` không nói lên điều gì.
2. **Thông lượng thật.** Con số 9,4 giờ/lượt là ngoại suy từ chi phí fine-tune, chưa ai đo bước tiền huấn luyện thật. Ô 6 đo và tính lại ngân sách.
3. **Vòng lưu/khôi phục Drive.** Colab đã đứt ba đêm liên tiếp.
4. **Cổng `aasist`** trên các tập sẽ chấm — 0,83 · 39,05 · 37,95 — như mọi lần.

Và một việc không nằm trong notebook: **seed 2 và 3 cho khởi tạo ngẫu nhiên fine-tune**. Mốc so sánh của cả nhánh hiện mới n=1 (13,68). Không có nó thì cả 33 giờ không có gì để so.

---

# Lượt chạy thật trên Colab — 09/09/2026

Chạy `audio_jepa_v41_preflight_tien_huan_luyen.ipynb` trên T4 RAM cao, Colab Pro. **G1–G5 xanh, G6 chết.** Nguyên nhân G6 chết **không phải tràn RAM** — xem mục cuối.

## Cổng nào xanh

| cổng | kết quả trên máy thật | khớp với đo ngoại tuyến? |
|---|---|---|
| G1 | `pos_embed` max\|hiệu\| **2,000**, cos **0,813**; nạp sai lưới cho `missing=[]`, `unexpected=[]`; encoder trùng từng bit v38/v40 trên 149 tensor | khớp tuyệt đối |
| G2 | đỉnh lr repo **1,000e-03** (gấp 3,33 lần config), của ta **1,000e-04**; wd 0,05 → **1e-06** | khớp tuyệt đối |
| G3 | mel (4,1,256,128), kaldi 254 khung → đệm 256, **2 hàng toàn 0**; rắc ngẫu nhiên **89,9 %** token đích còn hàng xóm | khớp |
| G4 | chạy được, mốc đã lưu Drive — **nhưng bộ số là một phát hiện, xem dưới** | — |
| G5 | một bước thật chạy được, gradient chảy — **nhưng chậm gấp rưỡi dự tính** | lệch |

Ba cái bẫy đã tái lập nguyên vẹn trên checkpoint thật. Không có gì phải sửa ở G1–G3.

## Phát hiện 1 — checkpoint gốc đã dị hướng nặng

Mốc "khoẻ mạnh" đo trên chính `JEPA.ckpt`, 64 clip VoxPopuli, quy cách mel của nhánh phát hiện:

| | `std_token` | `rank_eff` | `rank_raw` | `cos_cross` | `std_utt` |
|---|---|---|---|---|---|
| encoder | 0,0150 | 247,9 | 178,5 | **0,7192** | **0,0060** |
| target_encoder | 0,0149 | 248,6 | 179,0 | 0,7207 | 0,0060 |
| *trải đều trên mặt cầu D=768* | *0,0361* | *~768* | *~768* | *~0* | *0,0361* |

Đọc kỹ hàng cuối. Biểu diễn của checkpoint **không** giống một biểu diễn khoẻ mạnh:

- **`cos_cross` = 0,72.** Hai token lấy từ **hai utterance khác nhau** vẫn có cos 0,72. Biểu diễn bị chi phối bởi một hướng chung.
- **`std_utt` = 0,0060**, bằng 17 % mốc trải đều. Các utterance khác nhau cho embedding gần như giống nhau.
- `rank_eff` 248 và `rank_raw` 179 trên 768 — dùng khoảng một phần tư số chiều.

**Hệ quả 1: giải thích được `loss = 0,1325` ở bước đầu.** Con số này thấp bất thường với một mô hình vừa bị đổi quy cách mel *và* đổi mã hoá vị trí. Nhưng nếu mọi token đã hướng về một chỗ thì dự đoán token bị che là chuyện dễ. Cộng với bẫy 5 (89,9 % token đích còn nhìn thấy hàng xóm), mục tiêu JEPA ở đây gần như đã bão hoà.

**Hệ quả 2: đây là số đo trực tiếp đầu tiên cho quan sát khó chịu nhất của dự án** — mục 3 của bàn giao, encoder đóng băng có tiền huấn luyện gần bằng khởi tạo ngẫu nhiên (DFADD 8,47 so với 8,66; ASVspoof19 17,70 thua 14,25). Cho tới giờ điều đó chỉ suy ra được từ EER xuôi dòng. Giờ đã nhìn thấy ở tầng biểu diễn: **biểu diễn dị hướng và hạng thấp thì mang ít thông tin để mà đóng băng.**

**Nhưng phải loại một khả năng trước khi viết bất cứ điều gì.** Số trên đo bằng quy cách mel của nhánh phát hiện — thứ mà checkpoint chưa từng thấy. Dị hướng có thể do chính việc đổi quy cách gây ra. Ô **G4b** (trong `v41_o_thay_the.py`) đo lại cùng chỉ số bằng đúng cấu hình mà checkpoint được huấn luyện: clip 10 s, hop 39,06 ms, cửa sổ 97,66 ms, f_max 16 kHz, patch (16,16), `pos_embed` gốc.

- `cos_cross` vẫn ~0,7 → dị hướng thuộc về checkpoint. **Đây là kết quả cho bài.**
- `cos_cross` tụt hẳn → do ta đổi quy cách mel. Phải viết rõ, và mọi ngưỡng cổng lấy theo mốc quy cách phát hiện.

Chưa chạy G4b thì chưa được kết luận gì.

**Hệ quả 3: ngưỡng cổng đang lỏng hơn dự định.** Cổng đặt theo tỉ lệ so với mốc, mà mốc đã thấp: `std_utt` < 25 % của 0,0060 nghĩa là 0,0015 — gần như không bao giờ chạm. Cổng `cos_cross > 0,9` chỉ cách mốc 0,72 có 0,18. Sau khi có G4b thì đặt lại ngưỡng theo số thật.

## Phát hiện 2 — chậm gấp rưỡi dự tính

```
bước tiền huấn luyện :  640,0 ms/bước  = 20,00 ms/mẫu (batch 32)
dựng mel             :    1,62 ms/mẫu
-> một lượt 2 812 500 mẫu = 15,6 giờ GPU     (tài liệu ước 9,4 h)
```

Ước lượng 9,4 h là ngoại suy từ chi phí fine-tune với hệ số 1,5–2×. Hệ số thật là **2,9×**. Ngân sách hai nhánh N + T lên **42,2 h** thay vì 33,7 h; thêm mốc 300 h thành 63,4 h.

Tin tốt kèm theo: **dựng mel chỉ 1,62 ms/mẫu**, nhanh gấp 12 lần bước GPU. Kết luận "tính mel tại chỗ, không cache" ở mục 2.5 của tài liệu quyết định **đúng, và còn thoải mái hơn tính toán** — cần 0,08 worker, Colab có 8 vCPU. Trần đĩa cho đường cong quy mô coi như biến mất.

640 ms/bước là **fp32**. T4 có tensor core fp16. Ô **G5b** đo lại với AMP và với batch 64 trước khi chốt ngân sách — nếu AMP cho 2× thì 15,6 h về ~8 h và kế hoạch cũ còn nguyên.

## Vì sao G6 chết — **không phải tràn RAM**

Tài nguyên lúc chết:

| | |
|---|---|
| RAM hệ thống Colab | **8,3 / 51,0 GB** |
| RAM GPU | 4,9 / 15,0 GB |
| Đĩa Colab | 49,4 / 235,7 GB |
| **Google Drive** | **14,99 / 15 GB — 99 %** |

Thông báo của Colab là *"Đã vượt quá hạn mức bộ nhớ Google Drive"*. Chữ **"bộ nhớ"** ở đây là **dung lượng lưu trữ Drive**, không phải RAM. Nâng RAM Colab không liên quan gì — và đó là lý do lần đầu nâng RAM thì chạy tiếp được: các ô G2–G5 không ghi Drive, chỉ G6 ghi.

Ô 7 cũ `torch.save` khoảng **1,65 GB** (encoder + target + predictor + trạng thái AdamW) thẳng vào `MyDrive/jepa_spoof/`. Drive còn 0 GB. Chết.

**Lỗi thứ hai trong ô 7 cũ, độc lập với Drive:** `o2` được dựng chỉ trên tham số encoder rồi `load_state_dict(blob["opt"])`, trong khi `opt` bao **cả encoder lẫn predictor**. Đã kiểm: PyTorch ném `ValueError: loaded state dict contains a parameter group that doesn't match the size of optimizer's group`. Ô 7 sẽ hỏng kể cả khi Drive trống. Bản mới dựng optimizer trên đúng `encoder + predictor`.

## Hệ quả lớn hơn — kế hoạch lưu trữ không khả thi

Quy tắc "**lưu checkpoint lên Drive sau mỗi epoch**" ở mục 4 của bàn giao, với 1,65 GB một bản và 20 epoch, là **33 GB một nhánh**. Drive có 15 GB và đang dùng 14,99. Kế hoạch này chưa bao giờ khả thi, chỉ là chưa ai nhân ra.

Thay bằng:

| | dung lượng |
|---|---|
| `pre_last.pt` + `pre_prev.pt`, luân phiên, ghi đè mỗi epoch | 3,3 GB |
| ba mốc epoch 5/10/20, **chỉ `encoder`+`target_encoder`, fp16** | 1,0 GB |
| **một nhánh** | **4,3 GB** |
| **hai nhánh N và T** | **8,6 GB** |

Vẫn không vừa Drive hiện tại. Ba lối ra, theo thứ tự nên chọn:

1. **Đẩy checkpoint lên một repo riêng tư trên Hugging Face.** Dự án đã dùng HF cho dữ liệu và cho chính `JEPA.ckpt`; `huggingface_hub.upload_file` chạy thẳng từ Colab, nhanh hơn Drive và không đụng vào hạn mức 15 GB của Google. Đây là lời giải sạch nhất.
2. **Đĩa cục bộ Colab cho bản luân phiên** (186 GB trống, ghi 300 MB/s) **+ đẩy mốc lên HF mỗi 5 epoch.** Đĩa cục bộ mất khi phiên chết — nhưng bản trên HF thì không, và đó mới là thứ chống đứt.
3. **Dọn Drive.** Ứng viên rõ ràng: `p432_s1/s2/s3` (patch (4,32) — hướng đã loại, v40), `jep_ep2/ep3/ep4` và `fix_e6/e2/frz` (quy cách mel gốc — hướng đã loại). Khoảng 3 GB. *Không xoá gì trước khi đã chắc `res_*.npz` tương ứng còn giữ được kết quả.*

Ô 7 mới ghi ra đĩa cục bộ và **chỉ thăm dò Drive bằng một tệp 8 MB**, rồi in ngân sách lưu trữ thật.

## Việc tiếp theo

1. Dán ba ô trong `v41_o_thay_the.py` vào phiên đang chạy — **không cần khởi động lại**, G1–G5 còn nguyên trong bộ nhớ.
2. Chạy **G4b** trước: nó quyết định dị hướng là kết quả hay là tạo tác của ta.
3. Chạy **G5b**: AMP quyết định ngân sách là 42 h hay ~25 h.
4. Chạy **ô 7 mới**: đóng G6 và ra ngân sách lưu trữ.
5. Chọn nơi lưu checkpoint (HF là khuyến nghị) trước khi bắt đầu lượt thật.

---

# Lượt hai — sau khi vá, 09/09/2026 lúc 18:03

Chạy `v41_o_thay_the.py` vào chính phiên đang mở, không khởi động lại. **Bảy cổng xanh hết.**

## G4b — dị hướng là **thuộc tính của checkpoint**, không phải do ta

Cùng 64 clip VoxPopuli, hai đường đo. Hàng "gốc" dùng **đúng cấu hình mà checkpoint được huấn luyện**: clip 10 s, hop 39,06 ms, cửa sổ 97,66 ms, f_max 16 kHz, patch (16,16), `pos_embed` nạp nguyên từ checkpoint (ở đó nó đúng).

| | `std_token` | `rank_eff` | `rank_raw` | `cos_cross` | `std_utt` |
|---|---|---|---|---|---|
| **quy cách gốc** *(10 s, patch 16×16)* | 0,0159 | 263,1 | 199,0 | **0,6793** | 0,0061 |
| **quy cách phát hiện** *(2,56 s, patch 8×32)* | 0,0150 | 247,9 | 178,5 | **0,7192** | 0,0060 |
| *trải đều trên mặt cầu D=768* | *0,0361* | *~768* | *~768* | *~0* | *0,0361* |

**Δcos_cross = −0,040.** Hai cấu hình cho gần như cùng một bộ số. Việc ta đổi quy cách mel và sinh lại mã hoá vị trí **không** tạo ra dị hướng; nó có sẵn.

Nghĩa là: ngay ở cấu hình mà tác giả huấn luyện, biểu diễn của Audio-JEPA trên tiếng nói có `cos_cross` **0,68** giữa các token của **hai utterance khác nhau**, và `std_utt` bằng 17 % mức trải đều. Đây là **số đo trực tiếp** cho quan sát ở mục 3 của bàn giao — encoder đóng băng gần bằng khởi tạo ngẫu nhiên — và giải thích luôn `loss = 0,13` ở bước đầu: mọi thứ đã hướng về một chỗ thì dự đoán token bị che là chuyện dễ.

Kết quả này dùng được cho bài, và nó độc lập với chuyện tiền huấn luyện tiếng nói có ăn hay không.

*Một hệ quả về ngưỡng:* cổng đặt theo tỉ lệ so với mốc, mà mốc đã thấp — `std_utt` < 25 % của 0,0060 là 0,0015, gần như không bao giờ chạm. Nên đặt lại theo giá trị tuyệt đối khi vào lượt thật.

## G5b — AMP fp16 đảo ngược bài toán ngân sách

| chế độ | batch | ms/bước | ms/mẫu | giờ/lượt | loss |
|---|---|---|---|---|---|
| fp32 | 32 | 815,0 | 25,47 | 19,9 | 0,0989 |
| **amp16** | 32 | 299,1 | 9,35 | 7,3 | 0,0842 |
| **amp16** | **64** | **480,1** | **7,50** | **5,9** | 0,0732 |

**AMP fp16 nhanh hơn 3,39 lần.** Một lượt 2,81 M mẫu còn **5,9 giờ** ở batch 64 — *thấp hơn cả con số 9,4 h ước ban đầu*. Hai nhánh N + T còn **22,7 h** thay vì 50,8 h.

Hai điều phải ghi kèm, đừng bỏ:

- **fp32 đo được 815 ms/bước ở lượt này, 640 ms ở lượt trước** — cùng máy, cùng mã, lệch 27 %. Đo thời gian trên T4 của Colab nhiễu cỡ đó. Lấy số AMP kèm biên an toàn, đừng lập kế hoạch sát nút.
- **Cột `loss` KHÔNG phải phép kiểm AMP đúng hay sai.** Hàm bench vẫn `opt.step()` thật, nên mô hình học tiếp qua từng lượt đo và loss giảm dần vì lý do đó, không phải vì AMP. Muốn kiểm AMP đúng thì phải đo hai lượt từ **cùng một trạng thái trọng số** rồi so. Việc này còn nợ.

## G6 — xanh, và con số lưu trữ

```
lưu 1500 MB ra đĩa cục bộ trong 5,0s -> 302 MB/s
khôi phục: encoder trùng từng bit trên 149 tensor; optimizer 227 tensor có moment
Drive: DÙNG ĐƯỢC — ghi/đọc/xoá 8 MB OK
chỗ trống: đĩa Colab 185 GB | mount Drive 0,0 GB
```

Drive nhận được tệp 8 MB nhưng còn **0,0 GB** — nó không nhận nổi 1,5 GB. Đó chính xác là lý do ô 7 cũ chết.

| | |
|---|---|
| lưu mỗi epoch × 20 epoch, mỗi bản 1 500 MB | **30,0 GB** — kế hoạch cũ |
| luân phiên 2 bản + 3 mốc enc/tgt fp16 | **3,9 GB** |
| hai nhánh N và T | **7,9 GB** |

Lỗi optimizer cũng đã hết: `opt2` dựng trên `encoder + predictor` khôi phục được 227 tensor moment.

## Ngân sách sau khi đo lại tất cả

| # | việc | giờ (AMP b64) |
|---|---|---|
| 0 | mốc sụp đổ | 0 — xong |
| 1 | rand fine-tune seed 2, 3 | 1,5 |
| 2 | lượt chạy thử 5 epoch | 1,5 |
| 3 | nhánh N: 5,9 tiền huấn luyện + 1,5 fine-tune + 4 chấm | 11,4 |
| 4 | nhánh T | 11,4 |
| | **tổng** | **25,8 h** ≈ 48–52 đơn vị |
| 5 | *(có điều kiện)* mốc 300 h | +11,4 |

Tài khoản còn 189,7 đơn vị. Ngân sách không còn là ràng buộc; **lưu trữ mới là**, và 7,9 GB vẫn chưa vừa Drive hiện tại.

## Còn nợ trước khi bắt đầu lượt thật

1. **Nơi lưu checkpoint.** Đĩa Colab 185 GB cho bản luân phiên là đủ và nhanh (302 MB/s), nhưng nó chết theo phiên — đúng thứ ta đang phòng. Cần một chỗ bền: repo riêng tư trên Hugging Face là lựa chọn sạch nhất, không đụng hạn mức 15 GB của Google.
2. **Kiểm AMP đúng đắn** từ cùng một trạng thái trọng số.
3. **Seed 2 và 3 cho khởi tạo ngẫu nhiên fine-tune** — vẫn là n=1, vẫn là mốc so sánh của cả nhánh.
4. Đặt lại **ngưỡng cổng sụp đổ theo giá trị tuyệt đối**, vì mốc thấp làm cổng tỉ lệ trở nên vô dụng.
