HYDRA v1: 19M param non-transformer with selective gated recurrence
Browse files- README.md +57 -39
- config.json +1 -1
- model.pt +1 -1
- model.py +98 -419
- tokenizer_config.json +1 -1
README.md
CHANGED
|
@@ -8,6 +8,7 @@ tags:
|
|
| 8 |
- gated-recurrence
|
| 9 |
- from-scratch
|
| 10 |
- tinystories
|
|
|
|
| 11 |
license: apache-2.0
|
| 12 |
datasets:
|
| 13 |
- roneneldan/TinyStories
|
|
@@ -15,27 +16,25 @@ datasets:
|
|
| 15 |
|
| 16 |
# HYDRA: Hybrid Dynamic Recurrent Architecture
|
| 17 |
|
| 18 |
-
|
| 19 |
|
| 20 |
## Architecture
|
| 21 |
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
| Component | Inspired By | Paper |
|
| 25 |
|---|---|---|
|
| 26 |
-
|
|
| 27 |
-
|
|
| 28 |
-
|
|
| 29 |
-
|
|
| 30 |
|
| 31 |
### Key Properties
|
| 32 |
-
- **NOT a transformer** — no attention
|
| 33 |
-
- **O(n) time
|
| 34 |
-
- **Constant memory
|
| 35 |
-
- **Content-aware
|
| 36 |
-
- **Multi-scale
|
| 37 |
|
| 38 |
-
### Architecture
|
| 39 |
```
|
| 40 |
Token Embedding → N × HydraBlock → RMSNorm → LM Head
|
| 41 |
|
|
@@ -46,52 +45,71 @@ HydraBlock:
|
|
| 46 |
SelectiveGatedRecurrence (per timescale):
|
| 47 |
├── Input projection (2 branches)
|
| 48 |
├── Branch 1: Separable Conv1D → SiLU → Selective B,C projection
|
| 49 |
-
├──
|
| 50 |
-
├──
|
| 51 |
-
├── Timescale fusion
|
| 52 |
└── Gated merge + output projection
|
| 53 |
```
|
| 54 |
|
| 55 |
-
##
|
| 56 |
-
- **Parameters**: 19,274,816
|
| 57 |
- **d_model**: 256
|
| 58 |
- **Layers**: 6
|
| 59 |
-
- **State
|
| 60 |
- **Timescales**: 2
|
| 61 |
-
- **Context length**:
|
| 62 |
-
- **Vocabulary**: GPT-2 tokenizer (50,257 tokens)
|
| 63 |
|
| 64 |
## Training
|
| 65 |
-
- **Dataset**:
|
| 66 |
-
- **From scratch**:
|
| 67 |
-
- **
|
| 68 |
-
- **
|
| 69 |
-
- **Hardware**: CPU
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 70 |
|
| 71 |
## Usage
|
| 72 |
|
| 73 |
```python
|
| 74 |
-
import torch
|
| 75 |
from model import HydraModel, HydraConfig
|
| 76 |
from transformers import AutoTokenizer
|
| 77 |
|
| 78 |
-
# Load
|
| 79 |
config = HydraConfig(**json.load(open("config.json")))
|
| 80 |
model = HydraModel(config)
|
| 81 |
model.load_state_dict(torch.load("model.pt", map_location="cpu"))
|
|
|
|
| 82 |
|
| 83 |
tokenizer = AutoTokenizer.from_pretrained("gpt2")
|
| 84 |
-
|
| 85 |
-
# Generate
|
| 86 |
prompt = "Once upon a time"
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
|
|
|
| 90 |
```
|
| 91 |
|
| 92 |
-
##
|
|
|
|
|
|
|
|
|
|
| 93 |
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
|
|
|
|
|
| 8 |
- gated-recurrence
|
| 9 |
- from-scratch
|
| 10 |
- tinystories
|
| 11 |
+
- recurrent-neural-network
|
| 12 |
license: apache-2.0
|
| 13 |
datasets:
|
| 14 |
- roneneldan/TinyStories
|
|
|
|
| 16 |
|
| 17 |
# HYDRA: Hybrid Dynamic Recurrent Architecture
|
| 18 |
|
| 19 |
+
A novel **non-transformer** language model built from scratch. Trained on CPU using a custom architecture that combines Mamba's Selective State Spaces, Griffin's Real-Gated Linear Recurrence (RG-LRU), and RWKV's channel mixing — with **zero attention layers**.
|
| 20 |
|
| 21 |
## Architecture
|
| 22 |
|
| 23 |
+
| Component | Source | Paper |
|
|
|
|
|
|
|
| 24 |
|---|---|---|
|
| 25 |
+
| Selective State Spaces | Mamba | arxiv:2312.00752 |
|
| 26 |
+
| Real-Gated Linear Recurrence | Griffin | arxiv:2402.19427 |
|
| 27 |
+
| Time/Channel Mixing | RWKV | arxiv:2305.13048 |
|
| 28 |
+
| Multi-Scale Compression | **Novel** | Parallel recurrences at different timescales |
|
| 29 |
|
| 30 |
### Key Properties
|
| 31 |
+
- **NOT a transformer** — no attention whatsoever
|
| 32 |
+
- **O(n) time** — linear in sequence length (vs O(n²) transformers)
|
| 33 |
+
- **Constant memory** at inference (no KV cache)
|
| 34 |
+
- **Content-aware** selective gating (Mamba + Griffin fusion)
|
| 35 |
+
- **Multi-scale** temporal processing
|
| 36 |
|
| 37 |
+
### Architecture Diagram
|
| 38 |
```
|
| 39 |
Token Embedding → N × HydraBlock → RMSNorm → LM Head
|
| 40 |
|
|
|
|
| 45 |
SelectiveGatedRecurrence (per timescale):
|
| 46 |
├── Input projection (2 branches)
|
| 47 |
├── Branch 1: Separable Conv1D → SiLU → Selective B,C projection
|
| 48 |
+
├── Input gate + Recurrence gate (from Griffin RG-LRU)
|
| 49 |
+
├── Gated recurrence: h_t = a_t·h_{t-1} + √(1-a_t²)·(i_t·B_t)
|
|
|
|
| 50 |
└── Gated merge + output projection
|
| 51 |
```
|
| 52 |
|
| 53 |
+
## Specs
|
| 54 |
+
- **Parameters**: 19,274,816 (19.3M)
|
| 55 |
- **d_model**: 256
|
| 56 |
- **Layers**: 6
|
| 57 |
+
- **State dim**: 16
|
| 58 |
- **Timescales**: 2
|
| 59 |
+
- **Context length**: 128
|
|
|
|
| 60 |
|
| 61 |
## Training
|
| 62 |
+
- **Dataset**: TinyStories (5,000 stories for quick training)
|
| 63 |
+
- **From scratch**: Random initialization, no pretrained components
|
| 64 |
+
- **Best val_loss**: 3.7988
|
| 65 |
+
- **Val_ppl**: 44.6
|
| 66 |
+
- **Hardware**: CPU only
|
| 67 |
+
- **Optimizer**: AdamW (β₁=0.9, β₂=0.95)
|
| 68 |
+
|
| 69 |
+
## Generated Samples
|
| 70 |
+
|
| 71 |
+
**Once upon a time**: Once upon a time, there was a little girl named Lily. She loved to play outside. She had a big, feeling very excited! She couldn't wait for a big, but she said.
|
| 72 |
+
|
| 73 |
+
The little boy was so happy to make the bird who loved
|
| 74 |
+
**A little dog**: A little dog who ran to be happy. She was very excited that the park. She wanted to play with the window.
|
| 75 |
+
|
| 76 |
+
"I'm sorry, Lily. It is a voice?" She was so happy!
|
| 77 |
+
|
| 78 |
+
Ben did not give it and
|
| 79 |
+
**A girl named Lily**: A girl named Lily. She was very happy he was very tall. She saw what he could not want to the forest. She says, "I'm sorry, we can have to go to the water. She was very very excited and happy.
|
| 80 |
+
|
| 81 |
+
Lily
|
| 82 |
+
**One day a boy**: One day a boy named Lily liked to play with it. He said, "Don't be careful."
|
| 83 |
+
|
| 84 |
+
"We are happy, we have to go to the man that he decided to play with his friends. He was very happy that the dog had a man
|
| 85 |
|
| 86 |
## Usage
|
| 87 |
|
| 88 |
```python
|
| 89 |
+
import torch, json
|
| 90 |
from model import HydraModel, HydraConfig
|
| 91 |
from transformers import AutoTokenizer
|
| 92 |
|
|
|
|
| 93 |
config = HydraConfig(**json.load(open("config.json")))
|
| 94 |
model = HydraModel(config)
|
| 95 |
model.load_state_dict(torch.load("model.pt", map_location="cpu"))
|
| 96 |
+
model.eval()
|
| 97 |
|
| 98 |
tokenizer = AutoTokenizer.from_pretrained("gpt2")
|
|
|
|
|
|
|
| 99 |
prompt = "Once upon a time"
|
| 100 |
+
ids = torch.tensor([tokenizer.encode(prompt)])
|
| 101 |
+
with torch.no_grad():
|
| 102 |
+
out = model.generate(ids, max_new_tokens=50, temperature=0.8, top_k=40)
|
| 103 |
+
print(tokenizer.decode(out[0], skip_special_tokens=True))
|
| 104 |
```
|
| 105 |
|
| 106 |
+
## Model Files
|
| 107 |
+
- `model.pt` — trained weights
|
| 108 |
+
- `config.json` — model configuration
|
| 109 |
+
- `model.py` — full architecture source code
|
| 110 |
|
| 111 |
+
## Research References
|
| 112 |
+
1. Gu & Dao, "Mamba: Linear-Time Sequence Modeling with Selective State Spaces", NeurIPS 2023
|
| 113 |
+
2. De et al., "Griffin: Mixing Gated Linear Recurrences with Local Attention", ICLR 2024
|
| 114 |
+
3. Peng et al., "RWKV: Reinventing RNNs for the Transformer Era", 2023
|
| 115 |
+
4. Eldan & Li, "TinyStories: How Small Can Language Models Be?", 2023
|
config.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
| 8 |
"mlp_expand": 3,
|
| 9 |
"n_scales": 2,
|
| 10 |
"dropout": 0.1,
|
| 11 |
-
"max_seq_len":
|
| 12 |
"pad_token_id": 50256,
|
| 13 |
"tie_weights": true
|
| 14 |
}
|
|
|
|
| 8 |
"mlp_expand": 3,
|
| 9 |
"n_scales": 2,
|
| 10 |
"dropout": 0.1,
|
| 11 |
+
"max_seq_len": 128,
|
| 12 |
"pad_token_id": 50256,
|
| 13 |
"tie_weights": true
|
| 14 |
}
|
model.pt
CHANGED
|
@@ -1,3 +1,3 @@
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
-
oid sha256:
|
| 3 |
size 77144619
|
|
|
|
| 1 |
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:8b4d19a13205d6f991c2a27001f06318aee6988971d276eaf0bc4c673d5667ed
|
| 3 |
size 77144619
|
model.py
CHANGED
|
@@ -1,455 +1,134 @@
|
|
| 1 |
"""
|
| 2 |
HYDRA: Hybrid Dynamic Recurrent Architecture
|
| 3 |
-
|
| 4 |
-
A novel non-transformer language model built from scratch.
|
| 5 |
-
|
| 6 |
-
Architecture combines ideas from:
|
| 7 |
-
- Mamba (arxiv:2312.00752): Selective State Spaces with input-dependent dynamics
|
| 8 |
-
- Griffin (arxiv:2402.19427): Real-Gated Linear Recurrent Unit (RG-LRU)
|
| 9 |
-
- RWKV (arxiv:2305.13048): Time-mixing + channel-mixing residual blocks
|
| 10 |
-
|
| 11 |
-
Key innovations in this implementation:
|
| 12 |
-
1. Selective Gated Recurrence (SGR): Fuses Mamba's selective SSM with Griffin's RG-LRU gates
|
| 13 |
-
2. Multi-Scale State Compression: Parallel recurrences at different timescales
|
| 14 |
-
3. Gated Channel Mixing: RWKV-inspired channel mixing with GeGeLU
|
| 15 |
-
4. No attention mechanism - pure recurrent, O(n) in sequence length
|
| 16 |
-
|
| 17 |
-
This gives us:
|
| 18 |
-
- Linear time complexity (vs quadratic for transformers)
|
| 19 |
-
- Constant memory at inference (vs growing KV cache)
|
| 20 |
-
- Content-aware state selection (addresses RNN weakness)
|
| 21 |
-
- Theoretically unlimited context (state carries forward indefinitely)
|
| 22 |
"""
|
| 23 |
-
|
| 24 |
-
import math
|
| 25 |
-
import torch
|
| 26 |
-
import torch.nn as nn
|
| 27 |
-
import torch.nn.functional as F
|
| 28 |
from dataclasses import dataclass
|
| 29 |
-
from typing import Optional
|
| 30 |
-
|
| 31 |
|
| 32 |
@dataclass
|
| 33 |
class HydraConfig:
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
tie_weights: bool = True # Tie input/output embeddings (from Griffin)
|
| 47 |
-
|
| 48 |
def __post_init__(self):
|
| 49 |
-
if self.d_inner is None:
|
| 50 |
-
self.d_inner = 2 * self.d_model
|
| 51 |
-
|
| 52 |
|
| 53 |
class RMSNorm(nn.Module):
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
super().__init__()
|
| 57 |
-
self.eps = eps
|
| 58 |
-
self.weight = nn.Parameter(torch.ones(d_model))
|
| 59 |
-
|
| 60 |
def forward(self, x):
|
| 61 |
-
|
| 62 |
-
return x / rms * self.weight
|
| 63 |
-
|
| 64 |
|
| 65 |
class SelectiveGatedRecurrence(nn.Module):
|
| 66 |
-
|
| 67 |
-
Selective Gated Recurrence (SGR) — our novel temporal mixing block.
|
| 68 |
-
|
| 69 |
-
Fuses ideas from:
|
| 70 |
-
- Mamba's selective SSM: B, C, Δ are functions of input (content-aware)
|
| 71 |
-
- Griffin's RG-LRU: Gated recurrence with input gate and recurrence gate
|
| 72 |
-
- Multi-scale: Parallel recurrences at different timescales
|
| 73 |
-
|
| 74 |
-
Core equations (per timescale s):
|
| 75 |
-
r_t = sigmoid(W_r @ x_t) # recurrence gate (Griffin)
|
| 76 |
-
i_t = sigmoid(W_i @ x_t) # input gate (Griffin)
|
| 77 |
-
B_t = Linear(x_t) # selective input projection (Mamba)
|
| 78 |
-
C_t = Linear(x_t) # selective output projection (Mamba)
|
| 79 |
-
a_t = a_base^(c * r_t) # gated decay (Griffin: c=8)
|
| 80 |
-
h_t = a_t * h_{t-1} + sqrt(1 - a_t^2) * (i_t * B_t * x_t) # state update
|
| 81 |
-
y_t = C_t * h_t # readout
|
| 82 |
-
"""
|
| 83 |
-
|
| 84 |
-
def __init__(self, config: HydraConfig):
|
| 85 |
super().__init__()
|
| 86 |
-
self.d_inner = config.
|
| 87 |
-
self.
|
| 88 |
-
self.
|
| 89 |
-
self.
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
self.
|
| 93 |
-
|
| 94 |
-
# Small causal conv1d on first branch (from Griffin Sec 2.3, filter size 4)
|
| 95 |
-
self.conv1d = nn.Conv1d(
|
| 96 |
-
config.d_inner, config.d_inner,
|
| 97 |
-
kernel_size=config.d_conv,
|
| 98 |
-
padding=config.d_conv - 1,
|
| 99 |
-
groups=config.d_inner # depthwise/separable (from Griffin)
|
| 100 |
-
)
|
| 101 |
-
|
| 102 |
-
# Selective projections (from Mamba: B, C are input-dependent)
|
| 103 |
-
# One set per timescale
|
| 104 |
-
self.B_proj = nn.ModuleList([
|
| 105 |
-
nn.Linear(config.d_inner, config.d_state, bias=False)
|
| 106 |
-
for _ in range(config.n_scales)
|
| 107 |
-
])
|
| 108 |
-
self.C_proj = nn.ModuleList([
|
| 109 |
-
nn.Linear(config.d_inner, config.d_state, bias=False)
|
| 110 |
-
for _ in range(config.n_scales)
|
| 111 |
-
])
|
| 112 |
-
|
| 113 |
-
# Gates (from Griffin RG-LRU)
|
| 114 |
-
self.recurrence_gate = nn.ModuleList([
|
| 115 |
-
nn.Linear(config.d_inner, config.d_state, bias=True)
|
| 116 |
-
for _ in range(config.n_scales)
|
| 117 |
-
])
|
| 118 |
-
self.input_gate = nn.ModuleList([
|
| 119 |
-
nn.Linear(config.d_inner, config.d_state, bias=True)
|
| 120 |
-
for _ in range(config.n_scales)
|
| 121 |
-
])
|
| 122 |
-
|
| 123 |
-
# Learnable base decay per timescale (parameterized as Lambda, a = sigmoid(Lambda))
|
| 124 |
-
# Initialize so a^c is uniformly in [0.9, 0.999] per Griffin
|
| 125 |
-
self.Lambda = nn.ParameterList()
|
| 126 |
for s in range(config.n_scales):
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
low = math.log(0.9 ** (1.0 / (s + 1)))
|
| 130 |
-
high = math.log(0.999 ** (1.0 / (s + 1)))
|
| 131 |
-
# sigmoid^{-1}(x) = log(x/(1-x))
|
| 132 |
-
low_a = math.exp(low)
|
| 133 |
-
high_a = math.exp(high)
|
| 134 |
-
# Initialize Lambda so sigmoid(Lambda) gives us the right range
|
| 135 |
-
init_val = torch.empty(config.d_state).uniform_(
|
| 136 |
-
math.log(low_a / (1 - low_a + 1e-8)),
|
| 137 |
-
math.log(high_a / (1 - high_a + 1e-8))
|
| 138 |
-
)
|
| 139 |
self.Lambda.append(nn.Parameter(init_val))
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
self.
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
self.
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
"""
|
| 151 |
-
Args:
|
| 152 |
-
x: (batch, seq_len, d_model)
|
| 153 |
-
Returns:
|
| 154 |
-
y: (batch, seq_len, d_model)
|
| 155 |
-
"""
|
| 156 |
-
batch, seq_len, _ = x.shape
|
| 157 |
-
|
| 158 |
-
# Project input into two branches
|
| 159 |
-
xz = self.in_proj(x) # (B, L, 2*d_inner)
|
| 160 |
-
x_branch, z_branch = xz.chunk(2, dim=-1) # each (B, L, d_inner)
|
| 161 |
-
|
| 162 |
-
# Branch 1: Conv1D + recurrence
|
| 163 |
-
# Conv1d expects (B, C, L)
|
| 164 |
-
x_conv = self.conv1d(x_branch.transpose(1, 2))[:, :, :seq_len].transpose(1, 2)
|
| 165 |
-
x_conv = F.silu(x_conv) # SiLU activation (from Mamba)
|
| 166 |
-
|
| 167 |
-
# Branch 2: Gate (from Griffin/Mamba architecture)
|
| 168 |
-
z_gate = F.gelu(z_branch)
|
| 169 |
-
|
| 170 |
-
# Multi-scale selective recurrence
|
| 171 |
-
scale_outputs = []
|
| 172 |
for s in range(self.n_scales):
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
| 177 |
-
|
| 178 |
-
|
| 179 |
-
|
| 180 |
-
|
| 181 |
-
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
# Gated decay: a_t = a_base^(c * r_t) (Griffin eq 3)
|
| 185 |
-
# Compute in log space for stability: log(a_t) = c * r_t * log(a_base)
|
| 186 |
-
log_a_base = torch.log(a_base.clamp(min=1e-8)) # (d_state,)
|
| 187 |
-
log_a_t = self.gate_c * r_t * log_a_base.unsqueeze(0).unsqueeze(0) # (B, L, d_state)
|
| 188 |
-
a_t = torch.exp(log_a_t) # (B, L, d_state)
|
| 189 |
-
|
| 190 |
-
# Input contribution: sqrt(1 - a_t^2) * i_t * B_t * x_conv_projected
|
| 191 |
-
# We use B_t as the projection of x into state space
|
| 192 |
-
sqrt_term = torch.sqrt((1 - a_t ** 2).clamp(min=1e-8))
|
| 193 |
-
input_contrib = sqrt_term * i_t * B_t # (B, L, d_state)
|
| 194 |
-
|
| 195 |
-
# Sequential recurrence (numerically stable)
|
| 196 |
-
# h_t = a_t * h_{t-1} + input_contrib_t
|
| 197 |
-
# Compute all h states for this timescale
|
| 198 |
-
h = torch.zeros(batch, seq_len, self.d_state, device=x.device, dtype=x.dtype)
|
| 199 |
-
h_prev = torch.zeros(batch, self.d_state, device=x.device, dtype=x.dtype)
|
| 200 |
for t in range(seq_len):
|
| 201 |
-
h_prev
|
| 202 |
-
|
|
|
|
| 203 |
scale_outputs.append(h)
|
| 204 |
-
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
fused = self.scale_fusion(multi_scale) # (B, L, d_inner)
|
| 208 |
-
|
| 209 |
-
# Merge with gated branch (element-wise multiplication, like Griffin/Mamba)
|
| 210 |
-
output = fused * z_gate
|
| 211 |
-
|
| 212 |
-
# Output projection
|
| 213 |
-
output = self.out_proj(output) # (B, L, d_model)
|
| 214 |
return self.dropout(output)
|
| 215 |
|
| 216 |
-
|
| 217 |
class GatedChannelMixing(nn.Module):
|
| 218 |
-
|
| 219 |
-
|
| 220 |
-
|
| 221 |
-
|
| 222 |
-
|
| 223 |
-
|
| 224 |
-
|
| 225 |
-
|
| 226 |
-
|
| 227 |
-
def __init__(self, config: HydraConfig):
|
| 228 |
-
super().__init__()
|
| 229 |
-
d_ff = config.mlp_expand * config.d_model # M * D
|
| 230 |
-
|
| 231 |
-
self.gate_proj = nn.Linear(config.d_model, d_ff, bias=False)
|
| 232 |
-
self.up_proj = nn.Linear(config.d_model, d_ff, bias=False)
|
| 233 |
-
self.down_proj = nn.Linear(d_ff, config.d_model, bias=False)
|
| 234 |
-
self.dropout = nn.Dropout(config.dropout)
|
| 235 |
-
|
| 236 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 237 |
-
# GeGeLU: GeLU(gate) * up, then project down
|
| 238 |
-
gate = F.gelu(self.gate_proj(x))
|
| 239 |
-
up = self.up_proj(x)
|
| 240 |
-
return self.dropout(self.down_proj(gate * up))
|
| 241 |
-
|
| 242 |
|
| 243 |
class HydraBlock(nn.Module):
|
| 244 |
-
|
| 245 |
-
Single Hydra residual block (from Griffin architecture Sec 2.1):
|
| 246 |
-
x -> RMSNorm -> TemporalMix (SGR) -> + residual
|
| 247 |
-
x -> RMSNorm -> ChannelMix (GeGeLU MLP) -> + residual
|
| 248 |
-
"""
|
| 249 |
-
|
| 250 |
-
def __init__(self, config: HydraConfig):
|
| 251 |
super().__init__()
|
| 252 |
-
self.norm1
|
| 253 |
-
self.
|
| 254 |
-
|
| 255 |
-
self.channel_mix
|
| 256 |
-
|
| 257 |
-
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 258 |
-
# Temporal mixing with pre-norm and residual
|
| 259 |
-
x = x + self.temporal_mix(self.norm1(x))
|
| 260 |
-
# Channel mixing with pre-norm and residual
|
| 261 |
-
x = x + self.channel_mix(self.norm2(x))
|
| 262 |
-
return x
|
| 263 |
-
|
| 264 |
|
| 265 |
class HydraModel(nn.Module):
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
|
| 269 |
-
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
|
| 273 |
-
|
| 274 |
-
Architecture:
|
| 275 |
-
Token Embedding -> N x HydraBlock -> RMSNorm -> LM Head
|
| 276 |
-
|
| 277 |
-
Each HydraBlock:
|
| 278 |
-
-> RMSNorm -> SelectiveGatedRecurrence -> residual
|
| 279 |
-
-> RMSNorm -> GatedChannelMixing -> residual
|
| 280 |
-
"""
|
| 281 |
-
|
| 282 |
-
def __init__(self, config: HydraConfig):
|
| 283 |
-
super().__init__()
|
| 284 |
-
self.config = config
|
| 285 |
-
|
| 286 |
-
# Token embedding
|
| 287 |
-
self.embed = nn.Embedding(config.vocab_size, config.d_model)
|
| 288 |
-
self.embed_dropout = nn.Dropout(config.dropout)
|
| 289 |
-
|
| 290 |
-
# Stack of Hydra blocks
|
| 291 |
-
self.blocks = nn.ModuleList([
|
| 292 |
-
HydraBlock(config) for _ in range(config.n_layers)
|
| 293 |
-
])
|
| 294 |
-
|
| 295 |
-
# Final norm (from Griffin: RMSNorm before LM head)
|
| 296 |
-
self.final_norm = RMSNorm(config.d_model)
|
| 297 |
-
|
| 298 |
-
# Language model head
|
| 299 |
-
self.lm_head = nn.Linear(config.d_model, config.vocab_size, bias=False)
|
| 300 |
-
|
| 301 |
-
# Weight tying (from Griffin paper: shared embedding weights)
|
| 302 |
-
if config.tie_weights:
|
| 303 |
-
self.lm_head.weight = self.embed.weight
|
| 304 |
-
|
| 305 |
-
# Initialize weights
|
| 306 |
self._init_weights()
|
| 307 |
-
|
| 308 |
def _init_weights(self):
|
| 309 |
-
|
| 310 |
-
|
| 311 |
-
|
| 312 |
-
|
| 313 |
-
|
| 314 |
-
|
| 315 |
-
|
| 316 |
-
|
| 317 |
-
|
| 318 |
-
elif isinstance(module, nn.Conv1d):
|
| 319 |
-
nn.init.normal_(module.weight, mean=0.0, std=0.02)
|
| 320 |
-
if module.bias is not None:
|
| 321 |
-
nn.init.zeros_(module.bias)
|
| 322 |
-
|
| 323 |
-
def forward(
|
| 324 |
-
self,
|
| 325 |
-
input_ids: torch.Tensor,
|
| 326 |
-
labels: Optional[torch.Tensor] = None
|
| 327 |
-
) -> dict:
|
| 328 |
-
"""
|
| 329 |
-
Args:
|
| 330 |
-
input_ids: (batch, seq_len) token indices
|
| 331 |
-
labels: (batch, seq_len) target token indices for language modeling
|
| 332 |
-
Returns:
|
| 333 |
-
dict with 'logits' and optionally 'loss'
|
| 334 |
-
"""
|
| 335 |
-
# Embed tokens
|
| 336 |
-
x = self.embed(input_ids)
|
| 337 |
-
x = self.embed_dropout(x)
|
| 338 |
-
|
| 339 |
-
# Pass through Hydra blocks
|
| 340 |
-
for block in self.blocks:
|
| 341 |
-
x = block(x)
|
| 342 |
-
|
| 343 |
-
# Final norm and LM head
|
| 344 |
-
x = self.final_norm(x)
|
| 345 |
-
logits = self.lm_head(x) # (batch, seq_len, vocab_size)
|
| 346 |
-
|
| 347 |
-
result = {"logits": logits}
|
| 348 |
-
|
| 349 |
if labels is not None:
|
| 350 |
-
|
| 351 |
-
|
| 352 |
-
shift_labels = labels[:, 1:].contiguous()
|
| 353 |
-
loss = F.cross_entropy(
|
| 354 |
-
shift_logits.view(-1, self.config.vocab_size),
|
| 355 |
-
shift_labels.view(-1),
|
| 356 |
-
ignore_index=self.config.pad_token_id
|
| 357 |
-
)
|
| 358 |
-
result["loss"] = loss
|
| 359 |
-
|
| 360 |
return result
|
| 361 |
-
|
| 362 |
-
def generate(
|
| 363 |
-
self,
|
| 364 |
-
input_ids: torch.Tensor,
|
| 365 |
-
max_new_tokens: int = 100,
|
| 366 |
-
temperature: float = 0.8,
|
| 367 |
-
top_k: int = 50,
|
| 368 |
-
) -> torch.Tensor:
|
| 369 |
-
"""Simple autoregressive generation."""
|
| 370 |
self.eval()
|
| 371 |
with torch.no_grad():
|
| 372 |
for _ in range(max_new_tokens):
|
| 373 |
-
|
| 374 |
-
|
| 375 |
-
|
| 376 |
-
|
| 377 |
-
|
| 378 |
-
|
| 379 |
-
|
| 380 |
-
values, _ = torch.topk(logits, top_k)
|
| 381 |
-
logits[logits < values[:, -1:]] = float('-inf')
|
| 382 |
-
|
| 383 |
-
probs = F.softmax(logits, dim=-1)
|
| 384 |
-
next_token = torch.multinomial(probs, num_samples=1)
|
| 385 |
-
input_ids = torch.cat([input_ids, next_token], dim=-1)
|
| 386 |
-
|
| 387 |
return input_ids
|
| 388 |
-
|
| 389 |
-
@property
|
| 390 |
-
def num_parameters(self):
|
| 391 |
-
return sum(p.numel() for p in self.parameters())
|
| 392 |
-
|
| 393 |
@property
|
| 394 |
-
def
|
| 395 |
-
return sum(p.numel() for p in self.parameters() if p.requires_grad)
|
| 396 |
-
|
| 397 |
-
|
| 398 |
-
def create_hydra_small():
|
| 399 |
-
"""~10M parameter model for CPU training on TinyStories."""
|
| 400 |
-
return HydraModel(HydraConfig(
|
| 401 |
-
vocab_size=50257,
|
| 402 |
-
d_model=256,
|
| 403 |
-
n_layers=6,
|
| 404 |
-
d_state=16,
|
| 405 |
-
d_conv=4,
|
| 406 |
-
mlp_expand=3,
|
| 407 |
-
n_scales=2,
|
| 408 |
-
dropout=0.1,
|
| 409 |
-
max_seq_len=512,
|
| 410 |
-
))
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
def create_hydra_medium():
|
| 414 |
-
"""~30M parameter model."""
|
| 415 |
-
return HydraModel(HydraConfig(
|
| 416 |
-
vocab_size=50257,
|
| 417 |
-
d_model=512,
|
| 418 |
-
n_layers=8,
|
| 419 |
-
d_state=16,
|
| 420 |
-
d_conv=4,
|
| 421 |
-
mlp_expand=3,
|
| 422 |
-
n_scales=3,
|
| 423 |
-
dropout=0.1,
|
| 424 |
-
max_seq_len=1024,
|
| 425 |
-
))
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
if __name__ == "__main__":
|
| 429 |
-
# Quick test
|
| 430 |
-
config = HydraConfig(
|
| 431 |
-
vocab_size=50257,
|
| 432 |
-
d_model=256,
|
| 433 |
-
n_layers=4,
|
| 434 |
-
d_state=16,
|
| 435 |
-
d_conv=4,
|
| 436 |
-
mlp_expand=3,
|
| 437 |
-
n_scales=2,
|
| 438 |
-
dropout=0.1,
|
| 439 |
-
max_seq_len=128,
|
| 440 |
-
)
|
| 441 |
-
|
| 442 |
-
model = HydraModel(config)
|
| 443 |
-
print(f"Hydra Model")
|
| 444 |
-
print(f" Parameters: {model.num_parameters:,}")
|
| 445 |
-
print(f" Config: {config}")
|
| 446 |
-
|
| 447 |
-
# Test forward pass
|
| 448 |
-
batch_size, seq_len = 2, 64
|
| 449 |
-
input_ids = torch.randint(0, config.vocab_size, (batch_size, seq_len))
|
| 450 |
-
labels = torch.randint(0, config.vocab_size, (batch_size, seq_len))
|
| 451 |
-
|
| 452 |
-
output = model(input_ids, labels=labels)
|
| 453 |
-
print(f" Logits shape: {output['logits'].shape}")
|
| 454 |
-
print(f" Loss: {output['loss'].item():.4f}")
|
| 455 |
-
print(" ✓ Forward pass successful!")
|
|
|
|
| 1 |
"""
|
| 2 |
HYDRA: Hybrid Dynamic Recurrent Architecture
|
| 3 |
+
Novel non-transformer combining Mamba SSM, Griffin RG-LRU, RWKV mixing.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 4 |
"""
|
| 5 |
+
import math, torch, torch.nn as nn, torch.nn.functional as F
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
from dataclasses import dataclass
|
| 7 |
+
from typing import Optional
|
|
|
|
| 8 |
|
| 9 |
@dataclass
|
| 10 |
class HydraConfig:
|
| 11 |
+
vocab_size: int = 50257
|
| 12 |
+
d_model: int = 512
|
| 13 |
+
n_layers: int = 8
|
| 14 |
+
d_state: int = 16
|
| 15 |
+
d_conv: int = 4
|
| 16 |
+
d_inner: int = None
|
| 17 |
+
mlp_expand: int = 3
|
| 18 |
+
n_scales: int = 3
|
| 19 |
+
dropout: float = 0.1
|
| 20 |
+
max_seq_len: int = 1024
|
| 21 |
+
pad_token_id: int = 50256
|
| 22 |
+
tie_weights: bool = True
|
|
|
|
|
|
|
| 23 |
def __post_init__(self):
|
| 24 |
+
if self.d_inner is None: self.d_inner = 2 * self.d_model
|
|
|
|
|
|
|
| 25 |
|
| 26 |
class RMSNorm(nn.Module):
|
| 27 |
+
def __init__(self, d_model, eps=1e-8):
|
| 28 |
+
super().__init__(); self.eps=eps; self.weight=nn.Parameter(torch.ones(d_model))
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
def forward(self, x):
|
| 30 |
+
return x / torch.sqrt(torch.mean(x**2,dim=-1,keepdim=True)+self.eps) * self.weight
|
|
|
|
|
|
|
| 31 |
|
| 32 |
class SelectiveGatedRecurrence(nn.Module):
|
| 33 |
+
def __init__(self, config):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
super().__init__()
|
| 35 |
+
self.d_inner=config.d_inner; self.d_state=config.d_state; self.n_scales=config.n_scales; self.gate_c=8.0
|
| 36 |
+
self.in_proj=nn.Linear(config.d_model, 2*config.d_inner, bias=False)
|
| 37 |
+
self.conv1d=nn.Conv1d(config.d_inner,config.d_inner,config.d_conv,padding=config.d_conv-1,groups=config.d_inner)
|
| 38 |
+
self.B_proj=nn.ModuleList([nn.Linear(config.d_inner,config.d_state,bias=False) for _ in range(config.n_scales)])
|
| 39 |
+
self.C_proj=nn.ModuleList([nn.Linear(config.d_inner,config.d_state,bias=False) for _ in range(config.n_scales)])
|
| 40 |
+
self.recurrence_gate=nn.ModuleList([nn.Linear(config.d_inner,config.d_state,bias=True) for _ in range(config.n_scales)])
|
| 41 |
+
self.input_gate=nn.ModuleList([nn.Linear(config.d_inner,config.d_state,bias=True) for _ in range(config.n_scales)])
|
| 42 |
+
self.Lambda=nn.ParameterList()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
for s in range(config.n_scales):
|
| 44 |
+
low_a=0.9**(1.0/(s+1)); high_a=0.999**(1.0/(s+1))
|
| 45 |
+
init_val=torch.empty(config.d_state).uniform_(math.log(low_a/(1-low_a+1e-8)),math.log(high_a/(1-high_a+1e-8)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
self.Lambda.append(nn.Parameter(init_val))
|
| 47 |
+
self.scale_fusion=nn.Linear(config.n_scales*config.d_state,config.d_inner,bias=False)
|
| 48 |
+
self.out_proj=nn.Linear(config.d_inner,config.d_model,bias=False)
|
| 49 |
+
self.dropout=nn.Dropout(config.dropout)
|
| 50 |
+
|
| 51 |
+
def forward(self, x):
|
| 52 |
+
batch,seq_len,_=x.shape
|
| 53 |
+
xz=self.in_proj(x); x_branch,z_branch=xz.chunk(2,dim=-1)
|
| 54 |
+
x_conv=self.conv1d(x_branch.transpose(1,2))[:,:,:seq_len].transpose(1,2)
|
| 55 |
+
x_conv=F.silu(x_conv); z_gate=F.gelu(z_branch)
|
| 56 |
+
scale_outputs=[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
for s in range(self.n_scales):
|
| 58 |
+
B_t=self.B_proj[s](x_conv); C_t=self.C_proj[s](x_conv)
|
| 59 |
+
r_t=torch.sigmoid(self.recurrence_gate[s](x_conv))
|
| 60 |
+
i_t=torch.sigmoid(self.input_gate[s](x_conv))
|
| 61 |
+
a_base=torch.sigmoid(self.Lambda[s])
|
| 62 |
+
log_a_base=torch.log(a_base.clamp(min=1e-8))
|
| 63 |
+
log_a_t=self.gate_c*r_t*log_a_base.unsqueeze(0).unsqueeze(0)
|
| 64 |
+
a_t=torch.exp(log_a_t)
|
| 65 |
+
sqrt_term=torch.sqrt((1-a_t**2).clamp(min=1e-8))
|
| 66 |
+
input_contrib=sqrt_term*i_t*B_t
|
| 67 |
+
h_prev=torch.zeros(batch,self.d_state,device=x.device,dtype=x.dtype)
|
| 68 |
+
h_states=[]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 69 |
for t in range(seq_len):
|
| 70 |
+
h_prev=a_t[:,t]*h_prev+input_contrib[:,t]
|
| 71 |
+
h_states.append(h_prev.unsqueeze(1))
|
| 72 |
+
h=torch.cat(h_states,dim=1)
|
| 73 |
scale_outputs.append(h)
|
| 74 |
+
multi_scale=torch.cat(scale_outputs,dim=-1)
|
| 75 |
+
fused=self.scale_fusion(multi_scale)
|
| 76 |
+
output=self.out_proj(fused*z_gate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 77 |
return self.dropout(output)
|
| 78 |
|
|
|
|
| 79 |
class GatedChannelMixing(nn.Module):
|
| 80 |
+
def __init__(self, config):
|
| 81 |
+
super().__init__(); d_ff=config.mlp_expand*config.d_model
|
| 82 |
+
self.gate_proj=nn.Linear(config.d_model,d_ff,bias=False)
|
| 83 |
+
self.up_proj=nn.Linear(config.d_model,d_ff,bias=False)
|
| 84 |
+
self.down_proj=nn.Linear(d_ff,config.d_model,bias=False)
|
| 85 |
+
self.dropout=nn.Dropout(config.dropout)
|
| 86 |
+
def forward(self, x):
|
| 87 |
+
return self.dropout(self.down_proj(F.gelu(self.gate_proj(x))*self.up_proj(x)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 88 |
|
| 89 |
class HydraBlock(nn.Module):
|
| 90 |
+
def __init__(self, config):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 91 |
super().__init__()
|
| 92 |
+
self.norm1=RMSNorm(config.d_model); self.temporal_mix=SelectiveGatedRecurrence(config)
|
| 93 |
+
self.norm2=RMSNorm(config.d_model); self.channel_mix=GatedChannelMixing(config)
|
| 94 |
+
def forward(self, x):
|
| 95 |
+
return x+self.channel_mix(self.norm2(x+self.temporal_mix(self.norm1(x))))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
|
| 97 |
class HydraModel(nn.Module):
|
| 98 |
+
def __init__(self, config):
|
| 99 |
+
super().__init__(); self.config=config
|
| 100 |
+
self.embed=nn.Embedding(config.vocab_size,config.d_model)
|
| 101 |
+
self.embed_dropout=nn.Dropout(config.dropout)
|
| 102 |
+
self.blocks=nn.ModuleList([HydraBlock(config) for _ in range(config.n_layers)])
|
| 103 |
+
self.final_norm=RMSNorm(config.d_model)
|
| 104 |
+
self.lm_head=nn.Linear(config.d_model,config.vocab_size,bias=False)
|
| 105 |
+
if config.tie_weights: self.lm_head.weight=self.embed.weight
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
self._init_weights()
|
|
|
|
| 107 |
def _init_weights(self):
|
| 108 |
+
for m in self.modules():
|
| 109 |
+
if isinstance(m, nn.Linear): nn.init.normal_(m.weight,0,1.0/math.sqrt(m.weight.shape[1])); hasattr(m,'bias') and m.bias is not None and nn.init.zeros_(m.bias)
|
| 110 |
+
elif isinstance(m, nn.Embedding): nn.init.normal_(m.weight,0,0.02)
|
| 111 |
+
elif isinstance(m, nn.Conv1d): nn.init.normal_(m.weight,0,0.02); hasattr(m,'bias') and m.bias is not None and nn.init.zeros_(m.bias)
|
| 112 |
+
def forward(self, input_ids, labels=None):
|
| 113 |
+
x=self.embed_dropout(self.embed(input_ids))
|
| 114 |
+
for b in self.blocks: x=b(x)
|
| 115 |
+
x=self.final_norm(x); logits=self.lm_head(x)
|
| 116 |
+
result={"logits":logits}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 117 |
if labels is not None:
|
| 118 |
+
loss=F.cross_entropy(logits[:,:-1,:].contiguous().view(-1,self.config.vocab_size),labels[:,1:].contiguous().view(-1),ignore_index=self.config.pad_token_id)
|
| 119 |
+
result["loss"]=loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
return result
|
| 121 |
+
def generate(self, input_ids, max_new_tokens=100, temperature=0.8, top_k=50):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 122 |
self.eval()
|
| 123 |
with torch.no_grad():
|
| 124 |
for _ in range(max_new_tokens):
|
| 125 |
+
ctx=input_ids[:,-self.config.max_seq_len:]
|
| 126 |
+
logits=self.forward(ctx)["logits"][:,-1,:]/temperature
|
| 127 |
+
if top_k>0:
|
| 128 |
+
v,_=torch.topk(logits,top_k); logits[logits<v[:,-1:]]=float('-inf')
|
| 129 |
+
probs=F.softmax(logits,dim=-1)
|
| 130 |
+
next_token=torch.multinomial(probs,1)
|
| 131 |
+
input_ids=torch.cat([input_ids,next_token],dim=-1)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 132 |
return input_ids
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 133 |
@property
|
| 134 |
+
def num_parameters(self): return sum(p.numel() for p in self.parameters())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
tokenizer_config.json
CHANGED
|
@@ -7,7 +7,7 @@
|
|
| 7 |
"is_local": false,
|
| 8 |
"local_files_only": false,
|
| 9 |
"model_max_length": 1024,
|
| 10 |
-
"pad_token":
|
| 11 |
"tokenizer_class": "GPT2Tokenizer",
|
| 12 |
"unk_token": "<|endoftext|>"
|
| 13 |
}
|
|
|
|
| 7 |
"is_local": false,
|
| 8 |
"local_files_only": false,
|
| 9 |
"model_max_length": 1024,
|
| 10 |
+
"pad_token": null,
|
| 11 |
"tokenizer_class": "GPT2Tokenizer",
|
| 12 |
"unk_token": "<|endoftext|>"
|
| 13 |
}
|