v8
Browse files
improve_gainlora/IDEA_Overall.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
-
# SpecRoute: Định tuyến Phổ
|
| 2 |
|
| 3 |
-
> **Tài liệu thiết kế chính thức —
|
| 4 |
-
> Ràng buộc: Zero-replay nghiêm ngặt. Theory-first.
|
| 5 |
|
| 6 |
---
|
| 7 |
|
|
@@ -66,6 +66,36 @@ trong đó $V_t$ là các right singular vectors của $\Delta W_t$. Tính trự
|
|
| 66 |
- Không cần replay để duy trì routing → tự nhiên tuân thủ zero-replay.
|
| 67 |
- Độ chính xác routing được *đảm bảo* bởi chất lượng bảo vệ (được hình thức hoá bên dưới).
|
| 68 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
---
|
| 70 |
|
| 71 |
## 3. Khung Lý thuyết
|
|
@@ -141,11 +171,11 @@ $$w_{t^*}(h) \;\geq\; \frac{1}{1 + (T{-}1)\,\exp\!\bigl(-m/\tau\bigr)}, \qquad m
|
|
| 141 |
|
| 142 |
---
|
| 143 |
|
| 144 |
-
**Hệ quả 2** *(Capacity Bound — Kết nối Grassmannian).*
|
| 145 |
|
| 146 |
-
$$T_{\max} \;\leq\; \frac{d}{
|
| 147 |
|
| 148 |
-
Với T5-
|
| 149 |
|
| 150 |
### 3.4 Cam kết Trực giao từ Kiến trúc InfLoRA
|
| 151 |
|
|
@@ -165,29 +195,110 @@ $$\text{span}(V_t) \;=\; \text{rowspace}(\Delta W_t) \;\subseteq\; \text{rowspac
|
|
| 165 |
- GPM bases $\mathcal{B}$ span xấp xỉ $\text{rowspace}(A_s)$ cho các task $s < t$ (vì GPM tích lũy principal input directions, mà activation của task $s$ chủ yếu kích hoạt theo hướng $A_s$).
|
| 166 |
- Do đó: $\text{span}(V_t) \subseteq \text{null}(P_{\text{old}}) \approx \perp \text{span}(V_s)$ với mọi $s < t$. $\square$
|
| 167 |
|
| 168 |
-
**Chất lượng xấp xỉ:** Với GPM threshold $\varepsilon_0 = 0.995$ (capture ≥ 99.5% variance), $\delta_{t,s} \leq
|
| 169 |
|
| 170 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 171 |
|
| 172 |
---
|
| 173 |
|
| 174 |
-
### 3.
|
| 175 |
|
| 176 |
-
**
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
-
|
| 179 |
|
| 180 |
-
|
|
|
|
| 181 |
|
| 182 |
-
|
| 183 |
|
| 184 |
-
**
|
|
|
|
| 185 |
|
| 186 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
-
**InfLoRA gốc vi phạm (A):** $A_t$ được khởi tạo ngẫu nhiên (Kaiming) rồi chiếu vào null-space của GPM:
|
| 189 |
|
| 190 |
-
$$A_t \leftarrow A_t - A_t P_{\text{old}}, \quad \text{normalize}$$
|
| 191 |
|
| 192 |
Null-space (sau $t-1$ tasks) là một không gian $d - N_{\text{protected}}$ chiều. Kaiming random trong không gian này KHÔNG ĐẢM BẢO alignment với các hướng có liên quan đến task $t$. Khi null-space thu hẹp dần (Layer 7: 8/512 → 161/512 → 344/512 qua 13 tasks), xác suất random init bắt được đúng hướng task-relevant giảm theo.
|
| 193 |
|
|
@@ -197,40 +308,47 @@ Null-space (sau $t-1$ tasks) là một không gian $d - N_{\text{protected}}$ ch
|
|
| 197 |
|
| 198 |
## 4. Các Thành phần Framework
|
| 199 |
|
| 200 |
-
### C1 — Spectral Expert Signatures
|
| 201 |
|
| 202 |
-
|
| 203 |
|
| 204 |
-
|
|
|
|
|
|
|
| 205 |
|
| 206 |
-
|
| 207 |
-
- Lưu trên mỗi LoRA layer (encoder Q, V; decoder self/cross Q, V).
|
| 208 |
-
- **Bất biến** theo cấu trúc: weights đóng băng → signatures đóng băng → không trôi dạt.
|
| 209 |
|
| 210 |
-
|
| 211 |
|
| 212 |
-
**
|
| 213 |
|
| 214 |
$$w(h) = \mathrm{softmax}\!\left(\frac{[\alpha_1(h),\; \ldots,\; \alpha_T(h)]}{\tau}\right)$$
|
| 215 |
|
| 216 |
-
|
| 217 |
|
| 218 |
-
**Training** (task $t$
|
| 219 |
|
| 220 |
$$\alpha_t^{\mathrm{train}}(h) = \frac{\|A_t\, h\|^2}{r\,\|h\|^2} + \beta(n), \qquad \beta(n) = \tau \cdot \ln\!\left(\frac{\alpha_{\mathrm{target}} \cdot n}{1 - \alpha_{\mathrm{target}}}\right)$$
|
| 221 |
|
| 222 |
trong đó $n = |\{\text{task cũ}\}|$ và $\alpha_{\mathrm{target}} \in (0,1)$ là routing weight mục tiêu cho task hiện tại (mặc định 0.8).
|
| 223 |
|
| 224 |
-
**Lý giải
|
| 225 |
|
| 226 |
-
**
|
|
|
|
|
|
|
|
|
|
| 227 |
|
| 228 |
-
**Lý giải
|
| 229 |
|
| 230 |
-
|
| 231 |
-
|
| 232 |
-
|
| 233 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 234 |
|
| 235 |
### C3 — Capacity-Aware Subspace Allocation
|
| 236 |
|
|
@@ -318,6 +436,10 @@ A_t = A_t / norm(A_t, dim=1, keepdim=True) * sqrt(3) # normalize như InfLoRA g
|
|
| 318 |
|
| 319 |
**Điều kiện fallback:** Nếu `max_eigenvalue(C_tilde) < 1e-6`, null-space quá hẹp hoặc activation không có signal đủ mạnh. Trong trường hợp này, C5 nhường cho Kaiming init + InfLoRA projection tiêu chuẩn — không làm tệ hơn V6, chỉ không cải thiện. Điều kiện này chỉ xảy ra khi null-space gần như bão hoà, tức là ESA đã tiêu thụ gần hết capacity.
|
| 320 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 321 |
#### Ý nghĩa Lý thuyết Thông tin
|
| 322 |
|
| 323 |
Theo Data Processing Inequality, với bất kỳ ma trận $A_t$ nào:
|
|
@@ -349,6 +471,8 @@ $C_t$ được tính từ **dữ liệu training của task hiện tại** (task
|
|
| 349 |
| `previous_trans_input` | Frozen MLP copies | ❌ Loại bỏ | Signatures bất biến theo cấu trúc |
|
| 350 |
| KL distillation | Replay-based routing loss | ❌ Loại bỏ | Không learned routing → không cần distill |
|
| 351 |
| GPM trên routing params | Subspace cho routing | ❌ Loại bỏ | Không có routing parameters để bảo vệ |
|
|
|
|
|
|
|
| 352 |
|
| 353 |
**Hiệu ứng tổng thể:** Toàn bộ subspace và compute budget mà GainLoRA dành cho routing infrastructure được *thu hồi* cho task learning.
|
| 354 |
|
|
@@ -371,7 +495,7 @@ $C_t$ được tính từ **dữ liệu training của task hiện tại** (task
|
|
| 371 |
**Đảm bảo lý thuyết:**
|
| 372 |
- Routing margin $\geq \kappa_{\min}(t^*) - \varepsilon\, \kappa_{\max}$ — tỷ lệ thuận với chất lượng bảo vệ (Định lý 1).
|
| 373 |
- Routing weight $w_{t^*} \geq 1-\delta$ với nhiệt độ $\tau \leq m/\ln\!\bigl((T-1)/\delta\bigr)$ (Hệ quả 1).
|
| 374 |
-
- Capacity bound $T_{\max} \leq d/
|
| 375 |
- Routing hoàn toàn bất biến qua thời gian: $h$ từ frozen embedding table, $\mathcal{S}_t$ đóng băng sau training (Mệnh đề 1).
|
| 376 |
|
| 377 |
**Tại sao không phải đơn giản hoá mà là tiến bộ lý thuyết:** Kết quả này cho thấy các kiến trúc như GainLoRA đang giải quyết một bài toán không tồn tại (routing parameter learning). Chúng tôi chứng minh rằng bảo vệ tốt ↔ routing tốt — hai bài toán hóa ra là *một*.
|
|
@@ -403,18 +527,18 @@ $A_t$ này đảm bảo capture **variance task-relevant tối đa** trong null-
|
|
| 403 |
### Task 1 (`--run_single True`)
|
| 404 |
1. Load pretrained model + fresh LoRA ($A$: Kaiming, $B$: zeros).
|
| 405 |
2. Huấn luyện chuẩn (chỉ `lora_B`) — single expert, không routing.
|
| 406 |
-
3. Sau training:
|
| 407 |
-
4. Lưu: LoRA weights,
|
| 408 |
|
| 409 |
### Task $t \geq 2$
|
| 410 |
-
1. Load model + fresh LoRA; load LoRA weights
|
| 411 |
2. **[C5 — MỚI]** Pre-task forward pass (200 batch, no grad):
|
| 412 |
- Thu thập activation covariance $C_t$ của inputs task $t$
|
| 413 |
- Tính projected covariance: $\tilde{C}_t = Q C_t Q$ ($Q = I - P_{\text{old}}$)
|
| 414 |
- Eigenvectors của $\tilde{C}_t$ → khởi tạo $A_t$ (thay thế random Kaiming)
|
| 415 |
3. InfLoRA: chuẩn hoá $A_t$ (đã nằm trong null-space từ eigenvector decomposition).
|
| 416 |
-
4. Huấn luyện `lora_B` với
|
| 417 |
-
5. Sau training:
|
| 418 |
6. Lưu tất cả artifacts cho task tiếp theo.
|
| 419 |
|
| 420 |
---
|
|
@@ -423,10 +547,10 @@ $A_t$ này đảm bảo capture **variance task-relevant tối đa** trong null-
|
|
| 423 |
|
| 424 |
| Lý thuyết | Implementation | File |
|
| 425 |
|-----------|---------------|------|
|
| 426 |
-
| Spectral signature $\mathcal{S}_t$ | `
|
| 427 |
-
| Spectral affinity $\alpha_t(h)$ (inference) |
|
| 428 |
| A-row proxy $\alpha_t^{\mathrm{train}}$ (training) | A-row fit + adaptive bias $\beta(n)$ | `compute_spectral_routing()` |
|
| 429 |
-
| Symmetric inference SVD | `prepare_inference_routing()` → SVD của $B_t A_t$ |
|
| 430 |
| Routing $w = \mathrm{softmax}(\alpha / \tau)$ | `torch.softmax(fit_scores / temp)` | `compute_spectral_routing()` |
|
| 431 |
| Drift-free input $h$ | `inputs_embeds = embed_tokens(input_ids)` → mean-pool | `T5Stack.forward()` |
|
| 432 |
| GPM + InfLoRA null-space | `get_reg_matrix()` | `cl_trainer_specroute.py` |
|
|
@@ -445,11 +569,12 @@ $A_t$ này đảm bảo capture **variance task-relevant tối đa** trong null-
|
|
| 445 |
| Benchmarks | SuperNI (15 tasks, 2 orderings), Long (15 tasks, 2 orderings) |
|
| 446 |
| Metrics | AP (Average Performance, ↑), FT (Forgetting, ↓) |
|
| 447 |
| LoRA | $r = 8$, target=Q+V, InfLoRA (chỉ B trained, A đóng băng) |
|
| 448 |
-
| Routing | $\tau = 1.0$, $\alpha_{\mathrm{target}} = 0.8$, adaptive $\beta(n)$ (train);
|
| 449 |
| ESA | $\varepsilon_0 = 0.995$ (dynamic) |
|
| 450 |
| C4 | Gradient preconditioning bật (`--use_preconditioning True`), $\epsilon = 10^{-6}$; entropy reg đã loại bỏ V7 |
|
| 451 |
| **C5** | **N_batch = 100, `torch.linalg.eigh` trên projected covariance, fallback nếu max_eigval < 1e-6** |
|
| 452 |
| GPM repr. | 200 batches (giảm từ 1000 — SVD ổn định sau 200) |
|
|
|
|
| 453 |
| Precision | fp32 + gradient_checkpointing (T5 + P100: fp16 có risk NaN overflow với large softmax) |
|
| 454 |
| P100 BSZ | BSZ=8, GA=4 (effective 32); T4: BSZ=2, GA=8 |
|
| 455 |
| Thời gian (P100 16GB) | SuperNI T5-Small ≈ 2-3h; Long benchmark ≈ 3-4h — thoải mái trong 12h Kaggle |
|
|
|
|
| 1 |
+
# SpecRoute: Định tuyến Phổ Dẫn dắt bởi Dữ liệu trong Học Liên tục với LoRA
|
| 2 |
|
| 3 |
+
> **Tài liệu thiết kế chính thức — V8**
|
| 4 |
+
> Ràng buộc: Zero-replay nghiêm ngặt. Theory-first. Tổng hợp sau V2–V7.
|
| 5 |
|
| 6 |
---
|
| 7 |
|
|
|
|
| 66 |
- Không cần replay để duy trì routing → tự nhiên tuân thủ zero-replay.
|
| 67 |
- Độ chính xác routing được *đảm bảo* bởi chất lượng bảo vệ (được hình thức hoá bên dưới).
|
| 68 |
|
| 69 |
+
### 2.3 GPM–Routing Paradox: Duality sụp đổ với A ngẫu nhiên
|
| 70 |
+
|
| 71 |
+
**Đây là phát hiện cốt lõi giải thích V2–V6 thất bại (AP ≈ 27–40).**
|
| 72 |
+
|
| 73 |
+
Routing đo $\alpha_t(h) \propto \|A_t h\|^2$. Để phân biệt task $t^*$ với $s$, cần $A_{t^*}$ align với input $h \sim p_{t^*}$.
|
| 74 |
+
|
| 75 |
+
**Nghịch lý:** InfLoRA khởi tạo $A_t$ **ngẫu nhiên** (Kaiming) rồi chiếu vào null-space. Kết quả:
|
| 76 |
+
|
| 77 |
+
1. $\text{rowspace}(B_tA_t) \subseteq \text{rowspace}(A_t)$ — rowspace không mở rộng qua phép nhân trái.
|
| 78 |
+
2. $\text{rowspace}(A_t) = r$ hướng **ngẫu nhiên** trong $d'$-chiều null-space (không encode task identity).
|
| 79 |
+
3. Với same-domain tasks (yelp/amazon/sst2/imdb): input $h$ có variance theo hướng CHUNG → signatures không phân biệt được.
|
| 80 |
+
|
| 81 |
+
**Empirical:** V6 IMDB (task 8): EM = 0.0 suốt 10 epochs dù training loss giảm — routing inference gán sai expert. V3 RTE, MNLI cũng tương tự. Root cause = không phải null-space exhaustion mà là **routing signal không có trong random $A_t$**.
|
| 82 |
+
|
| 83 |
+
### 2.4 Lối Thoát: Differential Projection via Data-Informed Init
|
| 84 |
+
|
| 85 |
+
**Lemma (sẽ chứng minh ở §3.5):** Do InfLoRA constraint $A_t P_{t-1} = 0$:
|
| 86 |
+
$$\|A_t h\|^2 \;=\; \|A_t Q_{t-1} h\|^2 \quad \forall h, \quad Q_{t-1} = I - P_{t-1}$$
|
| 87 |
+
|
| 88 |
+
Routing $\alpha_t$ **chỉ nhìn thành phần $Q_{t-1}h$** — phần của $h$ NGOÀI span tất cả task cũ. Với $h \sim p_s$ ($s < t$): GPM đã capture $\geq 99.5\%$ variance của $p_s$ → $\|Q_{t-1}h\|^2 \leq 0.005\,\mathrm{tr}(C_s)$ → $\alpha_t(h) \approx 0$ tự nhiên. **Đây là differential projection — task-discriminative theo thiết kế.**
|
| 89 |
+
|
| 90 |
+
**Vấn đề còn lại:** Random $A_t$ không align với hướng có variance cao nhất của task $t$ trong null-space → $\|A_t Q_{t-1}h\|^2$ nhỏ dù $\|Q_{t-1}h\|^2$ đáng kể.
|
| 91 |
+
|
| 92 |
+
**Giải pháp — C5:** Khởi tạo $A_t$ = top-$r$ eigenvectors của $\tilde{C}_t = Q_{t-1}C_tQ_{t-1}$. Khi đó:
|
| 93 |
+
$$E_{h \sim p_t}[\|A_t h\|^2] = \sum_{i=1}^r \lambda_i(\tilde{C}_t) \quad \textbf{— GIÁ TRỊ CỰC ĐẠI}$$
|
| 94 |
+
|
| 95 |
+
**C5 biến random routing key thành optimal routing key — giải quyết GPM–Routing Paradox.**
|
| 96 |
+
|
| 97 |
+
> **Kết nối C5 ↔ C2:** C5 không chỉ giúp learning (maximize captured variance trong null-space) mà **đồng thời** maximize routing signal. Một initialization, hai mục tiêu. Hai contribution C1/C2 và C5 là bất khả phân.
|
| 98 |
+
|
| 99 |
---
|
| 100 |
|
| 101 |
## 3. Khung Lý thuyết
|
|
|
|
| 171 |
|
| 172 |
---
|
| 173 |
|
| 174 |
+
**Hệ quả 2** *(Capacity Bound — Kết nối Grassmannian).* Gọi $k_t$ là **GPM effective rank** của task $t$ — số eigenvectors thực tế được GPM giữ lại (threshold 99.5%). Số lượng tasks tối đa trước khi null-space sụp đổ:
|
| 175 |
|
| 176 |
+
$$T_{\max} \;\leq\; \frac{d}{\bar{k}\,(1 - \varepsilon)}, \qquad \bar{k} = \frac{1}{T}\sum_{t=1}^T k_t$$
|
| 177 |
|
| 178 |
+
**Lưu ý quan trọng:** $\bar{k}$ thực tế $\gg r$. Với T5-small ($d=512$) và NLP tasks phong phú, GPM giữ $k_t \approx 30\text{–}80$ dims/task để đạt 99.5% variance (không phải $r=8$). Ước tính thực tế: 15 tasks × 50 dims = 750 $> d$ → **null-space bão hòa là rủi ro thực**, không phải lý thuyết. Đây là lý do §3.10 thảo luận null-space collapse riêng. Bound Grassmannian vẫn đúng về *số lượng subspace $r$-chiều có thể pack*, nhưng capacity constraint của GPM là $\sum_t k_t \leq d$, chặt hơn $\sum_t r \leq d$.
|
| 179 |
|
| 180 |
### 3.4 Cam kết Trực giao từ Kiến trúc InfLoRA
|
| 181 |
|
|
|
|
| 195 |
- GPM bases $\mathcal{B}$ span xấp xỉ $\text{rowspace}(A_s)$ cho các task $s < t$ (vì GPM tích lũy principal input directions, mà activation của task $s$ chủ yếu kích hoạt theo hướng $A_s$).
|
| 196 |
- Do đó: $\text{span}(V_t) \subseteq \text{null}(P_{\text{old}}) \approx \perp \text{span}(V_s)$ với mọi $s < t$. $\square$
|
| 197 |
|
| 198 |
+
**Chất lượng xấp xỉ:** Với GPM threshold $\varepsilon_0 = 0.995$ (capture ≥ 99.5% variance), $\delta_{t,s} \leq 0.005 \ll \kappa_{\min}(t^*)$ trong thực tế.
|
| 199 |
|
| 200 |
+
|
| 201 |
+
**Phân tích độ nhạy GPM (Davis–Kahan perturbation).** GPM trong thực tế chỉ capture *xấp xỉ* principal directions nên có sai số $\Delta P_{t-1}$ so với GPM lý tưởng. Theo Davis–Kahan theorem, perturbation trong subspace angle bị chặn bởi:
|
| 202 |
+
$$\sin\!\bigl(\Theta(\hat{V}_s, V_s)\bigr) \leq \frac{\|\hat{C}_s - C_s\|_2}{\delta_{\text{gap}}(C_s)}$$
|
| 203 |
+
trong đó $\hat{C}_s$ là sample covariance (200 batches), $\delta_{\text{gap}}$ là eigenvalue gap. Với batch size 200 và T5-small $d=512$, sai số này nhỏ khi $\delta_{\text{gap}}$ đủ lớn (task distributions phân kỳ). Kết quả thực tế: margin trong Định lý 1 bị giảm thêm $O(\|\Delta P\|_F / \delta_{\text{gap}})$ — nhỏ với tasks có spectrum phân kỳ, lớn hơn với same-domain tasks (expected). Xẩy ra same-domain routing failure vẫn là giới hạn của *mọi* zero-replay CL method, không phải đặc thù SpecRoute.
|
| 204 |
+
|
| 205 |
+
### 3.5 Lemma về Differential Projection
|
| 206 |
+
|
| 207 |
+
**Lemma 1** *(Differential Projection — Exact).* Với $A_t$ thoả mãn InfLoRA constraint $A_t P_{t-1} = 0$, với **mọi** $h \in \mathbb{R}^d$:
|
| 208 |
+
$$\|A_t h\|^2 \;=\; \|A_t Q_{t-1} h\|^2, \quad Q_{t-1} = I - P_{t-1}$$
|
| 209 |
+
|
| 210 |
+
**Chứng minh.** Viết $h = P_{t-1} h + Q_{t-1} h$. Vì $A_t P_{t-1} = 0$ (các hàng $A_t$ trực giao với colspace của $P_{t-1}$):
|
| 211 |
+
$$A_t h = A_t P_{t-1} h + A_t Q_{t-1} h = 0 + A_t Q_{t-1} h \qquad \square$$
|
| 212 |
+
|
| 213 |
+
**Hệ quả A (Current expert trên old data — from Lemma 1):** Với $h \sim p_s$ ($s < t$):
|
| 214 |
+
$$E_{h \sim p_s}\!\left[\alpha_t(h)\right] = \frac{E[\|A_t Q_{t-1} h\|^2]}{r\,\|h\|^2} \leq \frac{\mathrm{tr}(Q_{t-1} C_s)}{r} \leq \frac{(1-\tau_\text{GPM})\,\mathrm{tr}(C_s)}{r} \leq \frac{0.005\,\mathrm{tr}(C_s)}{r}$$
|
| 215 |
+
|
| 216 |
+
**Hệ quả B (Old expert trên new data — GPM-capture argument):** Với $h \sim p_t$ ($t > s$). Vì GPM sau task $s$ tích lũy principal directions của task $s$'s activations, trong đó $A_s$'s rowspace (= top-$r$ directions của $\tilde{C}_s$) được capture vào $P_s \subseteq P_{t-1}$. Do đó:
|
| 217 |
+
$$\text{rowspace}(A_s) \subseteq \text{range}(P_{t-1}) \quad\Rightarrow\quad A_s Q_{t-1} = 0$$
|
| 218 |
+
Suy ra $A_s h_t = A_s P_{t-1} h_t$ và:
|
| 219 |
+
$$E_{h \sim p_t}[\alpha_s(h)] = \frac{E[\|A_s P_{t-1} h_t\|^2]}{r\,\|h_t\|^2} \leq \frac{\mathrm{tr}(P_{t-1} C_t)}{r\,\mathrm{tr}(C_t)} \cdot \frac{\mathrm{tr}(C_t)}{r}$$
|
| 220 |
+
Với task $t$ có domain mới (khác task cũ): $\mathrm{tr}(P_{t-1} C_t) / \mathrm{tr}(C_t) = \text{PEV}_{t,\text{old}} \ll 1$ — fraction variance của task $t$ được giải thích bởi các cơ sở cũ. Với same-domain tasks: $\text{PEV}_{t,\text{old}}$ lớn hơn, đây là giới hạn cơ bản của mọi zero-replay CL method, không phải lỗ hổng đặc thù của SpecRoute.
|
| 221 |
|
| 222 |
---
|
| 223 |
|
| 224 |
+
### 3.6 Định lý C5 Routing Optimality (Đóng góp Lý thuyết Chính)
|
| 225 |
|
| 226 |
+
**Định nghĩa 4** *(Restricted Stiefel Manifold).*
|
| 227 |
+
$$\mathcal{A}_t = \{A \in \mathbb{R}^{r \times d} : A P_{t-1} = 0,\; A A^\top = I_r\}$$
|
| 228 |
+
|
| 229 |
+
**Định lý 2** *(C5 Routing Optimality).* Với $C_t = E_{h \sim p_t}[hh^\top]$ và $\tilde{C}_t = Q_{t-1} C_t Q_{t-1}$:
|
| 230 |
+
$$\operatorname{argmax}_{A_t \in \mathcal{A}_t} E_{h \sim p_t}[\alpha_t(h)] \;=\; \text{top-}r\text{ eigenvectors của } \tilde{C}_t$$
|
| 231 |
+
Giá trị cực đại: $\displaystyle\frac{1}{r}\sum_{i=1}^r \lambda_i(\tilde{C}_t)$
|
| 232 |
+
|
| 233 |
+
**Chứng minh.** Từ Lemma 1:
|
| 234 |
+
$$E_{h \sim p_t}[\alpha_t(h)] = \frac{E[\|A_t Q_{t-1} h\|^2]}{r\,E[\|h\|^2]} = \frac{\mathrm{tr}(A_t\,\tilde{C}_t\,A_t^\top)}{r}$$
|
| 235 |
+
Với ràng buộc $A_t A_t^\top = I_r$, đây là **Constrained PCA** tiêu chuẩn trên $\tilde{C}_t$: lời giải là eigenvectors ứng với eigenvalues lớn nhất. Đây chính xác là C5. $\square$
|
| 236 |
+
|
| 237 |
+
**Ý nghĩa:** C5 **đồng thời** tối ưu:
|
| 238 |
+
1. **Learning quality:** maximize $\mathrm{tr}(A_t \tilde{C}_t A_t^\top)$ = variance captured trong null-space → $B_t$ học được hiệu quả.
|
| 239 |
+
2. **Routing signal:** maximize $E[\alpha_t(h)]$ → routing phân biệt task $t$ tốt hơn mọi init khác.
|
| 240 |
+
|
| 241 |
+
*Đây là lý do C5 và C2 là bất khả phân: C5 biến random routing key thành optimal routing key.*
|
| 242 |
+
|
| 243 |
+
---
|
| 244 |
|
| 245 |
+
### 3.7 Định lý Routing Margin với GPM + C5
|
| 246 |
|
| 247 |
+
**Định lý 3** *(Explicit Routing Margin).* Gọi $\lambda_t^\min = \lambda_r(\tilde{C}_t)$ (r-th eigenvalue của projected covariance). Với C5 init và A-row routing ($\tau_\text{GPM} = 0.995$):
|
| 248 |
+
$$\boxed{E_{h \sim p_t}[\alpha_t(h)] - \max_{s < t}\,E_{h \sim p_s}[\alpha_t(h)] \;\geq\; \frac{\lambda_t^\min}{r} - \frac{0.005\,\bar{\sigma}^2}{r}}$$
|
| 249 |
|
| 250 |
+
với $\bar{\sigma}^2 = \max_s \mathrm{tr}(C_s)$.
|
| 251 |
|
| 252 |
+
**Hệ quả (GPM–Routing Paradox Formalized):** Với random $A_t$ (không có C5), routing signal:
|
| 253 |
+
$$E_{h \sim p_t}[\alpha_t^\text{rand}(h)] \approx \frac{\mathrm{tr}(\tilde{C}_t)}{d'}$$
|
| 254 |
|
| 255 |
+
Tỷ lệ lợi thế C5 over random:
|
| 256 |
+
$$\frac{E[\alpha_t^\text{C5}(h)]}{E[\alpha_t^\text{rand}(h)]} \;=\; \underbrace{\frac{d'}{r}}_{\text{null-space factor}} \cdot \underbrace{\text{PEV}_r(\tilde{C}_t)}_{\text{task concentration}}$$
|
| 257 |
+
|
| 258 |
+
**Quan sát quan trọng:** Factor $d'/r$ và $\text{PEV}_r$ đều **tăng ý nghĩa về mặt routing khi null-space shrinks** (later tasks). C5 quan trọng nhất chính khi routing khó nhất.
|
| 259 |
+
|
| 260 |
+
Với T5-small task 8 ($d' \approx 351$, $r=8$): tỷ lệ $\approx 44\times \cdot \text{PEV}_8 \gg 1$.
|
| 261 |
+
|
| 262 |
+
---
|
| 263 |
+
|
| 264 |
+
### 3.8 Training–Inference Symmetry
|
| 265 |
+
|
| 266 |
+
**Mệnh đề 2** *(Routing Symmetry).* V8 training và inference dùng cùng formula:
|
| 267 |
+
|
| 268 |
+
| Phase | Formula |
|
| 269 |
+
|-------|---------|
|
| 270 |
+
| Training (task $t$) | $\alpha_t^\text{train}(h) = \dfrac{\|A_t h\|^2}{r\|h\|^2} + \beta(n)$ |
|
| 271 |
+
| Inference | $\alpha_t^\text{inf}(h) = \dfrac{\|A_t h\|^2}{r\|h\|^2}$ |
|
| 272 |
+
|
| 273 |
+
**Không có metric asymmetry.** V2–V7 dùng A-row proxy khi train nhưng SVD Rayleigh quotient khi inference — đây là nguồn gốc mismatch vì hai formula sống trong *hai metric space khác nhau*. V8 dùng A-row cho cả hai: đây là "symmetry" về mặt metric formula.
|
| 274 |
+
|
| 275 |
+
$\beta(n) = \tau\ln\!\bigl(\alpha_\text{target} \cdot n / (1-\alpha_\text{target})\bigr)$ — **cold-start mechanism** cho gradient flow khi $B_t = 0$ lúc đầu training. Nó không phải "fake routing quality"; expert $B_t$ học để transform task $t$'s features trong null-space, và những features đó (C5 A-row directions) vẫn có tín hiệu tại inference dù không còn β. β giải quyết vấn đề *gradient starvation*, không phải routing quality. Loại bỏ β khi inference là *có chủ đích* — không có task ID, không thể biết n để tính β, và không cần: $A_t$ đã align với task $t$'s null-space variance.
|
| 276 |
+
|
| 277 |
+
> **Về asymmetry β tạo ra:** Expert $B_t$ được train với score tổng $\alpha_t + \beta$ nhưng deploy không có β. Điều này CÓ ảnh hưởng nếu $\alpha_t$ lúc inference quá nhỏ để win softmax. Đây là lý do Phản biện 1 (asymmetric capacity giữa old vs new experts) là concern thực — và lý do mục tiêu của C5 maximize $\alpha_t$ là cần thiết: đảm bảo $\|A_t Q_{t-1} h\|^2$ đủ lớn kể cả không có β.
|
| 278 |
+
|
| 279 |
+
**Điều kiện α-sufficiency (để inference không cần β):** Routing của expert $t$ thắng tại inference nếu:
|
| 280 |
+
$$E[\alpha_t(h) \mid h \sim p_t] - \max_{s \neq t} E[\alpha_s(h) \mid h \sim p_t] > 0$$
|
| 281 |
+
Điều kiện này được hụ bởi Định lý 3: margin là $\lambda_t^\min/r - 0.005\bar{\sigma}^2/r$, dương khi $\lambda_t^\min > 0.005\bar{\sigma}^2$. C5 maximize $\lambda_t^\min = \lambda_r(\tilde{C}_t)$ (r-th eigenvalue của projected covariance), đây cũng chính là largest feasible $\lambda_r$ trong $\mathcal{A}_t$. Nếu $\lambda_r(\tilde{C}_t)$ quá nhỏ (null-space collapse hoặc task tương đồng domain), margin bị nhiễu — đây là giới hạn của setting, được thảo luận trong §3.10.
|
| 282 |
+
|
| 283 |
+
---
|
| 284 |
+
|
| 285 |
+
### 3.9 Drift Invariance
|
| 286 |
+
|
| 287 |
+
**Mệnh đề 3** *(Drift-Free Routing).* Hàm routing $h \mapsto \alpha_t(h)$ bất biến qua tất cả tasks: $h$ từ frozen embedding table trước mọi attention layer; $A_t$ đóng băng sau C5 init. $\square$
|
| 288 |
+
|
| 289 |
+
**Làm rõ về layer routing:** Routing được tính *một lần* tại input (token embedding, trước block transformer đầu tiên) — không phải routing riêng biệt tại mỗi transformer layer. Vector $h$ = mean-pool của token embeddings (frozen `embed_tokens`), không thay đổi qua training. Điều này đảm bảo routing hoàn toàn frozen và không drift. C5 initialization per-layer (mỗi attention layer có $A_t^{(l)}$ riêng) phục vụ *learning quality* chứ không phải routing — routing chỉ dùng encoder layers để aggregate signal.
|
| 290 |
+
|
| 291 |
+
---
|
| 292 |
+
|
| 293 |
+
### 3.10 Vấn đề Null-Space Collapse (Còn tồn tại, được giải một phần)
|
| 294 |
+
|
| 295 |
+
Định lý 1 giả định $h \in \text{span}(V_{t^*})$. Điều kiện này:
|
| 296 |
+
- **(A) Expert phải học được:** $A_t$ phải align với task-relevant directions. C5 giải quyết bằng data-informed init.
|
| 297 |
+
- **(B) Input phải co projection:** Inputs thực phải có energy trên $\text{span}(V_t)$.
|
| 298 |
+
|
| 299 |
+
C5 giải (A). (B) được đảm bảo khi null-space ($d'$) còn đủ rộng để capture task-t variance. Với $d=512$, $r=8$, 15 tasks: null-space vẫn đủ theo Hệ quả 2.
|
| 300 |
|
|
|
|
| 301 |
|
|
|
|
| 302 |
|
| 303 |
Null-space (sau $t-1$ tasks) là một không gian $d - N_{\text{protected}}$ chiều. Kaiming random trong không gian này KHÔNG ĐẢM BẢO alignment với các hướng có liên quan đến task $t$. Khi null-space thu hẹp dần (Layer 7: 8/512 → 161/512 → 344/512 qua 13 tasks), xác suất random init bắt được đúng hướng task-relevant giảm theo.
|
| 304 |
|
|
|
|
| 308 |
|
| 309 |
## 4. Các Thành phần Framework
|
| 310 |
|
| 311 |
+
### C1 — Spectral Expert Signatures (V8: A_t as Signature)
|
| 312 |
|
| 313 |
+
**V8 thay đổi từ V7:** Signature là $\mathcal{S}_t = A_t$ (model parameter), **không cần thin SVD post-training**. Lý do từ Định lý 2: rowspace của $V_t$ (từ SVD của $B_tA_t$) = rowspace của $A_t$ (phép nhân trái không mở rộng rowspace) — SVD chỉ thêm $\sigma$-weighting gây noise. Với C5 init, $A_t$ rows **đã là** task-discriminative directions.
|
| 314 |
|
| 315 |
+
- **Không cần `prepare_inference_routing()`** — loại bỏ $O(dr^2)$ overhead per task per layer.
|
| 316 |
+
- **Không tham số bổ sung** — $A_t$ là model parameter đã có.
|
| 317 |
+
- **Bất biến** — $A_t$ đóng băng sau C5 init (Mệnh đề 3).
|
| 318 |
|
| 319 |
+
### C2 — Data-Informed Differential Routing (V8)
|
|
|
|
|
|
|
| 320 |
|
| 321 |
+
**V8 thay đổi từ V7:** Cả training lẫn inference đều dùng **A-row formula** — loại bỏ hoàn toàn `prepare_inference_routing()` và SVD inference mismatch. Lý do từ Lemma 1 + Định lý 2: $A_t$ rows với C5 init đã là routing optimal directions; SVD của $B_tA_t$ chỉ thêm $\sigma^2$-weighting từ B optimization artifact, không có đảm bảo lý thuyết.
|
| 322 |
|
| 323 |
+
**Routing formula (Training và Inference):**
|
| 324 |
|
| 325 |
$$w(h) = \mathrm{softmax}\!\left(\frac{[\alpha_1(h),\; \ldots,\; \alpha_T(h)]}{\tau}\right)$$
|
| 326 |
|
| 327 |
+
$$\alpha_t(h) = \frac{\|A_t\, h\|^2}{r\,\|h\|^2}, \qquad \text{(A-row affinity — cả train lẫn inference)}$$
|
| 328 |
|
| 329 |
+
**Training** (task $t$ thêm adaptive bias):
|
| 330 |
|
| 331 |
$$\alpha_t^{\mathrm{train}}(h) = \frac{\|A_t\, h\|^2}{r\,\|h\|^2} + \beta(n), \qquad \beta(n) = \tau \cdot \ln\!\left(\frac{\alpha_{\mathrm{target}} \cdot n}{1 - \alpha_{\mathrm{target}}}\right)$$
|
| 332 |
|
| 333 |
trong đó $n = |\{\text{task cũ}\}|$ và $\alpha_{\mathrm{target}} \in (0,1)$ là routing weight mục tiêu cho task hiện tại (mặc định 0.8).
|
| 334 |
|
| 335 |
+
**Lý giải A-row routing (từ Lemma 1 + Định lý 2):**
|
| 336 |
|
| 337 |
+
- **Exact decomposition (Lemma 1):** $\|A_t h\|^2 = \|A_t Q_{t-1} h\|^2$ — routing chỉ nhìn null-space component, không bị ảnh hưởng bởi task cũ.
|
| 338 |
+
- **Optimality với C5 (Định lý 2):** C5 init chọn $A_t$ = argmax $E[\|A_t h\|^2]$ trên tất cả $A_t \in \mathcal{A}_t$ — A-row affinity là tốt nhất có thể trong constraint.
|
| 339 |
+
- **Margin bound (Định lý 3):** $E[\alpha_t|h \sim p_t] - \max_s E[\alpha_t|h \sim p_s] \geq \lambda_t^\min/r - 0.005\bar{\sigma}^2/r > 0$.
|
| 340 |
+
- **Loại bỏ `prepare_inference_routing()`:** $A_t$ đóng băng sau C5 init — là signature không cần tái tính SVD của $B_tA_t$.
|
| 341 |
|
| 342 |
+
**Lý giải adaptive $\beta(n)$:** Giải $w_t = \alpha_{\mathrm{target}}$ trong softmax → closed-form $\beta(n) = \tau\ln(\alpha_\text{target} \cdot n/(1-\alpha_\text{target}))$. Tránh $O(1/n)$ softmax dilution khi số task tăng.
|
| 343 |
|
| 344 |
+
**Lợi thế C5 so với random A-row (Hệ quả Định lý 3):**
|
| 345 |
+
$$\frac{E[\alpha_t^\text{C5}(h)]}{E[\alpha_t^\text{rand}(h)]} = \frac{d'}{r} \cdot \text{PEV}_r(\tilde{C}_t) \approx 44\times \text{ tại task 8 (T5-small)}$$
|
| 346 |
+
|
| 347 |
+
| Phase | Cơ chế routing | Notes |
|
| 348 |
+
|-------|----------------|-------|
|
| 349 |
+
| Training (task $t$) | $\|A_t h\|^2/(r\|h\|^2) + \beta(n)$ | $\beta$ bù softmax dilution |
|
| 350 |
+
| **Inference (mọi task)** | **$\|A_t h\|^2/(r\|h\|^2)$** | **V8: không SVD, không $\sigma^2$-weighting** |
|
| 351 |
+
| Đảm bảo lý thuyết | Định lý 3 margin bound + C5 advantage $44\times$ | Cả hai phase đều dùng cùng metric space |
|
| 352 |
|
| 353 |
### C3 — Capacity-Aware Subspace Allocation
|
| 354 |
|
|
|
|
| 436 |
|
| 437 |
**Điều kiện fallback:** Nếu `max_eigenvalue(C_tilde) < 1e-6`, null-space quá hẹp hoặc activation không có signal đủ mạnh. Trong trường hợp này, C5 nhường cho Kaiming init + InfLoRA projection tiêu chuẩn — không làm tệ hơn V6, chỉ không cải thiện. Điều kiện này chỉ xảy ra khi null-space gần như bão hoà, tức là ESA đã tiêu thụ gần hết capacity.
|
| 438 |
|
| 439 |
+
**C5 per-layer:** Mỗi LoRA layer (encoder Q, V; decoder self/cross Q, V) có $C_t$ riêng thu thập từ activation tương ứng của layer đó. GPM cũng lưu $P_{\text{old}}^{(l)}$ riêng theo layer $l$. Do đó eigenvector decomposition được thực hiện độc lập cho từng layer — mỗi $A_t^{(l)}$ chỉ capture variance task-relevant trong null-space của layer $l$.
|
| 440 |
+
|
| 441 |
+
**Hệ kết với Routing:** Routing sử dụng input embedding (frozen embedding table output, trước tất cả transformer layers) và A-row của các encoder layers — không phải per-layer routing riêng biệt. C5 per-layer cải thiện **học hiệu quả** của $B_t$ tại mỗi layer, còn routing signal (§3.2) được aggregate qua các encoder layers.
|
| 442 |
+
|
| 443 |
#### Ý nghĩa Lý thuyết Thông tin
|
| 444 |
|
| 445 |
Theo Data Processing Inequality, với bất kỳ ma trận $A_t$ nào:
|
|
|
|
| 471 |
| `previous_trans_input` | Frozen MLP copies | ❌ Loại bỏ | Signatures bất biến theo cấu trúc |
|
| 472 |
| KL distillation | Replay-based routing loss | ❌ Loại bỏ | Không learned routing → không cần distill |
|
| 473 |
| GPM trên routing params | Subspace cho routing | ❌ Loại bỏ | Không có routing parameters để bảo vệ |
|
| 474 |
+
| **`prepare_inference_routing()`** | **SVD của $B_tA_t$ post-training** | **❌ Loại bỏ (V8)** | **$A_t$ là signature, kh\u00f4ng c\u1ea7n t\u00e1i t\u00ednh SVD; lo\u1ea1i b\u1ecf $O(dr^2)$ overhead** |
|
| 475 |
+
| **SVD $\sigma^2$-weighted inference** | **Rayleigh quotient khác train formula** | **❌ Loại bỏ (V8)** | **Train-inference mismatch; A-row đủ từ Lemma 1 + Định lý 2** |
|
| 476 |
|
| 477 |
**Hiệu ứng tổng thể:** Toàn bộ subspace và compute budget mà GainLoRA dành cho routing infrastructure được *thu hồi* cho task learning.
|
| 478 |
|
|
|
|
| 495 |
**Đảm bảo lý thuyết:**
|
| 496 |
- Routing margin $\geq \kappa_{\min}(t^*) - \varepsilon\, \kappa_{\max}$ — tỷ lệ thuận với chất lượng bảo vệ (Định lý 1).
|
| 497 |
- Routing weight $w_{t^*} \geq 1-\delta$ với nhiệt độ $\tau \leq m/\ln\!\bigl((T-1)/\delta\bigr)$ (Hệ quả 1).
|
| 498 |
+
- Capacity bound $T_{\max} \leq d/(\bar{k}(1-\varepsilon))$ qua lý thuyết Grassmannian packing (Hệ quả 2), trong đó $\bar{k}$ là GPM effective rank ($\approx 30$–$80$ dims/task), không phải LoRA rank $r=8$.
|
| 499 |
- Routing hoàn toàn bất biến qua thời gian: $h$ từ frozen embedding table, $\mathcal{S}_t$ đóng băng sau training (Mệnh đề 1).
|
| 500 |
|
| 501 |
**Tại sao không phải đơn giản hoá mà là tiến bộ lý thuyết:** Kết quả này cho thấy các kiến trúc như GainLoRA đang giải quyết một bài toán không tồn tại (routing parameter learning). Chúng tôi chứng minh rằng bảo vệ tốt ↔ routing tốt — hai bài toán hóa ra là *một*.
|
|
|
|
| 527 |
### Task 1 (`--run_single True`)
|
| 528 |
1. Load pretrained model + fresh LoRA ($A$: Kaiming, $B$: zeros).
|
| 529 |
2. Huấn luyện chuẩn (chỉ `lora_B`) — single expert, không routing.
|
| 530 |
+
3. Sau training: cập nhật GPM bases (ESA threshold). **Không cần tính SVD hay `prepare_inference_routing()`** — $A_t$ là signature.
|
| 531 |
+
4. Lưu: LoRA weights, GPM reg files.
|
| 532 |
|
| 533 |
### Task $t \geq 2$
|
| 534 |
+
1. Load model + fresh LoRA; load LoRA weights cũ.
|
| 535 |
2. **[C5 — MỚI]** Pre-task forward pass (200 batch, no grad):
|
| 536 |
- Thu thập activation covariance $C_t$ của inputs task $t$
|
| 537 |
- Tính projected covariance: $\tilde{C}_t = Q C_t Q$ ($Q = I - P_{\text{old}}$)
|
| 538 |
- Eigenvectors của $\tilde{C}_t$ → khởi tạo $A_t$ (thay thế random Kaiming)
|
| 539 |
3. InfLoRA: chuẩn hoá $A_t$ (đã nằm trong null-space từ eigenvector decomposition).
|
| 540 |
+
4. Huấn luyện `lora_B` với A-row routing + adaptive bias $\beta(n)$ + gradient preconditioning (C4.1).
|
| 541 |
+
5. Sau training: cập nhật GPM bases (200 batches). **Không gọi `prepare_inference_routing()`** — $A_t$ đóng băng từ bước 2 là signature đủ dùng.
|
| 542 |
6. Lưu tất cả artifacts cho task tiếp theo.
|
| 543 |
|
| 544 |
---
|
|
|
|
| 547 |
|
| 548 |
| Lý thuyết | Implementation | File |
|
| 549 |
|-----------|---------------|------|
|
| 550 |
+
| Spectral signature $\mathcal{S}_t = A_t$ | `lora_A` weights (frozen after C5 init) — **không cần SVD** | `t5_specroute.py` |
|
| 551 |
+
| Spectral affinity $\alpha_t(h)$ (inference) | A-row fit: `(proj**2).sum(-1) / (r * h_norm_sq)` | `compute_spectral_routing()` |
|
| 552 |
| A-row proxy $\alpha_t^{\mathrm{train}}$ (training) | A-row fit + adaptive bias $\beta(n)$ | `compute_spectral_routing()` |
|
| 553 |
+
| ~~Symmetric inference SVD~~ | ~~`prepare_inference_routing()` → SVD của $B_t A_t$~~ | **❌ Loại bỏ hoàn toàn (V8)** |
|
| 554 |
| Routing $w = \mathrm{softmax}(\alpha / \tau)$ | `torch.softmax(fit_scores / temp)` | `compute_spectral_routing()` |
|
| 555 |
| Drift-free input $h$ | `inputs_embeds = embed_tokens(input_ids)` → mean-pool | `T5Stack.forward()` |
|
| 556 |
| GPM + InfLoRA null-space | `get_reg_matrix()` | `cl_trainer_specroute.py` |
|
|
|
|
| 569 |
| Benchmarks | SuperNI (15 tasks, 2 orderings), Long (15 tasks, 2 orderings) |
|
| 570 |
| Metrics | AP (Average Performance, ↑), FT (Forgetting, ↓) |
|
| 571 |
| LoRA | $r = 8$, target=Q+V, InfLoRA (chỉ B trained, A đóng băng) |
|
| 572 |
+
| Routing | $\tau = 1.0$, $\alpha_{\mathrm{target}} = 0.8$, adaptive $\beta(n)$ (train); **A-row formula (inference, V8 — không SVD)** |
|
| 573 |
| ESA | $\varepsilon_0 = 0.995$ (dynamic) |
|
| 574 |
| C4 | Gradient preconditioning bật (`--use_preconditioning True`), $\epsilon = 10^{-6}$; entropy reg đã loại bỏ V7 |
|
| 575 |
| **C5** | **N_batch = 100, `torch.linalg.eigh` trên projected covariance, fallback nếu max_eigval < 1e-6** |
|
| 576 |
| GPM repr. | 200 batches (giảm từ 1000 — SVD ổn định sau 200) |
|
| 577 |
+
| **Scalability note** | **C5 per-layer eigdecomp: $O(d^2)$ per layer per task. Với T5-small ($d=512$) tất cả layers: chấp nhận được. Với flan-t5-large ($d=1024$): 4× đắt hơn nhưng vẫn chỉ tuyến tính theo tasks. Với LLaMA-7B ($d=4096$): sử dụng randomized SVD hoặc Lanczos (top-$r$ eigenvecs không cần full decomp) giảm xuống $O(dr)$ per layer.** |
|
| 578 |
| Precision | fp32 + gradient_checkpointing (T5 + P100: fp16 có risk NaN overflow với large softmax) |
|
| 579 |
| P100 BSZ | BSZ=8, GA=4 (effective 32); T4: BSZ=2, GA=8 |
|
| 580 |
| Thời gian (P100 16GB) | SuperNI T5-Small ≈ 2-3h; Long benchmark ≈ 3-4h — thoải mái trong 12h Kaggle |
|
improve_gainlora/src/cl_trainer_specroute.py
CHANGED
|
@@ -11,6 +11,7 @@ Key differences from GainLoRA_InfLoRA_Trainer:
|
|
| 11 |
"""
|
| 12 |
|
| 13 |
import gc
|
|
|
|
| 14 |
import torch
|
| 15 |
from torch.utils.data import DataLoader
|
| 16 |
from torch.utils.data.distributed import DistributedSampler
|
|
@@ -89,7 +90,7 @@ class SpecRoute_Trainer(Seq2SeqTrainer):
|
|
| 89 |
compute_metrics=None, callbacks=None,
|
| 90 |
lambda_entropy=0.0, use_preconditioning=False,
|
| 91 |
precond_eps=1e-6, entropy_warmup_ratio=0.1,
|
| 92 |
-
n_batches_c5=
|
| 93 |
super().__init__(
|
| 94 |
model=model, args=args, train_dataset=train_dataset,
|
| 95 |
eval_dataset=eval_dataset, tokenizer=tokenizer,
|
|
@@ -350,8 +351,11 @@ class SpecRoute_Trainer(Seq2SeqTrainer):
|
|
| 350 |
feature_mat[index]
|
| 351 |
)
|
| 352 |
)
|
| 353 |
-
|
| 354 |
-
|
|
|
|
|
|
|
|
|
|
| 355 |
i += 1
|
| 356 |
|
| 357 |
# Free GPM bases — no longer needed after projection
|
|
@@ -390,7 +394,7 @@ class SpecRoute_Trainer(Seq2SeqTrainer):
|
|
| 390 |
else:
|
| 391 |
labels = None
|
| 392 |
outputs = self.model(**inputs)
|
| 393 |
-
if step >
|
| 394 |
break
|
| 395 |
print('end get representation')
|
| 396 |
|
|
|
|
| 11 |
"""
|
| 12 |
|
| 13 |
import gc
|
| 14 |
+
import math
|
| 15 |
import torch
|
| 16 |
from torch.utils.data import DataLoader
|
| 17 |
from torch.utils.data.distributed import DistributedSampler
|
|
|
|
| 90 |
compute_metrics=None, callbacks=None,
|
| 91 |
lambda_entropy=0.0, use_preconditioning=False,
|
| 92 |
precond_eps=1e-6, entropy_warmup_ratio=0.1,
|
| 93 |
+
n_batches_c5=100):
|
| 94 |
super().__init__(
|
| 95 |
model=model, args=args, train_dataset=train_dataset,
|
| 96 |
eval_dataset=eval_dataset, tokenizer=tokenizer,
|
|
|
|
| 351 |
feature_mat[index]
|
| 352 |
)
|
| 353 |
)
|
| 354 |
+
# V8 C5 normalization: scale rows to have norm = sqrt(3) (matching InfLoRA convention)
|
| 355 |
+
# Eigenvectors from C5 are already orthonormal unit vectors in null-space.
|
| 356 |
+
# We scale magnitude to sqrt(3) to match InfLoRA's Kaiming-like initialization scale.
|
| 357 |
+
module.lora_q.lora_A.data *= (math.sqrt(3) / module.lora_q.lora_A.data.norm(dim=1, keepdim=True).clamp(min=1e-8))
|
| 358 |
+
module.lora_v.lora_A.data *= (math.sqrt(3) / module.lora_v.lora_A.data.norm(dim=1, keepdim=True).clamp(min=1e-8))
|
| 359 |
i += 1
|
| 360 |
|
| 361 |
# Free GPM bases — no longer needed after projection
|
|
|
|
| 394 |
else:
|
| 395 |
labels = None
|
| 396 |
outputs = self.model(**inputs)
|
| 397 |
+
if step >= 200: # 200 batches for stable GPM SVD (IDEA §9)
|
| 398 |
break
|
| 399 |
print('end get representation')
|
| 400 |
|
improve_gainlora/src/run_t5.py
CHANGED
|
@@ -191,7 +191,7 @@ class ModelArguments:
|
|
| 191 |
metadata={"help": "Fraction of training steps before enabling entropy loss (C4, disabled in V7)."},
|
| 192 |
)
|
| 193 |
n_batches_c5: Optional[int] = field(
|
| 194 |
-
default=
|
| 195 |
metadata={"help": "Number of training batches for C5 activation covariance collection."},
|
| 196 |
)
|
| 197 |
|
|
@@ -1070,9 +1070,8 @@ def main():
|
|
| 1070 |
|
| 1071 |
trainer.model.encoder.is_inference = True
|
| 1072 |
|
| 1073 |
-
#
|
| 1074 |
-
|
| 1075 |
-
trainer.model.encoder.prepare_inference_routing()
|
| 1076 |
|
| 1077 |
# Collect attention weights for GainLoRA KL replay (not needed for SpecRoute)
|
| 1078 |
if training_args.model_name != 'specroute':
|
|
|
|
| 191 |
metadata={"help": "Fraction of training steps before enabling entropy loss (C4, disabled in V7)."},
|
| 192 |
)
|
| 193 |
n_batches_c5: Optional[int] = field(
|
| 194 |
+
default=100,
|
| 195 |
metadata={"help": "Number of training batches for C5 activation covariance collection."},
|
| 196 |
)
|
| 197 |
|
|
|
|
| 1070 |
|
| 1071 |
trainer.model.encoder.is_inference = True
|
| 1072 |
|
| 1073 |
+
# V8: prepare_inference_routing() removed — A_t rows are the routing signatures.
|
| 1074 |
+
# No SVD post-training needed (Lemma 1 + Theorem 2 in IDEA_Overall.md).
|
|
|
|
| 1075 |
|
| 1076 |
# Collect attention weights for GainLoRA KL replay (not needed for SpecRoute)
|
| 1077 |
if training_args.model_name != 'specroute':
|
improve_gainlora/src/t5_specroute.py
CHANGED
|
@@ -1,14 +1,17 @@
|
|
| 1 |
# coding=utf-8
|
| 2 |
"""
|
| 3 |
-
SpecRoute: T5 model with Spectral-Geometric Routing for Continual LoRA Learning.
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
-
|
| 11 |
-
-
|
|
|
|
|
|
|
|
|
|
| 12 |
"""
|
| 13 |
|
| 14 |
import copy
|
|
@@ -61,56 +64,22 @@ logger = logging.get_logger(__name__)
|
|
| 61 |
|
| 62 |
# ===================== Spectral Routing Functions =====================
|
| 63 |
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
Since delta_W = B(m×r) @ A(r×n) has rank ≤ r (typically 8), full SVD
|
| 69 |
-
of the m×n matrix is wasteful. Instead, we decompose via two small QR
|
| 70 |
-
factorizations and one tiny r×r SVD:
|
| 71 |
-
1. QR(B) → Q_B(m×r), R_B(r×r)
|
| 72 |
-
2. QR(A^T) → Q_A(n×r), R_A(r×r)
|
| 73 |
-
3. delta_W = Q_B @ (R_B @ R_A^T) @ Q_A^T
|
| 74 |
-
4. SVD(R_B @ R_A^T) → U_s, S, Vh_s (all r×r)
|
| 75 |
-
5. Vt = Vh_s @ Q_A^T (r×n)
|
| 76 |
-
|
| 77 |
-
Mathematically identical to full SVD, ~2000× faster for r=8, m=n=512.
|
| 78 |
-
|
| 79 |
-
Args:
|
| 80 |
-
B: (m, r) float tensor — lora_B weights
|
| 81 |
-
A: (r, n) float tensor — lora_A weights
|
| 82 |
-
device: target device for output (None = same as input)
|
| 83 |
-
|
| 84 |
-
Returns:
|
| 85 |
-
S: (r,) singular values (descending)
|
| 86 |
-
Vt: (r, n) right singular vectors transposed
|
| 87 |
-
"""
|
| 88 |
-
try:
|
| 89 |
-
Q_B, R_B = torch.linalg.qr(B) # Q_B: (m, r), R_B: (r, r)
|
| 90 |
-
Q_A, R_A = torch.linalg.qr(A.T) # Q_A: (n, r), R_A: (r, r)
|
| 91 |
-
small = R_B @ R_A.T # (r, r) — tiny matrix
|
| 92 |
-
_, S, Vh_s = torch.linalg.svd(small, full_matrices=False)
|
| 93 |
-
Vt = Vh_s @ Q_A.T # (r, n)
|
| 94 |
-
except RuntimeError:
|
| 95 |
-
# GPU linalg failure → CPU fallback
|
| 96 |
-
B_cpu, A_cpu = B.cpu(), A.cpu()
|
| 97 |
-
Q_B, R_B = torch.linalg.qr(B_cpu)
|
| 98 |
-
Q_A, R_A = torch.linalg.qr(A_cpu.T)
|
| 99 |
-
small = R_B @ R_A.T
|
| 100 |
-
_, S, Vh_s = torch.linalg.svd(small, full_matrices=False)
|
| 101 |
-
Vt = Vh_s @ Q_A.T
|
| 102 |
-
target = device if device is not None else B.device
|
| 103 |
-
S, Vt = S.to(target), Vt.to(target)
|
| 104 |
-
return S, Vt
|
| 105 |
|
| 106 |
|
| 107 |
def compute_spectral_signatures(model, config):
|
| 108 |
"""
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
|
|
|
|
|
|
|
|
|
| 112 |
|
| 113 |
-
Returns dict mapping layer keys to {'
|
| 114 |
"""
|
| 115 |
signatures = {}
|
| 116 |
with torch.no_grad():
|
|
@@ -119,36 +88,20 @@ def compute_spectral_signatures(model, config):
|
|
| 119 |
attn = model.encoder.block[j].layer[0].SelfAttention
|
| 120 |
for name, lora in [('q', attn.lora_q), ('v', attn.lora_v)]:
|
| 121 |
A = lora.lora_A.data.float()
|
| 122 |
-
B = lora.lora_B.data.float()
|
| 123 |
-
r = lora.r
|
| 124 |
-
S, Vt = _thin_svd_low_rank(B, A)
|
| 125 |
signatures[f'enc.{j}.{name}'] = {
|
| 126 |
-
'
|
| 127 |
-
'sigma': S[:r].cpu() # (r,)
|
| 128 |
}
|
| 129 |
# Decoder self-attention layers
|
| 130 |
for j in range(config.num_decoder_layers):
|
| 131 |
attn = model.decoder.block[j].layer[0].SelfAttention
|
| 132 |
for name, lora in [('q', attn.lora_q), ('v', attn.lora_v)]:
|
| 133 |
A = lora.lora_A.data.float()
|
| 134 |
-
|
| 135 |
-
r = lora.r
|
| 136 |
-
S, Vt = _thin_svd_low_rank(B, A)
|
| 137 |
-
signatures[f'dec.{j}.self.{name}'] = {
|
| 138 |
-
'V': Vt[:r].cpu(),
|
| 139 |
-
'sigma': S[:r].cpu()
|
| 140 |
-
}
|
| 141 |
# Decoder cross-attention layers
|
| 142 |
attn_cross = model.decoder.block[j].layer[1].EncDecAttention
|
| 143 |
for name, lora in [('q', attn_cross.lora_q), ('v', attn_cross.lora_v)]:
|
| 144 |
A = lora.lora_A.data.float()
|
| 145 |
-
|
| 146 |
-
r = lora.r
|
| 147 |
-
S, Vt = _thin_svd_low_rank(B, A)
|
| 148 |
-
signatures[f'dec.{j}.cross.{name}'] = {
|
| 149 |
-
'V': Vt[:r].cpu(),
|
| 150 |
-
'sigma': S[:r].cpu()
|
| 151 |
-
}
|
| 152 |
return signatures
|
| 153 |
|
| 154 |
|
|
@@ -159,8 +112,8 @@ class T5Stack(T5PreTrainedModel):
|
|
| 159 |
T5Stack with spectral routing instead of learned gating.
|
| 160 |
|
| 161 |
Instead of Trans_input + prompt_key (GainLoRA), routing weights are computed
|
| 162 |
-
from
|
| 163 |
-
(
|
| 164 |
"""
|
| 165 |
|
| 166 |
def __init__(self, config, embed_tokens=None, prompt_config=None):
|
|
@@ -184,16 +137,12 @@ class T5Stack(T5PreTrainedModel):
|
|
| 184 |
self.spectral_signatures = [] # List[dict] — one dict per old task
|
| 185 |
self.routing_temperature = prompt_config.get('attn_temperature', 1.0)
|
| 186 |
|
| 187 |
-
# Adaptive training bias:
|
| 188 |
-
# Ensures current task gets consistent routing weight ~
|
| 189 |
# of total number of tasks (fixes softmax dilution with constant bias).
|
| 190 |
-
# At inference,
|
| 191 |
self._target_routing_alpha = prompt_config.get('target_routing_alpha', 0.8)
|
| 192 |
|
| 193 |
-
# Precomputed SVD of current task's LoRA for symmetric inference routing.
|
| 194 |
-
# Populated by prepare_inference_routing() before prediction.
|
| 195 |
-
self._current_task_svd = None
|
| 196 |
-
|
| 197 |
# For inference logging
|
| 198 |
self.all_attn_weights = []
|
| 199 |
self.key_attention_weights = None
|
|
@@ -214,15 +163,22 @@ class T5Stack(T5PreTrainedModel):
|
|
| 214 |
|
| 215 |
def compute_spectral_routing(self, avg_inputs_embeds):
|
| 216 |
"""
|
| 217 |
-
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
|
| 227 |
Args:
|
| 228 |
avg_inputs_embeds: (B, 1, d_model) — averaged input token embeddings
|
|
@@ -235,75 +191,41 @@ class T5Stack(T5PreTrainedModel):
|
|
| 235 |
|
| 236 |
fits = []
|
| 237 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 238 |
if self.training:
|
| 239 |
-
#
|
| 240 |
-
#
|
| 241 |
-
current_fits_layers = []
|
| 242 |
-
for block in self.block:
|
| 243 |
-
attn = block.layer[0].SelfAttention
|
| 244 |
-
for lora in [attn.lora_q, attn.lora_v]:
|
| 245 |
-
A = lora.lora_A.data.float() # (r, d_model) — frozen
|
| 246 |
-
r = lora.r
|
| 247 |
-
A_h = A.to(h.device, dtype=h.dtype)
|
| 248 |
-
proj = torch.matmul(h, A_h.T) # (B, 1, r)
|
| 249 |
-
fit = (proj ** 2).sum(dim=-1) / (r * h_norm_sq) # (B, 1)
|
| 250 |
-
current_fits_layers.append(fit)
|
| 251 |
-
current_fit = torch.stack(current_fits_layers).mean(dim=0) # (B, 1)
|
| 252 |
-
|
| 253 |
-
# Adaptive training bias: β = T·ln(α·n_old/(1−α))
|
| 254 |
n_old = len(self.spectral_signatures)
|
| 255 |
if n_old > 0:
|
| 256 |
alpha = self._target_routing_alpha
|
| 257 |
beta = self.routing_temperature * math.log(alpha * n_old / (1.0 - alpha))
|
| 258 |
current_fit = current_fit + beta
|
| 259 |
-
|
| 260 |
-
|
| 261 |
-
|
| 262 |
-
|
| 263 |
-
|
| 264 |
-
for key, sig_data in self._current_task_svd.items():
|
| 265 |
-
if not key.startswith('enc.'):
|
| 266 |
-
continue
|
| 267 |
-
V = sig_data['V'].to(h.device, dtype=h.dtype)
|
| 268 |
-
sigma = sig_data['sigma'].to(h.device, dtype=h.dtype)
|
| 269 |
-
proj = torch.matmul(h, V.T)
|
| 270 |
-
sigma_sq = sigma ** 2
|
| 271 |
-
sigma_sq_sum = sigma_sq.sum() + 1e-8
|
| 272 |
-
weighted_proj = (proj ** 2 * sigma_sq.unsqueeze(0).unsqueeze(0)).sum(dim=-1)
|
| 273 |
-
fit = weighted_proj / (sigma_sq_sum * h_norm_sq)
|
| 274 |
-
cur_fits.append(fit)
|
| 275 |
-
if cur_fits:
|
| 276 |
-
current_fit = torch.stack(cur_fits).mean(dim=0)
|
| 277 |
-
else:
|
| 278 |
-
current_fit = torch.zeros(h.shape[0], 1, device=h.device, dtype=h.dtype)
|
| 279 |
-
else:
|
| 280 |
-
# Fallback: A-row fit (before prepare_inference_routing() is called)
|
| 281 |
-
current_fits_layers = []
|
| 282 |
-
for block in self.block:
|
| 283 |
-
attn = block.layer[0].SelfAttention
|
| 284 |
-
for lora in [attn.lora_q, attn.lora_v]:
|
| 285 |
-
A = lora.lora_A.data.float()
|
| 286 |
-
r = lora.r
|
| 287 |
-
A_h = A.to(h.device, dtype=h.dtype)
|
| 288 |
-
proj = torch.matmul(h, A_h.T)
|
| 289 |
-
fit = (proj ** 2).sum(dim=-1) / (r * h_norm_sq)
|
| 290 |
-
current_fits_layers.append(fit)
|
| 291 |
-
current_fit = torch.stack(current_fits_layers).mean(dim=0)
|
| 292 |
-
fits.append(current_fit)
|
| 293 |
-
|
| 294 |
-
# Old tasks: σ²-weighted spectral affinity (Rayleigh quotient)
|
| 295 |
for sig_dict in self.spectral_signatures:
|
| 296 |
task_fits = []
|
| 297 |
for key, sig_data in sig_dict.items():
|
| 298 |
if not key.startswith('enc.'):
|
| 299 |
continue
|
| 300 |
-
|
| 301 |
-
|
| 302 |
-
proj = torch.matmul(h,
|
| 303 |
-
|
| 304 |
-
sigma_sq_sum = sigma_sq.sum() + 1e-8
|
| 305 |
-
weighted_proj = (proj ** 2 * sigma_sq.unsqueeze(0).unsqueeze(0)).sum(dim=-1)
|
| 306 |
-
fit = weighted_proj / (sigma_sq_sum * h_norm_sq)
|
| 307 |
task_fits.append(fit)
|
| 308 |
if task_fits:
|
| 309 |
task_fit = torch.stack(task_fits).mean(dim=0)
|
|
@@ -311,38 +233,17 @@ class T5Stack(T5PreTrainedModel):
|
|
| 311 |
task_fit = torch.zeros(h.shape[0], 1, device=h.device, dtype=h.dtype)
|
| 312 |
fits.append(task_fit)
|
| 313 |
|
| 314 |
-
# Stack: (B, n_tasks)
|
| 315 |
fit_scores = torch.cat(fits, dim=1) # (B, n_tasks)
|
| 316 |
-
|
| 317 |
-
# Softmax routing with temperature
|
| 318 |
weights = torch.softmax(fit_scores / self.routing_temperature, dim=1) # (B, n_tasks)
|
| 319 |
|
| 320 |
return weights.unsqueeze(2) # (B, n_tasks, 1)
|
| 321 |
|
| 322 |
-
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
spectral signatures from the current (most recently trained) task's
|
| 328 |
-
LoRA weights, enabling the same σ²-weighted Rayleigh quotient formula
|
| 329 |
-
for all tasks during inference.
|
| 330 |
-
"""
|
| 331 |
-
sigs = {}
|
| 332 |
-
with torch.no_grad():
|
| 333 |
-
for j, block in enumerate(self.block):
|
| 334 |
-
attn = block.layer[0].SelfAttention
|
| 335 |
-
for name, lora in [('q', attn.lora_q), ('v', attn.lora_v)]:
|
| 336 |
-
A = lora.lora_A.data.float()
|
| 337 |
-
B = lora.lora_B.data.float()
|
| 338 |
-
r = lora.r
|
| 339 |
-
S, Vt = _thin_svd_low_rank(B, A)
|
| 340 |
-
sigs[f'enc.{j}.{name}'] = {
|
| 341 |
-
'V': Vt[:r].cpu(),
|
| 342 |
-
'sigma': S[:r].cpu()
|
| 343 |
-
}
|
| 344 |
-
self._current_task_svd = sigs
|
| 345 |
-
print(f"[SpecRoute] Prepared inference routing: {len(sigs)} encoder SVD signatures for current task")
|
| 346 |
|
| 347 |
def parallelize(self, device_map=None):
|
| 348 |
self.device_map = (
|
|
|
|
| 1 |
# coding=utf-8
|
| 2 |
"""
|
| 3 |
+
SpecRoute V8: T5 model with Spectral-Geometric Routing for Continual LoRA Learning.
|
| 4 |
+
|
| 5 |
+
Core insight (Routing-Protection Duality): orthogonal subspace protection (GPM)
|
| 6 |
+
and task-discriminative routing are dual manifestations of the same spectral structure.
|
| 7 |
+
Solving one automatically solves the other.
|
| 8 |
+
|
| 9 |
+
V8 key design choices (from IDEA_Overall.md):
|
| 10 |
+
- C1: A_t rows ARE the spectral signatures — no SVD of B_t A_t needed
|
| 11 |
+
- C2: A-row affinity for BOTH training and inference (symmetric metric space)
|
| 12 |
+
- C5: Data-informed init via Constrained PCA in null-space (Q C_t Q eigenvectors)
|
| 13 |
+
- Removed: Trans_input, prompt_key, memory_replay, prepare_inference_routing()
|
| 14 |
+
- Removed: SVD sigma^2-weighted inference (train-inference mismatch source)
|
| 15 |
"""
|
| 16 |
|
| 17 |
import copy
|
|
|
|
| 64 |
|
| 65 |
# ===================== Spectral Routing Functions =====================
|
| 66 |
|
| 67 |
+
# NOTE: _thin_svd_low_rank() was REMOVED in V8.
|
| 68 |
+
# Theorem 2 (C5 Routing Optimality) proves A_t rows with C5 init are already
|
| 69 |
+
# the optimal routing directions. SVD of B_t A_t only adds sigma^2-weighting
|
| 70 |
+
# from B optimization artifacts with no theoretical guarantee. See IDEA §3.6.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 71 |
|
| 72 |
|
| 73 |
def compute_spectral_signatures(model, config):
|
| 74 |
"""
|
| 75 |
+
V8: Store frozen A-row matrices as routing signatures (no SVD needed).
|
| 76 |
+
|
| 77 |
+
Theorem 2 (C5 Routing Optimality) proves that A_t rows with C5 init are
|
| 78 |
+
already the optimal routing directions in the null-space. SVD of B_t A_t
|
| 79 |
+
only adds sigma^2-weighting from B optimization artifacts (no theoretical
|
| 80 |
+
guarantee). A-row routing is exact, symmetric, and eliminates prepare_inference_routing().
|
| 81 |
|
| 82 |
+
Returns dict mapping layer keys to {'A': tensor (r, d_model)}.
|
| 83 |
"""
|
| 84 |
signatures = {}
|
| 85 |
with torch.no_grad():
|
|
|
|
| 88 |
attn = model.encoder.block[j].layer[0].SelfAttention
|
| 89 |
for name, lora in [('q', attn.lora_q), ('v', attn.lora_v)]:
|
| 90 |
A = lora.lora_A.data.float()
|
|
|
|
|
|
|
|
|
|
| 91 |
signatures[f'enc.{j}.{name}'] = {
|
| 92 |
+
'A': A.cpu(), # (r, d_model) — frozen routing directions
|
|
|
|
| 93 |
}
|
| 94 |
# Decoder self-attention layers
|
| 95 |
for j in range(config.num_decoder_layers):
|
| 96 |
attn = model.decoder.block[j].layer[0].SelfAttention
|
| 97 |
for name, lora in [('q', attn.lora_q), ('v', attn.lora_v)]:
|
| 98 |
A = lora.lora_A.data.float()
|
| 99 |
+
signatures[f'dec.{j}.self.{name}'] = {'A': A.cpu()}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
# Decoder cross-attention layers
|
| 101 |
attn_cross = model.decoder.block[j].layer[1].EncDecAttention
|
| 102 |
for name, lora in [('q', attn_cross.lora_q), ('v', attn_cross.lora_v)]:
|
| 103 |
A = lora.lora_A.data.float()
|
| 104 |
+
signatures[f'dec.{j}.cross.{name}'] = {'A': A.cpu()}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
return signatures
|
| 106 |
|
| 107 |
|
|
|
|
| 112 |
T5Stack with spectral routing instead of learned gating.
|
| 113 |
|
| 114 |
Instead of Trans_input + prompt_key (GainLoRA), routing weights are computed
|
| 115 |
+
from A-row affinity between input embeddings and spectral signatures
|
| 116 |
+
(A-row matrices of frozen LoRA A from previous tasks — V8, no SVD needed).
|
| 117 |
"""
|
| 118 |
|
| 119 |
def __init__(self, config, embed_tokens=None, prompt_config=None):
|
|
|
|
| 137 |
self.spectral_signatures = [] # List[dict] — one dict per old task
|
| 138 |
self.routing_temperature = prompt_config.get('attn_temperature', 1.0)
|
| 139 |
|
| 140 |
+
# Adaptive training bias: beta = T * ln(alpha * n_old / (1 - alpha))
|
| 141 |
+
# Ensures current task gets consistent routing weight ~alpha regardless
|
| 142 |
# of total number of tasks (fixes softmax dilution with constant bias).
|
| 143 |
+
# At inference, same A-row formula without bias (V8 symmetric routing).
|
| 144 |
self._target_routing_alpha = prompt_config.get('target_routing_alpha', 0.8)
|
| 145 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 146 |
# For inference logging
|
| 147 |
self.all_attn_weights = []
|
| 148 |
self.key_attention_weights = None
|
|
|
|
| 163 |
|
| 164 |
def compute_spectral_routing(self, avg_inputs_embeds):
|
| 165 |
"""
|
| 166 |
+
V8: A-row routing for ALL tasks (training and inference).
|
| 167 |
+
|
| 168 |
+
Theory (IDEA_Overall.md §3):
|
| 169 |
+
- Lemma 1 (Differential Projection): ||A_t h||^2 = ||A_t Q_{t-1} h||^2
|
| 170 |
+
→ routing only sees null-space component, cross-task leakage <= 0.005*tr(C_s)/r
|
| 171 |
+
- Corollary B (Reverse direction): alpha_s(h_t) = ||A_s P_{t-1} h_t||^2/(r||h_t||^2)
|
| 172 |
+
→ small when task t has most variance in null-space (different domain)
|
| 173 |
+
→ same-domain difficulty is a fundamental limit of ALL task-free CL methods
|
| 174 |
+
- Theorem 2 (C5 Routing Optimality): C5 init maximizes E[alpha_t(h)]
|
| 175 |
+
over all A_t in the restricted Stiefel manifold
|
| 176 |
+
- C5 advantage over random: d'/r * PEV_r(C_tilde) ≈ 44x at task 8 (T5-small)
|
| 177 |
+
|
| 178 |
+
Training: A-row fit + adaptive bias β(n) for current task.
|
| 179 |
+
β(n) is a cold-start mechanism (B_t=0 initially, gradient needs routing).
|
| 180 |
+
NOT a routing quality booster. Symmetry = same metric formula, not same distribution.
|
| 181 |
+
Inference: A-row fit for ALL tasks. No prepare_inference_routing().
|
| 182 |
|
| 183 |
Args:
|
| 184 |
avg_inputs_embeds: (B, 1, d_model) — averaged input token embeddings
|
|
|
|
| 191 |
|
| 192 |
fits = []
|
| 193 |
|
| 194 |
+
# === CURRENT TASK: A-row fit (+ adaptive bias during training) ===
|
| 195 |
+
current_fits_layers = []
|
| 196 |
+
for block in self.block:
|
| 197 |
+
attn = block.layer[0].SelfAttention
|
| 198 |
+
for lora in [attn.lora_q, attn.lora_v]:
|
| 199 |
+
A = lora.lora_A.data.float() # (r, d_model) — frozen after C5 init
|
| 200 |
+
r = lora.r
|
| 201 |
+
A_h = A.to(h.device, dtype=h.dtype)
|
| 202 |
+
proj = torch.matmul(h, A_h.T) # (B, 1, r)
|
| 203 |
+
fit = (proj ** 2).sum(dim=-1) / (r * h_norm_sq) # (B, 1)
|
| 204 |
+
current_fits_layers.append(fit)
|
| 205 |
+
current_fit = torch.stack(current_fits_layers).mean(dim=0) # (B, 1)
|
| 206 |
+
|
| 207 |
if self.training:
|
| 208 |
+
# Adaptive training bias: beta = T * ln(alpha * n_old / (1 - alpha))
|
| 209 |
+
# Compensates for softmax dilution as task count grows.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 210 |
n_old = len(self.spectral_signatures)
|
| 211 |
if n_old > 0:
|
| 212 |
alpha = self._target_routing_alpha
|
| 213 |
beta = self.routing_temperature * math.log(alpha * n_old / (1.0 - alpha))
|
| 214 |
current_fit = current_fit + beta
|
| 215 |
+
fits.append(current_fit)
|
| 216 |
+
|
| 217 |
+
# === OLD TASKS: A-row fit using saved A matrices ===
|
| 218 |
+
# Lemma 1 (Differential Projection): ||A_t h||^2 = ||A_t Q_{t-1} h||^2
|
| 219 |
+
# Cross-task leakage <= 0.005 * tr(C_s)/r with tau_GPM = 0.995
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 220 |
for sig_dict in self.spectral_signatures:
|
| 221 |
task_fits = []
|
| 222 |
for key, sig_data in sig_dict.items():
|
| 223 |
if not key.startswith('enc.'):
|
| 224 |
continue
|
| 225 |
+
A = sig_data['A'].to(h.device, dtype=h.dtype) # (r, d_model)
|
| 226 |
+
r = A.shape[0]
|
| 227 |
+
proj = torch.matmul(h, A.T) # (B, 1, r)
|
| 228 |
+
fit = (proj ** 2).sum(dim=-1) / (r * h_norm_sq)
|
|
|
|
|
|
|
|
|
|
| 229 |
task_fits.append(fit)
|
| 230 |
if task_fits:
|
| 231 |
task_fit = torch.stack(task_fits).mean(dim=0)
|
|
|
|
| 233 |
task_fit = torch.zeros(h.shape[0], 1, device=h.device, dtype=h.dtype)
|
| 234 |
fits.append(task_fit)
|
| 235 |
|
| 236 |
+
# Stack: (B, n_tasks) — all tasks use same A-row metric space
|
| 237 |
fit_scores = torch.cat(fits, dim=1) # (B, n_tasks)
|
|
|
|
|
|
|
| 238 |
weights = torch.softmax(fit_scores / self.routing_temperature, dim=1) # (B, n_tasks)
|
| 239 |
|
| 240 |
return weights.unsqueeze(2) # (B, n_tasks, 1)
|
| 241 |
|
| 242 |
+
# prepare_inference_routing() REMOVED in V8.
|
| 243 |
+
# Reasoning: Lemma 1 + Theorem 2 prove A_t rows (with C5 init) are already the
|
| 244 |
+
# optimal routing directions. SVD of B_t A_t only adds sigma^2-weighting from
|
| 245 |
+
# B optimization artifacts, causing train-inference mismatch. A_t is the signature.
|
| 246 |
+
# Eliminates O(d*r^2) SVD overhead per task per layer after training.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 247 |
|
| 248 |
def parallelize(self, device_map=None):
|
| 249 |
self.device_map = (
|