File size: 7,986 Bytes
f231e80 a9286e3 516c5fc a9286e3 f231e80 a9286e3 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | ---
license: apache-2.0
datasets:
- Ajay0728/Compositional-reasoning-arithmetic-datasets
library_name: pytorch
pipeline_tag: text-generation
tags:
- arithmetic
- compositional-generalization
- length-generalization
- lstm
- transformer
- seq2seq
- interpretability
---
# Compositional Reasoning on Arithmetic: LSTM and Transformer Checkpoints
This repo holds the 12 trained checkpoints from the paper *Partial Competence and Limited Transfer: Comparing LSTM and Transformer Generalization on Arithmetic Expressions* (arXiv ID pending).
The models are small character-level encoder-decoders, trained from scratch. Each one reads a fully parenthesized integer arithmetic expression and outputs its value:
```
Input: ((14 - 1) * 10)
Output: 130
```
They are research artifacts for studying generalization. They are not useful calculators: the best model gets about 35% exact match on held-out in-distribution expressions.
- **Code:** https://github.com/bcode0127-debug/Compositional-Reasoning-Arithmetic
- **Data:** [Ajay0728/Compositional-reasoning-arithmetic-datasets](https://huggingface.co/datasets/Ajay0728/Compositional-reasoning-arithmetic-datasets) (`datasets_v2`)
## Checkpoints
There are 2 architectures × 2 studies × 3 seeds = 12 runs. Each file is the checkpoint with the best validation accuracy from its run.
| Path | Best epoch | Val | Test |
|---|---|---|---|
| `lstm/study1/seed0/best_model.pt` | 43 | 35.1% | 33.6% |
| `lstm/study1/seed1/best_model.pt` | 46 | 35.9% | 35.5% |
| `lstm/study1/seed2/best_model.pt` | 51 | 36.1% | 34.6% |
| `lstm/study2/seed0/best_model.pt` | 57 | 32.5% | 31.8% |
| `lstm/study2/seed1/best_model.pt` | 195 | 30.1% | 31.6% |
| `lstm/study2/seed2/best_model.pt` | 66 | 32.0% | 28.0% |
| `transformer/study1/seed0/best_model.pt` | 114 | 7.5% | 7.9% |
| `transformer/study1/seed1/best_model.pt` | 160 | 6.8% | 7.7% |
| `transformer/study1/seed2/best_model.pt` | 160 | 7.5% | 7.9% |
| `transformer/study2/seed0/best_model.pt` | 34 | 3.7% | 1.9% |
| `transformer/study2/seed1/best_model.pt` | 162 | 4.2% | 2.8% |
| `transformer/study2/seed2/best_model.pt` | 51 | 3.8% | 2.1% |
Each `.pt` file is a dict with these keys:
- `model_state_dict`: the weights
- `optimizer_state_dict`: the Adam state, kept so training can be resumed. It makes the files about 3× larger than the weights alone.
- `epoch`, `val_loss`, `val_accuracy`, `seed`
- `dataset_version` (always `"datasets_v2"`) and `generator_commit` (the commit that generated the training data)
## Models
| | LSTM | Transformer |
|---|---|---|
| Structure | Bidirectional LSTM encoder, unidirectional LSTM decoder | 3 encoder + 3 decoder layers, 8 heads, sinusoidal positions |
| Size | embedding 128, hidden 256, ~2.1M params | d_model 256, ~5.5M params |
| Learning rate | 1e-3 | 3e-4 |
Both architectures share the same training setup:
- Character-level vocabulary of 20 tokens: `<PAD> <SOS> <EOS>`, then `0123456789+-*/() ` (the last one is a space)
- Teacher forcing, Adam, batch size 64
- 400 warmup steps, then cosine decay down to 1e-5
- Up to 200 epochs, early stopping on validation accuracy with patience 50
- `max_input_len=64` and `max_output_len=12`. Inputs that are too long raise an error instead of being truncated.
## Usage
You need the model and tokenizer code from the GitHub repo:
```bash
git clone https://github.com/bcode0127-debug/Compositional-Reasoning-Arithmetic.git
cd Compositional-Reasoning-Arithmetic
pip install -r requirements.txt huggingface_hub
```
```python
import torch
from huggingface_hub import hf_hub_download
from data.tokenizer import create_tokenizer
from models.lstm import create_lstm_model
from models.transformer import create_transformer_model
tok = create_tokenizer()
path = hf_hub_download("Ajay0728/Compositional-reasoning-arithmetic",
"lstm/study1/seed0/best_model.pt")
ckpt = torch.load(path, map_location="cpu")
model = create_lstm_model(vocab_size=tok.vocab_size, embedding_dim=128, hidden_size=256)
# Transformer:
# model = create_transformer_model(vocab_size=tok.vocab_size, d_model=256, nhead=8,
# num_encoder_layers=3, num_decoder_layers=3,
# pad_idx=tok.pad_idx)
model.load_state_dict(ckpt["model_state_dict"])
model.eval()
def solve(expr, max_in=64, max_out=12):
ids = tok.encode_checked(expr, max_in)
src = torch.tensor([ids + [tok.pad_idx] * (max_in - len(ids))])
out = [tok.sos_idx]
with torch.no_grad():
for _ in range(max_out - 1):
dec = torch.tensor([out + [tok.pad_idx] * (max_out - len(out))])
nxt = model(src, dec)[0, len(out) - 1].argmax().item()
if nxt in (tok.eos_idx, tok.pad_idx):
break
out.append(nxt)
return tok.decode(out[1:])
print(solve("((14 - 1) * 10)"))
```
`solve` does greedy autoregressive decoding. It is the same procedure behind the numbers below, implemented in `scripts/eval/cold_verify.py`. Inputs must use the training format: fully parenthesized, with spaces around each operator.
## Training data
The training data is `datasets_v2`, generated with `data/generate_controlled.py`. Every expression is a fully parenthesized integer expression. Division always comes out exact (no remainders), and every result satisfies |result| ≤ 10,000.
| Study | Train / Val / Test (in-distribution) | OOD |
|---|---|---|
| 1: Length | 2–3 operations, depth 2–3 (8,000 / 1,000 / 1,000) | 4, 5, 6, 7 operations, depth 3 (1,000 each) |
| 2: Depth | 3 operations, depth 2 (8,000 / 1,000 / 1,000) | 3 operations, depth 3 chains (1,000) |
Validation was used to pick each checkpoint. The test split was generated after training finished and is the primary in-distribution result.
Division makes up only about 3–5% of the operators in every split, because the exact-division requirement rejects most division candidates.
## Results
All numbers are exact-match accuracy under greedy autoregressive decoding. Val and OOD show the mean across seeds 0/1/2 with [min–max]. Test shows mean ± SD.
**Study 1: length generalization**
| Model | Val | Test | OOD ops4 | OOD ops5 | OOD ops6 | OOD ops7 |
|---|---|---|---|---|---|---|
| LSTM | 35.7% [35.1–36.1] | 34.57% ± 0.95 | 14.8% [13.7–16.4] | 8.2% [7.5–9.5] | 2.7% [2.5–2.8] | 2.1% [1.8–2.5] |
| Transformer | 7.3% [6.8–7.5] | 7.83% ± 0.12 | 0.7% [0.4–1.2] | 0.6% [0.5–0.7] | 0.3% [0.1–0.5] | 0.4% [0.2–0.6] |
**Study 2: depth generalization**
| Model | Val | Test | OOD |
|---|---|---|---|
| LSTM | 31.5% [30.1–32.5] | 30.47% ± 2.14 | 7.0% [6.3–7.8] |
| Transformer | 3.9% [3.7–4.2] | 2.27% ± 0.47 | 2.7% [2.0–3.1] |
The LSTM reaches partial competence in-distribution, and its accuracy falls steadily as expressions get longer or deeper. The Transformer stays weak in every split at this data scale.
## Limitations
- **Low accuracy:** these are small models trained on 8,000 examples, so they are wrong most of the time even in-distribution.
- **Narrow input format:** they only work on the exact input format and range they were trained on. Any other format is out of scope.
- **Training environment not recorded:** the torch/numpy versions used for training were never saved, so training can't be reproduced bit for bit. Evaluation only reloads the saved weights, so it doesn't depend on that environment. The test numbers were produced with torch 2.13.0 on CPU.
- **Older results are retired:** an earlier version of this project reported results from a pipeline that silently cut inputs to 20 characters. Those numbers are not valid. Every number on this card comes from the corrected v2 pipeline.
## Citation
```bibtex
@misc{jamanjyothi2026partial,
title = {Partial Competence and Limited Transfer: Comparing LSTM and Transformer
Generalization on Arithmetic Expressions},
author = {Jamanjyothi, Ajay},
year = {2026},
note = {arXiv preprint (ID pending)}
}
```
|