Spaces:
Running
Running
Upload 6 files
Browse files- Dockerfile +6 -1
- README.md +1 -1
- app.py +37 -3
- milox_model.py +81 -19
Dockerfile
CHANGED
|
@@ -1,9 +1,14 @@
|
|
| 1 |
FROM python:3.11-slim
|
| 2 |
|
| 3 |
ENV PYTHONUNBUFFERED=1 \
|
|
|
|
| 4 |
PIP_NO_CACHE_DIR=1 \
|
|
|
|
| 5 |
HF_HOME=/home/user/.cache/huggingface \
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
| 7 |
|
| 8 |
RUN useradd -m -u 1000 user
|
| 9 |
WORKDIR /home/user/app
|
|
|
|
| 1 |
FROM python:3.11-slim
|
| 2 |
|
| 3 |
ENV PYTHONUNBUFFERED=1 \
|
| 4 |
+
PYTHONDONTWRITEBYTECODE=1 \
|
| 5 |
PIP_NO_CACHE_DIR=1 \
|
| 6 |
+
PIP_DISABLE_PIP_VERSION_CHECK=1 \
|
| 7 |
HF_HOME=/home/user/.cache/huggingface \
|
| 8 |
+
TOKENIZERS_PARALLELISM=false \
|
| 9 |
+
MALLOC_ARENA_MAX=2
|
| 10 |
+
|
| 11 |
+
# Los hilos de PyTorch los fija app.py según las vCPU reales del contenedor (variable TORCH_THREADS para forzarlos).
|
| 12 |
|
| 13 |
RUN useradd -m -u 1000 user
|
| 14 |
WORKDIR /home/user/app
|
README.md
CHANGED
|
@@ -96,7 +96,7 @@ Los errores usan el formato de OpenAI: `{"error": {"message", "type", "param", "
|
|
| 96 |
| `MAX_TOKENS_CAP` | `1024` | Tope de `max_tokens` |
|
| 97 |
| `DEFAULT_MAX_TOKENS` | `512` | Valor si no envías `max_tokens` |
|
| 98 |
| `ALLOWED_ORIGINS` | `*` | Orígenes CORS (coma). Pon el dominio de tu app en producción |
|
| 99 |
-
| `TORCH_THREADS` |
|
| 100 |
|
| 101 |
## Cosas que debes saber
|
| 102 |
|
|
|
|
| 96 |
| `MAX_TOKENS_CAP` | `1024` | Tope de `max_tokens` |
|
| 97 |
| `DEFAULT_MAX_TOKENS` | `512` | Valor si no envías `max_tokens` |
|
| 98 |
| `ALLOWED_ORIGINS` | `*` | Orígenes CORS (coma). Pon el dominio de tu app en producción |
|
| 99 |
+
| `TORCH_THREADS` | vCPU reales del contenedor | Hilos de PyTorch (se detectan por cgroup, no por los núcleos del servidor) |
|
| 100 |
|
| 101 |
## Cosas que debes saber
|
| 102 |
|
app.py
CHANGED
|
@@ -10,6 +10,7 @@ Endpoints:
|
|
| 10 |
Docs interactivas: /docs
|
| 11 |
"""
|
| 12 |
import asyncio
|
|
|
|
| 13 |
import json
|
| 14 |
import os
|
| 15 |
import sys
|
|
@@ -57,15 +58,46 @@ class State:
|
|
| 57 |
STATE = State()
|
| 58 |
|
| 59 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 60 |
def load_model():
|
| 61 |
for fn in ("model.safetensors", "milox_config.json", "tokenizer.json"):
|
| 62 |
path = hf_hub_download(REPO_ID, fn, token=HF_TOKEN)
|
| 63 |
model_dir = os.path.dirname(path)
|
| 64 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 65 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 66 |
dtype = torch.float16 if device == "cuda" else torch.float32
|
| 67 |
model = mm.load_milox(model_dir, device=device, dtype=dtype, int8=(USE_INT8 and device == "cpu"))
|
| 68 |
tok = mm.ChatTokenizer(os.path.join(model_dir, "tokenizer.json"))
|
|
|
|
| 69 |
return model, tok, device
|
| 70 |
|
| 71 |
|
|
@@ -271,7 +303,7 @@ class StopHit(Exception):
|
|
| 271 |
pass
|
| 272 |
|
| 273 |
|
| 274 |
-
def run_generation(ids, p, stops, stop_ids, on_text, cancel):
|
| 275 |
"""Se ejecuta en un hilo. Llama on_text(delta) con texto nuevo. Devuelve dict(text, completion_tokens, finish)."""
|
| 276 |
tok, model = STATE.tok, STATE.model
|
| 277 |
if p["seed"] is not None:
|
|
@@ -287,6 +319,8 @@ def run_generation(ids, p, stops, stop_ids, on_text, cancel):
|
|
| 287 |
if cancel.is_set():
|
| 288 |
raise Abort()
|
| 289 |
out.append(t)
|
|
|
|
|
|
|
| 290 |
text = tok.decode(out)
|
| 291 |
if text.endswith("\ufffd"): # carácter UTF-8 incompleto: espera al siguiente token
|
| 292 |
return
|
|
@@ -336,7 +370,7 @@ async def respond(kind, ids, p, stops, stream, include_usage):
|
|
| 336 |
parts = []
|
| 337 |
try:
|
| 338 |
async with STATE.lock:
|
| 339 |
-
res = await asyncio.to_thread(run_generation, ids, p, stops, stop_ids, parts.append, cancel)
|
| 340 |
except asyncio.CancelledError:
|
| 341 |
cancel.set()
|
| 342 |
raise
|
|
|
|
| 10 |
Docs interactivas: /docs
|
| 11 |
"""
|
| 12 |
import asyncio
|
| 13 |
+
import gc
|
| 14 |
import json
|
| 15 |
import os
|
| 16 |
import sys
|
|
|
|
| 58 |
STATE = State()
|
| 59 |
|
| 60 |
|
| 61 |
+
def effective_cpus():
|
| 62 |
+
"""vCPUs realmente disponibles para este contenedor. os.cpu_count() devuelve los nucleos del servidor
|
| 63 |
+
anfitrion (p. ej. 8-48) aunque el Space solo tenga 2 vCPU; usar ese numero de hilos satura la CPU
|
| 64 |
+
y hace la generacion MUCHO mas lenta."""
|
| 65 |
+
n = os.cpu_count() or 2
|
| 66 |
+
try:
|
| 67 |
+
n = min(n, len(os.sched_getaffinity(0)))
|
| 68 |
+
except Exception: # noqa: BLE001
|
| 69 |
+
pass
|
| 70 |
+
try: # cgroup v2
|
| 71 |
+
quota, period = open("/sys/fs/cgroup/cpu.max").read().split()[:2]
|
| 72 |
+
if quota != "max":
|
| 73 |
+
n = min(n, max(1, int(float(quota) / float(period) + 0.5)))
|
| 74 |
+
except Exception: # noqa: BLE001
|
| 75 |
+
try: # cgroup v1
|
| 76 |
+
quota = int(open("/sys/fs/cgroup/cpu/cpu.cfs_quota_us").read())
|
| 77 |
+
period = int(open("/sys/fs/cgroup/cpu/cpu.cfs_period_us").read())
|
| 78 |
+
if quota > 0:
|
| 79 |
+
n = min(n, max(1, int(quota / period + 0.5)))
|
| 80 |
+
except Exception: # noqa: BLE001
|
| 81 |
+
pass
|
| 82 |
+
return max(1, n)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
def load_model():
|
| 86 |
for fn in ("model.safetensors", "milox_config.json", "tokenizer.json"):
|
| 87 |
path = hf_hub_download(REPO_ID, fn, token=HF_TOKEN)
|
| 88 |
model_dir = os.path.dirname(path)
|
| 89 |
+
threads = int(os.getenv("TORCH_THREADS", effective_cpus()))
|
| 90 |
+
torch.set_num_threads(threads)
|
| 91 |
+
try:
|
| 92 |
+
torch.set_num_interop_threads(1) # no hace falta paralelismo entre operaciones
|
| 93 |
+
except Exception: # noqa: BLE001
|
| 94 |
+
pass
|
| 95 |
+
print(f"Hilos de PyTorch: {threads}", flush=True)
|
| 96 |
device = "cuda" if torch.cuda.is_available() else "cpu"
|
| 97 |
dtype = torch.float16 if device == "cuda" else torch.float32
|
| 98 |
model = mm.load_milox(model_dir, device=device, dtype=dtype, int8=(USE_INT8 and device == "cpu"))
|
| 99 |
tok = mm.ChatTokenizer(os.path.join(model_dir, "tokenizer.json"))
|
| 100 |
+
gc.collect() # libera temporales de la carga
|
| 101 |
return model, tok, device
|
| 102 |
|
| 103 |
|
|
|
|
| 303 |
pass
|
| 304 |
|
| 305 |
|
| 306 |
+
def run_generation(ids, p, stops, stop_ids, on_text, cancel, live=True):
|
| 307 |
"""Se ejecuta en un hilo. Llama on_text(delta) con texto nuevo. Devuelve dict(text, completion_tokens, finish)."""
|
| 308 |
tok, model = STATE.tok, STATE.model
|
| 309 |
if p["seed"] is not None:
|
|
|
|
| 319 |
if cancel.is_set():
|
| 320 |
raise Abort()
|
| 321 |
out.append(t)
|
| 322 |
+
if not live and not stops: # respuesta no-stream sin stop: se decodifica una sola vez al final
|
| 323 |
+
return
|
| 324 |
text = tok.decode(out)
|
| 325 |
if text.endswith("\ufffd"): # carácter UTF-8 incompleto: espera al siguiente token
|
| 326 |
return
|
|
|
|
| 370 |
parts = []
|
| 371 |
try:
|
| 372 |
async with STATE.lock:
|
| 373 |
+
res = await asyncio.to_thread(run_generation, ids, p, stops, stop_ids, parts.append, cancel, False)
|
| 374 |
except asyncio.CancelledError:
|
| 375 |
cancel.set()
|
| 376 |
raise
|
milox_model.py
CHANGED
|
@@ -133,6 +133,22 @@ def apply_rope(q, k, cos, sin):
|
|
| 133 |
return q * cos + rotate_half(q) * sin, k * cos + rotate_half(k) * sin
|
| 134 |
|
| 135 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 136 |
class Attention(nn.Module):
|
| 137 |
def __init__(self, c: MiloXConfig):
|
| 138 |
super().__init__()
|
|
@@ -152,6 +168,31 @@ class Attention(nn.Module):
|
|
| 152 |
k = self.k_proj(x).view(B, T, self.nkv, self.hd).transpose(1, 2)
|
| 153 |
v = self.v_proj(x).view(B, T, self.nkv, self.hd).transpose(1, 2)
|
| 154 |
q, k = apply_rope(q, k, cos, sin)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 155 |
if past is not None:
|
| 156 |
k = torch.cat([past[0], k], dim=2)
|
| 157 |
v = torch.cat([past[1], v], dim=2)
|
|
@@ -213,7 +254,7 @@ def _blk_fwd(blk, x, cos, sin):
|
|
| 213 |
|
| 214 |
|
| 215 |
class MiloX(nn.Module):
|
| 216 |
-
def __init__(self, cfg: MiloXConfig):
|
| 217 |
super().__init__()
|
| 218 |
self.cfg = cfg
|
| 219 |
self.model = Backbone(cfg)
|
|
@@ -224,11 +265,12 @@ class MiloX(nn.Module):
|
|
| 224 |
self.register_buffer("rope_cos", cos, persistent=False)
|
| 225 |
self.register_buffer("rope_sin", sin, persistent=False)
|
| 226 |
self.grad_ckpt = False
|
| 227 |
-
|
| 228 |
-
|
| 229 |
-
|
| 230 |
-
|
| 231 |
-
|
|
|
|
| 232 |
|
| 233 |
@staticmethod
|
| 234 |
def _init_weights(m):
|
|
@@ -245,7 +287,7 @@ class MiloX(nn.Module):
|
|
| 245 |
|
| 246 |
def forward(self, idx, targets=None, past=None, use_cache=False, full_logits=False):
|
| 247 |
B, T = idx.shape
|
| 248 |
-
off = 0 if past is None else past
|
| 249 |
assert off + T <= self.cfg.max_seq_len, f"Secuencia demasiado larga ({off + T} > {self.cfg.max_seq_len})"
|
| 250 |
cos = self.rope_cos[off:off + T]
|
| 251 |
sin = self.rope_sin[off:off + T]
|
|
@@ -268,30 +310,37 @@ class MiloX(nn.Module):
|
|
| 268 |
loss = F.cross_entropy(logits.float().view(-1, logits.size(-1)), targets.reshape(-1), ignore_index=-100)
|
| 269 |
return logits, loss, presents
|
| 270 |
|
| 271 |
-
@torch.
|
| 272 |
def generate(self, ids, max_new_tokens=256, temperature=0.7, top_k=40, top_p=0.9,
|
| 273 |
repetition_penalty=1.1, stop_ids=(), on_token=None):
|
| 274 |
"""Genera tokens con KV-cache. `ids` es una lista de ints. Devuelve la lista de tokens nuevos."""
|
| 275 |
self.eval()
|
| 276 |
-
|
|
|
|
| 277 |
max_len = self.cfg.max_seq_len
|
| 278 |
if len(ids) >= max_len:
|
| 279 |
ids = ids[-(max_len - 1):]
|
|
|
|
|
|
|
|
|
|
| 280 |
cur = torch.tensor([ids], dtype=torch.long, device=device)
|
| 281 |
-
|
| 282 |
stop_ids = set(stop_ids)
|
| 283 |
-
block = [i for i in range(N_SPECIAL) if i not in stop_ids]
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
for _ in range(max_new_tokens):
|
| 285 |
-
total = cur.shape[1] + (
|
| 286 |
if total > max_len:
|
| 287 |
break
|
| 288 |
logits, _, past = self(cur, past=past, use_cache=True)
|
| 289 |
logits = logits[0, -1].float()
|
| 290 |
logits[block] = float("-inf")
|
| 291 |
-
if repetition_penalty != 1.0
|
| 292 |
-
|
| 293 |
-
|
| 294 |
-
logits[sidx] = torch.where(sc > 0, sc / repetition_penalty, sc * repetition_penalty)
|
| 295 |
if temperature <= 0:
|
| 296 |
nxt = int(torch.argmax(logits))
|
| 297 |
else:
|
|
@@ -310,7 +359,7 @@ class MiloX(nn.Module):
|
|
| 310 |
if nxt in stop_ids:
|
| 311 |
break
|
| 312 |
out.append(nxt)
|
| 313 |
-
seen
|
| 314 |
if on_token is not None:
|
| 315 |
on_token(nxt)
|
| 316 |
cur = torch.tensor([[nxt]], dtype=torch.long, device=device)
|
|
@@ -410,11 +459,24 @@ class ChatSession:
|
|
| 410 |
def load_milox(path, device="cpu", dtype=torch.float32, int8=False):
|
| 411 |
from safetensors.torch import load_file
|
| 412 |
cfg = MiloXConfig.from_json(os.path.join(path, "milox_config.json"))
|
| 413 |
-
model = MiloX(cfg)
|
| 414 |
sd = load_file(os.path.join(path, "model.safetensors"))
|
| 415 |
if cfg.tie_embeddings and "lm_head.weight" not in sd:
|
| 416 |
sd["lm_head.weight"] = sd["model.embed_tokens.weight"]
|
| 417 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 418 |
model.eval()
|
| 419 |
if int8:
|
| 420 |
try:
|
|
|
|
| 133 |
return q * cos + rotate_half(q) * sin, k * cos + rotate_half(k) * sin
|
| 134 |
|
| 135 |
|
| 136 |
+
class LayerCache:
|
| 137 |
+
"""KV-cache preasignado por capa: escribe en un buffer fijo en vez de hacer torch.cat en cada token
|
| 138 |
+
(menos copias de memoria, menos fragmentacion de RAM). Solo se usa al generar (batch = 1)."""
|
| 139 |
+
__slots__ = ("k", "v", "n")
|
| 140 |
+
|
| 141 |
+
def __init__(self, n_kv, cap, hd, dtype, device):
|
| 142 |
+
self.k = torch.empty(1, n_kv, cap, hd, dtype=dtype, device=device)
|
| 143 |
+
self.v = torch.empty(1, n_kv, cap, hd, dtype=dtype, device=device)
|
| 144 |
+
self.n = 0
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
def _past_len(past):
|
| 148 |
+
p0 = past[0]
|
| 149 |
+
return p0.n if isinstance(p0, LayerCache) else p0[0].shape[2]
|
| 150 |
+
|
| 151 |
+
|
| 152 |
class Attention(nn.Module):
|
| 153 |
def __init__(self, c: MiloXConfig):
|
| 154 |
super().__init__()
|
|
|
|
| 168 |
k = self.k_proj(x).view(B, T, self.nkv, self.hd).transpose(1, 2)
|
| 169 |
v = self.v_proj(x).view(B, T, self.nkv, self.hd).transpose(1, 2)
|
| 170 |
q, k = apply_rope(q, k, cos, sin)
|
| 171 |
+
if isinstance(past, LayerCache):
|
| 172 |
+
n0 = past.n
|
| 173 |
+
past.k[:, :, n0:n0 + T] = k
|
| 174 |
+
past.v[:, :, n0:n0 + T] = v
|
| 175 |
+
past.n = n0 + T
|
| 176 |
+
k = past.k[:, :, :past.n]
|
| 177 |
+
v = past.v[:, :, :past.n]
|
| 178 |
+
if T == 1 and n0 > 0:
|
| 179 |
+
# Decodificacion token a token: GQA sin repetir K/V. Las cabezas de q de un mismo grupo
|
| 180 |
+
# (h = j*rep + r) se apilan en la dimension de consulta; es el mismo calculo, sin copiar
|
| 181 |
+
# rep veces toda la cache en cada token y en cada capa.
|
| 182 |
+
qg = q.reshape(B, self.nkv, self.rep, self.hd)
|
| 183 |
+
y = F.scaled_dot_product_attention(qg, k, v)
|
| 184 |
+
return self.o_proj(y.reshape(B, 1, self.nh * self.hd)), past
|
| 185 |
+
if self.rep > 1:
|
| 186 |
+
k = k.repeat_interleave(self.rep, dim=1)
|
| 187 |
+
v = v.repeat_interleave(self.rep, dim=1)
|
| 188 |
+
if n0 == 0:
|
| 189 |
+
y = F.scaled_dot_product_attention(q, k, v, is_causal=True)
|
| 190 |
+
else:
|
| 191 |
+
S = k.shape[2]
|
| 192 |
+
mask = torch.ones(T, S, dtype=torch.bool, device=x.device).tril(diagonal=S - T)
|
| 193 |
+
y = F.scaled_dot_product_attention(q, k, v, attn_mask=mask)
|
| 194 |
+
y = y.transpose(1, 2).contiguous().view(B, T, self.nh * self.hd)
|
| 195 |
+
return self.o_proj(y), past
|
| 196 |
if past is not None:
|
| 197 |
k = torch.cat([past[0], k], dim=2)
|
| 198 |
v = torch.cat([past[1], v], dim=2)
|
|
|
|
| 254 |
|
| 255 |
|
| 256 |
class MiloX(nn.Module):
|
| 257 |
+
def __init__(self, cfg: MiloXConfig, init_weights=True):
|
| 258 |
super().__init__()
|
| 259 |
self.cfg = cfg
|
| 260 |
self.model = Backbone(cfg)
|
|
|
|
| 265 |
self.register_buffer("rope_cos", cos, persistent=False)
|
| 266 |
self.register_buffer("rope_sin", sin, persistent=False)
|
| 267 |
self.grad_ckpt = False
|
| 268 |
+
if init_weights: # al cargar un modelo exportado no hace falta (los pesos se sobrescriben)
|
| 269 |
+
self.apply(self._init_weights)
|
| 270 |
+
std = 0.02 / math.sqrt(2 * cfg.n_layers)
|
| 271 |
+
for n, p in self.named_parameters():
|
| 272 |
+
if n.endswith("o_proj.weight") or n.endswith("down_proj.weight"):
|
| 273 |
+
nn.init.normal_(p, mean=0.0, std=std)
|
| 274 |
|
| 275 |
@staticmethod
|
| 276 |
def _init_weights(m):
|
|
|
|
| 287 |
|
| 288 |
def forward(self, idx, targets=None, past=None, use_cache=False, full_logits=False):
|
| 289 |
B, T = idx.shape
|
| 290 |
+
off = 0 if past is None else _past_len(past)
|
| 291 |
assert off + T <= self.cfg.max_seq_len, f"Secuencia demasiado larga ({off + T} > {self.cfg.max_seq_len})"
|
| 292 |
cos = self.rope_cos[off:off + T]
|
| 293 |
sin = self.rope_sin[off:off + T]
|
|
|
|
| 310 |
loss = F.cross_entropy(logits.float().view(-1, logits.size(-1)), targets.reshape(-1), ignore_index=-100)
|
| 311 |
return logits, loss, presents
|
| 312 |
|
| 313 |
+
@torch.inference_mode()
|
| 314 |
def generate(self, ids, max_new_tokens=256, temperature=0.7, top_k=40, top_p=0.9,
|
| 315 |
repetition_penalty=1.1, stop_ids=(), on_token=None):
|
| 316 |
"""Genera tokens con KV-cache. `ids` es una lista de ints. Devuelve la lista de tokens nuevos."""
|
| 317 |
self.eval()
|
| 318 |
+
emb = self.model.embed_tokens.weight
|
| 319 |
+
device, dtype = emb.device, emb.dtype
|
| 320 |
max_len = self.cfg.max_seq_len
|
| 321 |
if len(ids) >= max_len:
|
| 322 |
ids = ids[-(max_len - 1):]
|
| 323 |
+
cap = min(max_len, len(ids) + max_new_tokens) # cache del tamano justo, no del maximo
|
| 324 |
+
hd = self.cfg.dim // self.cfg.n_heads
|
| 325 |
+
past = [LayerCache(self.cfg.n_kv_heads, cap, hd, dtype, device) for _ in range(self.cfg.n_layers)]
|
| 326 |
cur = torch.tensor([ids], dtype=torch.long, device=device)
|
| 327 |
+
out = []
|
| 328 |
stop_ids = set(stop_ids)
|
| 329 |
+
block = torch.tensor([i for i in range(N_SPECIAL) if i not in stop_ids], dtype=torch.long, device=device)
|
| 330 |
+
# tokens "vistos" para la penalizacion (ultimos 256 del prompt + todo lo generado), como mascara fija
|
| 331 |
+
seen = torch.zeros(self.cfg.vocab_size, dtype=torch.bool, device=device)
|
| 332 |
+
if ids:
|
| 333 |
+
seen[torch.tensor(ids[-256:], dtype=torch.long, device=device)] = True
|
| 334 |
for _ in range(max_new_tokens):
|
| 335 |
+
total = cur.shape[1] + _past_len(past)
|
| 336 |
if total > max_len:
|
| 337 |
break
|
| 338 |
logits, _, past = self(cur, past=past, use_cache=True)
|
| 339 |
logits = logits[0, -1].float()
|
| 340 |
logits[block] = float("-inf")
|
| 341 |
+
if repetition_penalty != 1.0:
|
| 342 |
+
logits = torch.where(seen, torch.where(logits > 0, logits / repetition_penalty,
|
| 343 |
+
logits * repetition_penalty), logits)
|
|
|
|
| 344 |
if temperature <= 0:
|
| 345 |
nxt = int(torch.argmax(logits))
|
| 346 |
else:
|
|
|
|
| 359 |
if nxt in stop_ids:
|
| 360 |
break
|
| 361 |
out.append(nxt)
|
| 362 |
+
seen[nxt] = True
|
| 363 |
if on_token is not None:
|
| 364 |
on_token(nxt)
|
| 365 |
cur = torch.tensor([[nxt]], dtype=torch.long, device=device)
|
|
|
|
| 459 |
def load_milox(path, device="cpu", dtype=torch.float32, int8=False):
|
| 460 |
from safetensors.torch import load_file
|
| 461 |
cfg = MiloXConfig.from_json(os.path.join(path, "milox_config.json"))
|
|
|
|
| 462 |
sd = load_file(os.path.join(path, "model.safetensors"))
|
| 463 |
if cfg.tie_embeddings and "lm_head.weight" not in sd:
|
| 464 |
sd["lm_head.weight"] = sd["model.embed_tokens.weight"]
|
| 465 |
+
try:
|
| 466 |
+
# Carga eficiente en RAM: el modelo se crea "vacio" (meta) y adopta directamente los tensores del
|
| 467 |
+
# archivo; evita inicializar pesos al azar y tener 2-3 copias del modelo a la vez durante el arranque.
|
| 468 |
+
with torch.device("meta"):
|
| 469 |
+
model = MiloX(cfg, init_weights=False)
|
| 470 |
+
model.load_state_dict(sd, strict=True, assign=True)
|
| 471 |
+
cos, sin = build_rope(cfg.dim // cfg.n_heads, cfg.max_seq_len, cfg.rope_theta)
|
| 472 |
+
model.rope_cos, model.rope_sin = cos, sin
|
| 473 |
+
if cfg.tie_embeddings:
|
| 474 |
+
model.lm_head.weight = model.model.embed_tokens.weight
|
| 475 |
+
except Exception as e: # torch antiguo u otro problema: camino clasico
|
| 476 |
+
print("Carga eficiente no disponible, se usa la carga clasica:", e)
|
| 477 |
+
model = MiloX(cfg, init_weights=False)
|
| 478 |
+
model.load_state_dict(sd, strict=True)
|
| 479 |
+
del sd
|
| 480 |
model.eval()
|
| 481 |
if int8:
|
| 482 |
try:
|