LaminarNet-50m v0.6.2 — best checkpoint (step 115500, val_loss=3.7881)
Browse files- README.md +323 -0
- config.json +24 -0
- laminarnet/__init__.py +3 -0
- laminarnet/model.py +292 -0
- model.safetensors +3 -0
- tokenizer.json +0 -0
- tokenizer_config.json +12 -0
README.md
ADDED
|
@@ -0,0 +1,323 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
---
|
| 2 |
+
language:
|
| 3 |
+
- en
|
| 4 |
+
license: apache-2.0
|
| 5 |
+
tags:
|
| 6 |
+
- laminarnet
|
| 7 |
+
- language-model
|
| 8 |
+
- causal-lm
|
| 9 |
+
- text-generation
|
| 10 |
+
- state-space-model
|
| 11 |
+
- custom-architecture
|
| 12 |
+
- rope
|
| 13 |
+
- geometric-drift-field
|
| 14 |
+
library_name: laminarnet
|
| 15 |
+
pipeline_tag: text-generation
|
| 16 |
+
model-index:
|
| 17 |
+
- name: LaminarNet-50m
|
| 18 |
+
results:
|
| 19 |
+
- task:
|
| 20 |
+
type: text-generation
|
| 21 |
+
metrics:
|
| 22 |
+
- name: Validation Loss
|
| 23 |
+
type: loss
|
| 24 |
+
value: 3.7881
|
| 25 |
+
---
|
| 26 |
+
|
| 27 |
+
# 🧠 LaminarNet-50m
|
| 28 |
+
|
| 29 |
+
**LaminarNet v0.6.2** — A novel **O(N) causal language model** featuring Geometric Drift Fields, Rotary Position Embeddings (RoPE), and multi-strata hierarchical processing. Designed as a faster, more efficient alternative to traditional Transformer architectures.
|
| 30 |
+
|
| 31 |
+
> **⚡ Key Innovation**: LaminarNet replaces standard attention with a **Geometric Drift Field** — an O(N) selective state-space mechanism using vectorized parallel scan, achieving linear complexity instead of quadratic.
|
| 32 |
+
|
| 33 |
+
---
|
| 34 |
+
|
| 35 |
+
## 📊 Model Details
|
| 36 |
+
|
| 37 |
+
| Property | Value |
|
| 38 |
+
|---|---|
|
| 39 |
+
| **Parameters** | **49.4M** |
|
| 40 |
+
| Architecture | LaminarNet v0.6.2 |
|
| 41 |
+
| `d_model` | 320 |
|
| 42 |
+
| `n_heads` | 5 |
|
| 43 |
+
| `n_layers` | 10 |
|
| 44 |
+
| `d_ff` | 1200 |
|
| 45 |
+
| `n_strata` | 2 |
|
| 46 |
+
| `strata_ratios` | (1, 2, 4) |
|
| 47 |
+
| Sequence Length | 1024 |
|
| 48 |
+
| Vocabulary | GPT-2 BPE (50,257 tokens) |
|
| 49 |
+
| Best Val Loss | **3.7881** |
|
| 50 |
+
| Training Steps | 115,500 |
|
| 51 |
+
| Training Tokens | ~1B (FineWeb) |
|
| 52 |
+
|
| 53 |
+
---
|
| 54 |
+
|
| 55 |
+
## 🚀 Quick Start
|
| 56 |
+
|
| 57 |
+
### 1. Installation
|
| 58 |
+
|
| 59 |
+
```bash
|
| 60 |
+
pip install laminarnet safetensors transformers torch
|
| 61 |
+
```
|
| 62 |
+
|
| 63 |
+
Or install from source:
|
| 64 |
+
|
| 65 |
+
```bash
|
| 66 |
+
git clone https://huggingface.co/Uunan/LaminarNet-50m
|
| 67 |
+
cd LaminarNet-50m
|
| 68 |
+
pip install -e ./laminarnet
|
| 69 |
+
```
|
| 70 |
+
|
| 71 |
+
### 2. Download & Load Model
|
| 72 |
+
|
| 73 |
+
```python
|
| 74 |
+
import torch
|
| 75 |
+
from collections import Counter
|
| 76 |
+
import torch.nn.functional as F
|
| 77 |
+
from transformers import AutoTokenizer
|
| 78 |
+
from huggingface_hub import hf_hub_download
|
| 79 |
+
|
| 80 |
+
# Download model files
|
| 81 |
+
config_path = hf_hub_download("Uunan/LaminarNet-50m", "config.json")
|
| 82 |
+
model_path = hf_hub_download("Uunan/LaminarNet-50m", "model.safetensors")
|
| 83 |
+
|
| 84 |
+
# Build model
|
| 85 |
+
from laminarnet import LaminarNet, LaminarNetConfig
|
| 86 |
+
|
| 87 |
+
config = LaminarNetConfig(
|
| 88 |
+
vocab_size=50257, d_model=320, n_heads=5, n_layers=10,
|
| 89 |
+
d_ff=1200, n_strata=2, strata_ratios=(1, 2, 4),
|
| 90 |
+
seq_len=1024, dropout=0.1, conv_kernel=4, rope_base=10000.0,
|
| 91 |
+
)
|
| 92 |
+
model = LaminarNet(config)
|
| 93 |
+
|
| 94 |
+
# Load weights
|
| 95 |
+
from safetensors.torch import load_file
|
| 96 |
+
state = load_file(model_path)
|
| 97 |
+
model.load_state_dict(state)
|
| 98 |
+
model.eval()
|
| 99 |
+
|
| 100 |
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
| 101 |
+
model = model.to(device)
|
| 102 |
+
|
| 103 |
+
print(f"✅ LaminarNet-50m loaded! ({sum(p.numel() for p in model.parameters()) / 1e6:.1f}M params)")
|
| 104 |
+
```
|
| 105 |
+
|
| 106 |
+
### 3. Generate Text
|
| 107 |
+
|
| 108 |
+
```python
|
| 109 |
+
tokenizer = AutoTokenizer.from_pretrained("gpt2")
|
| 110 |
+
|
| 111 |
+
@torch.no_grad()
|
| 112 |
+
def generate(model, tokenizer, prompt, max_new_tokens=200,
|
| 113 |
+
temperature=0.8, top_k=50, top_p=0.9, device="cpu",
|
| 114 |
+
repetition_penalty=1.2, no_repeat_ngram=3, frequency_penalty=0.3):
|
| 115 |
+
"""
|
| 116 |
+
Autoregressive text generation with full sampling controls.
|
| 117 |
+
|
| 118 |
+
Args:
|
| 119 |
+
prompt: Input text to continue from.
|
| 120 |
+
max_new_tokens: Maximum number of tokens to generate.
|
| 121 |
+
temperature: Sampling temperature (0.1 = conservative, 1.5 = creative).
|
| 122 |
+
top_k: Top-k sampling (0 = disabled).
|
| 123 |
+
top_p: Nucleus sampling threshold (0-1).
|
| 124 |
+
repetition_penalty: Penalize already-seen tokens (1.0 = off, 1.2+ = recommended).
|
| 125 |
+
no_repeat_ngram: Block repeated n-grams (3 = no trigram repeats, 0 = off).
|
| 126 |
+
frequency_penalty: Penalize tokens by their generation frequency (0.0 = off).
|
| 127 |
+
"""
|
| 128 |
+
SEQ_LEN = 1024
|
| 129 |
+
input_ids = tokenizer.encode(prompt, return_tensors="pt").to(device)
|
| 130 |
+
prompt_len = input_ids.shape[1]
|
| 131 |
+
|
| 132 |
+
if input_ids.shape[1] > SEQ_LEN:
|
| 133 |
+
input_ids = input_ids[:, -SEQ_LEN:]
|
| 134 |
+
|
| 135 |
+
eos_id = tokenizer.eos_token_id
|
| 136 |
+
|
| 137 |
+
for _ in range(max_new_tokens):
|
| 138 |
+
ctx = input_ids[:, -SEQ_LEN:]
|
| 139 |
+
logits = model(ctx)[:, -1, :]
|
| 140 |
+
|
| 141 |
+
# Repetition Penalty
|
| 142 |
+
if repetition_penalty != 1.0:
|
| 143 |
+
for tok_id in set(input_ids[0].tolist()):
|
| 144 |
+
if logits[0, tok_id] > 0:
|
| 145 |
+
logits[0, tok_id] /= repetition_penalty
|
| 146 |
+
else:
|
| 147 |
+
logits[0, tok_id] *= repetition_penalty
|
| 148 |
+
|
| 149 |
+
# Frequency Penalty
|
| 150 |
+
if frequency_penalty > 0:
|
| 151 |
+
token_counts = Counter(input_ids[0].tolist()[prompt_len:])
|
| 152 |
+
for tok_id, count in token_counts.items():
|
| 153 |
+
logits[0, tok_id] -= frequency_penalty * count
|
| 154 |
+
|
| 155 |
+
# N-gram Blocking
|
| 156 |
+
if no_repeat_ngram > 0 and input_ids.shape[1] >= no_repeat_ngram:
|
| 157 |
+
generated = input_ids[0].tolist()
|
| 158 |
+
prefix = tuple(generated[-(no_repeat_ngram - 1):])
|
| 159 |
+
for i in range(len(generated) - no_repeat_ngram + 1):
|
| 160 |
+
if tuple(generated[i:i + no_repeat_ngram - 1]) == prefix:
|
| 161 |
+
logits[0, generated[i + no_repeat_ngram - 1]] = float("-inf")
|
| 162 |
+
|
| 163 |
+
logits = logits / temperature
|
| 164 |
+
|
| 165 |
+
# Top-k
|
| 166 |
+
if top_k > 0:
|
| 167 |
+
vals, _ = torch.topk(logits, top_k)
|
| 168 |
+
logits[logits < vals[:, -1:]] = float("-inf")
|
| 169 |
+
|
| 170 |
+
# Top-p (nucleus)
|
| 171 |
+
if 0 < top_p < 1.0:
|
| 172 |
+
sorted_logits, sorted_idx = torch.sort(logits, descending=True)
|
| 173 |
+
cum_probs = torch.cumsum(F.softmax(sorted_logits, dim=-1), dim=-1)
|
| 174 |
+
remove_mask = cum_probs - F.softmax(sorted_logits, dim=-1) >= top_p
|
| 175 |
+
sorted_logits[remove_mask] = float("-inf")
|
| 176 |
+
logits = sorted_logits.scatter(1, sorted_idx, sorted_logits)
|
| 177 |
+
|
| 178 |
+
probs = F.softmax(logits, dim=-1)
|
| 179 |
+
next_tok = torch.multinomial(probs, num_samples=1)
|
| 180 |
+
input_ids = torch.cat([input_ids, next_tok], dim=1)
|
| 181 |
+
|
| 182 |
+
if next_tok.item() == eos_id:
|
| 183 |
+
break
|
| 184 |
+
|
| 185 |
+
return tokenizer.decode(input_ids[0], skip_special_tokens=True)
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
# Generate!
|
| 189 |
+
text = generate(model, tokenizer, "The meaning of life is", device=device)
|
| 190 |
+
print(text)
|
| 191 |
+
```
|
| 192 |
+
|
| 193 |
+
---
|
| 194 |
+
|
| 195 |
+
## 🎛️ Generation Parameters Guide
|
| 196 |
+
|
| 197 |
+
| Parameter | Default | Range | Description |
|
| 198 |
+
|---|---|---|---|
|
| 199 |
+
| `temperature` | 0.8 | 0.1 — 2.0 | Lower = more focused, higher = more creative |
|
| 200 |
+
| `top_k` | 50 | 0 — vocab_size | Keep only top-k most likely tokens |
|
| 201 |
+
| `top_p` | 0.9 | 0.0 — 1.0 | Nucleus sampling — keep tokens until cumulative prob ≥ p |
|
| 202 |
+
| `repetition_penalty` | 1.2 | 1.0 — 2.0 | Penalize previously generated tokens (1.0 = off) |
|
| 203 |
+
| `no_repeat_ngram` | 3 | 0 — 5 | Block repeated n-grams (0 = off) |
|
| 204 |
+
| `frequency_penalty` | 0.3 | 0.0 — 1.0 | Penalize tokens proportional to usage count (0 = off) |
|
| 205 |
+
| `max_new_tokens` | 200 | 1 — 1024 | Maximum tokens to generate |
|
| 206 |
+
|
| 207 |
+
### Recommended Presets
|
| 208 |
+
|
| 209 |
+
```python
|
| 210 |
+
# 📝 Factual / Focused
|
| 211 |
+
text = generate(model, tokenizer, prompt, temperature=0.5, top_k=30, top_p=0.85, device=device)
|
| 212 |
+
|
| 213 |
+
# 🎨 Creative / Story Writing
|
| 214 |
+
text = generate(model, tokenizer, prompt, temperature=1.0, top_k=80, top_p=0.95,
|
| 215 |
+
repetition_penalty=1.3, frequency_penalty=0.4, device=device)
|
| 216 |
+
|
| 217 |
+
# ⚡ Greedy (deterministic)
|
| 218 |
+
text = generate(model, tokenizer, prompt, temperature=0.1, top_k=1, top_p=1.0,
|
| 219 |
+
repetition_penalty=1.0, device=device)
|
| 220 |
+
```
|
| 221 |
+
|
| 222 |
+
---
|
| 223 |
+
|
| 224 |
+
## 🏗️ Architecture Deep Dive
|
| 225 |
+
|
| 226 |
+
LaminarNet introduces a unique **multi-strata** architecture that processes information at multiple temporal resolutions simultaneously:
|
| 227 |
+
|
| 228 |
+
```
|
| 229 |
+
Input Tokens
|
| 230 |
+
│
|
| 231 |
+
▼
|
| 232 |
+
┌─────────────┐
|
| 233 |
+
│ Embedding │ Token → d_model (320)
|
| 234 |
+
└──────┬──────┘
|
| 235 |
+
│
|
| 236 |
+
├──────── Stratum 0 (Fine, ratio=1) ── Full resolution
|
| 237 |
+
│
|
| 238 |
+
└──────── Stratum 1 (Coarse, ratio=2) ── Half resolution
|
| 239 |
+
│
|
| 240 |
+
┌────┴────┐
|
| 241 |
+
│ x10 │ LaminarBlock layers
|
| 242 |
+
│ Layers │
|
| 243 |
+
└────┬────┘
|
| 244 |
+
│
|
| 245 |
+
Each LaminarBlock:
|
| 246 |
+
┌─────────────────────────────────────────┐
|
| 247 |
+
│ 1. GeometricDriftField (per stratum) │ ← O(N) parallel scan + RoPE
|
| 248 |
+
│ 2. CrossStratumRouting (between strata) │ ← Dual-gated information exchange
|
| 249 |
+
│ 3. SwiGLU FFN (per stratum) │ ← Gated feed-forward
|
| 250 |
+
└─────────────────────────────────────────┘
|
| 251 |
+
│
|
| 252 |
+
▼
|
| 253 |
+
┌─────────────┐
|
| 254 |
+
│ LM Head │ d_model → vocab (weight-tied with embedding)
|
| 255 |
+
└─────────────┘
|
| 256 |
+
```
|
| 257 |
+
|
| 258 |
+
### Core Components
|
| 259 |
+
|
| 260 |
+
| Component | Description |
|
| 261 |
+
|---|---|
|
| 262 |
+
| **Geometric Drift Field** | Replaces self-attention with an O(N) selective state-space mechanism. Uses chunked parallel scan (chunk_size=256) with vectorized inter-chunk carry propagation. |
|
| 263 |
+
| **RoPE** | Rotary Position Embeddings applied to value vectors within the drift field for relative positional encoding. |
|
| 264 |
+
| **Cross-Stratum Routing** | Bidirectional gated information flow between fine and coarse strata using causal downsampling (left-padded AvgPool) and nearest-neighbor upsampling. |
|
| 265 |
+
| **SwiGLU FFN** | Gated feed-forward: `SiLU(W1·x) ⊙ W3·x` projected back via `W2`. |
|
| 266 |
+
| **RMSNorm** | Pre-norm with Root Mean Square normalization (float32 stable). |
|
| 267 |
+
| **Causal Conv1d** | Depthwise causal convolution (kernel=4) before drift field projections. |
|
| 268 |
+
|
| 269 |
+
---
|
| 270 |
+
|
| 271 |
+
## 📈 Training Details
|
| 272 |
+
|
| 273 |
+
| Property | Value |
|
| 274 |
+
|---|---|
|
| 275 |
+
| **Dataset** | FineWeb (~1B tokens, packed, no padding) |
|
| 276 |
+
| **Tokenizer** | GPT-2 BPE (50,257 tokens) |
|
| 277 |
+
| **Optimizer** | AdamW (lr=3e-4, weight_decay=0.01, betas=(0.9, 0.999)) |
|
| 278 |
+
| **LR Schedule** | Linear warmup (4000 steps) + cosine decay (min ratio 0.1) |
|
| 279 |
+
| **Batch Size** | 8 × 1024 = 8,192 tokens/step |
|
| 280 |
+
| **Gradient Clipping** | max_norm=1.0 |
|
| 281 |
+
| **Mixed Precision** | AMP (FP16) with GradScaler |
|
| 282 |
+
| **Epochs** | 1 |
|
| 283 |
+
| **Total Steps** | ~115,500 |
|
| 284 |
+
| **Training Hardware** | Google Colab (single GPU) |
|
| 285 |
+
|
| 286 |
+
---
|
| 287 |
+
|
| 288 |
+
## 📋 Sample Outputs
|
| 289 |
+
|
| 290 |
+
**Prompt**: *"The meaning of life is"*
|
| 291 |
+
|
| 292 |
+
> The meaning of life is that it takes a good deal of time to understand the nature of our lives. When we look at it, you're not only one who can live in an illusion or experience other than just feeling like someone else and then having lost control over everything as well as knowing that there are many things that come down into your psyche but also for us all this stuff needs to be done right.
|
| 293 |
+
|
| 294 |
+
**Prompt**: *"The meaning of life is"*
|
| 295 |
+
|
| 296 |
+
> The meaning of life is so intense that the person who makes it to Him will be able to accept a different image, one that he has been living in. His love for God and his grace was born out in her mind when she'd just finished living through what she knew – but I can imagine this: "My goodness knows that you are not alone!"
|
| 297 |
+
|
| 298 |
+
---
|
| 299 |
+
|
| 300 |
+
## ⚠️ Limitations
|
| 301 |
+
|
| 302 |
+
- **50M parameters** — This is a small research model, not suitable for production use.
|
| 303 |
+
- **English only** — Trained exclusively on English text from FineWeb.
|
| 304 |
+
- **No instruction tuning** — Base model only, not aligned or fine-tuned for chat/instructions.
|
| 305 |
+
- **May generate** incorrect facts, biased content, or repetitive text.
|
| 306 |
+
- **1024 token context** — Maximum sequence length is 1024 tokens.
|
| 307 |
+
|
| 308 |
+
---
|
| 309 |
+
|
| 310 |
+
## 📜 Citation
|
| 311 |
+
|
| 312 |
+
```bibtex
|
| 313 |
+
@misc{laminarnet2025,
|
| 314 |
+
title={LaminarNet: O(N) Language Model with Geometric Drift Fields},
|
| 315 |
+
author={Uunan},
|
| 316 |
+
year={2025},
|
| 317 |
+
url={https://huggingface.co/Uunan/LaminarNet-50m}
|
| 318 |
+
}
|
| 319 |
+
```
|
| 320 |
+
|
| 321 |
+
## 📄 License
|
| 322 |
+
|
| 323 |
+
Apache 2.0 — free for research and commercial use.
|
config.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"architectures": [
|
| 3 |
+
"LaminarNet"
|
| 4 |
+
],
|
| 5 |
+
"model_type": "laminarnet",
|
| 6 |
+
"vocab_size": 50257,
|
| 7 |
+
"d_model": 320,
|
| 8 |
+
"n_heads": 5,
|
| 9 |
+
"n_layers": 10,
|
| 10 |
+
"d_ff": 1200,
|
| 11 |
+
"n_strata": 2,
|
| 12 |
+
"strata_ratios": [
|
| 13 |
+
1,
|
| 14 |
+
2,
|
| 15 |
+
4
|
| 16 |
+
],
|
| 17 |
+
"seq_len": 1024,
|
| 18 |
+
"dropout": 0.1,
|
| 19 |
+
"conv_kernel": 4,
|
| 20 |
+
"rope_base": 10000.0,
|
| 21 |
+
"total_params": 49420160,
|
| 22 |
+
"best_val_loss": 3.788057290315628,
|
| 23 |
+
"training_steps": 115500
|
| 24 |
+
}
|
laminarnet/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .model import LaminarNet, LaminarNetConfig
|
| 2 |
+
|
| 3 |
+
__all__ = ["LaminarNet", "LaminarNetConfig"]
|
laminarnet/model.py
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
LaminarNet v0.6.0 — RoPE, Dual-Gate CSR, Parallel Carry, Strata Validation
|
| 3 |
+
Faster than Transformer: larger chunks, streamlined architecture, vectorized carry.
|
| 4 |
+
All temporal operations are strictly causal — no future information leakage.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
import math
|
| 8 |
+
from dataclasses import dataclass
|
| 9 |
+
from typing import Optional, List
|
| 10 |
+
|
| 11 |
+
import torch
|
| 12 |
+
import torch.nn as nn
|
| 13 |
+
import torch.nn.functional as F
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@dataclass
|
| 17 |
+
class LaminarNetConfig:
|
| 18 |
+
vocab_size: int = 50257
|
| 19 |
+
d_model: int = 256
|
| 20 |
+
n_heads: int = 8
|
| 21 |
+
n_layers: int = 8
|
| 22 |
+
d_ff: int = 1024
|
| 23 |
+
n_strata: int = 2
|
| 24 |
+
strata_ratios: tuple = (1, 2, 4)
|
| 25 |
+
seq_len: int = 1024
|
| 26 |
+
dropout: float = 0.1
|
| 27 |
+
conv_kernel: int = 4
|
| 28 |
+
rope_base: float = 10000.0
|
| 29 |
+
|
| 30 |
+
def __post_init__(self):
|
| 31 |
+
if len(self.strata_ratios) < self.n_strata:
|
| 32 |
+
raise ValueError(
|
| 33 |
+
f"strata_ratios length ({len(self.strata_ratios)}) must be "
|
| 34 |
+
f">= n_strata ({self.n_strata}). "
|
| 35 |
+
f"Provide at least {self.n_strata} ratios."
|
| 36 |
+
)
|
| 37 |
+
if self.strata_ratios[0] != 1:
|
| 38 |
+
raise ValueError(
|
| 39 |
+
f"strata_ratios[0] must be 1 (fine stratum), got {self.strata_ratios[0]}"
|
| 40 |
+
)
|
| 41 |
+
for i, r in enumerate(self.strata_ratios):
|
| 42 |
+
if not isinstance(r, int) or r < 1:
|
| 43 |
+
raise ValueError(
|
| 44 |
+
f"strata_ratios[{i}] must be a positive integer, got {r}"
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class RMSNorm(nn.Module):
|
| 49 |
+
def __init__(self, d, eps=1e-6):
|
| 50 |
+
super().__init__()
|
| 51 |
+
self.scale = nn.Parameter(torch.ones(d))
|
| 52 |
+
self.eps = eps
|
| 53 |
+
def forward(self, x):
|
| 54 |
+
orig_dtype = x.dtype
|
| 55 |
+
x_f32 = x.float()
|
| 56 |
+
rms = x_f32.pow(2).mean(-1, keepdim=True).add(self.eps).rsqrt()
|
| 57 |
+
return (self.scale * (x_f32 * rms)).to(orig_dtype)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
# ─────────────────────────────────────────────────────────────
|
| 61 |
+
# Rotary Position Embedding
|
| 62 |
+
# ─────────────────────────────────────────────────────────────
|
| 63 |
+
|
| 64 |
+
class RotaryPositionEmbedding(nn.Module):
|
| 65 |
+
"""RoPE — computes sincos on-the-fly, works for any sequence length."""
|
| 66 |
+
def __init__(self, dim: int, base: float = 10000.0):
|
| 67 |
+
super().__init__()
|
| 68 |
+
inv_freq = 1.0 / (base ** (torch.arange(0, dim, 2).float() / dim))
|
| 69 |
+
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
| 70 |
+
|
| 71 |
+
def forward(self, seq_len: int, device: torch.device, dtype: torch.dtype):
|
| 72 |
+
t = torch.arange(seq_len, device=device, dtype=torch.float32)
|
| 73 |
+
freqs = torch.outer(t, self.inv_freq) # (N, dim//2)
|
| 74 |
+
cos_f = freqs.cos().to(dtype) # (N, dim//2)
|
| 75 |
+
sin_f = freqs.sin().to(dtype) # (N, dim//2)
|
| 76 |
+
return cos_f, sin_f
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
|
| 80 |
+
"""Apply RoPE to x of shape (B, N, H, D). cos/sin are (N, D//2)."""
|
| 81 |
+
half = x.shape[-1] // 2
|
| 82 |
+
x1, x2 = x[..., :half], x[..., half:]
|
| 83 |
+
cos = cos.unsqueeze(0).unsqueeze(2) # (1, N, 1, D//2)
|
| 84 |
+
sin = sin.unsqueeze(0).unsqueeze(2)
|
| 85 |
+
return torch.cat([x1 * cos - x2 * sin, x1 * sin + x2 * cos], dim=-1)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
# ─────────────────────────────────────────────────────────────
|
| 89 |
+
# 1. O(N) Selective Geometric Drift Field — with RoPE
|
| 90 |
+
# ─────────────────────────────────────────────────────────────
|
| 91 |
+
|
| 92 |
+
class GeometricDriftField(nn.Module):
|
| 93 |
+
"""
|
| 94 |
+
Geometric Drift Field v6.0 — O(N) Vectorized Parallel Scan
|
| 95 |
+
with RoPE and fully parallel inter-chunk carry.
|
| 96 |
+
"""
|
| 97 |
+
|
| 98 |
+
def __init__(self, d_model: int, n_heads: int, dropout: float = 0.1,
|
| 99 |
+
conv_kernel: int = 4, rope_base: float = 10000.0):
|
| 100 |
+
super().__init__()
|
| 101 |
+
self.d_model = d_model
|
| 102 |
+
self.n_heads = n_heads
|
| 103 |
+
self.d_head = d_model // n_heads
|
| 104 |
+
|
| 105 |
+
# Fused Projections — now 3-way (no separate theta needed; RoPE replaces it)
|
| 106 |
+
self.in_proj = nn.Linear(d_model, d_model * 3, bias=False)
|
| 107 |
+
self.conv1d = nn.Conv1d(d_model, d_model, kernel_size=conv_kernel,
|
| 108 |
+
padding=conv_kernel-1, groups=d_model)
|
| 109 |
+
|
| 110 |
+
self.out_proj = nn.Linear(d_model, d_model, bias=False)
|
| 111 |
+
self.norm = RMSNorm(d_model)
|
| 112 |
+
self.dropout = nn.Dropout(dropout)
|
| 113 |
+
|
| 114 |
+
self.dt_bias = nn.Parameter(torch.ones(d_model) * -3.0)
|
| 115 |
+
self.rope = RotaryPositionEmbedding(self.d_head, base=rope_base)
|
| 116 |
+
|
| 117 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 118 |
+
residual = x
|
| 119 |
+
x = self.norm(x)
|
| 120 |
+
B, N, D = x.shape
|
| 121 |
+
|
| 122 |
+
# 1. Context & Proj (causal conv)
|
| 123 |
+
x_conv = self.conv1d(x.transpose(1, 2))[..., :N].transpose(1, 2)
|
| 124 |
+
x_conv = F.silu(x_conv)
|
| 125 |
+
|
| 126 |
+
fused = self.in_proj(x_conv)
|
| 127 |
+
dt_raw, v, gate = fused.chunk(3, dim=-1)
|
| 128 |
+
|
| 129 |
+
# 2. Selective Parameters
|
| 130 |
+
dt = F.softplus(dt_raw.float() + self.dt_bias.float()).clamp(min=0.001, max=2.0).to(dt_raw.dtype)
|
| 131 |
+
gate = torch.sigmoid(gate)
|
| 132 |
+
log_alpha = -dt
|
| 133 |
+
|
| 134 |
+
# 3. RoPE-based Positional Rotation
|
| 135 |
+
cos_f, sin_f = self.rope(N, device=x.device, dtype=x.dtype)
|
| 136 |
+
v_view = v.view(B, N, self.n_heads, self.d_head)
|
| 137 |
+
v_rotated = apply_rope(v_view, cos_f, sin_f).reshape(B, N, D)
|
| 138 |
+
|
| 139 |
+
# 4. O(N) Vectorized Parallel Scan — chunk_size=256 for speed
|
| 140 |
+
chunk_size = 256
|
| 141 |
+
v_in = v_rotated * dt
|
| 142 |
+
|
| 143 |
+
# Pad to nearest chunk multiple
|
| 144 |
+
orig_N = N
|
| 145 |
+
remainder = N % chunk_size
|
| 146 |
+
if remainder != 0:
|
| 147 |
+
pad_len = chunk_size - remainder
|
| 148 |
+
v_in = F.pad(v_in, (0, 0, 0, pad_len))
|
| 149 |
+
log_alpha = F.pad(log_alpha, (0, 0, 0, pad_len))
|
| 150 |
+
|
| 151 |
+
N_padded = v_in.shape[1]
|
| 152 |
+
num_chunks = N_padded // chunk_size
|
| 153 |
+
|
| 154 |
+
v_chunks = v_in.view(B, num_chunks, chunk_size, D)
|
| 155 |
+
la_chunks = log_alpha.view(B, num_chunks, chunk_size, D)
|
| 156 |
+
|
| 157 |
+
# Cumulative log-decay within each chunk
|
| 158 |
+
L_chunks = torch.cumsum(la_chunks, dim=2) # (B, C, T, D)
|
| 159 |
+
L_chunks = L_chunks.float().clamp(min=-20.0, max=0.0) # stabilize
|
| 160 |
+
|
| 161 |
+
# O(N) intra-chunk scan via cumsum (float32 for precision + stability)
|
| 162 |
+
L_max = L_chunks.max(dim=2, keepdim=True).values
|
| 163 |
+
L_stable = L_chunks - L_max
|
| 164 |
+
exp_neg_L_stable = torch.exp(-L_stable)
|
| 165 |
+
scaled_v = exp_neg_L_stable * v_chunks.float()
|
| 166 |
+
cum_scaled = torch.cumsum(scaled_v, dim=2)
|
| 167 |
+
exp_L_stable = torch.exp(L_stable)
|
| 168 |
+
chunk_out = exp_L_stable * cum_scaled # stay float32
|
| 169 |
+
|
| 170 |
+
# 5. Parallel inter-chunk carry (no Python for-loop) — all in float32
|
| 171 |
+
if num_chunks > 1:
|
| 172 |
+
chunk_boundary_decay = L_chunks[:, :, -1, :] # (B, C, D) already float32
|
| 173 |
+
chunk_boundary_out = chunk_out[:, :, -1, :] # (B, C, D) already float32
|
| 174 |
+
|
| 175 |
+
# Parallel prefix sum in log-space
|
| 176 |
+
cum_decay = torch.cumsum(chunk_boundary_decay, dim=1) # (B, C, D)
|
| 177 |
+
cum_decay = cum_decay.clamp(min=-80.0, max=0.0) # prevent extreme values
|
| 178 |
+
|
| 179 |
+
# Log-space stabilized parallel carry
|
| 180 |
+
stabilizer = cum_decay.max(dim=1, keepdim=True).values
|
| 181 |
+
norm_cum_decay = (cum_decay - stabilizer).clamp(min=-20.0, max=0.0)
|
| 182 |
+
|
| 183 |
+
seeds = chunk_boundary_out * torch.exp(-norm_cum_decay)
|
| 184 |
+
|
| 185 |
+
# Shift seeds right (carry[0] = 0, carry[c] uses chunks 0..c-1)
|
| 186 |
+
shifted_seeds = F.pad(seeds[:, :-1], (0, 0, 1, 0))
|
| 187 |
+
cum_seeds = torch.cumsum(shifted_seeds, dim=1)
|
| 188 |
+
|
| 189 |
+
carries = cum_seeds * torch.exp(norm_cum_decay)
|
| 190 |
+
|
| 191 |
+
# Apply all carries in parallel (L_chunks already clamped)
|
| 192 |
+
final_out = chunk_out + carries.unsqueeze(2) * torch.exp(L_chunks)
|
| 193 |
+
final_out = final_out.to(x.dtype) # cast back after all float32 math
|
| 194 |
+
final_out = final_out.view(B, -1, D)
|
| 195 |
+
else:
|
| 196 |
+
final_out = chunk_out.to(x.dtype)
|
| 197 |
+
final_out = final_out.view(B, -1, D)
|
| 198 |
+
|
| 199 |
+
final_out = final_out[:, :orig_N, :]
|
| 200 |
+
|
| 201 |
+
# 6. Output
|
| 202 |
+
out = self.out_proj(final_out * gate)
|
| 203 |
+
return residual + self.dropout(out)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
# ─────────────────────────────────────────────────────────────
|
| 207 |
+
# 2. Standard Infrastructure
|
| 208 |
+
# ─────────────────────────────────────────────────────────────
|
| 209 |
+
|
| 210 |
+
class CrossStratumRouting(nn.Module):
|
| 211 |
+
def __init__(self, d_model: int, stride: int):
|
| 212 |
+
super().__init__()
|
| 213 |
+
self.stride = stride
|
| 214 |
+
self.down = nn.AvgPool1d(kernel_size=stride, stride=stride)
|
| 215 |
+
self.up = nn.Upsample(scale_factor=stride, mode='nearest')
|
| 216 |
+
self.gate_f2c = nn.Sequential(nn.Linear(d_model, d_model), nn.Sigmoid())
|
| 217 |
+
self.gate_c2f = nn.Sequential(nn.Linear(d_model, d_model), nn.Sigmoid())
|
| 218 |
+
|
| 219 |
+
def forward(self, h_fine: torch.Tensor, h_coarse: torch.Tensor):
|
| 220 |
+
fine_t = h_fine.transpose(1, 2)
|
| 221 |
+
k = self.down.kernel_size if isinstance(self.down.kernel_size, int) else self.down.kernel_size[0]
|
| 222 |
+
fine_t = F.pad(fine_t, (k - 1, 0))
|
| 223 |
+
f_to_c = self.down(fine_t).transpose(1, 2)
|
| 224 |
+
Lc = h_coarse.shape[1]
|
| 225 |
+
if f_to_c.shape[1] < Lc:
|
| 226 |
+
f_to_c = F.pad(f_to_c, (0, 0, 0, Lc - f_to_c.shape[1]))
|
| 227 |
+
h_coarse = h_coarse + self.gate_f2c(f_to_c[:, :Lc, :]) * f_to_c[:, :Lc, :]
|
| 228 |
+
c_to_f = self.up(h_coarse.transpose(1, 2)).transpose(1, 2)
|
| 229 |
+
Lf = h_fine.shape[1]
|
| 230 |
+
if c_to_f.shape[1] < Lf:
|
| 231 |
+
c_to_f = F.pad(c_to_f, (0, 0, 0, Lf - c_to_f.shape[1]))
|
| 232 |
+
h_fine = h_fine + self.gate_c2f(c_to_f[:, :Lf, :]) * c_to_f[:, :Lf, :]
|
| 233 |
+
return h_fine, h_coarse
|
| 234 |
+
|
| 235 |
+
class SwiGLUFFN(nn.Module):
|
| 236 |
+
def __init__(self, d_model: int, d_ff: int, dropout: float = 0.1):
|
| 237 |
+
super().__init__()
|
| 238 |
+
self.norm = RMSNorm(d_model)
|
| 239 |
+
self.w1, self.w2, self.w3 = nn.Linear(d_model, d_ff, bias=False), nn.Linear(d_ff, d_model, bias=False), nn.Linear(d_model, d_ff, bias=False)
|
| 240 |
+
self.dropout = nn.Dropout(dropout)
|
| 241 |
+
def forward(self, x):
|
| 242 |
+
res = x
|
| 243 |
+
x = self.norm(x)
|
| 244 |
+
return res + self.dropout(self.w2(F.silu(self.w1(x)) * self.w3(x)))
|
| 245 |
+
|
| 246 |
+
class LaminarNet(nn.Module):
|
| 247 |
+
def __init__(self, config: LaminarNetConfig):
|
| 248 |
+
super().__init__()
|
| 249 |
+
self.config, d = config, config.d_model
|
| 250 |
+
self.tok_emb = nn.Embedding(config.vocab_size, d)
|
| 251 |
+
self.dropout = nn.Dropout(config.dropout)
|
| 252 |
+
self.strata_init = nn.ModuleList([nn.AvgPool1d(kernel_size=r, stride=r) for r in config.strata_ratios[1:config.n_strata]])
|
| 253 |
+
self.blocks = nn.ModuleList([LaminarBlock(config) for _ in range(config.n_layers)])
|
| 254 |
+
self.norm_out = RMSNorm(d)
|
| 255 |
+
self.head = nn.Linear(d, config.vocab_size, bias=False)
|
| 256 |
+
self.head.weight = self.tok_emb.weight
|
| 257 |
+
self.apply(lambda m: nn.init.normal_(m.weight, std=0.02) if isinstance(m, (nn.Linear, nn.Embedding)) else None)
|
| 258 |
+
|
| 259 |
+
def forward(self, ids):
|
| 260 |
+
B, N = ids.shape
|
| 261 |
+
x = self.dropout(self.tok_emb(ids))
|
| 262 |
+
# CAUSAL strata init: left-pad so each coarse position only sees past/current
|
| 263 |
+
coarse_strata = []
|
| 264 |
+
for pool in self.strata_init:
|
| 265 |
+
x_t = x.transpose(1, 2)
|
| 266 |
+
k = pool.kernel_size[0]
|
| 267 |
+
x_t = F.pad(x_t, (k - 1, 0))
|
| 268 |
+
coarse_strata.append(pool(x_t).transpose(1, 2))
|
| 269 |
+
strata = [x] + coarse_strata
|
| 270 |
+
for b in self.blocks: strata = b(strata)
|
| 271 |
+
return self.head(self.norm_out(strata[0]))
|
| 272 |
+
|
| 273 |
+
def count_parameters(self): return sum(p.numel() for p in self.parameters() if p.requires_grad)
|
| 274 |
+
|
| 275 |
+
class LaminarBlock(nn.Module):
|
| 276 |
+
def __init__(self, config):
|
| 277 |
+
super().__init__()
|
| 278 |
+
self.S = config.n_strata
|
| 279 |
+
self.gdfs = nn.ModuleList([GeometricDriftField(config.d_model, config.n_heads, config.dropout, config.conv_kernel, config.rope_base) for _ in range(self.S)])
|
| 280 |
+
self.csrs = nn.ModuleList([CrossStratumRouting(config.d_model, config.strata_ratios[s+1]//config.strata_ratios[s]) for s in range(self.S-1)])
|
| 281 |
+
self.ffns = nn.ModuleList([SwiGLUFFN(config.d_model, config.d_ff, config.dropout) for _ in range(self.S)])
|
| 282 |
+
def forward(self, strata):
|
| 283 |
+
for s in range(self.S): strata[s] = self.gdfs[s](strata[s])
|
| 284 |
+
for s in range(self.S - 1): strata[s], strata[s+1] = self.csrs[s](strata[s], strata[s+1])
|
| 285 |
+
for s in range(self.S): strata[s] = self.ffns[s](strata[s])
|
| 286 |
+
return strata
|
| 287 |
+
|
| 288 |
+
if __name__ == "__main__":
|
| 289 |
+
conf = LaminarNetConfig()
|
| 290 |
+
model = LaminarNet(conf)
|
| 291 |
+
x = torch.randint(0, conf.vocab_size, (2, 128))
|
| 292 |
+
print(f"LaminarNet v0.6.0 | Out: {model(x).shape}")
|
model.safetensors
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:f692c6044d94253ed0c4cc3198fa2e9c2ef12efe65413f67f2f915189fc92db4
|
| 3 |
+
size 197704472
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"add_prefix_space": false,
|
| 3 |
+
"backend": "tokenizers",
|
| 4 |
+
"bos_token": "<|endoftext|>",
|
| 5 |
+
"eos_token": "<|endoftext|>",
|
| 6 |
+
"errors": "replace",
|
| 7 |
+
"is_local": false,
|
| 8 |
+
"model_max_length": 1024,
|
| 9 |
+
"pad_token": null,
|
| 10 |
+
"tokenizer_class": "GPT2Tokenizer",
|
| 11 |
+
"unk_token": "<|endoftext|>"
|
| 12 |
+
}
|