Text Generation
Transformers
Safetensors
fixed-width-addition
arithmetic
interpretability
arxiv:2405.14813
custom_code
Instructions to use melephant/1-layer-addition with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use melephant/1-layer-addition with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="melephant/1-layer-addition", trust_remote_code=True)# Load model directly from transformers import AutoModelForCausalLM model = AutoModelForCausalLM.from_pretrained("melephant/1-layer-addition", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use melephant/1-layer-addition with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "melephant/1-layer-addition" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "melephant/1-layer-addition", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/melephant/1-layer-addition
- SGLang
How to use melephant/1-layer-addition with SGLang:
Install from pip and serve model
# Install SGLang from pip: pip install sglang # Start the SGLang server: python3 -m sglang.launch_server \ --model-path "melephant/1-layer-addition" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "melephant/1-layer-addition", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker images
docker run --gpus all \ --shm-size 32g \ -p 30000:30000 \ -v ~/.cache/huggingface:/root/.cache/huggingface \ --env "HF_TOKEN=<secret>" \ --ipc=host \ lmsysorg/sglang:latest \ python3 -m sglang.launch_server \ --model-path "melephant/1-layer-addition" \ --host 0.0.0.0 \ --port 30000 # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:30000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "melephant/1-layer-addition", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use melephant/1-layer-addition with Docker Model Runner:
docker model run hf.co/melephant/1-layer-addition
Publish addition-transformer run s85nnxtf
Browse filesLoadable model and complete training record.
- README.md +69 -0
- addition_transformer.py +84 -0
- attention.py +57 -0
- bilinear_mlp.py +33 -0
- config.json +43 -0
- configuration_addition.py +118 -0
- export_manifest.json +147 -0
- generation_config.json +7 -0
- initialization.py +41 -0
- model.safetensors +3 -0
- model_config.py +13 -0
- modeling_addition.py +123 -0
- tokenization_addition.py +111 -0
- tokenizer_config.json +25 -0
- training/checkpoints/final.pt +3 -0
- training/checkpoints/step_000500.pt +3 -0
- training/checkpoints/step_001000.pt +3 -0
- training/checkpoints/step_001500.pt +3 -0
- training/checkpoints/step_002000.pt +3 -0
- training/checkpoints/step_002500.pt +3 -0
- training/checkpoints/step_003000.pt +3 -0
- training/checkpoints/step_003500.pt +3 -0
- training/checkpoints/step_004000.pt +3 -0
- training/checkpoints/step_004500.pt +3 -0
- training/checkpoints/step_005000.pt +3 -0
- training/checkpoints/step_005500.pt +3 -0
- training/checkpoints/step_006000.pt +3 -0
- training/checkpoints/step_006500.pt +3 -0
- training/checkpoints/step_007000.pt +3 -0
- training/checkpoints/step_007500.pt +3 -0
- training/checkpoints/step_008000.pt +3 -0
- training/checkpoints/step_008500.pt +3 -0
- training/checkpoints/step_009000.pt +3 -0
- training/checkpoints/step_009500.pt +3 -0
- training/environment.json +19 -0
- training/metrics.jsonl +0 -0
- training/resolved_config.toml +38 -0
- training/source_snapshot.zip +3 -0
- transformer_block.py +42 -0
- vocab.json +15 -0
README.md
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
library_name: transformers
|
| 3 |
+
pipeline_tag: text-generation
|
| 4 |
+
tags:
|
| 5 |
+
- arithmetic
|
| 6 |
+
- interpretability
|
| 7 |
+
- arxiv:2405.14813
|
| 8 |
+
---
|
| 9 |
+
|
| 10 |
+
# Fixed-width addition transformer
|
| 11 |
+
|
| 12 |
+
Run `s85nnxtf` is a 1-block, bias-free causal transformer trained for
|
| 13 |
+
4-digit base-10 addition. Operands are zero-padded and answers use
|
| 14 |
+
5 digits, retaining overflow.
|
| 15 |
+
|
| 16 |
+
## Results
|
| 17 |
+
|
| 18 |
+
| Metric | Value |
|
| 19 |
+
| --- | ---: |
|
| 20 |
+
| Validation loss | 0.003520 |
|
| 21 |
+
| Validation generated-token accuracy | 99.85% |
|
| 22 |
+
| Validation exact-answer accuracy | 99.32% |
|
| 23 |
+
| No-carry exact-answer accuracy | 97.27% |
|
| 24 |
+
| Single-carry exact-answer accuracy | 100.00% |
|
| 25 |
+
| Multiple-carry exact-answer accuracy | 98.44% |
|
| 26 |
+
| Carry-chain exact-answer accuracy | 97.27% |
|
| 27 |
+
|
| 28 |
+
## Training configuration
|
| 29 |
+
|
| 30 |
+
- Updates: 10000
|
| 31 |
+
- Optimizer: muon
|
| 32 |
+
- Muon peak learning rate: 0.02
|
| 33 |
+
- AdamW peak learning rate: 0.0003
|
| 34 |
+
- Weight decay: 0.01
|
| 35 |
+
- Warmup updates: 100
|
| 36 |
+
- Minimum learning-rate ratio: 0.1
|
| 37 |
+
- Initialization: normal
|
| 38 |
+
- Seed: 0
|
| 39 |
+
- Source commit: `unavailable`
|
| 40 |
+
|
| 41 |
+
The complete resolved configuration, environment, metrics, source snapshot, and checkpoints are
|
| 42 |
+
available in [`training/`](./training/). Machine-readable hashes and metrics are in
|
| 43 |
+
[`export_manifest.json`](./export_manifest.json).
|
| 44 |
+
|
| 45 |
+
## Loading
|
| 46 |
+
|
| 47 |
+
This repository contains custom Transformers code. For reproducible or security-sensitive use,
|
| 48 |
+
pin the commit revision printed by the uploader.
|
| 49 |
+
|
| 50 |
+
```python
|
| 51 |
+
from transformers import AutoModelForCausalLM, AutoTokenizer
|
| 52 |
+
|
| 53 |
+
revision = "PINNED_COMMIT_HASH"
|
| 54 |
+
tokenizer = AutoTokenizer.from_pretrained(
|
| 55 |
+
"OWNER/REPO", trust_remote_code=True, revision=revision
|
| 56 |
+
)
|
| 57 |
+
model = AutoModelForCausalLM.from_pretrained(
|
| 58 |
+
"OWNER/REPO", trust_remote_code=True, revision=revision
|
| 59 |
+
)
|
| 60 |
+
inputs = tokenizer("0000 + 0000 =", return_tensors="pt")
|
| 61 |
+
output = model.generate(**inputs, max_new_tokens=model.config.answer_digits, do_sample=False)
|
| 62 |
+
print(tokenizer.decode(output[0], skip_special_tokens=True))
|
| 63 |
+
```
|
| 64 |
+
|
| 65 |
+
## Intended use and limitations
|
| 66 |
+
|
| 67 |
+
This model is intended for mechanistic-interpretability research on its configured fixed-width
|
| 68 |
+
addition task. It is not a general arithmetic system: inputs outside the configured grammar or
|
| 69 |
+
width are unsupported, and generated answers must not be treated as reliable calculations.
|
addition_transformer.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from torch import nn
|
| 7 |
+
|
| 8 |
+
from .initialization import initialize_module
|
| 9 |
+
from .model_config import AdditionModelConfig
|
| 10 |
+
from .transformer_block import TransformerBlock, TransformerBlockOutput
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@dataclass(frozen=True)
|
| 14 |
+
class TransformerOutput:
|
| 15 |
+
logits: torch.Tensor
|
| 16 |
+
block_outputs: tuple[TransformerBlockOutput, ...] = ()
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class AdditionTransformer(nn.Module):
|
| 20 |
+
def __init__(self, config: AdditionModelConfig, vocab_size: int) -> None:
|
| 21 |
+
super().__init__()
|
| 22 |
+
self.config = config
|
| 23 |
+
self.vocab_size = vocab_size
|
| 24 |
+
|
| 25 |
+
self.token_embedding = nn.Embedding(vocab_size, config.d_model)
|
| 26 |
+
self.position_embedding = nn.Embedding(config.max_seq_len, config.d_model)
|
| 27 |
+
self.blocks = nn.ModuleList(TransformerBlock(config) for _ in range(config.n_layers))
|
| 28 |
+
self.unembedding = nn.Linear(config.d_model, vocab_size, bias=False)
|
| 29 |
+
|
| 30 |
+
self.reset_parameters()
|
| 31 |
+
|
| 32 |
+
def reset_parameters(self) -> None:
|
| 33 |
+
initialize_module(self, self.config)
|
| 34 |
+
|
| 35 |
+
def forward(
|
| 36 |
+
self,
|
| 37 |
+
input_ids: torch.Tensor,
|
| 38 |
+
return_activations: bool = False,
|
| 39 |
+
) -> TransformerOutput:
|
| 40 |
+
batch, seq_len = input_ids.shape
|
| 41 |
+
if seq_len > self.config.max_seq_len:
|
| 42 |
+
raise ValueError(f"Sequence length {seq_len} exceeds max_seq_len={self.config.max_seq_len}.")
|
| 43 |
+
|
| 44 |
+
positions = torch.arange(seq_len, device=input_ids.device)
|
| 45 |
+
x = self.token_embedding(input_ids) + self.position_embedding(positions)[None, :, :]
|
| 46 |
+
# Modula-inspired attention scaling and residual interpolation; see arXiv:2405.14813.
|
| 47 |
+
block_outputs: list[TransformerBlockOutput] = []
|
| 48 |
+
for block in self.blocks:
|
| 49 |
+
block_output = block(x, return_pattern=return_activations)
|
| 50 |
+
x = block_output.residual_after_mlp
|
| 51 |
+
if return_activations:
|
| 52 |
+
block_outputs.append(block_output)
|
| 53 |
+
logits = self.unembedding(x)
|
| 54 |
+
|
| 55 |
+
return TransformerOutput(
|
| 56 |
+
logits=logits,
|
| 57 |
+
block_outputs=tuple(block_outputs),
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
def symmetrized_mlp_tensor(self, detach: bool = True, layer: int = 0) -> torch.Tensor:
|
| 61 |
+
if not 0 <= layer < len(self.blocks):
|
| 62 |
+
raise IndexError(f"Layer {layer} is outside the model's {len(self.blocks)} layers.")
|
| 63 |
+
return self.blocks[layer].mlp.symmetrized_bilinear_tensor(detach=detach)
|
| 64 |
+
|
| 65 |
+
def analysis_tensors(self, detach: bool = True) -> dict[str, torch.Tensor]:
|
| 66 |
+
tensors: dict[str, torch.Tensor] = {}
|
| 67 |
+
for layer, block in enumerate(self.blocks):
|
| 68 |
+
prefix = f"blocks.{layer}"
|
| 69 |
+
bilinear_tensor = block.mlp.bilinear_tensor(detach=False)
|
| 70 |
+
tensors.update(
|
| 71 |
+
{
|
| 72 |
+
f"{prefix}.attention.W_Q": block.attention.W_Q.weight,
|
| 73 |
+
f"{prefix}.attention.W_K": block.attention.W_K.weight,
|
| 74 |
+
f"{prefix}.attention.W_V": block.attention.W_V.weight,
|
| 75 |
+
f"{prefix}.attention.W_O": block.attention.W_O.weight,
|
| 76 |
+
f"{prefix}.mlp.W_1": block.mlp.W_1.weight,
|
| 77 |
+
f"{prefix}.mlp.W_2": block.mlp.W_2.weight,
|
| 78 |
+
f"{prefix}.mlp.W_O": block.mlp.W_O.weight,
|
| 79 |
+
f"{prefix}.mlp.bilinear_tensor": bilinear_tensor,
|
| 80 |
+
f"{prefix}.mlp.symmetrized_bilinear_tensor": 0.5
|
| 81 |
+
* (bilinear_tensor + bilinear_tensor.transpose(-1, -2)),
|
| 82 |
+
}
|
| 83 |
+
)
|
| 84 |
+
return {name: tensor.detach() for name, tensor in tensors.items()} if detach else tensors
|
attention.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
from torch import nn
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
@dataclass(frozen=True)
|
| 11 |
+
class AttentionOutput:
|
| 12 |
+
values: torch.Tensor
|
| 13 |
+
pattern: torch.Tensor | None = None
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class CausalSelfAttention(nn.Module):
|
| 17 |
+
def __init__(self, d_model: int, n_heads: int, bias: bool = False) -> None:
|
| 18 |
+
super().__init__()
|
| 19 |
+
if d_model % n_heads != 0:
|
| 20 |
+
raise ValueError("d_model must be divisible by n_heads.")
|
| 21 |
+
|
| 22 |
+
self.d_model = d_model
|
| 23 |
+
self.n_heads = n_heads
|
| 24 |
+
self.d_head = d_model // n_heads
|
| 25 |
+
|
| 26 |
+
self.W_Q = nn.Linear(d_model, d_model, bias=bias)
|
| 27 |
+
self.W_K = nn.Linear(d_model, d_model, bias=bias)
|
| 28 |
+
self.W_V = nn.Linear(d_model, d_model, bias=bias)
|
| 29 |
+
self.W_O = nn.Linear(d_model, d_model, bias=bias)
|
| 30 |
+
self.register_buffer("_causal_mask", torch.empty(0, 0, dtype=torch.bool), persistent=False)
|
| 31 |
+
|
| 32 |
+
def forward(self, x: torch.Tensor, return_pattern: bool = False) -> AttentionOutput:
|
| 33 |
+
batch, seq_len, _ = x.shape
|
| 34 |
+
q = self._split_heads(self.W_Q(x))
|
| 35 |
+
k = self._split_heads(self.W_K(x))
|
| 36 |
+
v = self._split_heads(self.W_V(x))
|
| 37 |
+
|
| 38 |
+
scores = torch.matmul(q, k.transpose(-1, -2)) / self.d_head
|
| 39 |
+
if self._causal_mask.shape[0] < seq_len or self._causal_mask.device != x.device:
|
| 40 |
+
self._causal_mask = torch.triu(
|
| 41 |
+
torch.ones(seq_len, seq_len, dtype=torch.bool, device=x.device),
|
| 42 |
+
diagonal=1,
|
| 43 |
+
)
|
| 44 |
+
causal_mask = self._causal_mask[:seq_len, :seq_len]
|
| 45 |
+
scores = scores.masked_fill(causal_mask, float("-inf"))
|
| 46 |
+
pattern = F.softmax(scores, dim=-1)
|
| 47 |
+
|
| 48 |
+
attended = torch.matmul(pattern, v) / 3.0
|
| 49 |
+
values = self.W_O(self._merge_heads(attended, batch, seq_len))
|
| 50 |
+
return AttentionOutput(values=values, pattern=pattern if return_pattern else None)
|
| 51 |
+
|
| 52 |
+
def _split_heads(self, x: torch.Tensor) -> torch.Tensor:
|
| 53 |
+
batch, seq_len, _ = x.shape
|
| 54 |
+
return x.view(batch, seq_len, self.n_heads, self.d_head).transpose(1, 2)
|
| 55 |
+
|
| 56 |
+
def _merge_heads(self, x: torch.Tensor, batch: int, seq_len: int) -> torch.Tensor:
|
| 57 |
+
return x.transpose(1, 2).contiguous().view(batch, seq_len, self.d_model)
|
bilinear_mlp.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import torch
|
| 4 |
+
from torch import nn
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class BilinearMLP(nn.Module):
|
| 8 |
+
"""Bilinear MLP: W_O((W_1 x) * (W_2 x))."""
|
| 9 |
+
|
| 10 |
+
def __init__(self, d_model: int, d_hidden: int, bias: bool = False) -> None:
|
| 11 |
+
super().__init__()
|
| 12 |
+
self.d_model = d_model
|
| 13 |
+
self.d_hidden = d_hidden
|
| 14 |
+
self.W_1 = nn.Linear(d_model, d_hidden, bias=bias)
|
| 15 |
+
self.W_2 = nn.Linear(d_model, d_hidden, bias=bias)
|
| 16 |
+
self.W_O = nn.Linear(d_hidden, d_model, bias=bias)
|
| 17 |
+
|
| 18 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 19 |
+
return self.W_O(self.W_1(x) * self.W_2(x))
|
| 20 |
+
|
| 21 |
+
def bilinear_tensor(self, detach: bool = False) -> torch.Tensor:
|
| 22 |
+
tensor = torch.einsum(
|
| 23 |
+
"oh,hi,hj->oij",
|
| 24 |
+
self.W_O.weight,
|
| 25 |
+
self.W_1.weight,
|
| 26 |
+
self.W_2.weight,
|
| 27 |
+
)
|
| 28 |
+
return tensor.detach() if detach else tensor
|
| 29 |
+
|
| 30 |
+
def symmetrized_bilinear_tensor(self, detach: bool = False) -> torch.Tensor:
|
| 31 |
+
tensor = self.bilinear_tensor(detach=False)
|
| 32 |
+
symmetrized = 0.5 * (tensor + tensor.transpose(-1, -2))
|
| 33 |
+
return symmetrized.detach() if detach else symmetrized
|
config.json
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"answer_digits": 5,
|
| 3 |
+
"architecture_version": 2,
|
| 4 |
+
"architectures": [
|
| 5 |
+
"AdditionForCausalLM"
|
| 6 |
+
],
|
| 7 |
+
"attention_logit_divisor": "d_head",
|
| 8 |
+
"attention_output_scale": 0.3333333333333333,
|
| 9 |
+
"auto_map": {
|
| 10 |
+
"AutoConfig": "configuration_addition.AdditionConfig",
|
| 11 |
+
"AutoModelForCausalLM": "modeling_addition.AdditionForCausalLM"
|
| 12 |
+
},
|
| 13 |
+
"base": 10,
|
| 14 |
+
"bias": false,
|
| 15 |
+
"bos_token_id": 0,
|
| 16 |
+
"d_mlp": 128,
|
| 17 |
+
"d_model": 64,
|
| 18 |
+
"digit_token_offset": 3,
|
| 19 |
+
"dtype": "float32",
|
| 20 |
+
"eos_token_id": null,
|
| 21 |
+
"equals_token_id": 2,
|
| 22 |
+
"export_format_version": 2,
|
| 23 |
+
"hidden_size": 64,
|
| 24 |
+
"init_mode": "normal",
|
| 25 |
+
"intermediate_size": 128,
|
| 26 |
+
"is_decoder": true,
|
| 27 |
+
"max_position_embeddings": 16,
|
| 28 |
+
"max_seq_len": 16,
|
| 29 |
+
"model_type": "fixed-width-addition",
|
| 30 |
+
"n_heads": 4,
|
| 31 |
+
"n_layers": 1,
|
| 32 |
+
"normalization": "none",
|
| 33 |
+
"num_attention_heads": 4,
|
| 34 |
+
"num_hidden_layers": 1,
|
| 35 |
+
"operand_digits": 4,
|
| 36 |
+
"pad_token_id": null,
|
| 37 |
+
"plus_token_id": 1,
|
| 38 |
+
"residual_alpha": 0.5,
|
| 39 |
+
"tie_word_embeddings": false,
|
| 40 |
+
"transformers_version": "5.15.0",
|
| 41 |
+
"use_cache": false,
|
| 42 |
+
"vocab_size": 13
|
| 43 |
+
}
|
configuration_addition.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from transformers import PreTrainedConfig
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class AdditionConfig(PreTrainedConfig):
|
| 7 |
+
model_type = "fixed-width-addition"
|
| 8 |
+
|
| 9 |
+
def __init__(
|
| 10 |
+
self,
|
| 11 |
+
operand_digits: int = 5,
|
| 12 |
+
d_model: int = 64,
|
| 13 |
+
n_heads: int = 4,
|
| 14 |
+
d_mlp: int = 128,
|
| 15 |
+
n_layers: int = 1,
|
| 16 |
+
max_seq_len: int = 19,
|
| 17 |
+
init_mode: str = "normal",
|
| 18 |
+
residual_alpha: float | None = None,
|
| 19 |
+
**kwargs,
|
| 20 |
+
) -> None:
|
| 21 |
+
expected_residual_alpha = 1.0 / (2.0 * n_layers) if n_layers > 0 else 0.0
|
| 22 |
+
if residual_alpha is not None and residual_alpha != expected_residual_alpha:
|
| 23 |
+
raise ValueError(
|
| 24 |
+
f"residual_alpha must be 1 / (2 * n_layers) = {expected_residual_alpha}."
|
| 25 |
+
)
|
| 26 |
+
invariants = {
|
| 27 |
+
"answer_digits": operand_digits + 1,
|
| 28 |
+
"base": 10,
|
| 29 |
+
"vocab_size": 13,
|
| 30 |
+
"hidden_size": d_model,
|
| 31 |
+
"num_attention_heads": n_heads,
|
| 32 |
+
"intermediate_size": d_mlp,
|
| 33 |
+
"num_hidden_layers": n_layers,
|
| 34 |
+
"max_position_embeddings": max_seq_len,
|
| 35 |
+
"bias": False,
|
| 36 |
+
"normalization": "none",
|
| 37 |
+
"attention_logit_divisor": "d_head",
|
| 38 |
+
"attention_output_scale": 1.0 / 3.0,
|
| 39 |
+
"bos_token_id": 0,
|
| 40 |
+
"plus_token_id": 1,
|
| 41 |
+
"equals_token_id": 2,
|
| 42 |
+
"digit_token_offset": 3,
|
| 43 |
+
"architecture_version": 2,
|
| 44 |
+
"export_format_version": 2,
|
| 45 |
+
"use_cache": False,
|
| 46 |
+
"tie_word_embeddings": False,
|
| 47 |
+
"is_decoder": True,
|
| 48 |
+
"is_encoder_decoder": False,
|
| 49 |
+
"eos_token_id": None,
|
| 50 |
+
"pad_token_id": None,
|
| 51 |
+
}
|
| 52 |
+
for name, expected in invariants.items():
|
| 53 |
+
if name in kwargs and kwargs.pop(name) != expected:
|
| 54 |
+
raise ValueError(f"{name} is fixed at {expected!r} for this architecture.")
|
| 55 |
+
self.operand_digits = operand_digits
|
| 56 |
+
self.answer_digits = operand_digits + 1
|
| 57 |
+
self.base = 10
|
| 58 |
+
self.vocab_size = 13
|
| 59 |
+
self.d_model = d_model
|
| 60 |
+
self.hidden_size = d_model
|
| 61 |
+
self.n_heads = n_heads
|
| 62 |
+
self.num_attention_heads = n_heads
|
| 63 |
+
self.d_mlp = d_mlp
|
| 64 |
+
self.intermediate_size = d_mlp
|
| 65 |
+
self.n_layers = n_layers
|
| 66 |
+
self.num_hidden_layers = n_layers
|
| 67 |
+
self.max_seq_len = max_seq_len
|
| 68 |
+
self.max_position_embeddings = max_seq_len
|
| 69 |
+
self.init_mode = str(init_mode)
|
| 70 |
+
self.residual_alpha = expected_residual_alpha
|
| 71 |
+
self.bias = False
|
| 72 |
+
self.normalization = "none"
|
| 73 |
+
self.attention_logit_divisor = "d_head"
|
| 74 |
+
self.attention_output_scale = 1.0 / 3.0
|
| 75 |
+
self.bos_token_id = 0
|
| 76 |
+
self.plus_token_id = 1
|
| 77 |
+
self.equals_token_id = 2
|
| 78 |
+
self.digit_token_offset = 3
|
| 79 |
+
self.architecture_version = 2
|
| 80 |
+
self.export_format_version = 2
|
| 81 |
+
self.use_cache = False
|
| 82 |
+
self.tie_word_embeddings = False
|
| 83 |
+
self.is_decoder = True
|
| 84 |
+
self.is_encoder_decoder = False
|
| 85 |
+
self._validate_architecture()
|
| 86 |
+
super().__init__(
|
| 87 |
+
bos_token_id=self.bos_token_id,
|
| 88 |
+
eos_token_id=None,
|
| 89 |
+
pad_token_id=None,
|
| 90 |
+
tie_word_embeddings=False,
|
| 91 |
+
is_decoder=True,
|
| 92 |
+
**kwargs,
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
@property
|
| 96 |
+
def d_head(self) -> int:
|
| 97 |
+
return self.d_model // self.n_heads
|
| 98 |
+
|
| 99 |
+
def _validate_architecture(self) -> None:
|
| 100 |
+
if self.operand_digits < 1:
|
| 101 |
+
raise ValueError("operand_digits must be positive.")
|
| 102 |
+
if self.d_model < 1 or self.n_heads < 1 or self.d_mlp < 1 or self.n_layers < 1:
|
| 103 |
+
raise ValueError("Model dimensions must be positive.")
|
| 104 |
+
if self.d_model % self.n_heads != 0:
|
| 105 |
+
raise ValueError("d_model must be divisible by n_heads.")
|
| 106 |
+
required_length = 3 * self.operand_digits + 4
|
| 107 |
+
if self.max_seq_len < required_length:
|
| 108 |
+
raise ValueError(
|
| 109 |
+
f"max_seq_len={self.max_seq_len} is too small for {required_length} full-sequence tokens."
|
| 110 |
+
)
|
| 111 |
+
if self.init_mode not in {"normal", "orthogonal"}:
|
| 112 |
+
raise ValueError(f"Unsupported init_mode: {self.init_mode}")
|
| 113 |
+
expected_residual_alpha = 1.0 / (2.0 * self.n_layers)
|
| 114 |
+
if self.residual_alpha != expected_residual_alpha:
|
| 115 |
+
raise ValueError("residual_alpha must equal 1 / (2 * n_layers).")
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
AdditionConfig.register_for_auto_class("AutoConfig")
|
export_manifest.json
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"environment": {
|
| 3 |
+
"cuda": "12.6",
|
| 4 |
+
"device": "cuda",
|
| 5 |
+
"device_name": "NVIDIA GeForce RTX 4060 Laptop GPU",
|
| 6 |
+
"git_commit": null,
|
| 7 |
+
"git_dirty": true,
|
| 8 |
+
"packages": {
|
| 9 |
+
"huggingface-hub": "1.27.0",
|
| 10 |
+
"numpy": "2.5.2",
|
| 11 |
+
"pytest": "9.1.1",
|
| 12 |
+
"safetensors": "0.8.0",
|
| 13 |
+
"sympy": "1.14.0",
|
| 14 |
+
"transformers": "5.15.0",
|
| 15 |
+
"wandb": "0.28.1"
|
| 16 |
+
},
|
| 17 |
+
"platform": "Windows-11-10.0.26200-SP0",
|
| 18 |
+
"python": "3.12.4",
|
| 19 |
+
"torch": "2.13.0+cu126"
|
| 20 |
+
},
|
| 21 |
+
"export_files_sha256": {
|
| 22 |
+
"README.md": "b64a05029567e76bcd032dc4ea57e18c1c805845a196ffab30af6b28a0d06165",
|
| 23 |
+
"addition_transformer.py": "af1e1e1641aeaf2f974252e7177730f6356278c4fff0f47686d88aeed5673089",
|
| 24 |
+
"attention.py": "d1c738ba96f937c39cd66ad470d786acfad70ac368ecb5c1c25290b20edc2106",
|
| 25 |
+
"bilinear_mlp.py": "95d8b248058bdbce7ea7c13ba9ebf49ee69b20b95714a43040f19c9c528225c7",
|
| 26 |
+
"config.json": "481d105a5848390efe0e98edae059cded1b22611902ea15d2ab8b079cea86517",
|
| 27 |
+
"configuration_addition.py": "7f152f6c5917adedfb211e9332a9db08780363671932190b7df1d782c4fa3de5",
|
| 28 |
+
"generation_config.json": "13cc88dd90bbb2a181ade617b6a41b35998e933cc1eb356f7906c320a7d04b5d",
|
| 29 |
+
"initialization.py": "01de663e2494370c6bb2a9d4f770411104dbc44cbb97917fa18ba89ed9959d32",
|
| 30 |
+
"model.safetensors": "68be5fc852029537ac74bc0f13af4533a3efda7a4aac85396bd3a94b62f47098",
|
| 31 |
+
"model_config.py": "211bc4cc3e7379dfe469d5acb3bcfbe4c65adf710af5f2dd21ea49212ccb08fb",
|
| 32 |
+
"modeling_addition.py": "b5349de597570045eb3c21803f6a4b077605f5468f99e26ddb1678964d01fcea",
|
| 33 |
+
"tokenization_addition.py": "bc63473854f8f8fecfc3375757d257dfb34a33a0c30ee5a1ddffb3b984b8602f",
|
| 34 |
+
"tokenizer_config.json": "630ab54705ab78ecddef9c0c6543f477036b5a71cb2736a3db59462f464cbd06",
|
| 35 |
+
"transformer_block.py": "a66ce47f5b504abcc83bcbdfecbc71e01a9968d10bde534ad375e0c7158f04dc",
|
| 36 |
+
"vocab.json": "d6fdf48e6e2087546f1bfb7285a71b1072f5452934e8a33e831d069f7faf56a8"
|
| 37 |
+
},
|
| 38 |
+
"final_metrics": {
|
| 39 |
+
"examples_processed": 2560000,
|
| 40 |
+
"grad/clip_fraction": 0.0,
|
| 41 |
+
"grad/global_norm": 0.17957071997225285,
|
| 42 |
+
"lr/adamw": 2.9999999999999997e-05,
|
| 43 |
+
"lr/muon": 0.002,
|
| 44 |
+
"test/carry_chain/exact_accuracy": 0.97265625,
|
| 45 |
+
"test/carry_chain/generated_token_accuracy": 0.99375,
|
| 46 |
+
"test/carry_chain/loss": 0.012557083368301391,
|
| 47 |
+
"test/carry_chain/position_0/accuracy": 0.99609375,
|
| 48 |
+
"test/carry_chain/position_1/accuracy": 0.9765625,
|
| 49 |
+
"test/carry_chain/position_2/accuracy": 0.99609375,
|
| 50 |
+
"test/carry_chain/position_3/accuracy": 1.0,
|
| 51 |
+
"test/carry_chain/position_4/accuracy": 1.0,
|
| 52 |
+
"test/carry_chain/teacher_forced_token_accuracy": 0.99453125,
|
| 53 |
+
"test/multiple_carry/exact_accuracy": 0.984375,
|
| 54 |
+
"test/multiple_carry/generated_token_accuracy": 0.996875,
|
| 55 |
+
"test/multiple_carry/loss": 0.005255512893199921,
|
| 56 |
+
"test/multiple_carry/position_0/accuracy": 1.0,
|
| 57 |
+
"test/multiple_carry/position_1/accuracy": 0.984375,
|
| 58 |
+
"test/multiple_carry/position_2/accuracy": 1.0,
|
| 59 |
+
"test/multiple_carry/position_3/accuracy": 1.0,
|
| 60 |
+
"test/multiple_carry/position_4/accuracy": 1.0,
|
| 61 |
+
"test/multiple_carry/teacher_forced_token_accuracy": 0.996875,
|
| 62 |
+
"test/no_carry/exact_accuracy": 0.97265625,
|
| 63 |
+
"test/no_carry/generated_token_accuracy": 0.99453125,
|
| 64 |
+
"test/no_carry/loss": 0.014795759320259094,
|
| 65 |
+
"test/no_carry/position_0/accuracy": 1.0,
|
| 66 |
+
"test/no_carry/position_1/accuracy": 0.9765625,
|
| 67 |
+
"test/no_carry/position_2/accuracy": 0.99609375,
|
| 68 |
+
"test/no_carry/position_3/accuracy": 1.0,
|
| 69 |
+
"test/no_carry/position_4/accuracy": 1.0,
|
| 70 |
+
"test/no_carry/teacher_forced_token_accuracy": 0.99453125,
|
| 71 |
+
"test/single_carry/exact_accuracy": 1.0,
|
| 72 |
+
"test/single_carry/generated_token_accuracy": 1.0,
|
| 73 |
+
"test/single_carry/loss": 0.0010194769129157066,
|
| 74 |
+
"test/single_carry/position_0/accuracy": 1.0,
|
| 75 |
+
"test/single_carry/position_1/accuracy": 1.0,
|
| 76 |
+
"test/single_carry/position_2/accuracy": 1.0,
|
| 77 |
+
"test/single_carry/position_3/accuracy": 1.0,
|
| 78 |
+
"test/single_carry/position_4/accuracy": 1.0,
|
| 79 |
+
"test/single_carry/teacher_forced_token_accuracy": 1.0,
|
| 80 |
+
"throughput/examples_per_second": 12624.497642389888,
|
| 81 |
+
"train/loss": 0.002294534471002407,
|
| 82 |
+
"train/token_accuracy": 0.9989843726158142,
|
| 83 |
+
"val/exact_accuracy": 0.9931640625,
|
| 84 |
+
"val/generated_token_accuracy": 0.99853515625,
|
| 85 |
+
"val/loss": 0.003520155092701316,
|
| 86 |
+
"val/position_0/accuracy": 0.99951171875,
|
| 87 |
+
"val/position_1/accuracy": 0.994140625,
|
| 88 |
+
"val/position_2/accuracy": 0.9990234375,
|
| 89 |
+
"val/position_3/accuracy": 1.0,
|
| 90 |
+
"val/position_4/accuracy": 1.0,
|
| 91 |
+
"val/teacher_forced_token_accuracy": 0.9986328125
|
| 92 |
+
},
|
| 93 |
+
"resolved_config": {
|
| 94 |
+
"data": {
|
| 95 |
+
"base": 10,
|
| 96 |
+
"digits": 4
|
| 97 |
+
},
|
| 98 |
+
"model": {
|
| 99 |
+
"d_mlp": 128,
|
| 100 |
+
"d_model": 64,
|
| 101 |
+
"init_mode": "normal",
|
| 102 |
+
"max_seq_len": 16,
|
| 103 |
+
"n_heads": 4,
|
| 104 |
+
"n_layers": 1
|
| 105 |
+
},
|
| 106 |
+
"train": {
|
| 107 |
+
"adamw_lr": 0.0003,
|
| 108 |
+
"batch_size": 256,
|
| 109 |
+
"checkpoint_every": 500,
|
| 110 |
+
"device": "auto",
|
| 111 |
+
"eval_batch_size": 512,
|
| 112 |
+
"eval_every": 100,
|
| 113 |
+
"eval_seed": 1,
|
| 114 |
+
"grad_clip_norm": 1.0,
|
| 115 |
+
"log_every": 10,
|
| 116 |
+
"min_lr_ratio": 0.1,
|
| 117 |
+
"muon_lr": 0.02,
|
| 118 |
+
"num_targeted_examples": 256,
|
| 119 |
+
"num_val_examples": 2048,
|
| 120 |
+
"optimizer": "muon",
|
| 121 |
+
"progress_bar": true,
|
| 122 |
+
"run_root": ".",
|
| 123 |
+
"seed": 0,
|
| 124 |
+
"steps": 10000,
|
| 125 |
+
"weight_decay": 0.01
|
| 126 |
+
},
|
| 127 |
+
"wandb": {
|
| 128 |
+
"entity": null,
|
| 129 |
+
"group": null,
|
| 130 |
+
"log_model": false,
|
| 131 |
+
"mode": "auto",
|
| 132 |
+
"name": null,
|
| 133 |
+
"project": "circuits-addition",
|
| 134 |
+
"tags": []
|
| 135 |
+
}
|
| 136 |
+
},
|
| 137 |
+
"run_id": "s85nnxtf",
|
| 138 |
+
"schema_version": 1,
|
| 139 |
+
"training_files_sha256": {
|
| 140 |
+
"checkpoints/final.pt": "8ab3e4443c586c75fde95d36fdfed473f4e6fbf374bd318e6165e875727c5285",
|
| 141 |
+
"environment.json": "6c7ded30b327853d18e87320549e118d92e48cb606ce96daa673c94eb07a965a",
|
| 142 |
+
"metrics.jsonl": "da503ac47bc1604fc84d07353c9eab7acc1b82be07dc846960e1979049e47d4f",
|
| 143 |
+
"resolved_config.toml": "c87577c6f48541b9243ba37952ba82eb11cc008c62bc075779423f8ba35e7c68",
|
| 144 |
+
"source_snapshot.zip": "a4aca21623a11159fef2e4d3dce007e03737b6909872cda92210c3903c5ea357"
|
| 145 |
+
},
|
| 146 |
+
"training_step": 10000
|
| 147 |
+
}
|
generation_config.json
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"bos_token_id": 0,
|
| 3 |
+
"do_sample": false,
|
| 4 |
+
"max_new_tokens": 5,
|
| 5 |
+
"transformers_version": "5.15.0",
|
| 6 |
+
"use_cache": false
|
| 7 |
+
}
|
initialization.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import math
|
| 4 |
+
|
| 5 |
+
from torch import nn
|
| 6 |
+
|
| 7 |
+
from .model_config import AdditionModelConfig
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def initialize_module(module: nn.Module, config: AdditionModelConfig) -> None:
|
| 11 |
+
init_mode = str(config.init_mode)
|
| 12 |
+
if init_mode == "normal":
|
| 13 |
+
_initialize_normal(module)
|
| 14 |
+
elif init_mode == "orthogonal":
|
| 15 |
+
_initialize_orthogonal(module)
|
| 16 |
+
else:
|
| 17 |
+
raise ValueError(f"Unsupported init mode: {init_mode}")
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def _initialize_normal(module: nn.Module) -> None:
|
| 21 |
+
for child in module.modules():
|
| 22 |
+
if isinstance(child, nn.Embedding):
|
| 23 |
+
nn.init.normal_(child.weight, mean=0.0, std=1.0 / math.sqrt(2.0))
|
| 24 |
+
elif isinstance(child, nn.Linear):
|
| 25 |
+
nn.init.normal_(child.weight, mean=0.0, std=1.0 / math.sqrt(child.in_features))
|
| 26 |
+
_zero_bias(child)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def _initialize_orthogonal(module: nn.Module) -> None:
|
| 30 |
+
for child in module.modules():
|
| 31 |
+
if isinstance(child, nn.Embedding):
|
| 32 |
+
nn.init.normal_(child.weight, mean=0.0, std=1.0 / math.sqrt(2.0))
|
| 33 |
+
elif isinstance(child, nn.Linear):
|
| 34 |
+
gain = math.sqrt(child.out_features / child.in_features) if child.out_features > child.in_features else 1.0
|
| 35 |
+
nn.init.orthogonal_(child.weight, gain=gain)
|
| 36 |
+
_zero_bias(child)
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def _zero_bias(module: nn.Linear) -> None:
|
| 40 |
+
if module.bias is not None:
|
| 41 |
+
nn.init.zeros_(module.bias)
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:68be5fc852029537ac74bc0f13af4533a3efda7a4aac85396bd3a94b62f47098
|
| 3 |
+
size 175592
|
model_config.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import Protocol
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
class AdditionModelConfig(Protocol):
|
| 7 |
+
d_model: int
|
| 8 |
+
n_heads: int
|
| 9 |
+
d_mlp: int
|
| 10 |
+
n_layers: int
|
| 11 |
+
max_seq_len: int
|
| 12 |
+
init_mode: str
|
| 13 |
+
residual_alpha: float
|
modeling_addition.py
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from typing import Any
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
import torch.nn.functional as F
|
| 7 |
+
from torch import nn
|
| 8 |
+
from transformers import GenerationMixin, PreTrainedModel
|
| 9 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 10 |
+
|
| 11 |
+
from .addition_transformer import AdditionTransformer
|
| 12 |
+
from .configuration_addition import AdditionConfig
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class AdditionForCausalLM(PreTrainedModel, GenerationMixin):
|
| 16 |
+
config_class = AdditionConfig
|
| 17 |
+
base_model_prefix = "model"
|
| 18 |
+
main_input_name = "input_ids"
|
| 19 |
+
|
| 20 |
+
def __init__(self, config: AdditionConfig) -> None:
|
| 21 |
+
super().__init__(config)
|
| 22 |
+
self.model = AdditionTransformer(config, vocab_size=config.vocab_size)
|
| 23 |
+
self.post_init()
|
| 24 |
+
|
| 25 |
+
def _init_weights(self, module: nn.Module) -> None:
|
| 26 |
+
# AdditionTransformer owns initialization so research and exported models remain identical.
|
| 27 |
+
return None
|
| 28 |
+
|
| 29 |
+
def get_input_embeddings(self) -> nn.Embedding:
|
| 30 |
+
return self.model.token_embedding
|
| 31 |
+
|
| 32 |
+
def set_input_embeddings(self, value: nn.Embedding) -> None:
|
| 33 |
+
self.model.token_embedding = value
|
| 34 |
+
|
| 35 |
+
def get_output_embeddings(self) -> nn.Linear:
|
| 36 |
+
return self.model.unembedding
|
| 37 |
+
|
| 38 |
+
def set_output_embeddings(self, value: nn.Linear) -> None:
|
| 39 |
+
self.model.unembedding = value
|
| 40 |
+
|
| 41 |
+
def forward(
|
| 42 |
+
self,
|
| 43 |
+
input_ids: torch.Tensor,
|
| 44 |
+
attention_mask: torch.Tensor | None = None,
|
| 45 |
+
labels: torch.Tensor | None = None,
|
| 46 |
+
past_key_values: Any | None = None,
|
| 47 |
+
use_cache: bool | None = None,
|
| 48 |
+
output_attentions: bool | None = None,
|
| 49 |
+
output_hidden_states: bool | None = None,
|
| 50 |
+
return_dict: bool | None = None,
|
| 51 |
+
**kwargs: Any,
|
| 52 |
+
) -> CausalLMOutputWithPast | tuple[torch.Tensor, ...]:
|
| 53 |
+
if kwargs:
|
| 54 |
+
names = ", ".join(sorted(kwargs))
|
| 55 |
+
raise TypeError(f"Unsupported model inputs: {names}")
|
| 56 |
+
if past_key_values is not None or use_cache:
|
| 57 |
+
raise ValueError("AdditionForCausalLM does not implement a key-value cache.")
|
| 58 |
+
if attention_mask is not None:
|
| 59 |
+
if attention_mask.shape != input_ids.shape:
|
| 60 |
+
raise ValueError("attention_mask must have the same shape as input_ids.")
|
| 61 |
+
if not bool(torch.all(attention_mask != 0)):
|
| 62 |
+
raise ValueError("Padding is unsupported; attention_mask must contain only ones.")
|
| 63 |
+
|
| 64 |
+
return_dict = self.config.return_dict if return_dict is None else return_dict
|
| 65 |
+
output_attentions = bool(output_attentions)
|
| 66 |
+
output_hidden_states = bool(output_hidden_states)
|
| 67 |
+
core_output = self.model(
|
| 68 |
+
input_ids,
|
| 69 |
+
return_activations=output_attentions or output_hidden_states,
|
| 70 |
+
)
|
| 71 |
+
loss = None
|
| 72 |
+
if labels is not None:
|
| 73 |
+
if labels.shape != input_ids.shape:
|
| 74 |
+
raise ValueError("labels must have the same shape as input_ids.")
|
| 75 |
+
loss = F.cross_entropy(
|
| 76 |
+
core_output.logits[:, :-1, :].contiguous().view(-1, self.config.vocab_size),
|
| 77 |
+
labels[:, 1:].contiguous().view(-1),
|
| 78 |
+
ignore_index=-100,
|
| 79 |
+
)
|
| 80 |
+
|
| 81 |
+
hidden_states = None
|
| 82 |
+
if output_hidden_states:
|
| 83 |
+
hidden_states = (
|
| 84 |
+
core_output.block_outputs[0].residual_pre_attention,
|
| 85 |
+
*(block.residual_after_mlp for block in core_output.block_outputs),
|
| 86 |
+
)
|
| 87 |
+
attentions = (
|
| 88 |
+
tuple(block.attention_pattern for block in core_output.block_outputs)
|
| 89 |
+
if output_attentions
|
| 90 |
+
else None
|
| 91 |
+
)
|
| 92 |
+
|
| 93 |
+
if not return_dict:
|
| 94 |
+
values = (core_output.logits, None, hidden_states, attentions)
|
| 95 |
+
return ((loss,) + values) if loss is not None else values
|
| 96 |
+
return CausalLMOutputWithPast(
|
| 97 |
+
loss=loss,
|
| 98 |
+
logits=core_output.logits,
|
| 99 |
+
past_key_values=None,
|
| 100 |
+
hidden_states=hidden_states,
|
| 101 |
+
attentions=attentions,
|
| 102 |
+
)
|
| 103 |
+
|
| 104 |
+
def prepare_inputs_for_generation(
|
| 105 |
+
self,
|
| 106 |
+
input_ids: torch.Tensor,
|
| 107 |
+
attention_mask: torch.Tensor | None = None,
|
| 108 |
+
**kwargs: Any,
|
| 109 |
+
) -> dict[str, torch.Tensor | None | bool]:
|
| 110 |
+
return {
|
| 111 |
+
"input_ids": input_ids,
|
| 112 |
+
"attention_mask": attention_mask,
|
| 113 |
+
"use_cache": False,
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
def analysis_tensors(self, detach: bool = True) -> dict[str, torch.Tensor]:
|
| 117 |
+
return self.model.analysis_tensors(detach=detach)
|
| 118 |
+
|
| 119 |
+
def symmetrized_mlp_tensor(self, detach: bool = True, layer: int = 0) -> torch.Tensor:
|
| 120 |
+
return self.model.symmetrized_mlp_tensor(detach=detach, layer=layer)
|
| 121 |
+
|
| 122 |
+
|
| 123 |
+
AdditionForCausalLM.register_for_auto_class("AutoModelForCausalLM")
|
tokenization_addition.py
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import json
|
| 4 |
+
from pathlib import Path
|
| 5 |
+
|
| 6 |
+
from transformers import PreTrainedTokenizer
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
CANONICAL_VOCAB = {"<BOS>": 0, "+": 1, "=": 2, **{str(digit): digit + 3 for digit in range(10)}}
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
class AdditionTokenizer(PreTrainedTokenizer):
|
| 13 |
+
vocab_files_names = {"vocab_file": "vocab.json"}
|
| 14 |
+
model_input_names = ["input_ids", "attention_mask"]
|
| 15 |
+
|
| 16 |
+
def __init__(self, vocab_file: str | None = None, **kwargs) -> None:
|
| 17 |
+
if vocab_file is None:
|
| 18 |
+
vocab = dict(CANONICAL_VOCAB)
|
| 19 |
+
else:
|
| 20 |
+
with Path(vocab_file).open("r", encoding="utf-8") as handle:
|
| 21 |
+
vocab = json.load(handle)
|
| 22 |
+
if vocab != CANONICAL_VOCAB:
|
| 23 |
+
raise ValueError("AdditionTokenizer requires the canonical 13-token vocabulary.")
|
| 24 |
+
self._vocab = vocab
|
| 25 |
+
self._ids_to_tokens = {token_id: token for token, token_id in vocab.items()}
|
| 26 |
+
kwargs.pop("bos_token", None)
|
| 27 |
+
kwargs.pop("eos_token", None)
|
| 28 |
+
kwargs.pop("pad_token", None)
|
| 29 |
+
kwargs.pop("unk_token", None)
|
| 30 |
+
super().__init__(
|
| 31 |
+
bos_token="<BOS>",
|
| 32 |
+
eos_token=None,
|
| 33 |
+
pad_token=None,
|
| 34 |
+
unk_token=None,
|
| 35 |
+
**kwargs,
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
@property
|
| 39 |
+
def vocab_size(self) -> int:
|
| 40 |
+
return len(self._vocab)
|
| 41 |
+
|
| 42 |
+
def get_vocab(self) -> dict[str, int]:
|
| 43 |
+
return dict(self._vocab)
|
| 44 |
+
|
| 45 |
+
def _tokenize(self, text: str, **kwargs) -> list[str]:
|
| 46 |
+
compact = "".join(text.split())
|
| 47 |
+
invalid = sorted(set(compact) - set("0123456789+="))
|
| 48 |
+
if invalid:
|
| 49 |
+
raise ValueError(f"Unsupported characters for addition tokenizer: {''.join(invalid)}")
|
| 50 |
+
return list(compact)
|
| 51 |
+
|
| 52 |
+
def _convert_token_to_id(self, token: str) -> int:
|
| 53 |
+
try:
|
| 54 |
+
return self._vocab[token]
|
| 55 |
+
except KeyError as exc:
|
| 56 |
+
raise ValueError(f"Unknown addition token: {token!r}") from exc
|
| 57 |
+
|
| 58 |
+
def _convert_id_to_token(self, index: int) -> str:
|
| 59 |
+
try:
|
| 60 |
+
return self._ids_to_tokens[index]
|
| 61 |
+
except KeyError as exc:
|
| 62 |
+
raise ValueError(f"Unknown addition token ID: {index}") from exc
|
| 63 |
+
|
| 64 |
+
def convert_tokens_to_string(self, tokens: list[str]) -> str:
|
| 65 |
+
return "".join(tokens)
|
| 66 |
+
|
| 67 |
+
def build_inputs_with_special_tokens(
|
| 68 |
+
self,
|
| 69 |
+
token_ids_0: list[int],
|
| 70 |
+
token_ids_1: list[int] | None = None,
|
| 71 |
+
) -> list[int]:
|
| 72 |
+
if token_ids_1 is not None:
|
| 73 |
+
raise ValueError("AdditionTokenizer does not support sequence pairs.")
|
| 74 |
+
return [self.bos_token_id, *token_ids_0]
|
| 75 |
+
|
| 76 |
+
def get_special_tokens_mask(
|
| 77 |
+
self,
|
| 78 |
+
token_ids_0: list[int],
|
| 79 |
+
token_ids_1: list[int] | None = None,
|
| 80 |
+
already_has_special_tokens: bool = False,
|
| 81 |
+
) -> list[int]:
|
| 82 |
+
if already_has_special_tokens:
|
| 83 |
+
return [int(token_id == self.bos_token_id) for token_id in token_ids_0]
|
| 84 |
+
if token_ids_1 is not None:
|
| 85 |
+
raise ValueError("AdditionTokenizer does not support sequence pairs.")
|
| 86 |
+
return [1, *([0] * len(token_ids_0))]
|
| 87 |
+
|
| 88 |
+
def create_token_type_ids_from_sequences(
|
| 89 |
+
self,
|
| 90 |
+
token_ids_0: list[int],
|
| 91 |
+
token_ids_1: list[int] | None = None,
|
| 92 |
+
) -> list[int]:
|
| 93 |
+
if token_ids_1 is not None:
|
| 94 |
+
raise ValueError("AdditionTokenizer does not support sequence pairs.")
|
| 95 |
+
return [0] * (len(token_ids_0) + 1)
|
| 96 |
+
|
| 97 |
+
def save_vocabulary(
|
| 98 |
+
self,
|
| 99 |
+
save_directory: str,
|
| 100 |
+
filename_prefix: str | None = None,
|
| 101 |
+
) -> tuple[str]:
|
| 102 |
+
directory = Path(save_directory)
|
| 103 |
+
directory.mkdir(parents=True, exist_ok=True)
|
| 104 |
+
filename = f"{filename_prefix + '-' if filename_prefix else ''}vocab.json"
|
| 105 |
+
path = directory / filename
|
| 106 |
+
path.write_text(json.dumps(self._vocab, indent=2, sort_keys=True) + "\n", encoding="utf-8")
|
| 107 |
+
return (str(path),)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
AdditionTokenizer.register_for_auto_class("AutoTokenizer")
|
| 111 |
+
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"added_tokens_decoder": {
|
| 3 |
+
"0": {
|
| 4 |
+
"content": "<BOS>",
|
| 5 |
+
"lstrip": false,
|
| 6 |
+
"normalized": false,
|
| 7 |
+
"rstrip": false,
|
| 8 |
+
"single_word": false,
|
| 9 |
+
"special": true
|
| 10 |
+
}
|
| 11 |
+
},
|
| 12 |
+
"auto_map": {
|
| 13 |
+
"AutoTokenizer": [
|
| 14 |
+
"tokenization_addition.AdditionTokenizer",
|
| 15 |
+
null
|
| 16 |
+
]
|
| 17 |
+
},
|
| 18 |
+
"backend": "custom",
|
| 19 |
+
"bos_token": "<BOS>",
|
| 20 |
+
"eos_token": null,
|
| 21 |
+
"model_max_length": 16,
|
| 22 |
+
"pad_token": null,
|
| 23 |
+
"tokenizer_class": "AdditionTokenizer",
|
| 24 |
+
"unk_token": null
|
| 25 |
+
}
|
training/checkpoints/final.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8ab3e4443c586c75fde95d36fdfed473f4e6fbf374bd318e6165e875727c5285
|
| 3 |
+
size 383443
|
training/checkpoints/step_000500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:06d05526d4b34992b4da3a982cbb8badad4df5f1a9550617dda1c6fb4afa4a70
|
| 3 |
+
size 381663
|
training/checkpoints/step_001000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:200f8aef8138075175f2a153cbcd97c1269a83b69990b9f7a9294dfab95b1808
|
| 3 |
+
size 381727
|
training/checkpoints/step_001500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:772ae2a4867745aeb9931c210603c6d334140ac279a29b1b3f6ca0733c29fcc5
|
| 3 |
+
size 381663
|
training/checkpoints/step_002000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:edab6419dd327151bf51b9a314c08aec9fecdff60d1196f6958bde5da8b8d682
|
| 3 |
+
size 381663
|
training/checkpoints/step_002500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:2adc6ebebdd9016fb311619613154e1db9c570385594b48de2605296074d39fd
|
| 3 |
+
size 381663
|
training/checkpoints/step_003000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:7136124680252d2a8aa94cbe6c865215c345c90b517ce741000934610fbdbb5e
|
| 3 |
+
size 381663
|
training/checkpoints/step_003500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ac6a0222a796adc6f11b013a4f56ca1afe9c19f6bd7225c7c3a9b3203afd6a6d
|
| 3 |
+
size 381663
|
training/checkpoints/step_004000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:4cb9659ba205822deb527d40942c998040aa8f885d59200f15cb6ebbdbc7d1ec
|
| 3 |
+
size 381599
|
training/checkpoints/step_004500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:559b0aeb57198d423779e49c4e96faaf88ce0f01062cf26b4c6584b01ec8a813
|
| 3 |
+
size 381663
|
training/checkpoints/step_005000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ac80e4bd49d0e656c668b6b9a43743c352d5fe198792c10a1c86ff3a6c72fc1b
|
| 3 |
+
size 381663
|
training/checkpoints/step_005500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:bd2b9f0b15fae7313f80ce1ce890fd58f4ba810f28bf615b8023d8c69bc17fa8
|
| 3 |
+
size 381663
|
training/checkpoints/step_006000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:e4ff8b1b2e7de3594bc19a5544bb38a745b43512360a5511249072551e720894
|
| 3 |
+
size 381663
|
training/checkpoints/step_006500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:c377fa05c9be37a02c1013fc38f30b07d9e477c1bdfe051931749601b427be32
|
| 3 |
+
size 381663
|
training/checkpoints/step_007000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:47b3d5544981cb3b76ac807ca3f5edfd0f43034a598629d9c6966487f78f39df
|
| 3 |
+
size 381727
|
training/checkpoints/step_007500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8fc1f3fd25e18774689a0b39bd0d84c665f0ceb4d42a043a9ff2c73cfed0b8c7
|
| 3 |
+
size 381663
|
training/checkpoints/step_008000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:97767f8940dbfbfd4482f74585735f89e88cadae42720a4cabd9df4f3b99f877
|
| 3 |
+
size 381663
|
training/checkpoints/step_008500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b63c4feec4a98147b79add7eef550c0e646111d950a0bf90cdd44ad93eac80d3
|
| 3 |
+
size 381663
|
training/checkpoints/step_009000.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:ce8731c55d43b4f6654396f1c136e5d7e647dc79b02982eb9adbe20fb1346f82
|
| 3 |
+
size 381663
|
training/checkpoints/step_009500.pt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:778c9a47d189067cbd932e93f36a067acd67cc775d75ade7efe30cce8024fbf6
|
| 3 |
+
size 381663
|
training/environment.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"cuda": "12.6",
|
| 3 |
+
"device": "cuda",
|
| 4 |
+
"device_name": "NVIDIA GeForce RTX 4060 Laptop GPU",
|
| 5 |
+
"git_commit": null,
|
| 6 |
+
"git_dirty": true,
|
| 7 |
+
"packages": {
|
| 8 |
+
"huggingface-hub": "1.27.0",
|
| 9 |
+
"numpy": "2.5.2",
|
| 10 |
+
"pytest": "9.1.1",
|
| 11 |
+
"safetensors": "0.8.0",
|
| 12 |
+
"sympy": "1.14.0",
|
| 13 |
+
"transformers": "5.15.0",
|
| 14 |
+
"wandb": "0.28.1"
|
| 15 |
+
},
|
| 16 |
+
"platform": "Windows-11-10.0.26200-SP0",
|
| 17 |
+
"python": "3.12.4",
|
| 18 |
+
"torch": "2.13.0+cu126"
|
| 19 |
+
}
|
training/metrics.jsonl
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
training/resolved_config.toml
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[data]
|
| 2 |
+
digits = 4
|
| 3 |
+
base = 10
|
| 4 |
+
|
| 5 |
+
[model]
|
| 6 |
+
d_model = 64
|
| 7 |
+
n_heads = 4
|
| 8 |
+
d_mlp = 128
|
| 9 |
+
n_layers = 1
|
| 10 |
+
max_seq_len = 16
|
| 11 |
+
init_mode = "normal"
|
| 12 |
+
|
| 13 |
+
[train]
|
| 14 |
+
steps = 10000
|
| 15 |
+
batch_size = 256
|
| 16 |
+
optimizer = "muon"
|
| 17 |
+
muon_lr = 0.02
|
| 18 |
+
adamw_lr = 0.0003
|
| 19 |
+
weight_decay = 0.01
|
| 20 |
+
min_lr_ratio = 0.1
|
| 21 |
+
grad_clip_norm = 1.0
|
| 22 |
+
log_every = 10
|
| 23 |
+
eval_every = 100
|
| 24 |
+
checkpoint_every = 500
|
| 25 |
+
progress_bar = true
|
| 26 |
+
run_root = "."
|
| 27 |
+
seed = 0
|
| 28 |
+
eval_seed = 1
|
| 29 |
+
device = "auto"
|
| 30 |
+
num_val_examples = 2048
|
| 31 |
+
num_targeted_examples = 256
|
| 32 |
+
eval_batch_size = 512
|
| 33 |
+
|
| 34 |
+
[wandb]
|
| 35 |
+
mode = "auto"
|
| 36 |
+
project = "circuits-addition"
|
| 37 |
+
tags = []
|
| 38 |
+
log_model = false
|
training/source_snapshot.zip
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:a4aca21623a11159fef2e4d3dce007e03737b6909872cda92210c3903c5ea357
|
| 3 |
+
size 160699
|
transformer_block.py
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
from dataclasses import dataclass
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
from torch import nn
|
| 7 |
+
|
| 8 |
+
from .attention import CausalSelfAttention
|
| 9 |
+
from .bilinear_mlp import BilinearMLP
|
| 10 |
+
from .model_config import AdditionModelConfig
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
@dataclass(frozen=True)
|
| 14 |
+
class TransformerBlockOutput:
|
| 15 |
+
residual_pre_attention: torch.Tensor
|
| 16 |
+
attention_out: torch.Tensor
|
| 17 |
+
attention_pattern: torch.Tensor | None
|
| 18 |
+
residual_after_attention: torch.Tensor
|
| 19 |
+
mlp_out: torch.Tensor
|
| 20 |
+
residual_after_mlp: torch.Tensor
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class TransformerBlock(nn.Module):
|
| 24 |
+
def __init__(self, config: AdditionModelConfig) -> None:
|
| 25 |
+
super().__init__()
|
| 26 |
+
self.residual_alpha = config.residual_alpha
|
| 27 |
+
self.attention = CausalSelfAttention(config.d_model, config.n_heads, bias=False)
|
| 28 |
+
self.mlp = BilinearMLP(config.d_model, config.d_mlp, bias=False)
|
| 29 |
+
|
| 30 |
+
def forward(self, x: torch.Tensor, return_pattern: bool = False) -> TransformerBlockOutput:
|
| 31 |
+
attention_output = self.attention(x, return_pattern=return_pattern)
|
| 32 |
+
residual_after_attention = torch.lerp(x, attention_output.values, self.residual_alpha)
|
| 33 |
+
mlp_out = self.mlp(residual_after_attention)
|
| 34 |
+
residual_after_mlp = torch.lerp(residual_after_attention, mlp_out, self.residual_alpha)
|
| 35 |
+
return TransformerBlockOutput(
|
| 36 |
+
residual_pre_attention=x,
|
| 37 |
+
attention_out=attention_output.values,
|
| 38 |
+
attention_pattern=attention_output.pattern,
|
| 39 |
+
residual_after_attention=residual_after_attention,
|
| 40 |
+
mlp_out=mlp_out,
|
| 41 |
+
residual_after_mlp=residual_after_mlp,
|
| 42 |
+
)
|
vocab.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"+": 1,
|
| 3 |
+
"0": 3,
|
| 4 |
+
"1": 4,
|
| 5 |
+
"2": 5,
|
| 6 |
+
"3": 6,
|
| 7 |
+
"4": 7,
|
| 8 |
+
"5": 8,
|
| 9 |
+
"6": 9,
|
| 10 |
+
"7": 10,
|
| 11 |
+
"8": 11,
|
| 12 |
+
"9": 12,
|
| 13 |
+
"<BOS>": 0,
|
| 14 |
+
"=": 2
|
| 15 |
+
}
|