Project-Chronos 0.1.1__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.
- chronos/__init__.py +16 -0
- chronos/_backend_legacy.py +64 -0
- chronos/app.py +122 -0
- chronos/backend/__init__.py +40 -0
- chronos/backend/dispatcher.py +347 -0
- chronos/backend/ext/__init__.py +2 -0
- chronos/backend/ext/opencl.py +22 -0
- chronos/cli.py +62 -0
- chronos/data/__init__.py +0 -0
- chronos/data/flexible_dataset.py +309 -0
- chronos/deps.py +72 -0
- chronos/eval/__init__.py +2 -0
- chronos/eval/benchmark.py +159 -0
- chronos/eval/io_profiler.py +232 -0
- chronos/io/__init__.py +13 -0
- chronos/io/async_prefetcher.py +220 -0
- chronos/io/cluster_layout.py +317 -0
- chronos/io/expert_store.py +559 -0
- chronos/io/io_simulator.py +40 -0
- chronos/io/storage.py +237 -0
- chronos/mlx/__init__.py +32 -0
- chronos/mlx/attention.py +230 -0
- chronos/mlx/expert_store.py +155 -0
- chronos/mlx/inference.py +159 -0
- chronos/mlx/model.py +181 -0
- chronos/mlx/moe.py +102 -0
- chronos/model/__init__.py +6 -0
- chronos/model/config.py +54 -0
- chronos/model/hf_io.py +149 -0
- chronos/model/hybrid_attention.py +237 -0
- chronos/model/lookahead_router.py +55 -0
- chronos/model/model_chronos.py +271 -0
- chronos/model/moe_chronos.py +189 -0
- chronos/model/temporal_loss.py +124 -0
- chronos/router/__init__.py +12 -0
- chronos/router/expert_predictor.py +163 -0
- chronos/router/intent_classifier.py +193 -0
- chronos/router/prefill_scheduler.py +248 -0
- chronos/runtime/__init__.py +2 -0
- chronos/runtime/cache_manager.py +117 -0
- chronos/runtime/inference_engine.py +295 -0
- chronos/runtime/metrics.py +75 -0
- chronos/serving/__init__.py +13 -0
- chronos/serving/vllm_adapter.py +85 -0
- chronos/trainer/__init__.py +6 -0
- chronos/trainer/chronos_trainer.py +151 -0
- chronos/trainer/distill_trainer.py +193 -0
- chronos/trainer/dpo_trainer.py +190 -0
- chronos/trainer/grpo_trainer.py +288 -0
- chronos/trainer/loss_mixin.py +165 -0
- chronos/trainer/optim_utils.py +101 -0
- chronos/trainer/orpo_trainer.py +189 -0
- chronos/trainer/reward.py +130 -0
- chronos/trainer/sft_trainer.py +189 -0
- chronos/tuning/__init__.py +1 -0
- chronos/tuning/_base_tuner.py +284 -0
- chronos/tuning/chronos_auto_tuner.py +203 -0
- project_chronos-0.1.1.dist-info/METADATA +670 -0
- project_chronos-0.1.1.dist-info/RECORD +83 -0
- project_chronos-0.1.1.dist-info/WHEEL +5 -0
- project_chronos-0.1.1.dist-info/entry_points.txt +7 -0
- project_chronos-0.1.1.dist-info/licenses/LICENSE +177 -0
- project_chronos-0.1.1.dist-info/top_level.txt +8 -0
- train_chronos.py +133 -0
- train_chronos_distill.py +95 -0
- train_chronos_dpo.py +82 -0
- train_chronos_grpo.py +88 -0
- train_chronos_orpo.py +81 -0
- train_chronos_sft.py +85 -0
- ui/__init__.py +3 -0
- ui/estimator.py +204 -0
- ui/gradio_compat.py +72 -0
- ui/i18n.py +540 -0
- ui/presets.py +194 -0
- ui/tabs/__init__.py +3 -0
- ui/tabs/autotune_tab.py +210 -0
- ui/tabs/benchmark_tab.py +196 -0
- ui/tabs/config_tab.py +318 -0
- ui/tabs/data_tab.py +148 -0
- ui/tabs/inference_tab.py +496 -0
- ui/tabs/iomon_tab.py +37 -0
- ui/tabs/pipeline_tab.py +134 -0
- ui/tabs/train_tab.py +1688 -0
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""
|
|
2
|
+
chronos/tuning/_base_tuner.py — Vendored subset of Auto_Fine_Tuning's AutoTuner.
|
|
3
|
+
|
|
4
|
+
Only the base class skeleton (event queue, start/stop/poll, Optuna loop) is kept.
|
|
5
|
+
The full probe-trial machinery from Auto_Fine_Tuning is intentionally omitted —
|
|
6
|
+
ChronosAutoTuner uses its own lightweight training loop instead.
|
|
7
|
+
|
|
8
|
+
This file is derived from Auto_Fine_Tuning/core/auto_tuner.py (Apache-2.0).
|
|
9
|
+
See THIRD_PARTY_NOTICES.md for full attribution.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import os
|
|
13
|
+
import gc
|
|
14
|
+
import time
|
|
15
|
+
import queue
|
|
16
|
+
import threading
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from typing import List, Optional, Dict, Any
|
|
19
|
+
|
|
20
|
+
try:
|
|
21
|
+
import optuna
|
|
22
|
+
from optuna.samplers import TPESampler
|
|
23
|
+
from optuna.pruners import MedianPruner
|
|
24
|
+
optuna.logging.set_verbosity(optuna.logging.WARNING)
|
|
25
|
+
OPTUNA_AVAILABLE = True
|
|
26
|
+
except ImportError:
|
|
27
|
+
OPTUNA_AVAILABLE = False
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@dataclass
|
|
31
|
+
class SearchSpaceConfig:
|
|
32
|
+
tune_lora_r: bool = True
|
|
33
|
+
lora_r_choices: List[int] = field(default_factory=lambda: [4, 8, 16, 32])
|
|
34
|
+
tune_lr: bool = True
|
|
35
|
+
lr_min: float = 5e-5
|
|
36
|
+
lr_max: float = 5e-4
|
|
37
|
+
tune_batch: bool = True
|
|
38
|
+
batch_size_choices: List[int] = field(default_factory=lambda: [1, 2, 4, 8])
|
|
39
|
+
tune_grad_accum: bool = False
|
|
40
|
+
grad_accum_choices: List[int] = field(default_factory=lambda: [1, 2, 4, 8])
|
|
41
|
+
tune_warmup: bool = True
|
|
42
|
+
warmup_min: float = 0.01
|
|
43
|
+
warmup_max: float = 0.15
|
|
44
|
+
tune_scheduler: bool = True
|
|
45
|
+
scheduler_choices: List[str] = field(default_factory=lambda: ["cosine", "linear", "constant"])
|
|
46
|
+
tune_lora_alpha: bool = True
|
|
47
|
+
alpha_multiplier_choices: List[int] = field(default_factory=lambda: [1, 2])
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class TrialResult:
|
|
52
|
+
trial_number: int
|
|
53
|
+
params: Dict[str, Any]
|
|
54
|
+
train_loss: float
|
|
55
|
+
duration_s: float
|
|
56
|
+
status: str
|
|
57
|
+
error: str = ""
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
@dataclass
|
|
61
|
+
class AutoTuneResult:
|
|
62
|
+
best_params: Dict[str, Any]
|
|
63
|
+
best_loss: float
|
|
64
|
+
trials: List[TrialResult]
|
|
65
|
+
param_importances: Dict[str, float]
|
|
66
|
+
n_completed: int
|
|
67
|
+
n_pruned: int
|
|
68
|
+
n_failed: int
|
|
69
|
+
elapsed_s: float
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class AutoTuner:
|
|
73
|
+
"""
|
|
74
|
+
Base class: Optuna TPE loop + event queue.
|
|
75
|
+
Subclasses must implement _run_probe_trial().
|
|
76
|
+
"""
|
|
77
|
+
|
|
78
|
+
def __init__(self):
|
|
79
|
+
self._event_queue: queue.Queue = queue.Queue(maxsize=5000)
|
|
80
|
+
self._stop_event = threading.Event()
|
|
81
|
+
self._thread: Optional[threading.Thread] = None
|
|
82
|
+
self.study = None
|
|
83
|
+
self.result: Optional[AutoTuneResult] = None
|
|
84
|
+
self.trials: List[TrialResult] = []
|
|
85
|
+
self.status: str = "idle"
|
|
86
|
+
|
|
87
|
+
def is_running(self) -> bool:
|
|
88
|
+
return self._thread is not None and self._thread.is_alive()
|
|
89
|
+
|
|
90
|
+
def start(
|
|
91
|
+
self,
|
|
92
|
+
model_id: str,
|
|
93
|
+
dataset_path: str,
|
|
94
|
+
train_ratio: float = 0.95,
|
|
95
|
+
prompt_template: str = "alpaca",
|
|
96
|
+
think_mode: str = "keep",
|
|
97
|
+
search_space: Optional[SearchSpaceConfig] = None,
|
|
98
|
+
n_trials: int = 20,
|
|
99
|
+
probe_steps: int = 80,
|
|
100
|
+
output_dir: str = "auto_tune_cache",
|
|
101
|
+
seed: int = 42,
|
|
102
|
+
**kwargs,
|
|
103
|
+
) -> None:
|
|
104
|
+
if self.is_running():
|
|
105
|
+
raise RuntimeError("Auto-tune already running.")
|
|
106
|
+
if not OPTUNA_AVAILABLE:
|
|
107
|
+
raise RuntimeError("optuna not installed: pip install optuna")
|
|
108
|
+
|
|
109
|
+
self._stop_event.clear()
|
|
110
|
+
self.trials.clear()
|
|
111
|
+
self.result = None
|
|
112
|
+
self.status = "loading"
|
|
113
|
+
|
|
114
|
+
self._thread = threading.Thread(
|
|
115
|
+
target=self._loop,
|
|
116
|
+
kwargs=dict(
|
|
117
|
+
model_id=model_id,
|
|
118
|
+
dataset_path=dataset_path,
|
|
119
|
+
train_ratio=train_ratio,
|
|
120
|
+
search_space=search_space or SearchSpaceConfig(),
|
|
121
|
+
n_trials=n_trials,
|
|
122
|
+
probe_steps=probe_steps,
|
|
123
|
+
output_dir=output_dir,
|
|
124
|
+
seed=seed,
|
|
125
|
+
**kwargs,
|
|
126
|
+
),
|
|
127
|
+
daemon=True,
|
|
128
|
+
name="chronos-auto-tune",
|
|
129
|
+
)
|
|
130
|
+
self._thread.start()
|
|
131
|
+
|
|
132
|
+
def stop(self) -> None:
|
|
133
|
+
self._stop_event.set()
|
|
134
|
+
self.status = "stopped"
|
|
135
|
+
self._put({"type": "status", "status": "stopped"})
|
|
136
|
+
|
|
137
|
+
def poll(self) -> List[Dict]:
|
|
138
|
+
events = []
|
|
139
|
+
while True:
|
|
140
|
+
try:
|
|
141
|
+
events.append(self._event_queue.get_nowait())
|
|
142
|
+
except queue.Empty:
|
|
143
|
+
break
|
|
144
|
+
return events
|
|
145
|
+
|
|
146
|
+
def _put(self, event: Dict) -> None:
|
|
147
|
+
try:
|
|
148
|
+
self._event_queue.put_nowait(event)
|
|
149
|
+
except queue.Full:
|
|
150
|
+
pass
|
|
151
|
+
|
|
152
|
+
def _loop(self, model_id, dataset_path, train_ratio, search_space,
|
|
153
|
+
n_trials, probe_steps, output_dir, seed, **kwargs):
|
|
154
|
+
self._put({"type": "log", "line": "=== Auto-tune started ==="})
|
|
155
|
+
t0 = time.time()
|
|
156
|
+
try:
|
|
157
|
+
sampler = TPESampler(seed=seed, n_startup_trials=max(5, n_trials // 5))
|
|
158
|
+
pruner = MedianPruner(n_startup_trials=5, n_warmup_steps=probe_steps // 4)
|
|
159
|
+
self.study = optuna.create_study(
|
|
160
|
+
direction="minimize", sampler=sampler, pruner=pruner,
|
|
161
|
+
study_name="chronos_auto_tune",
|
|
162
|
+
)
|
|
163
|
+
self.status = "running"
|
|
164
|
+
self._put({"type": "status", "status": "running"})
|
|
165
|
+
|
|
166
|
+
def objective(trial):
|
|
167
|
+
if self._stop_event.is_set():
|
|
168
|
+
raise optuna.exceptions.OptunaError("user stopped")
|
|
169
|
+
t_start = time.time()
|
|
170
|
+
params = self._sample_params(trial, search_space)
|
|
171
|
+
self._put({"type": "log",
|
|
172
|
+
"line": f"[Trial {trial.number+1}/{n_trials}] {_fmt(params)}"})
|
|
173
|
+
try:
|
|
174
|
+
loss = self._run_probe_trial(
|
|
175
|
+
trial, model_id, dataset_path, train_ratio,
|
|
176
|
+
probe_steps, output_dir, seed, params, **kwargs,
|
|
177
|
+
)
|
|
178
|
+
status = "complete"
|
|
179
|
+
except optuna.exceptions.TrialPruned:
|
|
180
|
+
loss = float("inf")
|
|
181
|
+
status = "pruned"
|
|
182
|
+
except Exception as e:
|
|
183
|
+
self._put({"type": "log", "line": f"[Trial {trial.number}] failed: {e}"})
|
|
184
|
+
loss = float("inf")
|
|
185
|
+
status = "failed"
|
|
186
|
+
|
|
187
|
+
self.trials.append(TrialResult(
|
|
188
|
+
trial_number=trial.number, params=params,
|
|
189
|
+
train_loss=loss, duration_s=round(time.time() - t_start, 1),
|
|
190
|
+
status=status,
|
|
191
|
+
))
|
|
192
|
+
best = min((t.train_loss for t in self.trials if t.status == "complete"),
|
|
193
|
+
default=float("inf"))
|
|
194
|
+
self._put({"type": "log",
|
|
195
|
+
"line": f" → loss={loss:.4f} best={best:.4f} {status}"})
|
|
196
|
+
if status == "pruned":
|
|
197
|
+
raise optuna.exceptions.TrialPruned()
|
|
198
|
+
return loss
|
|
199
|
+
|
|
200
|
+
def _stop_cb(study, _trial):
|
|
201
|
+
if self._stop_event.is_set():
|
|
202
|
+
study.stop()
|
|
203
|
+
|
|
204
|
+
self.study.optimize(objective, n_trials=n_trials,
|
|
205
|
+
callbacks=[_stop_cb], gc_after_trial=True)
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
importances = optuna.importance.get_param_importances(self.study)
|
|
209
|
+
except Exception:
|
|
210
|
+
importances = {}
|
|
211
|
+
|
|
212
|
+
completed = [t for t in self.trials if t.status == "complete"]
|
|
213
|
+
best_params = self.study.best_params if completed else {}
|
|
214
|
+
best_loss = self.study.best_value if completed else float("inf")
|
|
215
|
+
|
|
216
|
+
self.result = AutoTuneResult(
|
|
217
|
+
best_params=best_params, best_loss=best_loss,
|
|
218
|
+
trials=self.trials, param_importances=importances,
|
|
219
|
+
n_completed=len(completed),
|
|
220
|
+
n_pruned=len([t for t in self.trials if t.status == "pruned"]),
|
|
221
|
+
n_failed=len([t for t in self.trials if t.status == "failed"]),
|
|
222
|
+
elapsed_s=time.time() - t0,
|
|
223
|
+
)
|
|
224
|
+
self.status = "finished"
|
|
225
|
+
self._put({"type": "finished", "best_params": best_params, "best_loss": best_loss})
|
|
226
|
+
self._put({"type": "log",
|
|
227
|
+
"line": (f"=== Done === best_loss={best_loss:.4f} "
|
|
228
|
+
f"completed={self.result.n_completed} "
|
|
229
|
+
f"elapsed={self.result.elapsed_s:.0f}s")})
|
|
230
|
+
except Exception as e:
|
|
231
|
+
import traceback
|
|
232
|
+
self.status = "error"
|
|
233
|
+
self._put({"type": "status", "status": "error"})
|
|
234
|
+
self._put({"type": "log", "line": f"[ERROR] {e}\n{traceback.format_exc()}"})
|
|
235
|
+
|
|
236
|
+
def _sample_params(self, trial, ss: SearchSpaceConfig) -> dict:
|
|
237
|
+
params = {}
|
|
238
|
+
params["lora_r"] = (trial.suggest_categorical("lora_r", ss.lora_r_choices)
|
|
239
|
+
if ss.tune_lora_r else 16)
|
|
240
|
+
mult = (trial.suggest_categorical("alpha_mult", ss.alpha_multiplier_choices)
|
|
241
|
+
if ss.tune_lora_alpha else 2)
|
|
242
|
+
params["lora_alpha"] = params["lora_r"] * mult
|
|
243
|
+
params["learning_rate"] = (
|
|
244
|
+
trial.suggest_float("learning_rate", ss.lr_min, ss.lr_max, log=True)
|
|
245
|
+
if ss.tune_lr else 2e-4)
|
|
246
|
+
params["per_device_train_batch_size"] = (
|
|
247
|
+
trial.suggest_categorical("batch_size", ss.batch_size_choices)
|
|
248
|
+
if ss.tune_batch else 4)
|
|
249
|
+
params["gradient_accumulation_steps"] = (
|
|
250
|
+
trial.suggest_categorical("grad_accum", ss.grad_accum_choices)
|
|
251
|
+
if ss.tune_grad_accum else 4)
|
|
252
|
+
params["warmup_ratio"] = (
|
|
253
|
+
trial.suggest_float("warmup_ratio", ss.warmup_min, ss.warmup_max)
|
|
254
|
+
if ss.tune_warmup else 0.05)
|
|
255
|
+
params["lr_scheduler_type"] = (
|
|
256
|
+
trial.suggest_categorical("scheduler", ss.scheduler_choices)
|
|
257
|
+
if ss.tune_scheduler else "cosine")
|
|
258
|
+
return params
|
|
259
|
+
|
|
260
|
+
def _run_probe_trial(self, trial, model_id, dataset_path, train_ratio,
|
|
261
|
+
probe_steps, output_dir, seed, params, **kwargs) -> float:
|
|
262
|
+
raise NotImplementedError
|
|
263
|
+
|
|
264
|
+
def get_best_config_patch(self) -> Dict[str, Any]:
|
|
265
|
+
if not self.result or not self.result.best_params:
|
|
266
|
+
return {}
|
|
267
|
+
return dict(self.result.best_params)
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def _fmt(p: dict) -> str:
|
|
271
|
+
return ", ".join(
|
|
272
|
+
f"{k}={v:.2e}" if isinstance(v, float) else f"{k}={v}"
|
|
273
|
+
for k, v in p.items()
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def _gc_collect():
|
|
278
|
+
gc.collect()
|
|
279
|
+
try:
|
|
280
|
+
import torch
|
|
281
|
+
if torch.cuda.is_available():
|
|
282
|
+
torch.cuda.empty_cache()
|
|
283
|
+
except Exception:
|
|
284
|
+
pass
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
"""
|
|
2
|
+
chronos/tuning/chronos_auto_tuner.py — ChronosAutoTuner: λ1/λ2/lookahead Optuna search.
|
|
3
|
+
|
|
4
|
+
Extends the vendored AutoTuner base with Chronos-specific hyperparameters.
|
|
5
|
+
Uses a lightweight training loop (no external unsloth/trl dependency).
|
|
6
|
+
"""
|
|
7
|
+
import os
|
|
8
|
+
import time
|
|
9
|
+
import dataclasses
|
|
10
|
+
from dataclasses import dataclass, field
|
|
11
|
+
from typing import List
|
|
12
|
+
|
|
13
|
+
import chronos.deps # ensure minimind on sys.path
|
|
14
|
+
|
|
15
|
+
from chronos.tuning._base_tuner import (
|
|
16
|
+
AutoTuner, SearchSpaceConfig, AutoTuneResult, _gc_collect,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
import optuna
|
|
21
|
+
OPTUNA_AVAILABLE = True
|
|
22
|
+
except ImportError:
|
|
23
|
+
OPTUNA_AVAILABLE = False
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass
|
|
27
|
+
class ChronosSearchSpaceConfig(SearchSpaceConfig):
|
|
28
|
+
tune_lambda_balance: bool = True
|
|
29
|
+
lambda_balance_min: float = 1e-4
|
|
30
|
+
lambda_balance_max: float = 1e-2
|
|
31
|
+
|
|
32
|
+
tune_lambda_temporal: bool = True
|
|
33
|
+
lambda_temporal_min: float = 1e-4
|
|
34
|
+
lambda_temporal_max: float = 1e-2
|
|
35
|
+
|
|
36
|
+
tune_lookahead_steps: bool = False
|
|
37
|
+
lookahead_steps_choices: List[int] = field(default_factory=lambda: [1, 2])
|
|
38
|
+
|
|
39
|
+
tune_lr: bool = True
|
|
40
|
+
|
|
41
|
+
# Extended (UI fix 6): structural knobs that can also be searched.
|
|
42
|
+
tune_hidden_size: bool = False
|
|
43
|
+
hidden_size_choices: List[int] = field(default_factory=lambda: [128, 256, 384, 512])
|
|
44
|
+
|
|
45
|
+
tune_num_experts: bool = False
|
|
46
|
+
num_experts_choices: List[int] = field(default_factory=lambda: [2, 4, 6, 8])
|
|
47
|
+
|
|
48
|
+
tune_num_shared_experts: bool = False
|
|
49
|
+
num_shared_experts_choices: List[int] = field(default_factory=lambda: [0, 1, 2])
|
|
50
|
+
|
|
51
|
+
tune_kv_latent_dim: bool = False
|
|
52
|
+
kv_latent_dim_choices: List[int] = field(default_factory=lambda: [32, 64, 96, 128])
|
|
53
|
+
|
|
54
|
+
tune_lambda_lookahead: bool = False
|
|
55
|
+
lambda_lookahead_min: float = 1e-3
|
|
56
|
+
lambda_lookahead_max: float = 1.0
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class ChronosAutoTuner(AutoTuner):
|
|
60
|
+
"""
|
|
61
|
+
Extends AutoTuner to search over Chronos-specific hyperparameters
|
|
62
|
+
(λ1, λ2, lookahead_steps) using a lightweight native training loop
|
|
63
|
+
(no trl/unsloth dependency required).
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def _sample_params(self, trial, ss) -> dict:
|
|
67
|
+
params = {}
|
|
68
|
+
|
|
69
|
+
if ss.tune_lr:
|
|
70
|
+
params["learning_rate"] = trial.suggest_float(
|
|
71
|
+
"learning_rate", ss.lr_min, ss.lr_max, log=True
|
|
72
|
+
)
|
|
73
|
+
else:
|
|
74
|
+
params["learning_rate"] = 2e-4
|
|
75
|
+
|
|
76
|
+
if isinstance(ss, ChronosSearchSpaceConfig):
|
|
77
|
+
if ss.tune_lambda_balance:
|
|
78
|
+
params["lambda_balance"] = trial.suggest_float(
|
|
79
|
+
"lambda_balance", ss.lambda_balance_min, ss.lambda_balance_max, log=True
|
|
80
|
+
)
|
|
81
|
+
else:
|
|
82
|
+
params["lambda_balance"] = 5e-4
|
|
83
|
+
|
|
84
|
+
if ss.tune_lambda_temporal:
|
|
85
|
+
params["lambda_temporal"] = trial.suggest_float(
|
|
86
|
+
"lambda_temporal", ss.lambda_temporal_min, ss.lambda_temporal_max, log=True
|
|
87
|
+
)
|
|
88
|
+
else:
|
|
89
|
+
params["lambda_temporal"] = 1e-3
|
|
90
|
+
|
|
91
|
+
if ss.tune_lookahead_steps:
|
|
92
|
+
params["lookahead_steps"] = trial.suggest_categorical(
|
|
93
|
+
"lookahead_steps", ss.lookahead_steps_choices
|
|
94
|
+
)
|
|
95
|
+
else:
|
|
96
|
+
params["lookahead_steps"] = 2
|
|
97
|
+
|
|
98
|
+
# Extended (UI fix 6) — categorical structural knobs
|
|
99
|
+
if ss.tune_hidden_size:
|
|
100
|
+
params["hidden_size"] = trial.suggest_categorical(
|
|
101
|
+
"hidden_size", ss.hidden_size_choices)
|
|
102
|
+
if ss.tune_num_experts:
|
|
103
|
+
params["num_experts"] = trial.suggest_categorical(
|
|
104
|
+
"num_experts", ss.num_experts_choices)
|
|
105
|
+
if ss.tune_num_shared_experts:
|
|
106
|
+
params["num_shared_experts"] = trial.suggest_categorical(
|
|
107
|
+
"num_shared_experts", ss.num_shared_experts_choices)
|
|
108
|
+
if ss.tune_kv_latent_dim:
|
|
109
|
+
params["kv_latent_dim"] = trial.suggest_categorical(
|
|
110
|
+
"kv_latent_dim", ss.kv_latent_dim_choices)
|
|
111
|
+
if ss.tune_lambda_lookahead:
|
|
112
|
+
params["lambda_lookahead"] = trial.suggest_float(
|
|
113
|
+
"lambda_lookahead", ss.lambda_lookahead_min, ss.lambda_lookahead_max, log=True)
|
|
114
|
+
else:
|
|
115
|
+
params["lambda_balance"] = 5e-4
|
|
116
|
+
params["lambda_temporal"] = 1e-3
|
|
117
|
+
params["lookahead_steps"] = 2
|
|
118
|
+
|
|
119
|
+
return params
|
|
120
|
+
|
|
121
|
+
def _run_probe_trial(
|
|
122
|
+
self, trial, model_id, dataset_path, train_ratio,
|
|
123
|
+
probe_steps, output_dir, seed, params, **kwargs
|
|
124
|
+
) -> float:
|
|
125
|
+
import torch
|
|
126
|
+
from chronos.backend import resolve_training_device
|
|
127
|
+
from chronos.model.config import ChronosConfig
|
|
128
|
+
from chronos.model.model_chronos import ChronosForCausalLM
|
|
129
|
+
from chronos.model.temporal_loss import total_loss
|
|
130
|
+
from chronos.model.moe_chronos import ChronosMOEFeedForward
|
|
131
|
+
|
|
132
|
+
_, device = resolve_training_device("auto")
|
|
133
|
+
lr = params.get("learning_rate", 2e-4)
|
|
134
|
+
lb = params.get("lambda_balance", 5e-4)
|
|
135
|
+
lt = params.get("lambda_temporal", 1e-3)
|
|
136
|
+
la = params.get("lookahead_steps", 2)
|
|
137
|
+
hs = int(params.get("hidden_size", 256))
|
|
138
|
+
ne = int(params.get("num_experts", 4))
|
|
139
|
+
ns = int(params.get("num_shared_experts", 1))
|
|
140
|
+
kv = int(params.get("kv_latent_dim", 64))
|
|
141
|
+
|
|
142
|
+
cfg = ChronosConfig(
|
|
143
|
+
hidden_size=hs,
|
|
144
|
+
num_hidden_layers=4,
|
|
145
|
+
num_experts=ne,
|
|
146
|
+
num_shared_experts=ns,
|
|
147
|
+
kv_latent_dim=kv,
|
|
148
|
+
lookahead_steps=la,
|
|
149
|
+
lambda_balance=lb,
|
|
150
|
+
lambda_temporal=lt,
|
|
151
|
+
use_hybrid_attention=True,
|
|
152
|
+
use_moe=True,
|
|
153
|
+
)
|
|
154
|
+
model = ChronosForCausalLM(cfg).to(device)
|
|
155
|
+
if os.path.exists(model_id):
|
|
156
|
+
try:
|
|
157
|
+
w = torch.load(model_id, map_location="cpu")
|
|
158
|
+
model.load_state_dict(w, strict=False)
|
|
159
|
+
except Exception:
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
optimizer = torch.optim.AdamW(model.parameters(), lr=lr)
|
|
163
|
+
vocab_size = cfg.vocab_size
|
|
164
|
+
seq_len = 64
|
|
165
|
+
batch = 2
|
|
166
|
+
model.train()
|
|
167
|
+
losses = []
|
|
168
|
+
for step in range(probe_steps):
|
|
169
|
+
ids = torch.randint(0, vocab_size, (batch, seq_len), device=device)
|
|
170
|
+
out, _ = model(ids, labels=ids)
|
|
171
|
+
moe_layers = [l.mlp for l in model.model.layers
|
|
172
|
+
if isinstance(l.mlp, ChronosMOEFeedForward)]
|
|
173
|
+
if moe_layers and moe_layers[0].last_router_probs is not None:
|
|
174
|
+
probs = torch.stack(
|
|
175
|
+
[l.last_router_probs for l in moe_layers], dim=2
|
|
176
|
+
).mean(dim=2)
|
|
177
|
+
loss = total_loss(out.loss, out.aux_loss, probs, lb, lt)
|
|
178
|
+
else:
|
|
179
|
+
loss = out.loss + out.aux_loss
|
|
180
|
+
|
|
181
|
+
loss.backward()
|
|
182
|
+
optimizer.step()
|
|
183
|
+
optimizer.zero_grad(set_to_none=True)
|
|
184
|
+
losses.append(loss.item())
|
|
185
|
+
trial.report(loss.item(), step=step)
|
|
186
|
+
if trial.should_prune():
|
|
187
|
+
del model
|
|
188
|
+
_gc_collect()
|
|
189
|
+
raise optuna.exceptions.TrialPruned()
|
|
190
|
+
|
|
191
|
+
final_loss = sum(losses[-max(1, probe_steps // 5):]) / max(1, probe_steps // 5)
|
|
192
|
+
del model
|
|
193
|
+
_gc_collect()
|
|
194
|
+
return final_loss
|
|
195
|
+
|
|
196
|
+
def get_best_chronos_config_patch(self) -> dict:
|
|
197
|
+
patch = self.get_best_config_patch()
|
|
198
|
+
if self.result and self.result.best_params:
|
|
199
|
+
p = self.result.best_params
|
|
200
|
+
for key in ("lambda_balance", "lambda_temporal", "lookahead_steps"):
|
|
201
|
+
if key in p:
|
|
202
|
+
patch[key] = p[key]
|
|
203
|
+
return patch
|