natmin322 commited on
Commit
2d2ef83
·
1 Parent(s): bc95dae
improve_gainlora/IDEA_Overall.md CHANGED
@@ -1,7 +1,7 @@
1
- # SpecRoute: Định tuyến Phổ thông qua Duality Định tuyến–Bảo vệ trong Học liên tục với LoRA
2
 
3
- > **Tài liệu thiết kế chính thức — V7**
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).* Số lượng tối đa subspace $r$-chiều trong $\mathbb{R}^d$ với pairwise overlap $\delta \leq \varepsilon$:
145
 
146
- $$T_{\max} \;\leq\; \frac{d}{r\,(1 - \varepsilon)}$$
147
 
148
- Với T5-Small ($d = 512$, $r = 8$, $\varepsilon = 0.02$): $T_{\max} \leq 65 \gg 15$ tasks. Điều này kết nối capacity học liên tục với lý thuyết Grassmannian packing.
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 1 - 0.995 = 0.005 \ll \kappa_{\min}(t^*)$ trong thực tế.
169
 
170
- **Sửa reviewer:** Reviewer nói "GPM không đảm bảo orthogonality của $\Delta W_t$" — *đúng* với GPM gradient projection. Nhưng cơ chế bảo đảm orthogonality là **InfLoRA A-projection** (bước khởi tạo), không phải gradient projection. Theorem 1 không cần giả định — nó là hệ quả tất yếu của kiến trúc InfLoRA đã có sẵn.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
 
172
  ---
173
 
174
- ### 3.5 Drift Invariance
175
 
176
- **Mệnh đề 1** *(Drift-Free Routing).* Hàm routing $h \mapsto \alpha_t(h)$ hoàn toàn ổn định qua tất cả các task.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
177
 
178
- **Chứng minh.** Routing input được tính từ frozen embedding table, *trước* bất kỳ transformer block nào. LoRA chỉ tồn tại trong các attention layer sâu hơn → $h$ độc lập với mọi tham số LoRA. Kết hợp với $\mathcal{S}_t$ đóng băng, $\alpha_t(h)$ bất biến với mọi thay đổi tích luỹ. $\square$
179
 
180
- ### 3.6 Vấn đề then chốt: Null-Space Collapse
 
181
 
182
- Định lý 1 giả định $h \in \mathrm{span}(V_{t^*})$. Trong thực tế điều này đòi hỏi hai điều kiện:
183
 
184
- **(A) Expert phải học được:** $A_t$ phải nằm trong subspace có liên quan đến task $t$ để $B_t$ có thể học các biến đổi có ý nghĩa, tạo ra $\sigma_{t,i} > 0$ đáng kể.
 
185
 
186
- **(B) Input phải có projection:** Inputs thực tế của task $t$ phải chiếu có năng lượng lên $\text{span}(V_t)$.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- Sau khi train task $t$, tính $\mathcal{S}_t = (V_t, \boldsymbol{\sigma}_t)$ qua **thin SVD**:
203
 
204
- $$B_t,\, A_t \;\xrightarrow[\text{QR + SVD}]{O(dr^2)}\; (V_t,\, \boldsymbol{\sigma}_t)$$
 
 
205
 
206
- - QR decomposition của $B$ và $A^\top$, sau đó SVD của $r \times r$ core → chính xác, $O(dr^2)$ so với $O(d^2 r)$.
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
- ### C2 — Spectral Affinity Routing
211
 
212
- **Inference** (tất cả task, routing SVD đối xứng):
213
 
214
  $$w(h) = \mathrm{softmax}\!\left(\frac{[\alpha_1(h),\; \ldots,\; \alpha_T(h)]}{\tau}\right)$$
215
 
216
- Mọi task đều dùng cùng công thức $\sigma^2$-weighted spectral affinity (Định nghĩa 2). Sau khi train task $t$, tính $\mathcal{S}_t$ một lần qua `prepare_inference_routing()` và sử dụng cùng signatures của task cũ.
217
 
218
- **Training** (task $t$, SVD cuối chưa biết vì $B_t$ đang train):
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 proxy A-row:** Với $B_t$ full-rank bất kỳ, column span của $V_t$ (từ SVD của $B_t A_t$) bằng $\mathrm{range}(A_t^\top)$. Vì vậy các hàng $A$ span *cùng* subspace input mà $V_t$ hội tụ sẽ capture. Proxy đo alignment input với subspace này dùng weighting đồng đều (chưa có $\sigma$).
225
 
226
- **Lý giải adaptive $\beta(n)$:** Bias hằng số gây routing weight của task hiện tại giảm $O(1/n)$ theo số task (softmax dilution). Công thức adaptive chuẩn hoá điều này: giải $w_t = \alpha_{\mathrm{target}}$ trong phương trình softmax cho ra closed-form trên. Đảm bảo task hiện tại nhận được routing weight $\approx \alpha_{\mathrm{target}}$ bất kể $n$.
 
 
 
227
 
228
- **Lý giải symmetric inference:** Tại inference, $B_t$ đóng băng và $\Delta W_t = B_t A_t$ có SVD xác định. Dùng cùng $\sigma^2$-weighted Rayleigh quotient cho tất cả task đảm bảo *đối xứng đo lường* — mọi affinity đều sống trên cùng metric space, và Định lý 1 áp dụng thống nhất.
229
 
230
- | Phase | Cơ chế routing | Nhiệt độ | Lý do |
231
- |-------|----------------|----------|-------|
232
- | Training (task $t$) | A-row fit + adaptive $\beta(n)$ | $\tau = 1.0$ | $B=0$ cold-start; β bù softmax dilution |
233
- | Inference (mọi task) | SVD spectral affinity (đối xứng) | $\tau = 1.0$ | Mọi task dùng σ²-weighted Rayleigh quotient |
 
 
 
 
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/r(1-\varepsilon)$ qua lý thuyết Grassmannian packing (Hệ quả 2).
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: tính $\mathcal{S}_1$ (thin SVD) + GPM bases (ESA threshold).
407
- 4. Lưu: LoRA weights, spectral signatures, GPM reg files.
408
 
409
  ### Task $t \geq 2$
410
- 1. Load model + fresh LoRA; load LoRA weights và spectral signatures cũ.
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 spectral affinity routing + adaptive bias $\beta(n)$ + gradient preconditioning (C4.1).
417
- 5. Sau training: tính $\mathcal{S}_t$ (cả inference routing và storage) + cập nhật GPM bases (200 batches, đủ cho SVD ổn định).
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$ | `compute_spectral_signatures()` (thin QR+SVD) | `t5_specroute.py` |
427
- | Spectral affinity $\alpha_t(h)$ (inference) | σ²-weighted Rayleigh quotient | `compute_spectral_routing()` |
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$ | `t5_specroute.py` |
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); SVD đối xứng (inference) |
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=200):
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
- module.lora_q.lora_A.data /= (math.sqrt(3) * module.lora_q.lora_A.data.norm(dim=1, keepdim=True))
354
- module.lora_v.lora_A.data /= (math.sqrt(3) * module.lora_v.lora_A.data.norm(dim=1, keepdim=True))
 
 
 
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 > 100: # 100 batches sufficient for stable SVD
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=200,
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
- # SpecRoute V3: precompute SVD of current task's LoRA for symmetric inference routing
1074
- if training_args.model_name == 'specroute' and hasattr(trainer.model.encoder, 'prepare_inference_routing'):
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
- Replaces GainLoRA's learned gating (Trans_input + prompt_key) with spectral
6
- projection-based routing using SVD signatures from frozen LoRA weights.
7
-
8
- Key changes from t5_gainlora_inflora.py:
9
- - Removed: Trans_input, prompt_key, previous_trans_input, previous_prompts_keys
10
- - Added: Spectral signatures + projection-based routing (softmax over weighted projection fits)
11
- - Removed: memory_replay (no KL loss on gating since routing is parameter-based)
 
 
 
12
  """
13
 
14
  import copy
@@ -61,56 +64,22 @@ logger = logging.get_logger(__name__)
61
 
62
  # ===================== Spectral Routing Functions =====================
63
 
64
- def _thin_svd_low_rank(B, A, device=None):
65
- """
66
- Compute SVD of delta_W = B @ A efficiently using QR decomposition.
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
- Compute spectral signatures from all LoRA branches after training.
110
- For each LoRA layer, computes exact SVD of B@A and stores top-r right
111
- singular vectors (input directions) and singular values (importance).
 
 
 
112
 
113
- Returns dict mapping layer keys to {'V': tensor, 'sigma': tensor}.
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
- 'V': Vt[:r].cpu(), # (r, d_model)
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
- B = lora.lora_B.data.float()
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
- B = lora.lora_B.data.float()
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 projection fits between input embeddings and spectral signatures
163
- (SVD of frozen LoRA weights from previous tasks).
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: β = T·ln(α·n_old/(1−α))
188
- # Ensures current task gets consistent routing weight ~α regardless
189
  # of total number of tasks (fixes softmax dilution with constant bias).
190
- # At inference, uses symmetric SVD-based routing instead (no bias needed).
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
- Compute routing weights using spectral projection fits.
218
-
219
- Training mode: A-row fit + adaptive bias for current task (cold-start safe).
220
- Inference mode: SVD-based fit for ALL tasks (symmetric, no bias needed).
221
-
222
- The adaptive bias β = T·ln(α·n_old/(1−α)) ensures the current task gets
223
- routing weight ≈ α regardless of the number of previous tasks, compensating
224
- for softmax dilution. At inference, all tasks use the same σ²-weighted
225
- Rayleigh quotient (SVD fit), eliminating the train-test asymmetry.
 
 
 
 
 
 
 
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
- # === TRAINING MODE: A-row fit + adaptive bias for current task ===
240
- # A rows give stable non-zero signal even when B=0 (cold-start).
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
- fits.append(current_fit)
260
- else:
261
- # === INFERENCE MODE: SVD-based spectral routing (symmetric) ===
262
- if self._current_task_svd is not None:
263
- cur_fits = []
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
- V = sig_data['V'].to(h.device, dtype=h.dtype)
301
- sigma = sig_data['sigma'].to(h.device, dtype=h.dtype)
302
- proj = torch.matmul(h, V.T)
303
- sigma_sq = sigma ** 2
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
- def prepare_inference_routing(self):
323
- """
324
- Precompute SVD of current task's LoRA for symmetric inference routing.
325
-
326
- Must be called after training (B≠0) and before prediction. Computes
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 = (