Repository navigation
Methods
github-actions[bot] edited this page Nov 5, 2026
·
4 revisions
contilab uses one shared classification head for all scenarios and expresses the scenario as a per-sample logit mask (disallowed logits are set to −1e9):
| Scenario | Training mask | Evaluation mask |
|---|---|---|
| Task-IL (task-incremental) | classes of the sample's own task | classes of the evaluated task |
| Class-IL (class-incremental) | all classes seen so far | all classes seen so far |
| Domain-IL (domain-incremental) | all (shared) classes | all classes |
Replayed samples always use their own task's mask, which matters in Task-IL.
All strategies share the same optimiser, epochs, batch size and memory budget. Defaults (vision / text):
| Strategy | Key | Idea | Defaults |
|---|---|---|---|
| Naive fine-tuning | naive |
Train on each task in turn (lower bound) | — |
| Joint | joint |
All tasks merged into one task (upper bound); evaluated per original task | — |
| EWC (online) | ewc |
Quadratic penalty λ/2 · Σ F (θ − θ*)² with a diagonal empirical Fisher, accumulated F ← γF + F_new
|
λ = 30 / 10, γ = 1, 1024 Fisher samples, stiffness cap 1 |
| LwF | lwf |
Distil the previous model's outputs on the current batch: T² · KL(p_old ‖ p_new) on old classes |
T = 2, α = 1 |
| ER | er |
Reservoir buffer of dataset indices; each step trains on current + replay batch | M = 500 / 200 |
| ER-ACE | er_ace |
ER with an asymmetric cross-entropy: the incoming batch's softmax covers only the classes present in it, the replay batch's all allowed classes (Caccia et al. 2022) | M = 500 / 200 |
| DER++ | derpp |
ER + MSE to logits stored at insertion time + CE on a second replay batch | M = 500 / 200, α = 0.1, β = 0.5 |
| A-GEM | agem |
Project the gradient when it conflicts with a reference gradient from previous tasks' memory | M = 500 / 200, reference batch 256 / 64 |
| GEM | gem |
One reference gradient per previous task; when the gradient conflicts with any of them, project it by the dual QP (Lopez-Paz & Ranzato 2017) | M = 500 / 200, reference batch 256 / 64, margin 0.5 |
| LoRA-per-task | lora_per_task |
Frozen backbone, one LoRA adapter per task, shared head; Task-IL selects the task's adapter, Class/Domain-IL merges seen adapters with a per-seed λ chosen on validation data | r = 8, α = 16, lr 1e-3 with AdamW |
| O-LoRA | olora |
Frozen backbone, a new LoRA adapter per task trained while the earlier ones stay active and frozen, with a penalty that keeps its subspace orthogonal to theirs; inference sums all adapters, without task identity (Wang et al. 2023) | r = 8, α = 16, λ₁ = 0.5, λ₂ = 0, lr 1e-3 with AdamW |
Implementation notes:
-
EWC computes per-sample gradients with nested
vmap(grad), chunked to a 1 GiB memory budget, so it also runs on language models. Under SGD a parameter whose stiffnesslr · λ · Fexceeds 2 oscillates with growing amplitude around its anchor, and the accumulated Fisher only grows, so long streams used to diverge. The stored Fisher is clamped atmax_stiffness / (lr · λ)(method.params.max_stiffness, default 1;nullturns it off), which leaves every parameter below the bound unchanged. Adam-type optimisers normalise the step, so nothing is clamped for them. -
Replay buffers store global indices into the device-resident
TrainPool, not copies of samples. -
A-GEM projects per member:
g ← g − (g·g_ref / ‖g_ref‖²) g_refonly wheng·g_ref < 0. -
GEM uses the same memory and reference-batch size as A-GEM, so the two differ only in the projection:
- The reference batch is split evenly over the previous tasks, and each part gives that task's gradient
G_k. - The original keeps a fixed memory per task and uses all of it every step.
- Each part is a separate forward and backward pass, so a step costs about as much as A-GEM's.
- When
G_k · g < 0for some k, the update isg + Gᵀv, with v the solution of the dualmin ½ vᵀ(GGᵀ + εI)v + (Gg)ᵀvsubject tov ≥ margin; the margin (0.5) and ε (10⁻³) are those of the reference code. - The QP is solved exactly, per member and in batch: every possible set of free variables is solved and the best feasible solution kept. This is 2^K small linear systems for K ≤ 10 previous tasks; above that it falls back to coordinate descent.
- The reference batch is split evenly over the previous tasks, and each part gives that task's gradient
- ER-ACE runs two forward passes (incoming and replay batch) and adds their losses, like the reference implementation, instead of ER's single pass over the concatenated batch.
-
O-LoRA penalises
λ₁ · Σᵢ |A_i A_tᵀ|(L1 over the entries, as in the reference code) between the new adapterA_tand every earlier adapterA_iof the same module. The original is a generative T5/LLaMA setup; here the backbone feeds contilab's shared classification head, which keeps training across tasks as in LoRA-per-task. -
LoRA is implemented in-house (no PEFT) for
nn.Linearandnn.Conv2d; adapters merge asΔW = (α/r)·B·A. A per-member scale buffer lets each seed use its own merge coefficient. - Strategies draw randomness from generators derived from each member's seed, never from the data-order
generator, so e.g.
EWC(λ=0)reproducesnaiveexactly.
Defaults follow the literature. Sweep them with the experiment's sweep: block (see
Configuration); each value appears as its own row (ewc [method.params.lam=10]) in tables.
contilab — continual learning benchmark, distillation & model-merging framework · Repository · MIT License