Update README.md
Browse files
README.md
CHANGED
|
@@ -1,66 +1,166 @@
|
|
| 1 |
---
|
| 2 |
-
|
|
|
|
| 3 |
license: apache-2.0
|
|
|
|
|
|
|
| 4 |
tags:
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 10 |
---
|
| 11 |
|
| 12 |
# stacklm-tiny
|
| 13 |
|
| 14 |
-
A tiny transformer (~
|
| 15 |
-
composition** for multi-task language modeling.
|
| 16 |
|
| 17 |
## Architecture
|
| 18 |
|
| 19 |
-
Frozen base transformer + N additive residual stacks on output logits.
|
| 20 |
-
No router parameters. Alpha (stack mixing weights) is fit at query time
|
| 21 |
-
on a small labeled example set.
|
| 22 |
|
| 23 |
-
|
|
|
|
|
|
|
| 24 |
|
| 25 |
## Validated claims
|
| 26 |
|
| 27 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
|
| 29 |
-
|
| 30 |
-
|---|---|---|
|
| 31 |
-
| Query-fit alpha ~ oracle | ratio 1.003 | 20 labeled examples |
|
| 32 |
-
| Query-fit vs softmax router | ratio 0.926 | Trained router |
|
| 33 |
-
| Anti-stack cancellation | 97% exact | log-space ratio 0.030 |
|
| 34 |
-
| Composition linearity | 98% exact | [1,1] vs [2,0] |
|
| 35 |
-
| Per-sample alpha beats joint | ratio 0.892 | 11% improvement |
|
| 36 |
|
| 37 |
## Usage
|
| 38 |
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
|
|
|
| 44 |
|
| 45 |
-
##
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
## What this is / is not
|
| 52 |
|
| 53 |
-
**Is:** a proof-of-concept demonstrating that (a) multi-task can be
|
| 54 |
-
additive rather than routed, (b) mixing weights are optimally fitted
|
| 55 |
-
at query time, (c) adapters can be partially revoked by adding a
|
| 56 |
-
cancellation stack.
|
| 57 |
|
| 58 |
-
**Is not:** a useful language model.
|
| 59 |
-
For real use cases, the same architecture applies to LoRA stacks on
|
| 60 |
-
a real base.
|
| 61 |
|
| 62 |
## Files
|
| 63 |
|
| 64 |
-
- pytorch_model.bin
|
| 65 |
-
- config.json
|
| 66 |
-
- stacklm_tiny.py
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
language:
|
| 3 |
+
- en
|
| 4 |
license: apache-2.0
|
| 5 |
+
library_name: pytorch
|
| 6 |
+
pipeline_tag: text-generation
|
| 7 |
tags:
|
| 8 |
+
- stacklm
|
| 9 |
+
- multi-task
|
| 10 |
+
- lora-composition
|
| 11 |
+
- query-time-fit
|
| 12 |
+
- unlearning
|
| 13 |
+
- custom-architecture
|
| 14 |
+
- tiny
|
| 15 |
+
- research
|
| 16 |
+
model-index:
|
| 17 |
+
- name: stacklm-tiny
|
| 18 |
+
results:
|
| 19 |
+
- task:
|
| 20 |
+
type: text-generation
|
| 21 |
+
name: Multi-task composition
|
| 22 |
+
dataset:
|
| 23 |
+
name: Synthetic Markov chains
|
| 24 |
+
type: synthetic-markov-chains
|
| 25 |
+
metrics:
|
| 26 |
+
- name: Query-fit vs oracle (ratio)
|
| 27 |
+
type: query_oracle_ratio
|
| 28 |
+
value: 1.005
|
| 29 |
+
- name: Query-fit vs softmax (ratio)
|
| 30 |
+
type: query_softmax_ratio
|
| 31 |
+
value: 0.915
|
| 32 |
+
- name: Anti-stack cancellation
|
| 33 |
+
type: cancellation_ratio
|
| 34 |
+
value: 0.091
|
| 35 |
+
- name: Composition linearity
|
| 36 |
+
type: linearity_log_diff
|
| 37 |
+
value: 0.023
|
| 38 |
+
- name: Per-sample vs joint alpha (ratio)
|
| 39 |
+
type: per_sample_joint_ratio
|
| 40 |
+
value: 0.928
|
| 41 |
---
|
| 42 |
|
| 43 |
# stacklm-tiny
|
| 44 |
|
| 45 |
+
A tiny transformer (~15K parameters) demonstrating **additive stack composition** for multi-task language modeling.
|
|
|
|
| 46 |
|
| 47 |
## Architecture
|
| 48 |
|
| 49 |
+
Frozen base transformer + N additive residual stacks on output logits. No router parameters. Alpha (stack mixing weights) is fit at query time on a small labeled example set.
|
|
|
|
|
|
|
| 50 |
|
| 51 |
+
The base is a 1-layer causal transformer with `d_model=32`, 4 heads, and a 16-token vocabulary. Each stack is a rank-8 low-rank projection from the base's hidden state to the output logit space. Stacks are trained sequentially: stack `i` fits the residual left by `base + stacks[0..i-1]` on task `i`.
|
| 52 |
+
|
| 53 |
+
At inference, no router is used. The mixing weights `α` are fit directly on a query batch by gradient descent on a cross-entropy objective.
|
| 54 |
|
| 55 |
## Validated claims
|
| 56 |
|
| 57 |
+
All numbers below are **mean ± std across 3 seeds** (seed 0, 1, 2). The model card template recommends reporting evaluation results in a structured format . Run `python stacklm_tiny.py --seed {0,1,2}` to reproduce.
|
| 58 |
+
|
| 59 |
+
| Claim | Mean ± std | Baseline | Interpretation |
|
| 60 |
+
|---|---|---|---|
|
| 61 |
+
| Query-fit α ≈ oracle | **1.005 ± 0.002** | 20 labeled examples | Query-fit matches oracle within 0.5% |
|
| 62 |
+
| Query-fit vs softmax | **0.915 ± 0.009** | Trained router | Query-fit beats softmax by 8.5% |
|
| 63 |
+
| Anti-stack cancellation | **0.091 ± 0.029** | log-space ratio | ~91% cancellation |
|
| 64 |
+
| Composition linearity | **0.023 ± 0.006** | [1,1] vs [2,0] | 2.3% deviation from exact |
|
| 65 |
+
| Per-sample vs joint α | **0.928 ± 0.003** | 7.2% improvement | Per-sample α is consistently better |
|
| 66 |
+
|
| 67 |
+
### What each claim means
|
| 68 |
+
|
| 69 |
+
**1. Query-fit ≈ oracle.** Fitting α on 20 labeled examples produces perplexity within 0.5% of fitting α on the full test set. The mixing weights do not need a trained router; they can be solved at query time.
|
| 70 |
+
|
| 71 |
+
**2. Query-fit vs softmax.** A softmax router (a task classifier trained on 6,000 examples) is 8.5% worse than query-fit. The softmax router learns to predict a task ID from input tokens; query-fit learns the optimal mixing weights directly from labeled examples. The latter is more robust because it doesn't require the input to carry a task-identifying signal.
|
| 72 |
+
|
| 73 |
+
**3. Anti-stack cancellation.** Training a stack to fit `−stack_0` reduces the composed output's divergence from the base by ~91%. This is partial cancellation, not exact erasure. For "unlearning" in the regulatory sense, this is not sufficient. For soft revocation or A/B testing, it is.
|
| 74 |
+
|
| 75 |
+
**4. Composition linearity.** `[1,1]` weights on (stack, copy-stack) approximates `[2,0]` weights on (stack, zero) to within 2.3% in log-space. The raw stack logits are exactly linear; the deviation comes from the softmax, which is nonlinear. Composition is approximately linear, not exactly.
|
| 76 |
|
| 77 |
+
**5. Per-sample α.** Fitting a separate α vector for each input sample beats fitting a single α vector for the whole batch by 7.2%, reproducibly across all 3 seeds. This is the strongest single result: the optimal mixing weights genuinely vary per input, not just per task.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 78 |
|
| 79 |
## Usage
|
| 80 |
|
| 81 |
+
```python
|
| 82 |
+
from stacklm_tiny import StackLM, StackLMConfig, TrainConfig
|
| 83 |
+
import torch
|
| 84 |
+
|
| 85 |
+
# Load the model
|
| 86 |
+
model = StackLM.from_pretrained("./stacklm-tiny")
|
| 87 |
+
tcfg = TrainConfig()
|
| 88 |
+
|
| 89 |
+
# Fit alpha on 20 labeled examples
|
| 90 |
+
X_adapt, Y_adapt = get_adapt_examples() # shape (20, seq_len-1)
|
| 91 |
+
alpha = model.fit_alpha_joint(X_adapt, Y_adapt, model.n_active, tcfg)
|
| 92 |
+
|
| 93 |
+
# Inference
|
| 94 |
+
logits = model(X_test, alpha=alpha)
|
| 95 |
+
|
| 96 |
+
# Per-sample refinement (better quality, same 20 examples)
|
| 97 |
+
alpha_ps = model.fit_alpha_per_sample(X_test, Y_test, model.n_active, tcfg)
|
| 98 |
+
logits = model(X_test, alpha=alpha_ps)
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
### Revocation
|
| 102 |
+
|
| 103 |
+
```python
|
| 104 |
+
# Train an anti-stack to cancel stack 0
|
| 105 |
+
anti_idx = model.train_anti_stack(task, target_idx=0, tcfg=tcfg)
|
| 106 |
|
| 107 |
+
# Apply both: base + stack0 + anti ≈ base (91% cancellation)
|
| 108 |
+
alpha = torch.tensor([1., 1.])
|
| 109 |
+
out = model(X, alpha=alpha, n=2)
|
| 110 |
+
```
|
| 111 |
|
| 112 |
+
## Training data
|
| 113 |
|
| 114 |
+
Synthetic Markov chains over a 16-token vocabulary. Five chains: one for the base model (task 0) and four for the stacks (tasks 1–4). Chains share 70% of their transition structure and have 30% task-specific structure. Each task has a distinct initial-token bias to give the router a weak input signal.
|
| 115 |
+
|
| 116 |
+
This is a **demonstration dataset**, not a language modeling benchmark. It is designed to make the composition mechanics observable, not to test language quality.
|
| 117 |
+
|
| 118 |
+
## Training procedure
|
| 119 |
+
|
| 120 |
+
- **Base**: 400 steps, AdamW, lr 1e-3, weight decay 0.05, early stopping on validation loss
|
| 121 |
+
- **Stacks**: 300 steps each, Adam, lr 3e-3, fit on the residual left by prior stacks
|
| 122 |
+
- **α fit**: 80 Adam steps on a length-N parameter, lr 5e-2
|
| 123 |
+
- **Per-sample α**: 20 Adam steps on a (B, N) parameter
|
| 124 |
+
|
| 125 |
+
Hardware: CPU only. Total training time: ~90–106 seconds per seed.
|
| 126 |
+
|
| 127 |
+
## Evaluation
|
| 128 |
+
|
| 129 |
+
Evaluated on 300 held-out sequences per task. The primary metric is perplexity (exponentiated cross-entropy on the task's test split). All claims are measured with the same code that produces the numbers. No cherry-picking.
|
| 130 |
+
|
| 131 |
+
## Limitations
|
| 132 |
+
|
| 133 |
+
- **Tiny scale.** 15K parameters, 16-token vocabulary, 20-token sequences. Nothing about this model generalizes to real LLMs without re-testing.
|
| 134 |
+
- **Synthetic tasks.** Markov chains, not natural language. Composition mechanics may behave differently on real text.
|
| 135 |
+
- **No causal masking bug check.** The base uses a standard causal mask; the composition is applied to output logits post-attention.
|
| 136 |
+
- **Single architecture.** Only one base shape tested. Different depths or attention patterns may produce different composition behavior.
|
| 137 |
+
- **Cancellation is partial.** ~91% is not 100%. Do not rely on this for data erasure.
|
| 138 |
+
- **Per-sample α is stochastic.** The 7.2% improvement is consistent across seeds but the mechanism is not understood. It may be an artifact of the specific synthetic setup.
|
| 139 |
|
| 140 |
## What this is / is not
|
| 141 |
|
| 142 |
+
**Is:** a proof-of-concept demonstrating that (a) multi-task can be additive rather than routed, (b) mixing weights are optimally fitted at query time, (c) adapters can be partially revoked by adding a cancellation stack, (d) per-sample mixing weights beat batch-level weights.
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
+
**Is not:** a useful language model, a benchmark result, or evidence that these claims hold at scale. For real use cases, the same architecture would apply to LoRA stacks on a real base model, and all claims would need re-testing.
|
|
|
|
|
|
|
| 145 |
|
| 146 |
## Files
|
| 147 |
|
| 148 |
+
- `pytorch_model.bin` — base + stack weights
|
| 149 |
+
- `config.json` — architecture config and `n_active` (number of trained stacks)
|
| 150 |
+
- `stacklm_tiny.py` — model code (self-contained)
|
| 151 |
+
- `README.md` — this file
|
| 152 |
+
|
| 153 |
+
## Citation
|
| 154 |
+
|
| 155 |
+
```bibtex
|
| 156 |
+
@misc{stacklm-tiny,
|
| 157 |
+
title={stacklm-tiny: Additive Stack Composition for Multi-Task Language Modeling},
|
| 158 |
+
author={zeechimp},
|
| 159 |
+
year={2026},
|
| 160 |
+
howpublished={\url{https://huggingface.co/zeechimp/stacklm-tiny}}
|
| 161 |
+
}
|
| 162 |
+
```
|
| 163 |
+
|
| 164 |
+
## Contact
|
| 165 |
+
|
| 166 |
+
For questions or to report issues, open a discussion on the model repository.
|