topoexploit 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- topoexploit-0.1.0.dist-info/METADATA +523 -0
- topoexploit-0.1.0.dist-info/RECORD +23 -0
- topoexploit-0.1.0.dist-info/WHEEL +5 -0
- topoexploit-0.1.0.dist-info/entry_points.txt +6 -0
- topoexploit-0.1.0.dist-info/licenses/LICENSE +661 -0
- topoexploit-0.1.0.dist-info/top_level.txt +1 -0
- topogpt3/__init__.py +125 -0
- topogpt3/__main__.py +49 -0
- topogpt3/api_server.py +1178 -0
- topogpt3/continuation.py +114 -0
- topogpt3/ewc.py +369 -0
- topogpt3/exploitgym_config.py +130 -0
- topogpt3/exploitgym_loader.py +416 -0
- topogpt3/hodge_cm.py +160 -0
- topogpt3/inference.py +731 -0
- topogpt3/inference_hrm.py +1502 -0
- topogpt3/jlens.py +958 -0
- topogpt3/lens_model.py +389 -0
- topogpt3/merged_config.py +187 -0
- topogpt3/model.py +3824 -0
- topogpt3/structured_output.py +251 -0
- topogpt3/train.py +1939 -0
- topogpt3/vanilla_control.py +240 -0
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Vanilla transformer control for TopoGPT3 Hodge-CM ablation.
|
|
3
|
+
Matched size: D=384, L=8, H=8, SwiGLU FFN, RoPE, RMSNorm, tied embeddings.
|
|
4
|
+
Target: ~33M to compare against TopoGPT3 37.4M (which includes TorusBrain+MoE+spectral).
|
|
5
|
+
Same vocab (50257), full curriculum interface (codealpaca, code_feedback,
|
|
6
|
+
magicoder_evol, tiny_the_stack) with train/val/holdout disjuntos — nada
|
|
7
|
+
recortado. Usa tokens reales del tier cuando existen; sintetico solo con
|
|
8
|
+
--allow-synthetic explícito.
|
|
9
|
+
"""
|
|
10
|
+
import argparse
|
|
11
|
+
import math
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import torch
|
|
14
|
+
import torch.nn as nn
|
|
15
|
+
import torch.nn.functional as F
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
TIER_NAMES = ("codealpaca", "code_feedback", "magicoder_evol", "tiny_the_stack")
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def resolve_device(req: str) -> torch.device:
|
|
23
|
+
if req == "cuda":
|
|
24
|
+
if not torch.cuda.is_available():
|
|
25
|
+
print("WARNING: --device cuda pedido pero torch.cuda.is_available()=False. "
|
|
26
|
+
"RTX 2060 detectada pero este torch +cu130 exige driver >=580 "
|
|
27
|
+
"(tienes 550/CUDA 12.4). Reinstala cu124: pip install torch "
|
|
28
|
+
"--index-url https://download.pytorch.org/whl/cu124. Fallo a cpu.")
|
|
29
|
+
return torch.device("cpu")
|
|
30
|
+
return torch.device("cuda")
|
|
31
|
+
if req == "cpu":
|
|
32
|
+
return torch.device("cpu")
|
|
33
|
+
if torch.cuda.is_available():
|
|
34
|
+
return torch.device("cuda")
|
|
35
|
+
print("NOTA: cuda no disponible para este build torch. Uso cpu. "
|
|
36
|
+
"Para tu RTX 2060: pip install torch --index-url "
|
|
37
|
+
"https://download.pytorch.org/whl/cu124")
|
|
38
|
+
return torch.device("cpu")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def find_tier_tokens(tier: str):
|
|
42
|
+
"""Busca train real tokenizado del tier. Devuelve lista de ints o None.
|
|
43
|
+
Nada silencioso: si no hay, el llamador decide (error o sintetico explícito)."""
|
|
44
|
+
repo = Path(__file__).resolve().parents[1]
|
|
45
|
+
cands = [
|
|
46
|
+
repo / "data/tokenized" / tier / "train.bin",
|
|
47
|
+
repo / "data/tokenized" / tier / "train.npy",
|
|
48
|
+
repo / "data" / tier / "train.bin",
|
|
49
|
+
repo / "data" / tier / "train.npy",
|
|
50
|
+
]
|
|
51
|
+
for p in cands:
|
|
52
|
+
if p.exists():
|
|
53
|
+
import numpy as np
|
|
54
|
+
if p.suffix == ".npy":
|
|
55
|
+
return np.load(str(p)).astype("int64").tolist()
|
|
56
|
+
return np.fromfile(str(p), dtype=np.uint16).astype("int64").tolist()
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class VanillaConfig:
|
|
62
|
+
VOCAB_SIZE: int = 50257
|
|
63
|
+
D_MODEL: int = 384
|
|
64
|
+
N_HEADS: int = 8
|
|
65
|
+
N_LAYERS: int = 8
|
|
66
|
+
MAX_SEQ_LEN: int = 512
|
|
67
|
+
DROPOUT: float = 0.1
|
|
68
|
+
LR: float = 3e-4
|
|
69
|
+
WEIGHT_DECAY: float = 0.1
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class RMSNorm(nn.Module):
|
|
73
|
+
def __init__(self, d: int, eps: float = 1e-6):
|
|
74
|
+
super().__init__()
|
|
75
|
+
self.eps = eps
|
|
76
|
+
self.weight = nn.Parameter(torch.ones(d))
|
|
77
|
+
|
|
78
|
+
def forward(self, x):
|
|
79
|
+
rms = x.pow(2).mean(-1, keepdim=True).add(self.eps).sqrt()
|
|
80
|
+
return x / rms * self.weight
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class RotaryEmbedding(nn.Module):
|
|
84
|
+
def __init__(self, d_head: int, max_seq_len: int = 2048, base: int = 10000):
|
|
85
|
+
super().__init__()
|
|
86
|
+
inv_freq = 1.0 / (base ** (torch.arange(0, d_head, 2).float() / d_head))
|
|
87
|
+
self.register_buffer("inv_freq", inv_freq)
|
|
88
|
+
self._build_cache(max_seq_len)
|
|
89
|
+
|
|
90
|
+
def _build_cache(self, seq_len: int):
|
|
91
|
+
t = torch.arange(seq_len, device=self.inv_freq.device).float()
|
|
92
|
+
freqs = torch.outer(t, self.inv_freq)
|
|
93
|
+
emb = torch.cat([freqs, freqs], dim=-1)
|
|
94
|
+
self.register_buffer("_cos", emb.cos(), persistent=False)
|
|
95
|
+
self.register_buffer("_sin", emb.sin(), persistent=False)
|
|
96
|
+
|
|
97
|
+
@staticmethod
|
|
98
|
+
def _rot_half(x):
|
|
99
|
+
x1, x2 = x[..., : x.shape[-1] // 2], x[..., x.shape[-1] // 2:]
|
|
100
|
+
return torch.cat([-x2, x1], dim=-1)
|
|
101
|
+
|
|
102
|
+
def forward(self, q, k):
|
|
103
|
+
sq, sk = q.shape[2], k.shape[2]
|
|
104
|
+
if max(sq, sk) > self._cos.shape[0]:
|
|
105
|
+
self._build_cache(max(sq, sk) * 2)
|
|
106
|
+
cq = self._cos[:sq].unsqueeze(0).unsqueeze(0)
|
|
107
|
+
sq_ = self._sin[:sq].unsqueeze(0).unsqueeze(0)
|
|
108
|
+
ck = self._cos[:sk].unsqueeze(0).unsqueeze(0)
|
|
109
|
+
sk_ = self._sin[:sk].unsqueeze(0).unsqueeze(0)
|
|
110
|
+
return q * cq + self._rot_half(q) * sq_, k * ck + self._rot_half(k) * sk_
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
class CausalAttention(nn.Module):
|
|
114
|
+
def __init__(self, cfg: VanillaConfig):
|
|
115
|
+
super().__init__()
|
|
116
|
+
assert cfg.D_MODEL % cfg.N_HEADS == 0
|
|
117
|
+
self.n_heads = cfg.N_HEADS
|
|
118
|
+
self.d_head = cfg.D_MODEL // cfg.N_HEADS
|
|
119
|
+
self.qkv = nn.Linear(cfg.D_MODEL, 3 * cfg.D_MODEL, bias=False)
|
|
120
|
+
self.out = nn.Linear(cfg.D_MODEL, cfg.D_MODEL, bias=False)
|
|
121
|
+
self.rope = RotaryEmbedding(self.d_head, cfg.MAX_SEQ_LEN)
|
|
122
|
+
self.drop = cfg.DROPOUT
|
|
123
|
+
|
|
124
|
+
def forward(self, x):
|
|
125
|
+
B, S, D = x.shape
|
|
126
|
+
qkv = self.qkv(x).view(B, S, 3, self.n_heads, self.d_head)
|
|
127
|
+
q, k, v = qkv[:, :, 0].transpose(1, 2), qkv[:, :, 1].transpose(1, 2), qkv[:, :, 2].transpose(1, 2)
|
|
128
|
+
q, k = self.rope(q, k)
|
|
129
|
+
y = F.scaled_dot_product_attention(q, k, v, is_causal=True,
|
|
130
|
+
dropout_p=self.drop if self.training else 0.0)
|
|
131
|
+
return self.out(y.transpose(1, 2).contiguous().view(B, S, D))
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class SwiGLU(nn.Module):
|
|
135
|
+
def __init__(self, d: int):
|
|
136
|
+
super().__init__()
|
|
137
|
+
inner = (int(d * 8 / 3) + 7) // 8 * 8
|
|
138
|
+
self.gate = nn.Linear(d, inner, bias=False)
|
|
139
|
+
self.up = nn.Linear(d, inner, bias=False)
|
|
140
|
+
self.down = nn.Linear(inner, d, bias=False)
|
|
141
|
+
|
|
142
|
+
def forward(self, x):
|
|
143
|
+
return self.down(F.silu(self.gate(x)) * self.up(x))
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
class VanillaLayer(nn.Module):
|
|
147
|
+
def __init__(self, cfg: VanillaConfig):
|
|
148
|
+
super().__init__()
|
|
149
|
+
self.n1 = RMSNorm(cfg.D_MODEL)
|
|
150
|
+
self.n2 = RMSNorm(cfg.D_MODEL)
|
|
151
|
+
self.attn = CausalAttention(cfg)
|
|
152
|
+
self.ffn = SwiGLU(cfg.D_MODEL)
|
|
153
|
+
self.drop = nn.Dropout(cfg.DROPOUT)
|
|
154
|
+
|
|
155
|
+
def forward(self, x):
|
|
156
|
+
x = x + self.drop(self.attn(self.n1(x)))
|
|
157
|
+
x = x + self.drop(self.ffn(self.n2(x)))
|
|
158
|
+
return x
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class VanillaTransformer(nn.Module):
|
|
162
|
+
"""Tied-embedding decoder-only transformer. Same training interface as TopoGPT2."""
|
|
163
|
+
|
|
164
|
+
def __init__(self, cfg: VanillaConfig = VanillaConfig()):
|
|
165
|
+
super().__init__()
|
|
166
|
+
self.cfg = cfg
|
|
167
|
+
self.embed = nn.Embedding(cfg.VOCAB_SIZE, cfg.D_MODEL)
|
|
168
|
+
nn.init.normal_(self.embed.weight, std=0.02)
|
|
169
|
+
self.layers = nn.ModuleList([VanillaLayer(cfg) for _ in range(cfg.N_LAYERS)])
|
|
170
|
+
self.norm = RMSNorm(cfg.D_MODEL)
|
|
171
|
+
self.lm_head = nn.Linear(cfg.D_MODEL, cfg.VOCAB_SIZE, bias=False)
|
|
172
|
+
self.lm_head.weight = self.embed.weight
|
|
173
|
+
self.apply(self._init)
|
|
174
|
+
|
|
175
|
+
@staticmethod
|
|
176
|
+
def _init(m):
|
|
177
|
+
if isinstance(m, nn.Linear) and not hasattr(m, "_tied"):
|
|
178
|
+
nn.init.normal_(m.weight, std=0.02)
|
|
179
|
+
|
|
180
|
+
def forward(self, ids):
|
|
181
|
+
x = self.embed(ids)
|
|
182
|
+
for layer in self.layers:
|
|
183
|
+
x = layer(x)
|
|
184
|
+
return self.lm_head(self.norm(x))
|
|
185
|
+
|
|
186
|
+
def count_params(self):
|
|
187
|
+
total = sum(p.numel() for p in self.parameters())
|
|
188
|
+
return {"total": total, "trainable": total}
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def build_optimizer(model: VanillaTransformer, cfg: VanillaConfig):
|
|
192
|
+
return torch.optim.AdamW(model.parameters(), lr=cfg.LR, weight_decay=cfg.WEIGHT_DECAY)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
if __name__ == "__main__":
|
|
196
|
+
ap = argparse.ArgumentParser()
|
|
197
|
+
ap.add_argument("--device", default="auto", choices=["auto", "cuda", "cpu"])
|
|
198
|
+
ap.add_argument("--tier", default="codealpaca", choices=list(TIER_NAMES),
|
|
199
|
+
help="tier curriculum de donde tomar tokens reales")
|
|
200
|
+
ap.add_argument("--seq-len", type=int, default=32)
|
|
201
|
+
ap.add_argument("--batch", type=int, default=2)
|
|
202
|
+
ap.add_argument("--allow-synthetic", action="store_true",
|
|
203
|
+
help="permite tokens randint solo como smoke test; sin esto, falla si no hay data real")
|
|
204
|
+
ap.add_argument("--train-step", action="store_true",
|
|
205
|
+
help="corre 1 step AdamW + grad norm (smoke de entrenamiento en GPU si disponible)")
|
|
206
|
+
args = ap.parse_args()
|
|
207
|
+
device = resolve_device(args.device)
|
|
208
|
+
print(f"device={device} cuda_avail={torch.cuda.is_available()} "
|
|
209
|
+
f"{torch.cuda.get_device_name(0) if torch.cuda.is_available() else 'sin-gpu-para-este-build'}")
|
|
210
|
+
cfg = VanillaConfig()
|
|
211
|
+
m = VanillaTransformer(cfg).to(device)
|
|
212
|
+
print(m.count_params())
|
|
213
|
+
toks = find_tier_tokens(args.tier)
|
|
214
|
+
if toks is not None and len(toks) >= args.batch * args.seq_len:
|
|
215
|
+
import torch as _t
|
|
216
|
+
ids = _t.tensor(toks[:args.batch * args.seq_len], dtype=_t.long).view(args.batch, args.seq_len).to(device)
|
|
217
|
+
print(f"data=tier-real:{args.tier} ntok={len(toks)}")
|
|
218
|
+
elif args.allow_synthetic:
|
|
219
|
+
print(f"WARNING: sin tokens reales de {args.tier} "
|
|
220
|
+
f"(corre topogpt3/train.py --prepare-data para los 4 tiers). Uso sintetico SOLO smoke.")
|
|
221
|
+
ids = torch.randint(0, cfg.VOCAB_SIZE, (args.batch, args.seq_len), device=device)
|
|
222
|
+
else:
|
|
223
|
+
raise SystemExit(
|
|
224
|
+
f"Sin tokens reales de tier {args.tier}. Corre: python3 -m topogpt3.train --prepare-data "
|
|
225
|
+
f"(descarga CodeAlpaca + CodeFeedback + Magicoder + Stack, splits train/val/holdout). "
|
|
226
|
+
f"O repite con --allow-synthetic para smoke. No uso sintetico silencioso.")
|
|
227
|
+
use_amp = device.type == "cuda"
|
|
228
|
+
with torch.autocast(device_type="cuda", enabled=use_amp):
|
|
229
|
+
logits = m(ids)
|
|
230
|
+
print("forward:", tuple(logits.shape))
|
|
231
|
+
if args.train_step:
|
|
232
|
+
opt = build_optimizer(m, cfg)
|
|
233
|
+
tgt = torch.roll(ids, -1, dims=1)
|
|
234
|
+
with torch.autocast(device_type="cuda", enabled=use_amp):
|
|
235
|
+
loss = F.cross_entropy(logits[:, :-1].reshape(-1, cfg.VOCAB_SIZE), tgt[:, :-1].reshape(-1))
|
|
236
|
+
opt.zero_grad()
|
|
237
|
+
loss.backward()
|
|
238
|
+
gn = torch.nn.utils.clip_grad_norm_(m.parameters(), 1.0)
|
|
239
|
+
opt.step()
|
|
240
|
+
print(f"train-step loss={loss.item():.4f} grad_norm={gn:.4f}")
|