Instructions to use smithblack-0/llama3_baseline_dev with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use smithblack-0/llama3_baseline_dev with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("text-generation", model="smithblack-0/llama3_baseline_dev", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("smithblack-0/llama3_baseline_dev", trust_remote_code=True, dtype="auto") - Notebooks
- Google Colab
- Kaggle
- Local Apps Settings
- vLLM
How to use smithblack-0/llama3_baseline_dev with vLLM:
Install from pip and serve model
# Install vLLM from pip: pip install vllm # Start the vLLM server: vllm serve "smithblack-0/llama3_baseline_dev" # Call the server using curl (OpenAI-compatible API): curl -X POST "http://localhost:8000/v1/completions" \ -H "Content-Type: application/json" \ --data '{ "model": "smithblack-0/llama3_baseline_dev", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }'Use Docker
docker model run hf.co/smithblack-0/llama3_baseline_dev
- SGLang
How to use smithblack-0/llama3_baseline_dev 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 "smithblack-0/llama3_baseline_dev" \ --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": "smithblack-0/llama3_baseline_dev", "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 "smithblack-0/llama3_baseline_dev" \ --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": "smithblack-0/llama3_baseline_dev", "prompt": "Once upon a time,", "max_tokens": 512, "temperature": 0.5 }' - Docker Model Runner
How to use smithblack-0/llama3_baseline_dev with Docker Model Runner:
docker model run hf.co/smithblack-0/llama3_baseline_dev
Update architecture and tokenizer
Browse files- README.md +61 -0
- __init__.py +9 -0
- attention.py +149 -0
- config.json +22 -0
- configuration.py +177 -0
- decoder_layer.py +75 -0
- huggingface.py +248 -0
- mlp.py +52 -0
- model.py +118 -0
- rope.py +143 -0
- tokenizer.json +0 -0
- tokenizer_config.json +13 -0
README.md
CHANGED
|
@@ -1,3 +1,64 @@
|
|
| 1 |
---
|
|
|
|
|
|
|
| 2 |
license: mit
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
---
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
language:
|
| 3 |
+
- en
|
| 4 |
license: mit
|
| 5 |
+
library_name: transformers
|
| 6 |
+
pipeline_tag: text-generation
|
| 7 |
+
tags:
|
| 8 |
+
- pytorch
|
| 9 |
+
- research
|
| 10 |
+
- llama
|
| 11 |
---
|
| 12 |
+
|
| 13 |
+
# advanced-transformers-lib -- Llama 3 Baseline
|
| 14 |
+
|
| 15 |
+
A Llama 3-style decoder-only transformer architecture for research. No pretrained
|
| 16 |
+
weights -- pull the architecture from the Hub and instantiate a freshly initialised
|
| 17 |
+
model from config. Override any parameter at instantiation time.
|
| 18 |
+
|
| 19 |
+
> **Important:** `trust_remote_code=True` is required. It downloads the architecture
|
| 20 |
+
> source files from the Hub and imports them into your Python process. Review the
|
| 21 |
+
> source at [smithblack-0/llama3_baseline_dev](https://huggingface.co/smithblack-0/llama3_baseline_dev) before use.
|
| 22 |
+
|
| 23 |
+
## Usage
|
| 24 |
+
|
| 25 |
+
```python
|
| 26 |
+
from transformers import AutoConfig, AutoModelForCausalLM, AutoTokenizer
|
| 27 |
+
|
| 28 |
+
# Pull architecture config -- override any parameter at instantiation time
|
| 29 |
+
config = AutoConfig.from_pretrained(
|
| 30 |
+
"smithblack-0/llama3_baseline_dev",
|
| 31 |
+
trust_remote_code=True,
|
| 32 |
+
num_hidden_layers=16, # example override
|
| 33 |
+
)
|
| 34 |
+
|
| 35 |
+
# Instantiate with fresh random weights -- no checkpoint required
|
| 36 |
+
model = AutoModelForCausalLM.from_config(config, trust_remote_code=True)
|
| 37 |
+
|
| 38 |
+
# Load tokenizer
|
| 39 |
+
tokenizer = AutoTokenizer.from_pretrained("smithblack-0/llama3_baseline_dev")
|
| 40 |
+
|
| 41 |
+
# Save and reload after training
|
| 42 |
+
model.save_pretrained("./checkpoint")
|
| 43 |
+
model = AutoModelForCausalLM.from_pretrained("./checkpoint", trust_remote_code=True)
|
| 44 |
+
```
|
| 45 |
+
|
| 46 |
+
## Default Configuration
|
| 47 |
+
|
| 48 |
+
| Parameter | Default |
|
| 49 |
+
|-----------|---------|
|
| 50 |
+
| `vocab_size` | 50277 |
|
| 51 |
+
| `hidden_size` | 768 |
|
| 52 |
+
| `intermediate_size` | 1568 |
|
| 53 |
+
| `num_hidden_layers` | 24 |
|
| 54 |
+
| `num_attention_heads` | 16 |
|
| 55 |
+
| `num_key_value_heads` | 4 |
|
| 56 |
+
| `head_dim` | 48 |
|
| 57 |
+
| `max_position_embeddings` | 8192 |
|
| 58 |
+
| `rope_theta` | 500000.0 |
|
| 59 |
+
|
| 60 |
+
## License
|
| 61 |
+
|
| 62 |
+
MIT. Clean-room synthesis: the human author has not read the Llama source code.
|
| 63 |
+
Architectural decisions derive from the published paper. Tokenizer is GPT-NeoX
|
| 64 |
+
(`EleutherAI/gpt-neox-20b`, Apache 2.0).
|
__init__.py
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from .attention import GroupedQueryAttention
|
| 2 |
+
from .configuration import Llama3Config
|
| 3 |
+
from .decoder_layer import DecoderLayer
|
| 4 |
+
from .huggingface import Llama3ForCausalLM
|
| 5 |
+
from .mlp import SwiGLUMLP
|
| 6 |
+
from .model import Llama3Model
|
| 7 |
+
from .rope import RotaryEmbedding
|
| 8 |
+
|
| 9 |
+
__all__ = ["DecoderLayer", "GroupedQueryAttention", "Llama3Config", "Llama3ForCausalLM", "Llama3Model", "RotaryEmbedding", "SwiGLUMLP"]
|
attention.py
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Grouped Query Attention (GQA).
|
| 2 |
+
|
| 3 |
+
GQA reduces KV cache memory by sharing key-value heads across groups of query heads.
|
| 4 |
+
With G query heads per KV head, the KV cache is G× smaller than standard multi-head
|
| 5 |
+
attention (MHA). This is the primary motivation for its use in Llama 3 at 128K context:
|
| 6 |
+
8 KV heads shared across 32 query heads gives a 4× cache reduction.
|
| 7 |
+
|
| 8 |
+
Setting num_key_value_heads == num_attention_heads recovers standard MHA.
|
| 9 |
+
Setting num_key_value_heads == 1 gives multi-query attention (MQA).
|
| 10 |
+
|
| 11 |
+
Attention is computed via torch.nn.functional.scaled_dot_product_attention (SDPA),
|
| 12 |
+
which selects FlashAttention when hardware and dtype allow, falling back to standard
|
| 13 |
+
attention otherwise. No custom kernel or additional dependency required.
|
| 14 |
+
|
| 15 |
+
KV caching is handled via HuggingFace's Cache protocol. The cache owns K/V storage and
|
| 16 |
+
accumulation; attention only calls cache.update() to store new projections and retrieve
|
| 17 |
+
the full accumulated history. This cleanly separates attention computation from cache
|
| 18 |
+
management: different Cache subclasses (DynamicCache, StaticCache, custom research
|
| 19 |
+
variants) can be dropped in without touching the attention logic.
|
| 20 |
+
|
| 21 |
+
No bias on any projection — a fixed architectural constant of this model.
|
| 22 |
+
"""
|
| 23 |
+
|
| 24 |
+
import math
|
| 25 |
+
|
| 26 |
+
import torch
|
| 27 |
+
import torch.nn as nn
|
| 28 |
+
import torch.nn.functional as F
|
| 29 |
+
from transformers import PretrainedConfig
|
| 30 |
+
from transformers.cache_utils import Cache
|
| 31 |
+
|
| 32 |
+
from .rope import RotaryEmbedding
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
class GroupedQueryAttention(nn.Module):
|
| 36 |
+
"""Grouped Query Attention with RoPE, causal masking, and KV cache support.
|
| 37 |
+
|
| 38 |
+
Implements GQA as used in Llama 3: Q heads are split into groups, each group
|
| 39 |
+
sharing a single KV head. Before attention is computed, K and V are expanded
|
| 40 |
+
by repeating each KV head across its group of query heads.
|
| 41 |
+
|
| 42 |
+
The forward pass is strictly causal. An optional pre-built boolean attention
|
| 43 |
+
mask can be threaded in from the caller; when absent, SDPA's native
|
| 44 |
+
``is_causal`` mode applies — correct for full-sequence training.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
config: Model config. Must expose ``num_attention_heads``,
|
| 48 |
+
``num_key_value_heads``, ``head_dim``, ``hidden_size``,
|
| 49 |
+
and ``attention_dropout``.
|
| 50 |
+
|
| 51 |
+
Raises:
|
| 52 |
+
ValueError: If ``num_attention_heads`` is not divisible by
|
| 53 |
+
``num_key_value_heads``.
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
def __init__(self, config: PretrainedConfig) -> None:
|
| 57 |
+
super().__init__()
|
| 58 |
+
|
| 59 |
+
self.num_heads = config.num_attention_heads
|
| 60 |
+
self.num_kv_heads = config.num_key_value_heads
|
| 61 |
+
self.head_dim = config.head_dim
|
| 62 |
+
self.num_groups = self.num_heads // self.num_kv_heads
|
| 63 |
+
self.attention_dropout = config.attention_dropout
|
| 64 |
+
|
| 65 |
+
if self.num_heads % self.num_kv_heads != 0:
|
| 66 |
+
raise ValueError(
|
| 67 |
+
f"num_attention_heads ({self.num_heads}) must be divisible by "
|
| 68 |
+
f"num_key_value_heads ({self.num_kv_heads})."
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
# No bias on any projection — architectural constant.
|
| 72 |
+
self.q_proj = nn.Linear(config.hidden_size, self.num_heads * self.head_dim, bias=False)
|
| 73 |
+
self.k_proj = nn.Linear(config.hidden_size, self.num_kv_heads * self.head_dim, bias=False)
|
| 74 |
+
self.v_proj = nn.Linear(config.hidden_size, self.num_kv_heads * self.head_dim, bias=False)
|
| 75 |
+
self.o_proj = nn.Linear(self.num_heads * self.head_dim, config.hidden_size, bias=False)
|
| 76 |
+
|
| 77 |
+
self.rope = RotaryEmbedding(config)
|
| 78 |
+
|
| 79 |
+
def forward(
|
| 80 |
+
self,
|
| 81 |
+
x: torch.Tensor,
|
| 82 |
+
position_ids: torch.Tensor,
|
| 83 |
+
cache: Cache | None = None,
|
| 84 |
+
layer_idx: int = 0,
|
| 85 |
+
causal_mask: torch.Tensor | None = None,
|
| 86 |
+
) -> torch.Tensor:
|
| 87 |
+
"""Apply grouped query attention to the input.
|
| 88 |
+
|
| 89 |
+
Args:
|
| 90 |
+
x: Input of shape (batch, seq_len, hidden_size).
|
| 91 |
+
position_ids: Absolute positions of shape (batch, seq_len). Used by
|
| 92 |
+
RoPE to rotate Q and K at the correct frequencies.
|
| 93 |
+
cache: HuggingFace Cache object for KV accumulation, or None when
|
| 94 |
+
caching is disabled (``use_cache=False``). When provided,
|
| 95 |
+
``cache.update(k, v, layer_idx)`` stores the new K/V and returns
|
| 96 |
+
the full accumulated key and value tensors for this layer.
|
| 97 |
+
layer_idx: Which slot in the cache to read and write. Each decoder
|
| 98 |
+
layer has its own index so they accumulate independently.
|
| 99 |
+
causal_mask: Optional boolean attention mask of shape
|
| 100 |
+
(1, 1, seq_len, kv_len), where True indicates a position that
|
| 101 |
+
should be attended to. When None, SDPA's built-in ``is_causal``
|
| 102 |
+
mode is used, which is correct for full-sequence training
|
| 103 |
+
(square Q×K matrix). When provided, ``is_causal`` is disabled
|
| 104 |
+
and the explicit mask governs attention — required for any
|
| 105 |
+
generation pattern where Q and K lengths differ.
|
| 106 |
+
|
| 107 |
+
Returns:
|
| 108 |
+
Output tensor of shape (batch, seq_len, hidden_size).
|
| 109 |
+
"""
|
| 110 |
+
batch, seq_len, _ = x.shape
|
| 111 |
+
|
| 112 |
+
# Project and reshape: (batch, seq_len, heads * head_dim)
|
| 113 |
+
# → (batch, heads, seq_len, head_dim)
|
| 114 |
+
q = self.q_proj(x).view(batch, seq_len, self.num_heads, self.head_dim).transpose(1, 2)
|
| 115 |
+
k = self.k_proj(x).view(batch, seq_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
|
| 116 |
+
v = self.v_proj(x).view(batch, seq_len, self.num_kv_heads, self.head_dim).transpose(1, 2)
|
| 117 |
+
|
| 118 |
+
# Apply RoPE. attention_scaling is 1.0 for default/linear; YaRN returns a
|
| 119 |
+
# value != 1.0 that corrects attention magnitude after frequency manipulation.
|
| 120 |
+
q, k, attention_scaling = self.rope(q, k, position_ids)
|
| 121 |
+
|
| 122 |
+
if cache is not None:
|
| 123 |
+
k_full, v_full = cache.update(k, v, layer_idx)
|
| 124 |
+
else:
|
| 125 |
+
k_full, v_full = k, v
|
| 126 |
+
|
| 127 |
+
# Expand KV heads to align with query heads for GQA.
|
| 128 |
+
# Each KV head is repeated num_groups times so SDPA sees matching head counts.
|
| 129 |
+
if self.num_groups > 1:
|
| 130 |
+
k_full = k_full.repeat_interleave(self.num_groups, dim=1)
|
| 131 |
+
v_full = v_full.repeat_interleave(self.num_groups, dim=1)
|
| 132 |
+
|
| 133 |
+
attn_output = F.scaled_dot_product_attention(
|
| 134 |
+
q, k_full, v_full,
|
| 135 |
+
attn_mask=causal_mask,
|
| 136 |
+
dropout_p=self.attention_dropout if self.training else 0.0,
|
| 137 |
+
is_causal=causal_mask is None,
|
| 138 |
+
scale=attention_scaling / math.sqrt(self.head_dim),
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
# Merge heads and project back to hidden_size.
|
| 142 |
+
attn_output = (
|
| 143 |
+
attn_output
|
| 144 |
+
.transpose(1, 2)
|
| 145 |
+
.contiguous()
|
| 146 |
+
.view(batch, seq_len, self.num_heads * self.head_dim)
|
| 147 |
+
)
|
| 148 |
+
|
| 149 |
+
return self.o_proj(attn_output)
|
config.json
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"attention_dropout": 0.0,
|
| 3 |
+
"auto_map": {
|
| 4 |
+
"AutoConfig": "configuration.Llama3Config",
|
| 5 |
+
"AutoModelForCausalLM": "huggingface.Llama3ForCausalLM"
|
| 6 |
+
},
|
| 7 |
+
"head_dim": 48,
|
| 8 |
+
"hidden_size": 768,
|
| 9 |
+
"intermediate_size": 1568,
|
| 10 |
+
"max_position_embeddings": 8192,
|
| 11 |
+
"model_type": "llama3_baseline",
|
| 12 |
+
"num_attention_heads": 16,
|
| 13 |
+
"num_hidden_layers": 24,
|
| 14 |
+
"num_key_value_heads": 4,
|
| 15 |
+
"rms_norm_eps": 1e-05,
|
| 16 |
+
"rope_parameters": null,
|
| 17 |
+
"rope_theta": 500000.0,
|
| 18 |
+
"tie_word_embeddings": false,
|
| 19 |
+
"transformers_version": "5.3.0",
|
| 20 |
+
"use_cache": true,
|
| 21 |
+
"vocab_size": 50277
|
| 22 |
+
}
|
configuration.py
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Configuration for the Llama 3 baseline transformer.
|
| 2 |
+
|
| 3 |
+
All architectural parameters that vary across model scales or are meaningful research
|
| 4 |
+
variables are expressed here. Architectural constants of Llama 3 (no bias in linear
|
| 5 |
+
layers, SwiGLU activation with SiLU gate) are implemented in the relevant modules and
|
| 6 |
+
documented at the point of use — they are not config parameters because they do not
|
| 7 |
+
vary across Llama 3 scales and changing them produces a different architecture, not a
|
| 8 |
+
different scale of this one.
|
| 9 |
+
|
| 10 |
+
RoPE configuration is handled by HuggingFace's RotaryEmbeddingConfigMixin (mixed into
|
| 11 |
+
PretrainedConfig in transformers 5.x). rope_theta and rope_scaling are passed through
|
| 12 |
+
to the base class, which validates and standardises them into config.rope_parameters.
|
| 13 |
+
Do not bypass or duplicate this system.
|
| 14 |
+
"""
|
| 15 |
+
|
| 16 |
+
from transformers import PretrainedConfig
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class Llama3Config(PretrainedConfig):
|
| 20 |
+
"""Configuration class for the Llama 3 baseline decoder-only transformer.
|
| 21 |
+
|
| 22 |
+
This config is the single source of truth for every architectural dimension of the
|
| 23 |
+
model. Nothing in the architecture may use a literal number that belongs here —
|
| 24 |
+
doing so breaks the library's ability to express different model scales without
|
| 25 |
+
code changes.
|
| 26 |
+
|
| 27 |
+
RoPE scaling is handled by HuggingFace's rope system. Pass rope_scaling as a dict
|
| 28 |
+
using HF's format (key is ``rope_type``, not ``type``). Supported types:
|
| 29 |
+
``"linear"``, ``"dynamic"``, ``"yarn"``, ``"longrope"``, ``"llama3"``. HF validates
|
| 30 |
+
the dict and standardises it into ``config.rope_parameters``.
|
| 31 |
+
|
| 32 |
+
Registered with HuggingFace AutoClass via ``auto_map``. Instantiate from the Hub::
|
| 33 |
+
|
| 34 |
+
config = AutoConfig.from_pretrained(
|
| 35 |
+
"your-namespace/advanced-transformers-lib",
|
| 36 |
+
trust_remote_code=True,
|
| 37 |
+
num_hidden_layers=16, # override any parameter at instantiation time
|
| 38 |
+
)
|
| 39 |
+
model = AutoModelForCausalLM.from_config(config)
|
| 40 |
+
|
| 41 |
+
Args:
|
| 42 |
+
vocab_size: Vocabulary size. Controls the embedding table and output logits
|
| 43 |
+
dimension. Must match the tokenizer.
|
| 44 |
+
hidden_size: Model width. The central dimension from which all others are
|
| 45 |
+
derived or to which they project.
|
| 46 |
+
intermediate_size: FFN hidden dimension. Expressed directly rather than derived
|
| 47 |
+
from a formula because Llama 3 ratios vary by scale (~3.5x at 8B/70B,
|
| 48 |
+
~3.25x at 405B). A formula would be wrong for at least some scales.
|
| 49 |
+
num_hidden_layers: Number of transformer blocks stacked in sequence.
|
| 50 |
+
num_attention_heads: Number of query heads. Determines how hidden_size is
|
| 51 |
+
partitioned per head.
|
| 52 |
+
num_key_value_heads: Number of KV heads for Grouped Query Attention. Must
|
| 53 |
+
evenly divide num_attention_heads. Setting equal to num_attention_heads
|
| 54 |
+
gives standard MHA; setting to 1 gives MQA; values between give GQA.
|
| 55 |
+
Llama 3 uses 8 at all scales, motivated by KV cache memory at 128K context.
|
| 56 |
+
head_dim: Dimension per attention head. Normally hidden_size //
|
| 57 |
+
num_attention_heads, but exposed as a parameter for architectures that
|
| 58 |
+
decouple head count from head size. Computed automatically if None.
|
| 59 |
+
rms_norm_eps: Epsilon passed to torch.nn.RMSNorm. Prevents division by zero
|
| 60 |
+
when layer activations are near zero.
|
| 61 |
+
rope_theta: Base rotation frequency for RoPE. Controls how fast position angles
|
| 62 |
+
rotate per dimension — higher values mean slower rotation, preventing
|
| 63 |
+
positional aliasing at long sequence distances. Llama 3 uses 500,000
|
| 64 |
+
(vs ~10,000 typical) as a prerequisite for 128K context support. This
|
| 65 |
+
value has physical meaning tied to the target context length and must
|
| 66 |
+
never be hardcoded in the architecture.
|
| 67 |
+
max_position_embeddings: The context length the model was trained at. Used by
|
| 68 |
+
HF's rope system as original_max_position_embeddings for scaling types that
|
| 69 |
+
need it (yarn, longrope, llama3). This is the training context length, not
|
| 70 |
+
an inference ceiling — the rope module handles longer sequences at runtime
|
| 71 |
+
via lazy cache extension. Llama 3 base training context: 8192.
|
| 72 |
+
rope_scaling: Optional RoPE scaling configuration for extending context beyond
|
| 73 |
+
max_position_embeddings without retraining. Pass as a dict in HF's format
|
| 74 |
+
with ``rope_type`` as the key. HF's RotaryEmbeddingConfigMixin validates
|
| 75 |
+
and stores this. None means no scaling (default RoPE behaviour).
|
| 76 |
+
attention_dropout: Dropout probability applied to attention weights. Default
|
| 77 |
+
0.0 for deterministic behaviour.
|
| 78 |
+
use_cache: Whether the model returns past_key_values for KV caching. Set True
|
| 79 |
+
for inference, may be set False during training to reduce memory pressure.
|
| 80 |
+
output_hidden_states: Whether the model returns the hidden state tensor after
|
| 81 |
+
each decoder layer. Useful for probing or intermediate representation
|
| 82 |
+
extraction. Default False.
|
| 83 |
+
tie_word_embeddings: Whether the input embedding table and the LM head share
|
| 84 |
+
weights. False for Llama 3.
|
| 85 |
+
"""
|
| 86 |
+
|
| 87 |
+
model_type = "llama3_baseline"
|
| 88 |
+
|
| 89 |
+
# auto_map tells HuggingFace which classes to instantiate when loading this config
|
| 90 |
+
# with trust_remote_code=True. Paths are relative to the Hub repository root, not
|
| 91 |
+
# the local src/ layout — these are the paths used after HF downloads the files.
|
| 92 |
+
auto_map = {
|
| 93 |
+
"AutoConfig": "configuration.Llama3Config",
|
| 94 |
+
"AutoModelForCausalLM": "huggingface.Llama3ForCausalLM",
|
| 95 |
+
}
|
| 96 |
+
|
| 97 |
+
def __init__(
|
| 98 |
+
self,
|
| 99 |
+
vocab_size: int = 50277,
|
| 100 |
+
hidden_size: int = 768,
|
| 101 |
+
intermediate_size: int = 1568,
|
| 102 |
+
num_hidden_layers: int = 24,
|
| 103 |
+
num_attention_heads: int = 16,
|
| 104 |
+
num_key_value_heads: int = 4,
|
| 105 |
+
head_dim: int | None = None,
|
| 106 |
+
rms_norm_eps: float = 1e-5,
|
| 107 |
+
rope_theta: float = 500000.0,
|
| 108 |
+
max_position_embeddings: int = 8192,
|
| 109 |
+
rope_scaling: dict | None = None,
|
| 110 |
+
attention_dropout: float = 0.0,
|
| 111 |
+
use_cache: bool = True,
|
| 112 |
+
output_hidden_states: bool = False,
|
| 113 |
+
tie_word_embeddings: bool = False,
|
| 114 |
+
**kwargs,
|
| 115 |
+
):
|
| 116 |
+
# Validate structural constraints before storing anything, so that an invalid
|
| 117 |
+
# config fails loudly at construction rather than silently producing wrong
|
| 118 |
+
# shapes at forward-pass time.
|
| 119 |
+
if hidden_size % num_attention_heads != 0:
|
| 120 |
+
raise ValueError(
|
| 121 |
+
f"hidden_size ({hidden_size}) must be divisible by "
|
| 122 |
+
f"num_attention_heads ({num_attention_heads})."
|
| 123 |
+
)
|
| 124 |
+
if num_attention_heads % num_key_value_heads != 0:
|
| 125 |
+
raise ValueError(
|
| 126 |
+
f"num_attention_heads ({num_attention_heads}) must be divisible by "
|
| 127 |
+
f"num_key_value_heads ({num_key_value_heads}). GQA requires query "
|
| 128 |
+
f"heads to divide evenly across KV head groups."
|
| 129 |
+
)
|
| 130 |
+
|
| 131 |
+
# RoPE rotates dimensions in pairs. An odd head_dim has no valid pairing and
|
| 132 |
+
# produces a cos/sin cache of size head_dim+1 (torch.arange(0, odd, 2) rounds
|
| 133 |
+
# up), causing a shape mismatch at runtime. Catch it here rather than at
|
| 134 |
+
# forward-pass time.
|
| 135 |
+
resolved_head_dim = head_dim if head_dim is not None else hidden_size // num_attention_heads
|
| 136 |
+
if resolved_head_dim % 2 != 0:
|
| 137 |
+
raise ValueError(
|
| 138 |
+
f"head_dim must be even (RoPE rotates dimensions in pairs). "
|
| 139 |
+
f"Got head_dim={resolved_head_dim} from hidden_size={hidden_size} "
|
| 140 |
+
f"and num_attention_heads={num_attention_heads}."
|
| 141 |
+
)
|
| 142 |
+
|
| 143 |
+
# head_dim is normally hidden_size // num_attention_heads but is exposed as a
|
| 144 |
+
# parameter for architectures that decouple head count from head size.
|
| 145 |
+
if head_dim is None:
|
| 146 |
+
head_dim = hidden_size // num_attention_heads
|
| 147 |
+
|
| 148 |
+
self.vocab_size = vocab_size
|
| 149 |
+
self.max_position_embeddings = max_position_embeddings
|
| 150 |
+
self.hidden_size = hidden_size
|
| 151 |
+
self.intermediate_size = intermediate_size
|
| 152 |
+
self.num_hidden_layers = num_hidden_layers
|
| 153 |
+
self.num_attention_heads = num_attention_heads
|
| 154 |
+
self.num_key_value_heads = num_key_value_heads
|
| 155 |
+
self.head_dim = head_dim
|
| 156 |
+
self.rms_norm_eps = rms_norm_eps
|
| 157 |
+
self.attention_dropout = attention_dropout
|
| 158 |
+
self.use_cache = use_cache
|
| 159 |
+
|
| 160 |
+
# rope_theta, max_position_embeddings, and rope_scaling are passed to HF's
|
| 161 |
+
# base class, which owns rope configuration via RotaryEmbeddingConfigMixin.
|
| 162 |
+
# HF validates rope_scaling and standardises everything into rope_parameters.
|
| 163 |
+
# Do not store or validate these ourselves.
|
| 164 |
+
super().__init__(
|
| 165 |
+
rope_theta=rope_theta,
|
| 166 |
+
max_position_embeddings=max_position_embeddings,
|
| 167 |
+
rope_scaling=rope_scaling,
|
| 168 |
+
tie_word_embeddings=tie_word_embeddings,
|
| 169 |
+
output_hidden_states=output_hidden_states,
|
| 170 |
+
**kwargs,
|
| 171 |
+
)
|
| 172 |
+
|
| 173 |
+
# Promote auto_map to an instance attribute so PretrainedConfig.to_dict()
|
| 174 |
+
# serialises it into config.json. Class-level attributes are not picked up
|
| 175 |
+
# by to_dict() — only self.__dict__ is serialised. model_type is the sole
|
| 176 |
+
# exception handled specially by HF; auto_map is not.
|
| 177 |
+
self.auto_map = type(self).auto_map
|
decoder_layer.py
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Decoder layer — a single transformer block.
|
| 2 |
+
|
| 3 |
+
Each block applies pre-norm attention followed by pre-norm MLP, with residual
|
| 4 |
+
connections around both sublayers:
|
| 5 |
+
|
| 6 |
+
normed = RMSNorm(x)
|
| 7 |
+
h = x + Attention(normed, ...)
|
| 8 |
+
normed = RMSNorm(h)
|
| 9 |
+
out = h + MLP(normed)
|
| 10 |
+
|
| 11 |
+
Pre-norm keeps the residual stream unnormalised. Gradients flow more cleanly
|
| 12 |
+
through unnormalised residuals at depth, and each sublayer receives a stable,
|
| 13 |
+
normalised view of the signal.
|
| 14 |
+
|
| 15 |
+
Two independent RMSNorm instances are used — one before attention, one before MLP.
|
| 16 |
+
They learn different scalings because they precede layers with different dynamic
|
| 17 |
+
ranges. Sharing them would be wrong.
|
| 18 |
+
|
| 19 |
+
torch.nn.RMSNorm is used directly (available from PyTorch 2.4+). It omits mean
|
| 20 |
+
subtraction, is faster than LayerNorm, and proved more stable at scale.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
import torch
|
| 24 |
+
import torch.nn as nn
|
| 25 |
+
from transformers import PretrainedConfig
|
| 26 |
+
from transformers.cache_utils import Cache
|
| 27 |
+
|
| 28 |
+
from .attention import GroupedQueryAttention
|
| 29 |
+
from .mlp import SwiGLUMLP
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
class DecoderLayer(nn.Module):
|
| 33 |
+
"""A single pre-norm transformer decoder block.
|
| 34 |
+
|
| 35 |
+
Composes GroupedQueryAttention and SwiGLUMLP with residual connections and
|
| 36 |
+
independent RMSNorm instances on each sublayer input.
|
| 37 |
+
|
| 38 |
+
Args:
|
| 39 |
+
config: Model config passed through to attention and MLP. Must also expose
|
| 40 |
+
``hidden_size`` and ``rms_norm_eps``.
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
def __init__(self, config: PretrainedConfig) -> None:
|
| 44 |
+
super().__init__()
|
| 45 |
+
self.attn_norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 46 |
+
self.mlp_norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 47 |
+
self.attention = GroupedQueryAttention(config)
|
| 48 |
+
self.mlp = SwiGLUMLP(config)
|
| 49 |
+
|
| 50 |
+
def forward(
|
| 51 |
+
self,
|
| 52 |
+
x: torch.Tensor,
|
| 53 |
+
position_ids: torch.Tensor,
|
| 54 |
+
cache: Cache | None = None,
|
| 55 |
+
layer_idx: int = 0,
|
| 56 |
+
causal_mask: torch.Tensor | None = None,
|
| 57 |
+
) -> torch.Tensor:
|
| 58 |
+
"""Apply one decoder block to the input.
|
| 59 |
+
|
| 60 |
+
Args:
|
| 61 |
+
x: Input of shape (batch, seq_len, hidden_size).
|
| 62 |
+
position_ids: Absolute positions of shape (batch, seq_len).
|
| 63 |
+
cache: HuggingFace Cache object for KV accumulation, or None when
|
| 64 |
+
caching is disabled. Passed through to attention unchanged.
|
| 65 |
+
layer_idx: Cache slot index for this layer. Each layer has its own
|
| 66 |
+
index so they accumulate independently within the shared cache.
|
| 67 |
+
causal_mask: Optional boolean attention mask of shape
|
| 68 |
+
(1, 1, seq_len, kv_len). Passed through to attention unchanged.
|
| 69 |
+
|
| 70 |
+
Returns:
|
| 71 |
+
Output tensor of shape (batch, seq_len, hidden_size).
|
| 72 |
+
"""
|
| 73 |
+
attn_out = self.attention(self.attn_norm(x), position_ids, cache, layer_idx, causal_mask)
|
| 74 |
+
h = x + attn_out
|
| 75 |
+
return h + self.mlp(self.mlp_norm(h))
|
huggingface.py
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""HuggingFace wrapper for the Llama 3 baseline.
|
| 2 |
+
|
| 3 |
+
Llama3ForCausalLM wraps Llama3Model with everything a researcher needs to
|
| 4 |
+
train, evaluate, and generate from it through the HuggingFace ecosystem:
|
| 5 |
+
token embedding, vocabulary projection, next-token loss, weight tying, and
|
| 6 |
+
the full AutoClass and GenerationMixin contracts.
|
| 7 |
+
|
| 8 |
+
The token embedding lives here, not on the backbone. Llama3Model is a pure
|
| 9 |
+
transformer stack that accepts pre-embedded hidden states — it has no knowledge
|
| 10 |
+
of tokens or vocabulary. This is the correct HF convention: the backbone is
|
| 11 |
+
modality-agnostic; the token interface belongs on the task wrapper.
|
| 12 |
+
|
| 13 |
+
The LM head projects the backbone's (batch, seq, hidden_size) output to
|
| 14 |
+
(batch, seq, vocab_size) logits. When labels are provided, cross-entropy loss
|
| 15 |
+
is computed with a one-position shift: token i predicts token i+1. The shift
|
| 16 |
+
is applied here rather than expected from the caller — a causal LM always
|
| 17 |
+
trains this way and there is no use case for an unshifted loss.
|
| 18 |
+
|
| 19 |
+
Weight tying: when config.tie_word_embeddings is True, lm_head.weight is
|
| 20 |
+
directly assigned to embed_tokens.weight after post_init(). Both matrices are
|
| 21 |
+
shape (vocab_size, hidden_size) — same shape, no transpose needed.
|
| 22 |
+
|
| 23 |
+
KV caching uses HuggingFace's Cache protocol. GenerationMixin creates and
|
| 24 |
+
manages the DynamicCache for generate() calls, passing it as past_key_values
|
| 25 |
+
on every forward call. The backbone updates the cache in place and returns the
|
| 26 |
+
same object. _reorder_cache delegates to DynamicCache.reorder_cache() for beam
|
| 27 |
+
search, keeping all beam-reordering logic inside the cache implementation.
|
| 28 |
+
|
| 29 |
+
Returns a CausalLMOutputWithPast. ModelOutput subclasses support both attribute
|
| 30 |
+
access (output.logits) and dict-style access (output["logits"]), satisfying
|
| 31 |
+
GenerationMixin's attribute access requirements while keeping existing code unchanged.
|
| 32 |
+
"""
|
| 33 |
+
|
| 34 |
+
import torch
|
| 35 |
+
import torch.nn as nn
|
| 36 |
+
from transformers import PreTrainedModel, GenerationMixin
|
| 37 |
+
from transformers.cache_utils import Cache, DynamicCache
|
| 38 |
+
from transformers.modeling_outputs import CausalLMOutputWithPast
|
| 39 |
+
|
| 40 |
+
from .configuration import Llama3Config
|
| 41 |
+
from .model import Llama3Model
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
class Llama3ForCausalLM(PreTrainedModel, GenerationMixin):
|
| 45 |
+
"""Llama 3 causal language model: token embedding, backbone, LM head, HF contract.
|
| 46 |
+
|
| 47 |
+
Owns the token embedding and LM head. Delegates all transformer computation
|
| 48 |
+
to Llama3Model. Adds loss computation for training, weight tying between the
|
| 49 |
+
LM head and the input embedding, and the full HuggingFace AutoClass and
|
| 50 |
+
GenerationMixin contracts.
|
| 51 |
+
|
| 52 |
+
Args:
|
| 53 |
+
config: Model configuration. Must be a ``Llama3Config`` instance.
|
| 54 |
+
"""
|
| 55 |
+
|
| 56 |
+
config_class = Llama3Config
|
| 57 |
+
base_model_prefix = "model"
|
| 58 |
+
_no_split_modules = ["DecoderLayer"]
|
| 59 |
+
supports_gradient_checkpointing = True
|
| 60 |
+
|
| 61 |
+
def __init__(self, config: Llama3Config) -> None:
|
| 62 |
+
super().__init__(config)
|
| 63 |
+
self.embed_tokens = nn.Embedding(config.vocab_size, config.hidden_size)
|
| 64 |
+
self.model = Llama3Model(config)
|
| 65 |
+
|
| 66 |
+
# No bias — consistent with all other projections in this architecture.
|
| 67 |
+
self.lm_head = nn.Linear(config.hidden_size, config.vocab_size, bias=False)
|
| 68 |
+
self.post_init()
|
| 69 |
+
|
| 70 |
+
# Direct weight tying: both matrices are (vocab_size, hidden_size) — same shape,
|
| 71 |
+
# no transpose. Explicit here for visibility; post_init() → tie_weights() also
|
| 72 |
+
# performs this via get_input/output_embeddings(), but that is less readable.
|
| 73 |
+
if config.tie_word_embeddings:
|
| 74 |
+
self.lm_head.weight = self.embed_tokens.weight
|
| 75 |
+
|
| 76 |
+
def _init_weights(self, module: nn.Module) -> None:
|
| 77 |
+
# Suppress HF's default reinitialisation pass. HF's _init_weights overwrites
|
| 78 |
+
# all Linear and Embedding weights with normal(0, 0.02) after construction,
|
| 79 |
+
# silently replacing PyTorch's own defaults (kaiming_uniform_ for Linear,
|
| 80 |
+
# normal(0,1) for Embedding). PyTorch's reset_parameters() already ran at
|
| 81 |
+
# construction time and those initialisations should stand.
|
| 82 |
+
pass
|
| 83 |
+
|
| 84 |
+
def get_input_embeddings(self) -> nn.Embedding:
|
| 85 |
+
"""Return the token embedding matrix. Required by PreTrainedModel for weight tying and resize_token_embeddings."""
|
| 86 |
+
return self.embed_tokens
|
| 87 |
+
|
| 88 |
+
def set_input_embeddings(self, value: nn.Embedding) -> None:
|
| 89 |
+
"""Replace the token embedding matrix. Required by PreTrainedModel for weight tying and resize_token_embeddings."""
|
| 90 |
+
self.embed_tokens = value
|
| 91 |
+
|
| 92 |
+
def get_output_embeddings(self) -> nn.Linear:
|
| 93 |
+
"""Return the LM head. Required by PreTrainedModel for weight tying and resize_token_embeddings."""
|
| 94 |
+
return self.lm_head
|
| 95 |
+
|
| 96 |
+
def set_output_embeddings(self, value: nn.Linear) -> None:
|
| 97 |
+
"""Replace the LM head. Required by PreTrainedModel for weight tying and resize_token_embeddings."""
|
| 98 |
+
self.lm_head = value
|
| 99 |
+
|
| 100 |
+
def _reorder_cache(
|
| 101 |
+
self, past_key_values: Cache, beam_idx: torch.Tensor
|
| 102 |
+
) -> Cache:
|
| 103 |
+
"""Reorder the KV cache to match beam reordering during beam search.
|
| 104 |
+
|
| 105 |
+
GenerationMixin calls this after pruning and reordering beams at each
|
| 106 |
+
step. beam_idx[i] is the old batch position whose cache should move to
|
| 107 |
+
position i. DynamicCache.reorder_cache() handles the index-select on
|
| 108 |
+
every stored tensor's batch dimension, keeping the cache consistent with
|
| 109 |
+
the reordered beam hypotheses.
|
| 110 |
+
|
| 111 |
+
Args:
|
| 112 |
+
past_key_values: The active Cache object.
|
| 113 |
+
beam_idx: 1-D tensor of shape (batch * num_beams,) mapping new batch
|
| 114 |
+
positions to old ones.
|
| 115 |
+
|
| 116 |
+
Returns:
|
| 117 |
+
The same Cache object, reordered in place.
|
| 118 |
+
"""
|
| 119 |
+
past_key_values.reorder_cache(beam_idx)
|
| 120 |
+
return past_key_values
|
| 121 |
+
|
| 122 |
+
def forward(
|
| 123 |
+
self,
|
| 124 |
+
input_ids: torch.Tensor,
|
| 125 |
+
position_ids: torch.Tensor | None = None,
|
| 126 |
+
past_key_values: Cache | None = None,
|
| 127 |
+
use_cache: bool | None = None,
|
| 128 |
+
output_hidden_states: bool | None = None,
|
| 129 |
+
labels: torch.Tensor | None = None,
|
| 130 |
+
cache_position: torch.Tensor | None = None,
|
| 131 |
+
**kwargs,
|
| 132 |
+
) -> CausalLMOutputWithPast:
|
| 133 |
+
"""Run the causal language model.
|
| 134 |
+
|
| 135 |
+
Args:
|
| 136 |
+
input_ids: Token indices of shape (batch, seq_len).
|
| 137 |
+
position_ids: Absolute positions of shape (batch, seq_len). Passed
|
| 138 |
+
through to the backbone. When use_cache=True and this is None,
|
| 139 |
+
derived from cache_position.
|
| 140 |
+
past_key_values: A HuggingFace Cache object from a prior step, or
|
| 141 |
+
None. When use_cache=True and this is None, a fresh DynamicCache
|
| 142 |
+
is created here before calling the backbone.
|
| 143 |
+
use_cache: Whether to accumulate and return a KV cache. When True
|
| 144 |
+
and no cache is provided, a DynamicCache is created. When False,
|
| 145 |
+
None is passed to the backbone regardless of what was provided.
|
| 146 |
+
Defaults to config.use_cache when None.
|
| 147 |
+
output_hidden_states: Whether to return per-layer hidden states.
|
| 148 |
+
Passed through to the backbone.
|
| 149 |
+
labels: Target token indices of shape (batch, seq_len) for computing
|
| 150 |
+
next-token prediction loss. The loss is computed over positions
|
| 151 |
+
1..seq_len predicting from positions 0..seq_len-1 — the shift
|
| 152 |
+
is applied internally. Positions with label value -100 are
|
| 153 |
+
ignored by cross-entropy, following the HuggingFace convention
|
| 154 |
+
for padding and masked positions.
|
| 155 |
+
cache_position: 1-D integer tensor of shape (seq_len,) giving the
|
| 156 |
+
absolute position of each input token in the full sequence.
|
| 157 |
+
Provided by GenerationMixin during generate(). When use_cache=True
|
| 158 |
+
and this is None, it is derived from the current cache length.
|
| 159 |
+
**kwargs: Additional keyword arguments passed by GenerationMixin
|
| 160 |
+
(e.g. return_dict). Accepted and ignored for forward compatibility.
|
| 161 |
+
We always return CausalLMOutputWithPast regardless of return_dict.
|
| 162 |
+
|
| 163 |
+
Returns:
|
| 164 |
+
CausalLMOutputWithPast with fields:
|
| 165 |
+
- ``logits``: vocabulary scores of shape (batch, seq_len, vocab_size).
|
| 166 |
+
Always present.
|
| 167 |
+
- ``loss``: scalar cross-entropy loss, or None if labels not provided.
|
| 168 |
+
- ``past_key_values``: the updated Cache object, or None.
|
| 169 |
+
- ``hidden_states``: per-layer hidden states, or None.
|
| 170 |
+
"""
|
| 171 |
+
if kwargs.get("attention_mask") is not None:
|
| 172 |
+
raise ValueError(
|
| 173 |
+
"attention_mask is not supported. This model does not support padding masks. "
|
| 174 |
+
"For training on variable-length sequences, use right-padding with -100 labels."
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
# Resolve both flags against config defaults. Config sets the default;
|
| 178 |
+
# per-call arguments override it. Both fields in Llama3Config remain live.
|
| 179 |
+
use_cache = use_cache if use_cache is not None else self.config.use_cache
|
| 180 |
+
output_hidden_states = (
|
| 181 |
+
output_hidden_states
|
| 182 |
+
if output_hidden_states is not None
|
| 183 |
+
else self.config.output_hidden_states
|
| 184 |
+
)
|
| 185 |
+
|
| 186 |
+
# Cache lifecycle is owned here — the backbone only receives a cache or None
|
| 187 |
+
# and never decides whether to create one.
|
| 188 |
+
if use_cache:
|
| 189 |
+
if past_key_values is None:
|
| 190 |
+
past_key_values = DynamicCache()
|
| 191 |
+
else:
|
| 192 |
+
past_key_values = None
|
| 193 |
+
|
| 194 |
+
inputs_embeds = self.embed_tokens(input_ids)
|
| 195 |
+
batch, seq_len, _ = inputs_embeds.shape
|
| 196 |
+
|
| 197 |
+
# For training (use_cache=False), positions are always 0..seq_len-1.
|
| 198 |
+
# This is not inference from state — it is a trivial fact about a
|
| 199 |
+
# non-cached forward pass. The backbone requires explicit position_ids.
|
| 200 |
+
if not use_cache and position_ids is None:
|
| 201 |
+
position_ids = torch.arange(seq_len, device=inputs_embeds.device).unsqueeze(0).expand(batch, -1)
|
| 202 |
+
|
| 203 |
+
causal_mask = None
|
| 204 |
+
if use_cache:
|
| 205 |
+
# Derive absolute query positions from cache occupancy. cache_len is the
|
| 206 |
+
# number of tokens already stored; query positions are cache_len..cache_len+seq_len-1.
|
| 207 |
+
# cache_position is accepted in the signature for HuggingFace contract
|
| 208 |
+
# compatibility but is not used — get_seq_length() is the authoritative source.
|
| 209 |
+
cache_len = past_key_values.get_seq_length()
|
| 210 |
+
q_positions = torch.arange(cache_len, cache_len + seq_len, device=inputs_embeds.device)
|
| 211 |
+
|
| 212 |
+
if position_ids is None:
|
| 213 |
+
position_ids = q_positions.unsqueeze(0).expand(batch, -1)
|
| 214 |
+
|
| 215 |
+
# Build the causal attention mask. Each query at position p may attend to
|
| 216 |
+
# all keys at positions 0..p. k_len covers the full sequence after this step.
|
| 217 |
+
k_positions = torch.arange(cache_len + seq_len, device=inputs_embeds.device)
|
| 218 |
+
# mask[q, k] = True when key position k is within the causal horizon of query q.
|
| 219 |
+
# Shape: (1, 1, seq_len, k_len) — broadcast over batch and head dimensions.
|
| 220 |
+
causal_mask = (k_positions[None, :] <= q_positions[:, None]).unsqueeze(0).unsqueeze(0)
|
| 221 |
+
|
| 222 |
+
backbone_out = self.model(
|
| 223 |
+
inputs_embeds,
|
| 224 |
+
position_ids=position_ids,
|
| 225 |
+
past_key_values=past_key_values,
|
| 226 |
+
output_hidden_states=output_hidden_states,
|
| 227 |
+
causal_mask=causal_mask,
|
| 228 |
+
)
|
| 229 |
+
|
| 230 |
+
logits = self.lm_head(backbone_out["last_hidden_state"])
|
| 231 |
+
|
| 232 |
+
loss = None
|
| 233 |
+
if labels is not None:
|
| 234 |
+
# Shift so that each position predicts the next token. The final
|
| 235 |
+
# logit has no target; the first label has no corresponding input.
|
| 236 |
+
shift_logits = logits[:, :-1, :].contiguous()
|
| 237 |
+
shift_labels = labels[:, 1:].contiguous()
|
| 238 |
+
loss = nn.functional.cross_entropy(
|
| 239 |
+
shift_logits.view(-1, self.config.vocab_size),
|
| 240 |
+
shift_labels.view(-1),
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
return CausalLMOutputWithPast(
|
| 244 |
+
logits=logits,
|
| 245 |
+
loss=loss,
|
| 246 |
+
past_key_values=backbone_out["past_key_values"],
|
| 247 |
+
hidden_states=backbone_out["hidden_states"],
|
| 248 |
+
)
|
mlp.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""SwiGLU feed-forward sublayer.
|
| 2 |
+
|
| 3 |
+
SwiGLU is a gated linear unit variant that multiplies a SiLU-gated projection
|
| 4 |
+
element-wise against a separate up-projection:
|
| 5 |
+
|
| 6 |
+
output = W_down(SiLU(W_gate(x)) ⊙ W_up(x))
|
| 7 |
+
|
| 8 |
+
The gating mechanism gives the network more expressive control over which features
|
| 9 |
+
to propagate than a plain two-matrix FFN. It requires three weight matrices instead
|
| 10 |
+
of two, which is why intermediate_size in Llama 3 is set lower than the 4× multiplier
|
| 11 |
+
typical of two-matrix FFNs — the total parameter count remains comparable.
|
| 12 |
+
|
| 13 |
+
SiLU is used as the gate activation because Llama 3 committed to SwiGLU specifically
|
| 14 |
+
— a fixed architectural choice.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
import torch.nn as nn
|
| 19 |
+
import torch.nn.functional as F
|
| 20 |
+
from transformers import PretrainedConfig
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class SwiGLUMLP(nn.Module):
|
| 24 |
+
"""SwiGLU feed-forward sublayer.
|
| 25 |
+
|
| 26 |
+
Implements the three-matrix SwiGLU FFN used in Llama 3:
|
| 27 |
+
|
| 28 |
+
output = W_down(SiLU(W_gate(x)) ⊙ W_up(x))
|
| 29 |
+
|
| 30 |
+
No bias on any projection. SiLU as the gate activation is an architectural
|
| 31 |
+
constant — it is what defines SwiGLU specifically.
|
| 32 |
+
|
| 33 |
+
Args:
|
| 34 |
+
config: Model config. Must expose ``hidden_size`` and ``intermediate_size``.
|
| 35 |
+
"""
|
| 36 |
+
|
| 37 |
+
def __init__(self, config: PretrainedConfig) -> None:
|
| 38 |
+
super().__init__()
|
| 39 |
+
self.gate_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
|
| 40 |
+
self.up_proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
|
| 41 |
+
self.down_proj = nn.Linear(config.intermediate_size, config.hidden_size, bias=False)
|
| 42 |
+
|
| 43 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 44 |
+
"""Apply the SwiGLU feed-forward transformation.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
x: Input tensor of shape (batch, seq_len, hidden_size).
|
| 48 |
+
|
| 49 |
+
Returns:
|
| 50 |
+
Output tensor of shape (batch, seq_len, hidden_size).
|
| 51 |
+
"""
|
| 52 |
+
return self.down_proj(F.silu(self.gate_proj(x)) * self.up_proj(x))
|
model.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Transformer backbone for the Llama 3 baseline.
|
| 2 |
+
|
| 3 |
+
Llama3Model is a pure PyTorch module: a sequence of DecoderLayer blocks followed
|
| 4 |
+
by a final RMSNorm. It accepts pre-embedded hidden states and returns contextual
|
| 5 |
+
representations. It has no knowledge of tokens, vocabulary, generation, or the
|
| 6 |
+
HuggingFace contract — those concerns belong on Llama3ForCausalLM.
|
| 7 |
+
|
| 8 |
+
Keeping the embedding out of the backbone is the correct HF convention and makes
|
| 9 |
+
the backbone genuinely modality-agnostic. The token interface — embedding lookup,
|
| 10 |
+
LM head, weight tying — belongs on the task wrapper (Llama3ForCausalLM), which is
|
| 11 |
+
the only class that knows this backbone is being used for language modelling.
|
| 12 |
+
|
| 13 |
+
The final RMSNorm is necessary because the decoder stack uses pre-norm throughout:
|
| 14 |
+
each sublayer normalises its own input, leaving the residual stream itself
|
| 15 |
+
unnormalised. After many layers of accumulated residuals, that stream arrives at the
|
| 16 |
+
top with uncontrolled magnitude. The final norm brings it to a well-scaled state
|
| 17 |
+
before any projection. Without it, the LM head would receive signals of arbitrary
|
| 18 |
+
scale.
|
| 19 |
+
|
| 20 |
+
KV caching is caller-managed. If a Cache object is provided as past_key_values, it
|
| 21 |
+
is threaded through every decoder layer (each layer writes to its own slot via
|
| 22 |
+
layer_idx) and returned in the output dict. If None is provided, no caching occurs.
|
| 23 |
+
The decision of whether to create a cache and when belongs to the caller.
|
| 24 |
+
|
| 25 |
+
Returns a plain dict with keys:
|
| 26 |
+
- "last_hidden_state": normed backbone output, shape (batch, seq_len, hidden_size)
|
| 27 |
+
- "past_key_values": the Cache object passed in (updated in place), or None
|
| 28 |
+
- "hidden_states": tuple of per-layer activations if output_hidden_states=True, else None
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
import torch
|
| 32 |
+
import torch.nn as nn
|
| 33 |
+
from transformers.cache_utils import Cache
|
| 34 |
+
|
| 35 |
+
from .configuration import Llama3Config
|
| 36 |
+
from .decoder_layer import DecoderLayer
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
class Llama3Model(nn.Module):
|
| 40 |
+
"""Pure transformer backbone: decoder stack and final normalisation.
|
| 41 |
+
|
| 42 |
+
Accepts pre-embedded hidden states of shape (batch, seq_len, hidden_size)
|
| 43 |
+
and returns contextual representations of the same shape. No token embedding,
|
| 44 |
+
vocabulary projection, or HuggingFace lifecycle concerns.
|
| 45 |
+
|
| 46 |
+
RoPE is applied inside each attention layer. Positional information is
|
| 47 |
+
encoded in the relationship between Q and K, not added to the residual
|
| 48 |
+
stream, so the backbone is agnostic to how positions are represented.
|
| 49 |
+
|
| 50 |
+
Args:
|
| 51 |
+
config: Model configuration. Must be a ``Llama3Config`` instance.
|
| 52 |
+
"""
|
| 53 |
+
|
| 54 |
+
def __init__(self, config: Llama3Config) -> None:
|
| 55 |
+
super().__init__()
|
| 56 |
+
self.config = config
|
| 57 |
+
self.layers = nn.ModuleList(
|
| 58 |
+
[DecoderLayer(config) for _ in range(config.num_hidden_layers)]
|
| 59 |
+
)
|
| 60 |
+
# RMSNorm over LayerNorm: omits mean subtraction, faster, and proved more
|
| 61 |
+
# stable at scale. This is the final norm that stabilises the accumulated
|
| 62 |
+
# residual stream — distinct from the per-layer pre-norms inside each block.
|
| 63 |
+
self.norm = nn.RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
| 64 |
+
|
| 65 |
+
def forward(
|
| 66 |
+
self,
|
| 67 |
+
inputs_embeds: torch.Tensor,
|
| 68 |
+
position_ids: torch.Tensor,
|
| 69 |
+
past_key_values: Cache | None = None,
|
| 70 |
+
output_hidden_states: bool = False,
|
| 71 |
+
causal_mask: torch.Tensor | None = None,
|
| 72 |
+
) -> dict:
|
| 73 |
+
"""Run the transformer stack over a batch of pre-embedded sequences.
|
| 74 |
+
|
| 75 |
+
Args:
|
| 76 |
+
inputs_embeds: Pre-embedded input of shape (batch, seq_len, hidden_size).
|
| 77 |
+
position_ids: Absolute positions of shape (batch, seq_len). Required.
|
| 78 |
+
Must be provided explicitly by the caller — this module does not
|
| 79 |
+
infer positions from cache state. The caller owns the mapping from
|
| 80 |
+
tokens to sequence positions.
|
| 81 |
+
past_key_values: A Cache object carrying the accumulated K/V history from
|
| 82 |
+
prior forward passes, or None. When provided, each decoder layer writes
|
| 83 |
+
new K/V into its slot and reads back the full accumulated history. The
|
| 84 |
+
cache is updated in place and returned as-is. When None, no caching
|
| 85 |
+
occurs and None is returned for past_key_values.
|
| 86 |
+
output_hidden_states: When True, the output dict includes a tuple of
|
| 87 |
+
per-layer hidden states: (inputs_embeds, layer_0_out, ..., layer_N_out),
|
| 88 |
+
collected before the final norm.
|
| 89 |
+
causal_mask: Optional boolean attention mask of shape
|
| 90 |
+
(1, 1, seq_len, kv_len). Threaded unchanged into every decoder
|
| 91 |
+
layer. When None, each layer uses SDPA's native ``is_causal``
|
| 92 |
+
mode (correct for full-sequence training).
|
| 93 |
+
|
| 94 |
+
Returns:
|
| 95 |
+
Plain dict with keys:
|
| 96 |
+
- ``"last_hidden_state"``: normed backbone output,
|
| 97 |
+
shape (batch, seq_len, hidden_size).
|
| 98 |
+
- ``"past_key_values"``: the Cache object (updated in place), or None.
|
| 99 |
+
- ``"hidden_states"``: tuple of per-layer activations (including
|
| 100 |
+
inputs_embeds as position 0) if ``output_hidden_states`` is True,
|
| 101 |
+
else None. Collected before the final norm so each entry reflects the
|
| 102 |
+
unnormalised residual stream at that depth.
|
| 103 |
+
"""
|
| 104 |
+
hidden_states = inputs_embeds
|
| 105 |
+
all_hidden_states = (hidden_states,) if output_hidden_states else None
|
| 106 |
+
|
| 107 |
+
for i, layer in enumerate(self.layers):
|
| 108 |
+
hidden_states = layer(hidden_states, position_ids, cache=past_key_values, layer_idx=i, causal_mask=causal_mask)
|
| 109 |
+
if output_hidden_states:
|
| 110 |
+
all_hidden_states = all_hidden_states + (hidden_states,)
|
| 111 |
+
|
| 112 |
+
hidden_states = self.norm(hidden_states)
|
| 113 |
+
|
| 114 |
+
return {
|
| 115 |
+
"last_hidden_state": hidden_states,
|
| 116 |
+
"past_key_values": past_key_values,
|
| 117 |
+
"hidden_states": all_hidden_states,
|
| 118 |
+
}
|
rope.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Rotary Position Embeddings (RoPE).
|
| 2 |
+
|
| 3 |
+
RoPE encodes position in the *relationship* between query and key vectors rather than
|
| 4 |
+
adding it to the inputs directly. When the attention dot product Q·Kᵀ is computed, the
|
| 5 |
+
per-position rotations cancel to produce a score that depends only on the relative
|
| 6 |
+
distance between positions — not on their absolute values. This is what gives RoPE
|
| 7 |
+
better length generalisation than absolute learned embeddings.
|
| 8 |
+
|
| 9 |
+
Each pair of head dimensions (d, d+1) is assigned a rotation frequency
|
| 10 |
+
1 / theta^(2d / head_dim)
|
| 11 |
+
Higher theta → slower rotation per position → position encodings remain distinguishable
|
| 12 |
+
further apart before wrapping. Llama 3 uses theta=500,000 as a prerequisite for
|
| 13 |
+
128K context support.
|
| 14 |
+
|
| 15 |
+
Supported rope types: "default" (standard unscaled RoPE), "linear", and "yarn".
|
| 16 |
+
HuggingFace's ROPE_INIT_FUNCTIONS handles inv_freq computation for linear and yarn;
|
| 17 |
+
the default case is not in that registry and is computed directly here.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
import torch
|
| 21 |
+
import torch.nn as nn
|
| 22 |
+
from transformers import PretrainedConfig
|
| 23 |
+
from transformers.modeling_rope_utils import ROPE_INIT_FUNCTIONS
|
| 24 |
+
|
| 25 |
+
_SUPPORTED_ROPE_TYPES = {"default", "linear", "yarn"}
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def _rotate_half(x: torch.Tensor) -> torch.Tensor:
|
| 29 |
+
"""Apply the 90° rotation used in the RoPE update formula.
|
| 30 |
+
|
| 31 |
+
Splits the last dimension into two halves [x1, x2] and returns [-x2, x1].
|
| 32 |
+
Combined with ``x * cos + rotate_half(x) * sin``, this implements a 2D rotation
|
| 33 |
+
on each consecutive pair of dimensions.
|
| 34 |
+
"""
|
| 35 |
+
d = x.shape[-1] // 2
|
| 36 |
+
x1, x2 = x[..., :d], x[..., d:]
|
| 37 |
+
return torch.cat([-x2, x1], dim=-1)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
class RotaryEmbedding(nn.Module):
|
| 41 |
+
"""Rotary Position Embeddings as an nn.Module.
|
| 42 |
+
|
| 43 |
+
Computes position-dependent rotation frequencies from the model config, maintains
|
| 44 |
+
a lazily-extended cos/sin cache, and applies the rotations to query and key tensors.
|
| 45 |
+
|
| 46 |
+
The cos/sin cache grows automatically at runtime when a sequence longer than the
|
| 47 |
+
current cache is encountered. ``config.max_position_embeddings`` records the
|
| 48 |
+
training context length (required by HF's scaling computations) but does not cap
|
| 49 |
+
inference length.
|
| 50 |
+
|
| 51 |
+
Args:
|
| 52 |
+
config: Model config. Must expose ``rope_theta``, ``rope_parameters`` (set by
|
| 53 |
+
HF's RotaryEmbeddingConfigMixin), and ``head_dim``.
|
| 54 |
+
device: Optional device for initial buffer placement. Buffers move with the
|
| 55 |
+
model on ``.to()`` / ``.cuda()`` calls.
|
| 56 |
+
|
| 57 |
+
Raises:
|
| 58 |
+
NotImplementedError: If ``config.rope_parameters`` specifies an unsupported
|
| 59 |
+
rope type. Supported types: "default", "linear", "yarn".
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
def __init__(self, config: PretrainedConfig, device: torch.device | None = None) -> None:
|
| 63 |
+
super().__init__()
|
| 64 |
+
self.config = config
|
| 65 |
+
|
| 66 |
+
# rope_parameters is None when no rope_scaling was passed to the config.
|
| 67 |
+
rope_params = config.rope_parameters
|
| 68 |
+
self.rope_type = (
|
| 69 |
+
rope_params.get("rope_type", "default") if rope_params is not None else "default"
|
| 70 |
+
)
|
| 71 |
+
|
| 72 |
+
if self.rope_type not in _SUPPORTED_ROPE_TYPES:
|
| 73 |
+
raise NotImplementedError(
|
| 74 |
+
f"rope_type '{self.rope_type}' is not supported. "
|
| 75 |
+
f"Supported types: {sorted(_SUPPORTED_ROPE_TYPES)}"
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
if self.rope_type == "default":
|
| 79 |
+
# Standard RoPE: inv_freq = 1 / theta^(2i / head_dim).
|
| 80 |
+
# Not in ROPE_INIT_FUNCTIONS, so computed directly.
|
| 81 |
+
inv_freq = 1.0 / (
|
| 82 |
+
config.rope_theta
|
| 83 |
+
** (torch.arange(0, config.head_dim, 2, dtype=torch.float32, device=device) / config.head_dim)
|
| 84 |
+
)
|
| 85 |
+
self.attention_scaling: float = 1.0
|
| 86 |
+
else:
|
| 87 |
+
inv_freq, self.attention_scaling = ROPE_INIT_FUNCTIONS[self.rope_type](config, device)
|
| 88 |
+
|
| 89 |
+
self.register_buffer("inv_freq", inv_freq, persistent=False)
|
| 90 |
+
# Initialised as None; built on first forward call and extended lazily thereafter.
|
| 91 |
+
# Registered as buffers so they move with the model across devices.
|
| 92 |
+
self.register_buffer("_cos_cached", None, persistent=False)
|
| 93 |
+
self.register_buffer("_sin_cached", None, persistent=False)
|
| 94 |
+
|
| 95 |
+
def _extend_cache(self, seq_len: int, device: torch.device, dtype: torch.dtype) -> None:
|
| 96 |
+
"""Build the cos/sin table to cover positions [0, seq_len).
|
| 97 |
+
|
| 98 |
+
Registered as buffers so subsequent calls to ``.to()`` / ``.cuda()`` will
|
| 99 |
+
move them to the correct device. Rebuilds whenever the sequence grows or
|
| 100 |
+
the dtype changes (e.g. switching between fp32 and bf16).
|
| 101 |
+
"""
|
| 102 |
+
positions = torch.arange(seq_len, device=device, dtype=torch.float32)
|
| 103 |
+
# outer product → (seq_len, head_dim // 2); duplicate → (seq_len, head_dim)
|
| 104 |
+
freqs = torch.outer(positions, self.inv_freq)
|
| 105 |
+
emb = torch.cat((freqs, freqs), dim=-1)
|
| 106 |
+
self.register_buffer("_cos_cached", emb.cos().to(dtype), persistent=False)
|
| 107 |
+
self.register_buffer("_sin_cached", emb.sin().to(dtype), persistent=False)
|
| 108 |
+
|
| 109 |
+
def forward(
|
| 110 |
+
self,
|
| 111 |
+
q: torch.Tensor,
|
| 112 |
+
k: torch.Tensor,
|
| 113 |
+
position_ids: torch.Tensor,
|
| 114 |
+
) -> tuple[torch.Tensor, torch.Tensor, float]:
|
| 115 |
+
"""Apply rotary embeddings to query and key tensors.
|
| 116 |
+
|
| 117 |
+
The cos/sin cache is extended lazily when position_ids reference positions
|
| 118 |
+
beyond its current length.
|
| 119 |
+
|
| 120 |
+
Args:
|
| 121 |
+
q: Query tensor of shape (batch, num_heads, seq_len, head_dim).
|
| 122 |
+
k: Key tensor of shape (batch, num_kv_heads, seq_len, head_dim).
|
| 123 |
+
position_ids: Integer positions of shape (batch, seq_len).
|
| 124 |
+
|
| 125 |
+
Returns:
|
| 126 |
+
Tuple of (q_rotated, k_rotated, attention_scaling). attention_scaling is
|
| 127 |
+
1.0 for default and linear; YaRN returns a value != 1.0 that callers must
|
| 128 |
+
apply to attention logits to correct for frequency magnitude changes.
|
| 129 |
+
"""
|
| 130 |
+
seq_len = int(position_ids.max().item()) + 1
|
| 131 |
+
|
| 132 |
+
if self._cos_cached is None or seq_len > self._cos_cached.shape[0] or self._cos_cached.dtype != q.dtype:
|
| 133 |
+
self._extend_cache(seq_len, device=q.device, dtype=q.dtype)
|
| 134 |
+
|
| 135 |
+
# Gather cos/sin for the given positions → (batch, seq_len, head_dim),
|
| 136 |
+
# then unsqueeze the head axis for broadcast over all heads.
|
| 137 |
+
cos = self._cos_cached[position_ids].unsqueeze(1)
|
| 138 |
+
sin = self._sin_cached[position_ids].unsqueeze(1)
|
| 139 |
+
|
| 140 |
+
q_rotated = q * cos + _rotate_half(q) * sin
|
| 141 |
+
k_rotated = k * cos + _rotate_half(k) * sin
|
| 142 |
+
|
| 143 |
+
return q_rotated, k_rotated, self.attention_scaling
|
tokenizer.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
tokenizer_config.json
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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": 1000000000000000019884624838656,
|
| 9 |
+
"pad_token": "<|padding|>",
|
| 10 |
+
"tokenizer_class": "GPTNeoXTokenizerFast",
|
| 11 |
+
"trim_offsets": true,
|
| 12 |
+
"unk_token": "<|endoftext|>"
|
| 13 |
+
}
|