Project-Chronos 0.2.0__tar.gz → 0.2.1__tar.gz
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.
- {project_chronos-0.2.0 → project_chronos-0.2.1}/PKG-INFO +1 -1
- {project_chronos-0.2.0 → project_chronos-0.2.1}/Project_Chronos.egg-info/PKG-INFO +1 -1
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/expert_store.py +21 -5
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/inference.py +128 -46
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/training/trainer.py +127 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/checkpoint.py +3 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/config.py +6 -3
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/temporal_loss.py +121 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/chronos_trainer.py +15 -9
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/distill_trainer.py +11 -7
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/dpo_trainer.py +11 -9
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/grpo_trainer.py +6 -4
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/loss_mixin.py +35 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/orpo_trainer.py +11 -9
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/sft_trainer.py +12 -8
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/stage_utils.py +15 -2
- {project_chronos-0.2.0 → project_chronos-0.2.1}/pyproject.toml +1 -1
- {project_chronos-0.2.0 → project_chronos-0.2.1}/tests/test_smoke.py +378 -1
- {project_chronos-0.2.0 → project_chronos-0.2.1}/train_chronos.py +10 -18
- {project_chronos-0.2.0 → project_chronos-0.2.1}/train_chronos_distill.py +10 -2
- {project_chronos-0.2.0 → project_chronos-0.2.1}/train_chronos_dpo.py +10 -2
- {project_chronos-0.2.0 → project_chronos-0.2.1}/train_chronos_grpo.py +11 -3
- {project_chronos-0.2.0 → project_chronos-0.2.1}/train_chronos_orpo.py +10 -2
- {project_chronos-0.2.0 → project_chronos-0.2.1}/train_chronos_sft.py +10 -2
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/presets.py +3 -2
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/config_tab.py +6 -3
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/inference_tab.py +157 -5
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/train_tab.py +442 -72
- {project_chronos-0.2.0 → project_chronos-0.2.1}/LICENSE +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/MANIFEST.in +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/Project_Chronos.egg-info/SOURCES.txt +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/Project_Chronos.egg-info/dependency_links.txt +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/Project_Chronos.egg-info/entry_points.txt +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/Project_Chronos.egg-info/requires.txt +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/Project_Chronos.egg-info/top_level.txt +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/README.md +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/_backend_legacy.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/app.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/backend/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/backend/dispatcher.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/backend/ext/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/backend/ext/opencl.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/backend/mac_diagnostics.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/cli.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/data/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/data/flexible_dataset.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/deps.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/eval/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/eval/benchmark.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/eval/io_profiler.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/export.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/io/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/io/async_prefetcher.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/io/cluster_layout.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/io/expert_store.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/io/io_simulator.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/io/storage.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/attention.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/model.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/moe.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/training/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/mlx/training/io.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/hf_io.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/hybrid_attention.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/lookahead_router.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/model_chronos.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/model/moe_chronos.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/router/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/router/expert_predictor.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/router/intent_classifier.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/router/prefill_scheduler.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/runtime/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/runtime/cache_manager.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/runtime/inference_engine.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/runtime/metrics.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/serving/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/serving/vllm_adapter.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/device_utils.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/optim_utils.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/trainer/reward.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/tuning/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/tuning/_base_tuner.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/tuning/chronos_auto_tuner.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/chronos/verify.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/requirements.txt +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/setup.cfg +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/setup.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/tests/test_smoke_cuda.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/estimator.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/gradio_compat.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/i18n.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/__init__.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/autotune_tab.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/benchmark_tab.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/data_tab.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/export_tab.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/iomon_tab.py +0 -0
- {project_chronos-0.2.0 → project_chronos-0.2.1}/ui/tabs/pipeline_tab.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Project_Chronos
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: On-device low-latency lookahead dual-layer MoE inference — Lookahead Routing + Async Prefetch + Hybrid Attention
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Keywords: llm,moe,inference,lookahead-routing,hybrid-attention,edge-ai
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: Project_Chronos
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.1
|
|
4
4
|
Summary: On-device low-latency lookahead dual-layer MoE inference — Lookahead Routing + Async Prefetch + Hybrid Attention
|
|
5
5
|
License-Expression: Apache-2.0
|
|
6
6
|
Keywords: llm,moe,inference,lookahead-routing,hybrid-attention,edge-ai
|
|
@@ -72,6 +72,7 @@ class MLXExpertStore:
|
|
|
72
72
|
self._warm_lru: collections.OrderedDict = collections.OrderedDict()
|
|
73
73
|
self._cluster_manifest: ClusterManifest | None = None
|
|
74
74
|
self._loaded_clusters: set[int] = set()
|
|
75
|
+
self._protected_experts: set[int] = set()
|
|
75
76
|
self.storage_format = "npy"
|
|
76
77
|
self.attach_cluster_manifest(ssd_dir)
|
|
77
78
|
self._stats = collections.Counter()
|
|
@@ -296,18 +297,25 @@ class MLXExpertStore:
|
|
|
296
297
|
self._warm_lru.move_to_end(expert_id)
|
|
297
298
|
self._evict_warm_locked(protected_expert_id=protected_expert_id)
|
|
298
299
|
|
|
300
|
+
def set_protected_experts(self, expert_ids: List[int] | set[int] | tuple[int, ...]):
|
|
301
|
+
with self._lock:
|
|
302
|
+
self._protected_experts = {
|
|
303
|
+
int(eid) for eid in expert_ids
|
|
304
|
+
if 0 <= int(eid) < int(self.num_experts)
|
|
305
|
+
}
|
|
306
|
+
|
|
299
307
|
def _evict_warm_locked(self, protected_expert_id: int | None = None):
|
|
300
308
|
while len(self._warm_lru) > self._warm_capacity:
|
|
301
309
|
evict_id = None
|
|
302
310
|
for candidate in self._warm_lru.keys():
|
|
303
|
-
if candidate == protected_expert_id:
|
|
311
|
+
if candidate == protected_expert_id or candidate in self._protected_experts:
|
|
304
312
|
continue
|
|
305
313
|
if candidate not in self._hot_lru:
|
|
306
314
|
evict_id = candidate
|
|
307
315
|
break
|
|
308
316
|
if evict_id is None:
|
|
309
317
|
for candidate in self._warm_lru.keys():
|
|
310
|
-
if candidate != protected_expert_id:
|
|
318
|
+
if candidate != protected_expert_id and candidate not in self._protected_experts:
|
|
311
319
|
evict_id = candidate
|
|
312
320
|
break
|
|
313
321
|
if evict_id is None:
|
|
@@ -455,9 +463,17 @@ class MLXExpertStore:
|
|
|
455
463
|
self._hot_lru.move_to_end(expert_id)
|
|
456
464
|
else:
|
|
457
465
|
while len(self._hot_lru) >= self._capacity:
|
|
458
|
-
evicted
|
|
459
|
-
|
|
460
|
-
|
|
466
|
+
evicted = None
|
|
467
|
+
for candidate in self._hot_lru.keys():
|
|
468
|
+
if candidate != expert_id and candidate not in self._protected_experts:
|
|
469
|
+
evicted = candidate
|
|
470
|
+
break
|
|
471
|
+
if evicted is None:
|
|
472
|
+
evicted, _ = self._hot_lru.popitem(last=False)
|
|
473
|
+
if evicted == expert_id:
|
|
474
|
+
continue
|
|
475
|
+
else:
|
|
476
|
+
self._hot_lru.pop(evicted, None)
|
|
461
477
|
# Keep warm weights in unified memory, but drop the
|
|
462
478
|
# executable live module so masked MoE cannot call an
|
|
463
479
|
# expert outside the hot execution budget.
|
|
@@ -47,7 +47,11 @@ class ChronosMLXInferenceEngine:
|
|
|
47
47
|
self.last_stats: dict = {}
|
|
48
48
|
self._last_predicted: set[int] = set()
|
|
49
49
|
self._stats_lock = threading.Lock()
|
|
50
|
-
self._runtime_stats =
|
|
50
|
+
self._runtime_stats = self._new_runtime_stats()
|
|
51
|
+
|
|
52
|
+
@staticmethod
|
|
53
|
+
def _new_runtime_stats(all_resident_fast_path: bool = False) -> dict:
|
|
54
|
+
return {
|
|
51
55
|
"resident_hits": 0,
|
|
52
56
|
"resident_misses": 0,
|
|
53
57
|
"resident_vram_hits": 0,
|
|
@@ -60,6 +64,9 @@ class ChronosMLXInferenceEngine:
|
|
|
60
64
|
"prefetch_wait_time_s": 0.0,
|
|
61
65
|
"sync_ssd_loads": 0,
|
|
62
66
|
"on_demand_load_time_s": 0.0,
|
|
67
|
+
"cold_prediction_hits": 0,
|
|
68
|
+
"cold_prediction_total": 0,
|
|
69
|
+
"all_resident_fast_path": bool(all_resident_fast_path),
|
|
63
70
|
}
|
|
64
71
|
|
|
65
72
|
def setup(self, warm_expert_ids: Optional[List[int]] = None):
|
|
@@ -90,6 +97,9 @@ class ChronosMLXInferenceEngine:
|
|
|
90
97
|
if not was_hot and not was_warm:
|
|
91
98
|
with self._stats_lock:
|
|
92
99
|
self._runtime_stats["sync_ssd_loads"] += 1
|
|
100
|
+
self._runtime_stats["cold_prediction_total"] += 1
|
|
101
|
+
if eid in self._last_predicted:
|
|
102
|
+
self._runtime_stats["cold_prediction_hits"] += 1
|
|
93
103
|
ok = self.store.promote_to_vram(eid)
|
|
94
104
|
elapsed = time.monotonic() - t0
|
|
95
105
|
with self._stats_lock:
|
|
@@ -129,6 +139,14 @@ class ChronosMLXInferenceEngine:
|
|
|
129
139
|
moe.runtime_on_demand_loader = loader
|
|
130
140
|
moe.runtime_touch_expert = touch
|
|
131
141
|
|
|
142
|
+
def _clear_runtime_hooks(self):
|
|
143
|
+
for layer in self.model.layers:
|
|
144
|
+
moe = getattr(layer, "mlp", None)
|
|
145
|
+
if isinstance(moe, ChronosMLXMOE):
|
|
146
|
+
moe.runtime_miss_policy = "sync_on_demand"
|
|
147
|
+
moe.runtime_on_demand_loader = None
|
|
148
|
+
moe.runtime_touch_expert = None
|
|
149
|
+
|
|
132
150
|
# ── Prefetch background thread ────────────────────────────────
|
|
133
151
|
|
|
134
152
|
def _prefetch_loop(self):
|
|
@@ -149,37 +167,63 @@ class ChronosMLXInferenceEngine:
|
|
|
149
167
|
with self._stats_lock:
|
|
150
168
|
self._runtime_stats["prefetch_queue_drops"] += 1
|
|
151
169
|
|
|
152
|
-
def _prefetch_and_promote_window(
|
|
153
|
-
|
|
170
|
+
def _prefetch_and_promote_window(
|
|
171
|
+
self,
|
|
172
|
+
prefetch_ids: List[int],
|
|
173
|
+
promote_ids: Optional[List[int]] = None,
|
|
174
|
+
timeout_s: float = 0.012,
|
|
175
|
+
) -> None:
|
|
176
|
+
if not prefetch_ids or self.store.storage_format == "full_dram":
|
|
154
177
|
return
|
|
178
|
+
promote_ids = list(dict.fromkeys(int(eid) for eid in (promote_ids or prefetch_ids)))
|
|
155
179
|
pending = []
|
|
156
|
-
for eid in dict.fromkeys(int(eid) for eid in
|
|
180
|
+
for eid in dict.fromkeys(int(eid) for eid in prefetch_ids):
|
|
157
181
|
with self.store._lock:
|
|
158
182
|
if eid in self.store._hot_lru:
|
|
159
183
|
continue
|
|
160
184
|
if eid in self.store._warm and self.store._layer_states_complete(self.store._warm.get(eid)):
|
|
161
|
-
pending.append(eid)
|
|
162
185
|
continue
|
|
163
186
|
pending.append(eid)
|
|
164
|
-
if
|
|
165
|
-
|
|
166
|
-
self._schedule_prefetch(pending)
|
|
187
|
+
if pending:
|
|
188
|
+
self._schedule_prefetch(pending)
|
|
167
189
|
deadline = time.monotonic() + max(0.0, float(timeout_s))
|
|
168
190
|
while time.monotonic() < deadline:
|
|
169
|
-
self._promote_ready(
|
|
191
|
+
self._promote_ready(promote_ids)
|
|
170
192
|
with self.store._lock:
|
|
171
193
|
ready = all(
|
|
172
194
|
eid in self.store._hot_lru
|
|
173
195
|
or (eid in self.store._warm and self.store._layer_states_complete(self.store._warm.get(eid)))
|
|
174
|
-
for eid in
|
|
196
|
+
for eid in promote_ids
|
|
175
197
|
)
|
|
176
198
|
if ready:
|
|
177
199
|
break
|
|
178
200
|
time.sleep(0.001)
|
|
179
|
-
self._promote_ready(
|
|
201
|
+
self._promote_ready(promote_ids)
|
|
180
202
|
with self._stats_lock:
|
|
181
203
|
self._runtime_stats["prefetch_wait_time_s"] += max(0.0, time.monotonic() - (deadline - max(0.0, float(timeout_s))))
|
|
182
204
|
|
|
205
|
+
def _all_resident_fast_path_enabled(self) -> bool:
|
|
206
|
+
if self.store.storage_format == "full_dram":
|
|
207
|
+
return True
|
|
208
|
+
num_experts = int(getattr(self.config, "num_experts", 0) or 0)
|
|
209
|
+
if num_experts <= 0:
|
|
210
|
+
return False
|
|
211
|
+
if int(getattr(self.store, "_capacity", 0) or 0) < num_experts:
|
|
212
|
+
return False
|
|
213
|
+
if int(getattr(self.store, "_warm_capacity", 0) or 0) < num_experts:
|
|
214
|
+
return False
|
|
215
|
+
return len(self.store.hot_expert_ids()) >= num_experts
|
|
216
|
+
|
|
217
|
+
def _protect_prediction_window(self, expert_ids: List[int]) -> None:
|
|
218
|
+
capacity = max(
|
|
219
|
+
1,
|
|
220
|
+
int(getattr(self.store, "_warm_capacity", 1) or 1),
|
|
221
|
+
int(getattr(self.store, "_capacity", 1) or 1),
|
|
222
|
+
)
|
|
223
|
+
hot_ids = sorted(self.store.hot_expert_ids())
|
|
224
|
+
protected = list(dict.fromkeys([int(eid) for eid in expert_ids] + hot_ids))[:capacity]
|
|
225
|
+
self.store.set_protected_experts(protected)
|
|
226
|
+
|
|
183
227
|
def stop(self):
|
|
184
228
|
self._stop.set()
|
|
185
229
|
try:
|
|
@@ -210,23 +254,15 @@ class ChronosMLXInferenceEngine:
|
|
|
210
254
|
setup_mem = self._memory_snapshot()
|
|
211
255
|
self._last_predicted = set()
|
|
212
256
|
with self._stats_lock:
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
"prediction_hits": 0,
|
|
221
|
-
"prediction_total": 0,
|
|
222
|
-
"prefetch_queue_drops": 0,
|
|
223
|
-
"prefetch_wait_time_s": 0.0,
|
|
224
|
-
"sync_ssd_loads": 0,
|
|
225
|
-
"on_demand_load_time_s": 0.0,
|
|
226
|
-
}
|
|
227
|
-
self._install_runtime_hooks()
|
|
257
|
+
all_resident_fast_path = self._all_resident_fast_path_enabled()
|
|
258
|
+
self._runtime_stats = self._new_runtime_stats(all_resident_fast_path)
|
|
259
|
+
if all_resident_fast_path:
|
|
260
|
+
self._clear_runtime_hooks()
|
|
261
|
+
self.store.set_protected_experts([])
|
|
262
|
+
else:
|
|
263
|
+
self._install_runtime_hooks()
|
|
228
264
|
# ── Prefill phase: front-load expert IO ───────────────────────────
|
|
229
|
-
if scheduler is not None:
|
|
265
|
+
if scheduler is not None and not all_resident_fast_path:
|
|
230
266
|
import torch, numpy as np
|
|
231
267
|
ids_np = np.array(input_ids.tolist(), dtype=np.int64)
|
|
232
268
|
ids_pt = torch.from_numpy(ids_np)
|
|
@@ -235,7 +271,7 @@ class ChronosMLXInferenceEngine:
|
|
|
235
271
|
|
|
236
272
|
# Prefill forward
|
|
237
273
|
prefill_t0 = time.monotonic()
|
|
238
|
-
if
|
|
274
|
+
if all_resident_fast_path:
|
|
239
275
|
prefill_masks = None
|
|
240
276
|
else:
|
|
241
277
|
prefill_masks = self._build_avail_masks(None)
|
|
@@ -248,21 +284,41 @@ class ChronosMLXInferenceEngine:
|
|
|
248
284
|
next_token = self._sample(logits[:, -1, :], temperature, top_p)
|
|
249
285
|
activated_ids: List[int] = []
|
|
250
286
|
tokens = 1
|
|
251
|
-
if scheduler is None and lookahead_probs is not None:
|
|
252
|
-
future_ids = self._predict_future_experts(
|
|
287
|
+
if not all_resident_fast_path and scheduler is None and lookahead_probs is not None:
|
|
288
|
+
future_ids = self._predict_future_experts(
|
|
289
|
+
lookahead_probs,
|
|
290
|
+
capacity=int(getattr(self.store, "_warm_capacity", self.config.num_experts) or self.config.num_experts),
|
|
291
|
+
)
|
|
292
|
+
immediate_ids = self._predict_future_experts(
|
|
293
|
+
lookahead_probs,
|
|
294
|
+
capacity=int(getattr(self.store, "_capacity", 1) or 1),
|
|
295
|
+
max_steps=1,
|
|
296
|
+
)
|
|
253
297
|
self._last_predicted = set(int(eid) for eid in future_ids)
|
|
254
|
-
self.
|
|
298
|
+
self._protect_prediction_window(future_ids)
|
|
299
|
+
self._prefetch_and_promote_window(future_ids, promote_ids=immediate_ids, timeout_s=0.025)
|
|
255
300
|
yield int(next_token.item())
|
|
256
301
|
|
|
257
302
|
for _ in range(max_new_tokens - 1):
|
|
258
|
-
if
|
|
303
|
+
if all_resident_fast_path:
|
|
304
|
+
avail_masks = None
|
|
305
|
+
elif scheduler is not None:
|
|
259
306
|
avail_masks = self._build_avail_masks(next_token)
|
|
260
307
|
else:
|
|
261
308
|
# LookaheadRouter-driven prefetch
|
|
262
309
|
if lookahead_probs is not None:
|
|
263
|
-
future_ids = self._predict_future_experts(
|
|
310
|
+
future_ids = self._predict_future_experts(
|
|
311
|
+
lookahead_probs,
|
|
312
|
+
capacity=int(getattr(self.store, "_warm_capacity", self.config.num_experts) or self.config.num_experts),
|
|
313
|
+
)
|
|
314
|
+
immediate_ids = self._predict_future_experts(
|
|
315
|
+
lookahead_probs,
|
|
316
|
+
capacity=int(getattr(self.store, "_capacity", 1) or 1),
|
|
317
|
+
max_steps=1,
|
|
318
|
+
)
|
|
264
319
|
self._last_predicted = set(int(eid) for eid in future_ids)
|
|
265
|
-
self.
|
|
320
|
+
self._protect_prediction_window(future_ids)
|
|
321
|
+
self._prefetch_and_promote_window(future_ids, promote_ids=immediate_ids)
|
|
266
322
|
avail_masks = self._build_avail_masks(next_token)
|
|
267
323
|
|
|
268
324
|
token_in = next_token.reshape(1, 1)
|
|
@@ -298,22 +354,39 @@ class ChronosMLXInferenceEngine:
|
|
|
298
354
|
**self.store.stats(),
|
|
299
355
|
}
|
|
300
356
|
|
|
301
|
-
def _predict_future_experts(
|
|
357
|
+
def _predict_future_experts(
|
|
358
|
+
self,
|
|
359
|
+
lookahead_probs: mx.array,
|
|
360
|
+
capacity: Optional[int] = None,
|
|
361
|
+
max_steps: Optional[int] = None,
|
|
362
|
+
) -> List[int]:
|
|
302
363
|
"""Extract bounded top-k expert IDs for future steps."""
|
|
303
364
|
# lookahead_probs: [B, S, K+1, E] — take last token, steps 1..K
|
|
304
365
|
future = lookahead_probs[0, -1, 1:, :] # [K, E]
|
|
366
|
+
if max_steps is not None:
|
|
367
|
+
future = future[:max(0, int(max_steps))]
|
|
368
|
+
if future.shape[0] <= 0:
|
|
369
|
+
return []
|
|
370
|
+
num_experts = int(getattr(self.config, "num_experts", future.shape[-1]) or future.shape[-1])
|
|
305
371
|
top_k = max(1, min(
|
|
306
372
|
int(getattr(self.config, "num_experts_per_tok", 1) or 1),
|
|
307
|
-
|
|
373
|
+
num_experts,
|
|
308
374
|
))
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
375
|
+
budget = num_experts if capacity is None else max(1, min(num_experts, int(capacity)))
|
|
376
|
+
scores = mx.zeros((num_experts,), dtype=mx.float32)
|
|
377
|
+
for offset in range(future.shape[0]):
|
|
378
|
+
step = future[offset].astype(mx.float32)
|
|
379
|
+
if top_k >= num_experts:
|
|
380
|
+
scores = scores + step / float(offset + 1)
|
|
381
|
+
continue
|
|
382
|
+
step_ids = mx.argpartition(-step, kth=top_k - 1, axis=-1)[:top_k]
|
|
383
|
+
step_vals = mx.take(step, step_ids) / float(offset + 1)
|
|
384
|
+
one_hot = (step_ids[:, None] == mx.arange(num_experts)).astype(mx.float32)
|
|
385
|
+
scores = scores + (one_hot * step_vals[:, None]).sum(axis=0)
|
|
386
|
+
mx.eval(scores)
|
|
387
|
+
scored = [(idx, float(value)) for idx, value in enumerate(scores.tolist()) if float(value) > 0.0]
|
|
388
|
+
scored.sort(key=lambda item: (-item[1], item[0]))
|
|
389
|
+
return [int(idx) for idx, _value in scored[:budget]]
|
|
317
390
|
|
|
318
391
|
def _build_avail_masks(self, _token) -> List[set[int]] | None:
|
|
319
392
|
"""Promote prefetched experts and return per-layer availability masks."""
|
|
@@ -339,9 +412,14 @@ class ChronosMLXInferenceEngine:
|
|
|
339
412
|
stats = dict(self._runtime_stats)
|
|
340
413
|
total = int(stats.get("selection_hits", 0)) + int(stats.get("selection_misses", 0))
|
|
341
414
|
pred_total = int(stats.get("prediction_total", 0))
|
|
415
|
+
cold_pred_total = int(stats.get("cold_prediction_total", 0))
|
|
416
|
+
all_resident_fast_path = bool(stats.get("all_resident_fast_path", False))
|
|
417
|
+
resident_hit_rate = 1.0 if all_resident_fast_path else (
|
|
418
|
+
float(stats.get("selection_hits", 0)) / max(total, 1)
|
|
419
|
+
)
|
|
342
420
|
return {
|
|
343
|
-
"resident_hit_rate": round(
|
|
344
|
-
"cache_hit_rate": round(
|
|
421
|
+
"resident_hit_rate": round(resident_hit_rate, 4),
|
|
422
|
+
"cache_hit_rate": round(resident_hit_rate, 4),
|
|
345
423
|
"cache_hits": int(stats.get("selection_hits", 0)),
|
|
346
424
|
"cache_misses": int(stats.get("selection_misses", 0)),
|
|
347
425
|
"expert_selection_hits": int(stats.get("selection_hits", 0)),
|
|
@@ -349,6 +427,10 @@ class ChronosMLXInferenceEngine:
|
|
|
349
427
|
"prediction_hit_rate": round(float(stats.get("prediction_hits", 0)) / max(pred_total, 1), 4),
|
|
350
428
|
"prediction_hits": int(stats.get("prediction_hits", 0)),
|
|
351
429
|
"prediction_total": pred_total,
|
|
430
|
+
"cold_miss_predict_hit_rate": round(float(stats.get("cold_prediction_hits", 0)) / max(cold_pred_total, 1), 4),
|
|
431
|
+
"cold_prediction_hits": int(stats.get("cold_prediction_hits", 0)),
|
|
432
|
+
"cold_prediction_total": cold_pred_total,
|
|
433
|
+
"all_resident_fast_path": all_resident_fast_path,
|
|
352
434
|
"resident_vram_hits": int(stats.get("resident_vram_hits", 0)),
|
|
353
435
|
"resident_ram_hits": int(stats.get("resident_ram_hits", 0)),
|
|
354
436
|
"prefetch_queue_drops": int(stats.get("prefetch_queue_drops", 0)),
|
|
@@ -101,6 +101,19 @@ def _temporal_locality_loss(router_mean: mx.array) -> mx.array:
|
|
|
101
101
|
return (diff * diff).sum(axis=-1).mean()
|
|
102
102
|
|
|
103
103
|
|
|
104
|
+
def _router_locality_loss(router_mean: mx.array, num_experts_per_tok: int) -> mx.array:
|
|
105
|
+
if router_mean.shape[1] <= 1:
|
|
106
|
+
return mx.zeros(())
|
|
107
|
+
B, _S, E = router_mean.shape
|
|
108
|
+
top_k = max(1, min(int(num_experts_per_tok or 1), E))
|
|
109
|
+
prev = mx.stop_gradient(router_mean[:, :-1, :])
|
|
110
|
+
cur = router_mean[:, 1:, :]
|
|
111
|
+
target_ids = mx.argpartition(-prev, kth=top_k - 1, axis=-1)[:, :, :top_k]
|
|
112
|
+
target_mask = (target_ids[..., None] == mx.arange(E)).astype(cur.dtype).sum(axis=-2)
|
|
113
|
+
mass = (cur * target_mask).sum(axis=-1)
|
|
114
|
+
return (-mx.log(mx.maximum(mass / float(top_k), 1e-9))).mean()
|
|
115
|
+
|
|
116
|
+
|
|
104
117
|
def _load_balance_loss(router_4d: mx.array, num_experts_per_tok: int) -> mx.array:
|
|
105
118
|
B, S, L, E = router_4d.shape
|
|
106
119
|
top_k = max(1, min(int(num_experts_per_tok or 1), E))
|
|
@@ -171,6 +184,96 @@ def _lookahead_topk_hit_loss(
|
|
|
171
184
|
return total / max(terms, 1)
|
|
172
185
|
|
|
173
186
|
|
|
187
|
+
def _lookahead_union_loss(
|
|
188
|
+
lookahead_probs: mx.array | None,
|
|
189
|
+
teacher_router_probs: mx.array | None,
|
|
190
|
+
lookahead_steps: int,
|
|
191
|
+
num_experts_per_tok: int,
|
|
192
|
+
) -> mx.array:
|
|
193
|
+
if lookahead_probs is None:
|
|
194
|
+
return mx.zeros(())
|
|
195
|
+
if teacher_router_probs is None:
|
|
196
|
+
return mx.zeros((), dtype=lookahead_probs.dtype)
|
|
197
|
+
B, S, Kp1, E = lookahead_probs.shape
|
|
198
|
+
K = min(int(lookahead_steps or 0), Kp1 - 1)
|
|
199
|
+
top_k = max(1, min(int(num_experts_per_tok or 1), E))
|
|
200
|
+
if K <= 0 or S <= K:
|
|
201
|
+
return mx.zeros((), dtype=lookahead_probs.dtype)
|
|
202
|
+
teacher = mx.stop_gradient(teacher_router_probs.astype(mx.float32))
|
|
203
|
+
union_target = mx.zeros((B, S - K, E), dtype=mx.float32)
|
|
204
|
+
for k in range(1, K + 1):
|
|
205
|
+
target_ids = mx.argpartition(-teacher[:, k:k + S - K, :], kth=top_k - 1, axis=-1)[:, :, :top_k]
|
|
206
|
+
target_mask = (target_ids[..., None] == mx.arange(E)).astype(mx.float32).sum(axis=-2)
|
|
207
|
+
union_target = mx.maximum(union_target, target_mask)
|
|
208
|
+
pred_union = lookahead_probs[:, :S - K, 1:K + 1, :].astype(mx.float32).max(axis=2)
|
|
209
|
+
mass = (pred_union * union_target).sum(axis=-1)
|
|
210
|
+
target_count = mx.maximum(union_target.sum(axis=-1), 1.0)
|
|
211
|
+
return (-mx.log(mx.maximum(mass / target_count, 1e-9))).mean()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def _offload_training_metrics(
|
|
215
|
+
router_mean: mx.array | None,
|
|
216
|
+
lookahead_probs: mx.array | None,
|
|
217
|
+
lookahead_steps: int,
|
|
218
|
+
num_experts_per_tok: int,
|
|
219
|
+
) -> dict[str, float]:
|
|
220
|
+
if router_mean is None:
|
|
221
|
+
return {}
|
|
222
|
+
B, S, E = router_mean.shape
|
|
223
|
+
top_k = max(1, min(int(num_experts_per_tok or 1), E))
|
|
224
|
+
top_ids = mx.argpartition(-router_mean, kth=top_k - 1, axis=-1)[:, :, :top_k]
|
|
225
|
+
mx.eval(top_ids)
|
|
226
|
+
import numpy as np
|
|
227
|
+
|
|
228
|
+
ids_np = np.array(top_ids)
|
|
229
|
+
out = {
|
|
230
|
+
"expert_working_set": float(len(set(int(v) for v in ids_np.reshape(-1)))),
|
|
231
|
+
"router_adjacent_jaccard": 1.0,
|
|
232
|
+
"lookahead_topk_recall": 0.0,
|
|
233
|
+
"lookahead_union_recall": 0.0,
|
|
234
|
+
}
|
|
235
|
+
if S > 1:
|
|
236
|
+
vals = []
|
|
237
|
+
for b in range(B):
|
|
238
|
+
for t in range(S - 1):
|
|
239
|
+
a = set(int(v) for v in ids_np[b, t])
|
|
240
|
+
c = set(int(v) for v in ids_np[b, t + 1])
|
|
241
|
+
vals.append(len(a & c) / max(len(a | c), 1))
|
|
242
|
+
out["router_adjacent_jaccard"] = float(sum(vals) / max(len(vals), 1))
|
|
243
|
+
if lookahead_probs is not None and lookahead_steps > 0 and S > 1:
|
|
244
|
+
K = min(int(lookahead_steps), int(lookahead_probs.shape[2]) - 1)
|
|
245
|
+
pred = lookahead_probs.astype(mx.float32)
|
|
246
|
+
mx.eval(pred)
|
|
247
|
+
pred_np = np.array(pred)
|
|
248
|
+
recalls = []
|
|
249
|
+
union_recalls = []
|
|
250
|
+
pred_budget = max(top_k, min(E, top_k * max(1, K)))
|
|
251
|
+
for k in range(1, K + 1):
|
|
252
|
+
if S - k <= 0:
|
|
253
|
+
continue
|
|
254
|
+
pred_ids = np.argpartition(-pred_np[:, :-k, k, :], kth=top_k - 1, axis=-1)[:, :, :top_k]
|
|
255
|
+
for b in range(B):
|
|
256
|
+
for t in range(S - k):
|
|
257
|
+
target = set(int(v) for v in ids_np[b, t + k])
|
|
258
|
+
got = set(int(v) for v in pred_ids[b, t])
|
|
259
|
+
recalls.append(len(target & got) / float(top_k))
|
|
260
|
+
if K > 0 and S - K > 0:
|
|
261
|
+
pred_union_ids = np.argpartition(-pred_np[:, :S - K, 1:K + 1, :].max(axis=2),
|
|
262
|
+
kth=pred_budget - 1, axis=-1)[:, :, :pred_budget]
|
|
263
|
+
for b in range(B):
|
|
264
|
+
for t in range(S - K):
|
|
265
|
+
target = set()
|
|
266
|
+
for k in range(1, K + 1):
|
|
267
|
+
target.update(int(v) for v in ids_np[b, t + k])
|
|
268
|
+
got = set(int(v) for v in pred_union_ids[b, t])
|
|
269
|
+
union_recalls.append(len(target & got) / max(len(target), 1))
|
|
270
|
+
if recalls:
|
|
271
|
+
out["lookahead_topk_recall"] = float(sum(recalls) / len(recalls))
|
|
272
|
+
if union_recalls:
|
|
273
|
+
out["lookahead_union_recall"] = float(sum(union_recalls) / len(union_recalls))
|
|
274
|
+
return out
|
|
275
|
+
|
|
276
|
+
|
|
174
277
|
def _router_kl_anchor(current: mx.array, reference: mx.array | None, lambda_anchor: float) -> mx.array:
|
|
175
278
|
if reference is None or lambda_anchor <= 0.0:
|
|
176
279
|
return mx.zeros((), dtype=current.dtype)
|
|
@@ -184,6 +287,7 @@ def _router_kl_anchor(current: mx.array, reference: mx.array | None, lambda_anch
|
|
|
184
287
|
|
|
185
288
|
|
|
186
289
|
def _chronos_regularized_loss(model, base_loss: mx.array, lookahead_probs: mx.array | None, config, router_ref=None) -> mx.array:
|
|
290
|
+
setattr(model, "_last_lookahead_probs", lookahead_probs)
|
|
187
291
|
loss = base_loss.astype(mx.float32)
|
|
188
292
|
router_4d = _collect_router_probs(model)
|
|
189
293
|
if router_4d is None:
|
|
@@ -202,6 +306,12 @@ def _chronos_regularized_loss(model, base_loss: mx.array, lookahead_probs: mx.ar
|
|
|
202
306
|
lambda_temporal = float(getattr(config, "lambda_temporal", 0.0) or 0.0)
|
|
203
307
|
if lambda_temporal > 0.0:
|
|
204
308
|
loss = loss + lambda_temporal * _temporal_locality_loss(router_mean)
|
|
309
|
+
lambda_locality = float(getattr(config, "lambda_router_locality", 0.0) or 0.0)
|
|
310
|
+
if lambda_locality > 0.0:
|
|
311
|
+
loss = loss + lambda_locality * _router_locality_loss(
|
|
312
|
+
router_mean,
|
|
313
|
+
int(getattr(config, "num_experts_per_tok", 1) or 1),
|
|
314
|
+
)
|
|
205
315
|
|
|
206
316
|
lookahead_steps = int(getattr(config, "lookahead_steps", 0) or 0)
|
|
207
317
|
lambda_lookahead = float(getattr(config, "lambda_lookahead", 0.0) or 0.0)
|
|
@@ -220,6 +330,14 @@ def _chronos_regularized_loss(model, base_loss: mx.array, lookahead_probs: mx.ar
|
|
|
220
330
|
lookahead_steps,
|
|
221
331
|
int(getattr(config, "num_experts_per_tok", 1) or 1),
|
|
222
332
|
)
|
|
333
|
+
lambda_union = float(getattr(config, "lambda_lookahead_union", 0.0) or 0.0)
|
|
334
|
+
if lambda_union > 0.0:
|
|
335
|
+
loss = loss + lambda_union * _lookahead_union_loss(
|
|
336
|
+
lookahead_probs,
|
|
337
|
+
teacher,
|
|
338
|
+
lookahead_steps,
|
|
339
|
+
int(getattr(config, "num_experts_per_tok", 1) or 1),
|
|
340
|
+
)
|
|
223
341
|
|
|
224
342
|
loss = loss + _router_kl_anchor(
|
|
225
343
|
router_mean,
|
|
@@ -824,6 +942,14 @@ def run_mlx_stage(
|
|
|
824
942
|
tps = delta_steps / max(now - last_log_t, 1e-6)
|
|
825
943
|
last_log_t = now
|
|
826
944
|
last_log_step = steps
|
|
945
|
+
router_4d = _collect_router_probs(model)
|
|
946
|
+
router_mean = router_4d.mean(axis=2) if router_4d is not None else None
|
|
947
|
+
offload_metrics = _offload_training_metrics(
|
|
948
|
+
router_mean,
|
|
949
|
+
getattr(model, "_last_lookahead_probs", None),
|
|
950
|
+
int(getattr(config, "lookahead_steps", 0) or 0),
|
|
951
|
+
int(getattr(config, "num_experts_per_tok", 1) or 1),
|
|
952
|
+
)
|
|
827
953
|
_emit({
|
|
828
954
|
"event": "step",
|
|
829
955
|
"stage": stage,
|
|
@@ -840,6 +966,7 @@ def run_mlx_stage(
|
|
|
840
966
|
"dtype": dtype_name,
|
|
841
967
|
"lr": float(optimizer.learning_rate.item()) if "learning_rate" in optimizer.state else base_lr * lr_scale,
|
|
842
968
|
"rollbacks": rollback_count,
|
|
969
|
+
**offload_metrics,
|
|
843
970
|
})
|
|
844
971
|
if max_steps is not None and steps >= int(max_steps):
|
|
845
972
|
break
|
|
@@ -48,10 +48,13 @@ CHECKPOINT_CONFIG_FIELDS = (
|
|
|
48
48
|
"lambda_temporal",
|
|
49
49
|
"lambda_lookahead",
|
|
50
50
|
"lambda_lookahead_topk",
|
|
51
|
+
"lambda_lookahead_union",
|
|
52
|
+
"lambda_router_locality",
|
|
51
53
|
"lambda_router_anchor",
|
|
52
54
|
"fallback_mask_prob",
|
|
53
55
|
"offload_miss_policy_default",
|
|
54
56
|
"recommended_resident_experts",
|
|
57
|
+
"offload_working_set_experts",
|
|
55
58
|
"vram_budget_gb",
|
|
56
59
|
"pinned_memory_max_fraction",
|
|
57
60
|
"storage_format",
|
|
@@ -19,7 +19,7 @@ class ChronosConfig(MiniMindConfig):
|
|
|
19
19
|
self.lookahead_steps: int = kwargs.pop("lookahead_steps", 2)
|
|
20
20
|
self.num_shared_experts: int = kwargs.pop("num_shared_experts", 1)
|
|
21
21
|
self.lambda_balance: float = kwargs.pop("lambda_balance", 5e-4)
|
|
22
|
-
self.lambda_temporal: float = kwargs.pop("lambda_temporal",
|
|
22
|
+
self.lambda_temporal: float = kwargs.pop("lambda_temporal", 3e-3)
|
|
23
23
|
self.vram_budget_gb: float = kwargs.pop("vram_budget_gb", 4.0)
|
|
24
24
|
self.prefetch_depth: int = kwargs.pop("prefetch_depth", 2)
|
|
25
25
|
# Hybrid attention params
|
|
@@ -42,7 +42,9 @@ class ChronosConfig(MiniMindConfig):
|
|
|
42
42
|
# Auxiliary hit-rate loss for the lookahead head. The soft CE above
|
|
43
43
|
# learns distribution shape; this term directly rewards future top-k
|
|
44
44
|
# expert-set recall, which is what offload prefetch needs.
|
|
45
|
-
self.lambda_lookahead_topk: float = kwargs.pop("lambda_lookahead_topk", 0.
|
|
45
|
+
self.lambda_lookahead_topk: float = kwargs.pop("lambda_lookahead_topk", 0.15)
|
|
46
|
+
self.lambda_lookahead_union: float = kwargs.pop("lambda_lookahead_union", 0.05)
|
|
47
|
+
self.lambda_router_locality: float = kwargs.pop("lambda_router_locality", 0.02)
|
|
46
48
|
# M4: weight on the router KL anchor (alignment stages only).
|
|
47
49
|
# Prevents SFT/DPO/ORPO/GRPO gradients from drifting routing away
|
|
48
50
|
# from the pretrained distribution that the cluster layout was
|
|
@@ -52,7 +54,7 @@ class ChronosConfig(MiniMindConfig):
|
|
|
52
54
|
# Optional training-time miss simulation. When >0, MoE layers randomly
|
|
53
55
|
# route selected experts through the shared fallback while still adding
|
|
54
56
|
# the shared residual, matching the SSD/DRAM offload inference formula.
|
|
55
|
-
self.fallback_mask_prob: float = kwargs.pop("fallback_mask_prob", 0.
|
|
57
|
+
self.fallback_mask_prob: float = kwargs.pop("fallback_mask_prob", 0.08)
|
|
56
58
|
# Runtime offload policy. "on_demand" is the predictive low-latency
|
|
57
59
|
# path: cold SSD misses are queued for RAM and covered by shared
|
|
58
60
|
# fallback this token. "sync_on_demand" is the diagnostic path that
|
|
@@ -60,6 +62,7 @@ class ChronosConfig(MiniMindConfig):
|
|
|
60
62
|
# "fallback_diagnostic" keeps the pure shared-fallback stress path.
|
|
61
63
|
self.offload_miss_policy_default: str = kwargs.pop("offload_miss_policy_default", "on_demand")
|
|
62
64
|
self.recommended_resident_experts: int | None = kwargs.pop("recommended_resident_experts", None)
|
|
65
|
+
self.offload_working_set_experts: int | None = kwargs.pop("offload_working_set_experts", None)
|
|
63
66
|
# M7: regularization defaults. MiniMindConfig defaults dropout=0.0
|
|
64
67
|
# and weight_decay isn't even a config field — both major causes
|
|
65
68
|
# of overfitting on small corpora. Override here.
|