Download src/model/rope.py from Frankenstein-Labs/Cortex-0: direct link, hf CLI and curl.
- Browser
- Download file 2.36 kB
-
https://huggingface.co/Frankenstein-Labs/Cortex-0/resolve/main/src/model/rope.py
- Command line
-
hf download hf://Frankenstein-Labs/Cortex-0/src/model/rope.py
-
curl -L -o rope.py https://huggingface.co/Frankenstein-Labs/Cortex-0/resolve/main/src/model/rope.py
2.36 kB
| """Rotary Position Embedding (RoPE) — implémenté à la main. | |
| RoPE encode la position en **faisant tourner** par paires les dimensions de Q et K, | |
| au lieu d'**ajouter** un vecteur de position aux embeddings. Propriétés : | |
| * la position devient **relative** dans le produit scalaire Q·K ; | |
| * aucun paramètre appris, aucune contrainte sur la longueur ; | |
| * meilleure extrapolation en longueur qu'un embedding positionnel absolu appris. | |
| Justification complète : docs/architecture.md §2.1. | |
| Convention : les dimensions ``(2i, 2i+1)`` d'un vecteur forment la paire ``i``. | |
| """ | |
| from __future__ import annotations | |
| import torch | |
| def build_rope_cache( | |
| seq_len: int, | |
| head_dim: int, | |
| theta: float = 10000.0, | |
| device: torch.device | None = None, | |
| dtype: torch.dtype = torch.float32, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| """Précalcule ``cos`` et ``sin`` pour les positions ``0..seq_len-1``. | |
| Retourne deux tenseurs de forme ``(seq_len, head_dim)``, où chaque paire | |
| ``(2i, 2i+1)`` partage le même angle. | |
| """ | |
| if head_dim % 2 != 0: | |
| raise ValueError(f"head_dim doit être pair pour RoPE, reçu {head_dim}") | |
| half = head_dim // 2 | |
| # Fréquences décroissantes : inv_freq[i] = theta^(-2i/head_dim) | |
| inv_freq = 1.0 / (theta ** (torch.arange(0, half, device=device, dtype=torch.float32) / half)) | |
| positions = torch.arange(seq_len, device=device, dtype=torch.float32) | |
| freqs = torch.outer(positions, inv_freq) # (seq_len, half) | |
| emb = torch.cat([freqs, freqs], dim=-1) # (seq_len, head_dim) | |
| return emb.cos().to(dtype), emb.sin().to(dtype) | |
| def rotate_half(x: torch.Tensor) -> torch.Tensor: | |
| """Échange et inverse les deux moitiés : ``[a, b] -> [-b, a]`` sur la dernière dim.""" | |
| half = x.shape[-1] // 2 | |
| x1, x2 = x[..., :half], x[..., half:] | |
| return torch.cat([-x2, x1], dim=-1) | |
| def apply_rope( | |
| q: torch.Tensor, | |
| k: torch.Tensor, | |
| cos: torch.Tensor, | |
| sin: torch.Tensor, | |
| ) -> tuple[torch.Tensor, torch.Tensor]: | |
| """Applique RoPE à Q et K. | |
| ``q``, ``k`` : ``(batch, n_head, seq_len, head_dim)``. | |
| ``cos``, ``sin`` : ``(seq_len, head_dim)`` (broadcast sur batch et têtes). | |
| """ | |
| cos = cos[None, None, :, :] | |
| sin = sin[None, None, :, :] | |
| q_rot = q * cos + rotate_half(q) * sin | |
| k_rot = k * cos + rotate_half(k) * sin | |
| return q_rot, k_rot | |