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
ui/tabs/config_tab.py
ADDED
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ui/tabs/config_tab.py — ChronosConfig editor + live param estimator sidebar.
|
|
3
|
+
|
|
4
|
+
Merges the former Designer tab: the architecture sliders here are the
|
|
5
|
+
single source of truth; the right-hand "Estimator" panel reads the same
|
|
6
|
+
state and displays parameter counts, memory footprints and estimated
|
|
7
|
+
decode tokens/sec in real time.
|
|
8
|
+
|
|
9
|
+
Data-path is intentionally NOT in this tab — it lives in the Train tab
|
|
10
|
+
(and is per-stage in the Pipeline tab) so each workflow owns its own
|
|
11
|
+
dataset choice. Only save_dir is shared model-wide.
|
|
12
|
+
"""
|
|
13
|
+
from ui.gradio_compat import gr
|
|
14
|
+
|
|
15
|
+
from ui.i18n import t, register_translatable
|
|
16
|
+
from ui.estimator import (
|
|
17
|
+
ArchConfig, total_params, active_params, memory_footprint,
|
|
18
|
+
estimated_decode_tps, fmt_bytes, fmt_params,
|
|
19
|
+
)
|
|
20
|
+
from ui.presets import (
|
|
21
|
+
preset_names, get_preset, values_in_input_order, save_config, load_config,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Shared slider ranges (match Designer v1, which had the more permissive
|
|
26
|
+
# bounds). The old Config tab had stricter bounds that surprised users
|
|
27
|
+
# toggling between tabs and rejected values they typed manually.
|
|
28
|
+
RANGES = {
|
|
29
|
+
"hidden_size": (8, 16384, 8),
|
|
30
|
+
"num_hidden_layers": (1, 128, 1),
|
|
31
|
+
"num_experts": (1, 1024, 1),
|
|
32
|
+
"experts_per_tok": (1, 32, 1),
|
|
33
|
+
"shared_experts": (0, 32, 1),
|
|
34
|
+
"lookahead_steps": (0, 16, 1),
|
|
35
|
+
"kv_latent_dim": (4, 1024, 4),
|
|
36
|
+
"rope_dim": (2, 256, 2),
|
|
37
|
+
"sliding_window": (8, 65536, 8),
|
|
38
|
+
"moe_intermediate": (8, 65536, 8),
|
|
39
|
+
"vocab_size": (32, 200000, 32),
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _estimate(cfg_dict: dict):
|
|
44
|
+
"""Compute estimator outputs from a config dict.
|
|
45
|
+
|
|
46
|
+
A moe_intermediate_size of 0 (or missing) is treated as "auto-derive
|
|
47
|
+
from hidden_size", matching MiniMindConfig's default rule. This keeps
|
|
48
|
+
the estimator consistent with what the trainer actually builds.
|
|
49
|
+
"""
|
|
50
|
+
mis = int(cfg_dict.get("moe_intermediate_size", 0) or 0)
|
|
51
|
+
its = int(cfg_dict.get("intermediate_size", 0) or 0)
|
|
52
|
+
arch = ArchConfig(
|
|
53
|
+
hidden_size=int(cfg_dict.get("hidden_size", 256)),
|
|
54
|
+
num_hidden_layers=int(cfg_dict.get("num_hidden_layers", 4)),
|
|
55
|
+
num_attention_heads=int(cfg_dict.get("num_attention_heads", 8)),
|
|
56
|
+
num_key_value_heads=int(cfg_dict.get("num_key_value_heads", 4)),
|
|
57
|
+
vocab_size=int(cfg_dict.get("vocab_size", 6400)),
|
|
58
|
+
intermediate_size=its if its > 0 else None,
|
|
59
|
+
moe_intermediate_size=mis if mis > 0 else None,
|
|
60
|
+
num_experts=int(cfg_dict.get("num_experts", 4)),
|
|
61
|
+
num_experts_per_tok=int(cfg_dict.get("num_experts_per_tok", 1)),
|
|
62
|
+
num_shared_experts=int(cfg_dict.get("num_shared_experts", 1)),
|
|
63
|
+
lookahead_steps=int(cfg_dict.get("lookahead_steps", 2)),
|
|
64
|
+
kv_latent_dim=int(cfg_dict.get("kv_latent_dim", 64)),
|
|
65
|
+
rope_dim=int(cfg_dict.get("rope_dim", 32)),
|
|
66
|
+
sliding_window_size=int(cfg_dict.get("sliding_window_size", 2048)),
|
|
67
|
+
use_hybrid_attention=bool(cfg_dict.get("use_hybrid_attention", True)),
|
|
68
|
+
tie_word_embeddings=bool(cfg_dict.get("tie_word_embeddings", True)),
|
|
69
|
+
dtype=str(cfg_dict.get("dtype", "fp16")),
|
|
70
|
+
)
|
|
71
|
+
mem = memory_footprint(arch)
|
|
72
|
+
return (
|
|
73
|
+
fmt_params(total_params(arch)),
|
|
74
|
+
fmt_params(active_params(arch)),
|
|
75
|
+
fmt_bytes(mem["vram_estimate_bytes"]),
|
|
76
|
+
fmt_bytes(mem["ssd_estimate_bytes"]),
|
|
77
|
+
fmt_bytes(mem["kv_cache_bytes"]),
|
|
78
|
+
f"{estimated_decode_tps(arch):.1f}",
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def build_config_tab():
|
|
83
|
+
"""Returns (config_state, list_of_input_components, save_dir).
|
|
84
|
+
|
|
85
|
+
Note: the pre-M6 signature returned (state, inputs, data_path, save_dir).
|
|
86
|
+
data_path has been moved to the Train tab, so the tuple now has 3
|
|
87
|
+
elements. Callers must be updated.
|
|
88
|
+
"""
|
|
89
|
+
initial_preset = "Recommended-CN (≈120M)"
|
|
90
|
+
initial_cfg = get_preset(initial_preset)
|
|
91
|
+
config_state = gr.State(dict(initial_cfg))
|
|
92
|
+
|
|
93
|
+
with gr.Tab(t("tab.config")) as tab:
|
|
94
|
+
register_translatable(tab, "tab.config")
|
|
95
|
+
|
|
96
|
+
# ── Presets / Save / Load (top of tab) ─────────────────
|
|
97
|
+
with gr.Row():
|
|
98
|
+
preset_dd = gr.Dropdown(
|
|
99
|
+
choices=preset_names(),
|
|
100
|
+
value=initial_preset,
|
|
101
|
+
label=t("config.preset"),
|
|
102
|
+
scale=2,
|
|
103
|
+
)
|
|
104
|
+
register_translatable(preset_dd, "config.preset")
|
|
105
|
+
apply_preset_btn = gr.Button(t("config.load_preset"), variant="secondary", scale=1)
|
|
106
|
+
register_translatable(apply_preset_btn, "config.load_preset")
|
|
107
|
+
reset_btn = gr.Button(t("config.reset_minimind"), variant="secondary", scale=1)
|
|
108
|
+
register_translatable(reset_btn, "config.reset_minimind")
|
|
109
|
+
with gr.Row():
|
|
110
|
+
cfg_path = gr.Textbox(
|
|
111
|
+
value="./out/chronos_config.json",
|
|
112
|
+
label=t("config.cfg_path"),
|
|
113
|
+
scale=3,
|
|
114
|
+
)
|
|
115
|
+
register_translatable(cfg_path, "config.cfg_path")
|
|
116
|
+
save_btn = gr.Button(t("config.save_cfg"), variant="primary", scale=1)
|
|
117
|
+
register_translatable(save_btn, "config.save_cfg")
|
|
118
|
+
load_btn = gr.Button(t("config.load_cfg"), variant="primary", scale=1)
|
|
119
|
+
register_translatable(load_btn, "config.load_cfg")
|
|
120
|
+
save_status = gr.Markdown("", visible=True)
|
|
121
|
+
|
|
122
|
+
with gr.Row():
|
|
123
|
+
with gr.Column(scale=3):
|
|
124
|
+
gr.Markdown(f"### {t('config.arch')}")
|
|
125
|
+
|
|
126
|
+
D = initial_cfg
|
|
127
|
+
|
|
128
|
+
with gr.Row():
|
|
129
|
+
hidden_size = gr.Slider(*RANGES["hidden_size"], value=D["hidden_size"], label=t("config.hidden_size"))
|
|
130
|
+
num_layers = gr.Slider(*RANGES["num_hidden_layers"], value=D["num_hidden_layers"], label=t("config.num_layers"))
|
|
131
|
+
num_experts = gr.Slider(*RANGES["num_experts"], value=D["num_experts"], label=t("config.num_experts"))
|
|
132
|
+
experts_per_tok = gr.Slider(*RANGES["experts_per_tok"], value=D["num_experts_per_tok"], label=t("config.experts_per_tok"))
|
|
133
|
+
register_translatable(hidden_size, "config.hidden_size")
|
|
134
|
+
register_translatable(num_layers, "config.num_layers")
|
|
135
|
+
register_translatable(num_experts, "config.num_experts")
|
|
136
|
+
register_translatable(experts_per_tok, "config.experts_per_tok")
|
|
137
|
+
|
|
138
|
+
with gr.Row():
|
|
139
|
+
num_shared = gr.Slider(*RANGES["shared_experts"], value=D["num_shared_experts"], label=t("config.shared_experts"))
|
|
140
|
+
lookahead = gr.Slider(*RANGES["lookahead_steps"], value=D["lookahead_steps"], label=t("config.lookahead"))
|
|
141
|
+
kv_latent_dim = gr.Slider(*RANGES["kv_latent_dim"], value=D["kv_latent_dim"], label=t("config.kv_latent"))
|
|
142
|
+
sliding_window = gr.Slider(*RANGES["sliding_window"], value=D["sliding_window_size"], label=t("config.sliding_window"))
|
|
143
|
+
register_translatable(num_shared, "config.shared_experts")
|
|
144
|
+
register_translatable(lookahead, "config.lookahead")
|
|
145
|
+
register_translatable(kv_latent_dim, "config.kv_latent")
|
|
146
|
+
register_translatable(sliding_window, "config.sliding_window")
|
|
147
|
+
|
|
148
|
+
with gr.Row():
|
|
149
|
+
num_heads = gr.Slider(1, 256, value=D["num_attention_heads"], step=1, label=t("config.num_attn_heads"))
|
|
150
|
+
num_kv_heads = gr.Slider(1, 256, value=D["num_key_value_heads"], step=1, label=t("config.num_kv_heads"))
|
|
151
|
+
rope_dim = gr.Slider(*RANGES["rope_dim"], value=D["rope_dim"], label=t("config.rope_dim"))
|
|
152
|
+
moe_intermediate = gr.Slider(
|
|
153
|
+
0, 65536, value=D["moe_intermediate_size"], step=8,
|
|
154
|
+
label=t("config.moe_inter"),
|
|
155
|
+
)
|
|
156
|
+
register_translatable(num_heads, "config.num_attn_heads")
|
|
157
|
+
register_translatable(num_kv_heads, "config.num_kv_heads")
|
|
158
|
+
register_translatable(rope_dim, "config.rope_dim")
|
|
159
|
+
register_translatable(moe_intermediate, "config.moe_inter")
|
|
160
|
+
|
|
161
|
+
with gr.Row():
|
|
162
|
+
vocab_size = gr.Number(value=D["vocab_size"], precision=0, label=t("config.vocab_size"))
|
|
163
|
+
dtype = gr.Dropdown(
|
|
164
|
+
choices=["fp32", "bf16", "fp16", "int8", "nf4"],
|
|
165
|
+
value=D["dtype"], label=t("config.dtype"),
|
|
166
|
+
)
|
|
167
|
+
tie_word_embeddings = gr.Checkbox(value=D["tie_word_embeddings"], label=t("config.tie_emb"))
|
|
168
|
+
register_translatable(vocab_size, "config.vocab_size")
|
|
169
|
+
register_translatable(dtype, "config.dtype")
|
|
170
|
+
register_translatable(tie_word_embeddings, "config.tie_emb")
|
|
171
|
+
|
|
172
|
+
gr.Markdown(f"### {t('config.loss')}")
|
|
173
|
+
with gr.Row():
|
|
174
|
+
lambda_balance = gr.Number(value=D["lambda_balance"], label=t("config.lambda_balance"), precision=6)
|
|
175
|
+
lambda_temporal = gr.Number(value=D["lambda_temporal"], label=t("config.lambda_temporal"), precision=6)
|
|
176
|
+
lambda_lookahead = gr.Number(value=D["lambda_lookahead"], label=t("config.lambda_la"), precision=6)
|
|
177
|
+
lambda_router_anchor = gr.Number(value=D["lambda_router_anchor"], label=t("config.lambda_anchor"), precision=6)
|
|
178
|
+
register_translatable(lambda_balance, "config.lambda_balance")
|
|
179
|
+
register_translatable(lambda_temporal, "config.lambda_temporal")
|
|
180
|
+
register_translatable(lambda_lookahead, "config.lambda_la")
|
|
181
|
+
register_translatable(lambda_router_anchor, "config.lambda_anchor")
|
|
182
|
+
|
|
183
|
+
gr.Markdown(f"### {t('config.hw')}")
|
|
184
|
+
with gr.Row():
|
|
185
|
+
vram_budget = gr.Slider(0.01, 256.0, value=D["vram_budget_gb"], step=0.01, label=t("config.vram_budget"))
|
|
186
|
+
pinned_frac = gr.Slider(0.01, 0.95, value=D["pinned_memory_max_fraction"], step=0.01, label=t("config.pinned_frac"))
|
|
187
|
+
use_hybrid_attn = gr.Checkbox(value=D["use_hybrid_attention"], label=t("config.hybrid_attn"))
|
|
188
|
+
storage_format = gr.Dropdown(choices=["safetensors", "pt"], value=D["storage_format"],
|
|
189
|
+
label=t("config.storage_format"))
|
|
190
|
+
register_translatable(vram_budget, "config.vram_budget")
|
|
191
|
+
register_translatable(pinned_frac, "config.pinned_frac")
|
|
192
|
+
register_translatable(use_hybrid_attn, "config.hybrid_attn")
|
|
193
|
+
register_translatable(storage_format, "config.storage_format")
|
|
194
|
+
|
|
195
|
+
gr.Markdown(f"### {t('config.train')}")
|
|
196
|
+
with gr.Row():
|
|
197
|
+
learning_rate = gr.Number(value=D["learning_rate"], label=t("config.lr"), precision=6)
|
|
198
|
+
batch_size = gr.Slider(1, 512, value=D["batch_size"], step=1, label=t("config.batch_size"))
|
|
199
|
+
accum_steps = gr.Slider(1, 256, value=D["accumulation_steps"], step=1, label=t("config.accum"))
|
|
200
|
+
max_seq_len = gr.Slider(8, 131072, value=D["max_seq_len"], step=8, label=t("config.max_seq_len"))
|
|
201
|
+
register_translatable(learning_rate, "config.lr")
|
|
202
|
+
register_translatable(batch_size, "config.batch_size")
|
|
203
|
+
register_translatable(accum_steps, "config.accum")
|
|
204
|
+
register_translatable(max_seq_len, "config.max_seq_len")
|
|
205
|
+
|
|
206
|
+
with gr.Row():
|
|
207
|
+
epochs = gr.Slider(1, 1000, value=D["epochs"], step=1, label=t("config.epochs"))
|
|
208
|
+
save_interval = gr.Slider(10, 100000, value=D["save_interval"], step=10, label=t("config.save_interval"))
|
|
209
|
+
save_dir = gr.Textbox(label=t("config.save_dir"), value=D["save_dir"])
|
|
210
|
+
register_translatable(epochs, "config.epochs")
|
|
211
|
+
register_translatable(save_interval, "config.save_interval")
|
|
212
|
+
register_translatable(save_dir, "config.save_dir")
|
|
213
|
+
|
|
214
|
+
config_display = gr.JSON(label="Current Config (saved to config_state)", value=initial_cfg)
|
|
215
|
+
|
|
216
|
+
with gr.Column(scale=1, min_width=260):
|
|
217
|
+
gr.Markdown(f"### 🧬 {t('designer.title')}")
|
|
218
|
+
total_box = gr.Textbox(label=t("designer.total"), interactive=False)
|
|
219
|
+
active_box = gr.Textbox(label=t("designer.active"), interactive=False)
|
|
220
|
+
vram_box = gr.Textbox(label=t("designer.vram"), interactive=False)
|
|
221
|
+
ssd_box = gr.Textbox(label=t("designer.ssd"), interactive=False)
|
|
222
|
+
kv_box = gr.Textbox(label=t("designer.kv"), interactive=False)
|
|
223
|
+
tps_box = gr.Textbox(label=t("designer.tps"), interactive=False)
|
|
224
|
+
register_translatable(total_box, "designer.total")
|
|
225
|
+
register_translatable(active_box, "designer.active")
|
|
226
|
+
register_translatable(vram_box, "designer.vram")
|
|
227
|
+
register_translatable(ssd_box, "designer.ssd")
|
|
228
|
+
register_translatable(kv_box, "designer.kv")
|
|
229
|
+
register_translatable(tps_box, "designer.tps")
|
|
230
|
+
|
|
231
|
+
# IMPORTANT: this list also defines the order Autotune uses to push
|
|
232
|
+
# best-params back via gr.update(). Don't reorder without updating
|
|
233
|
+
# ui/tabs/autotune_tab.py.
|
|
234
|
+
all_inputs = [
|
|
235
|
+
hidden_size, num_layers, num_experts, experts_per_tok,
|
|
236
|
+
num_shared, lookahead, kv_latent_dim, sliding_window,
|
|
237
|
+
num_heads, num_kv_heads, rope_dim, moe_intermediate,
|
|
238
|
+
vocab_size, dtype, tie_word_embeddings,
|
|
239
|
+
lambda_balance, lambda_temporal, lambda_lookahead, lambda_router_anchor,
|
|
240
|
+
vram_budget, pinned_frac, use_hybrid_attn, storage_format,
|
|
241
|
+
learning_rate, batch_size, accum_steps, max_seq_len,
|
|
242
|
+
epochs, save_interval, save_dir,
|
|
243
|
+
]
|
|
244
|
+
estimator_outs = [total_box, active_box, vram_box, ssd_box, kv_box, tps_box]
|
|
245
|
+
|
|
246
|
+
def update_config(*vals):
|
|
247
|
+
(hs, nl, ne, ept, ns, la, kv, sw,
|
|
248
|
+
nh, nkv, rd, moe_i,
|
|
249
|
+
vs, dt, tie,
|
|
250
|
+
lb, lt, ll, lra,
|
|
251
|
+
vb, pf, ha, sf,
|
|
252
|
+
lr, bs, ac, msl, ep, si, sd) = vals
|
|
253
|
+
cfg = {
|
|
254
|
+
"hidden_size": int(hs), "num_hidden_layers": int(nl),
|
|
255
|
+
"num_experts": int(ne), "num_experts_per_tok": int(ept),
|
|
256
|
+
"num_shared_experts": int(ns), "lookahead_steps": int(la),
|
|
257
|
+
"kv_latent_dim": int(kv), "sliding_window_size": int(sw),
|
|
258
|
+
"num_attention_heads": int(nh), "num_key_value_heads": int(nkv),
|
|
259
|
+
"rope_dim": int(rd), "moe_intermediate_size": int(moe_i),
|
|
260
|
+
"vocab_size": int(vs), "dtype": str(dt),
|
|
261
|
+
"tie_word_embeddings": bool(tie),
|
|
262
|
+
"lambda_balance": float(lb), "lambda_temporal": float(lt),
|
|
263
|
+
"lambda_lookahead": float(ll), "lambda_router_anchor": float(lra),
|
|
264
|
+
"vram_budget_gb": float(vb), "pinned_memory_max_fraction": float(pf),
|
|
265
|
+
"use_hybrid_attention": bool(ha),
|
|
266
|
+
"storage_format": str(sf),
|
|
267
|
+
"learning_rate": float(lr), "batch_size": int(bs),
|
|
268
|
+
"accumulation_steps": int(ac), "max_seq_len": int(msl),
|
|
269
|
+
"epochs": int(ep), "save_interval": int(si),
|
|
270
|
+
"save_dir": sd,
|
|
271
|
+
}
|
|
272
|
+
est = _estimate(cfg)
|
|
273
|
+
return (cfg, cfg) + est
|
|
274
|
+
|
|
275
|
+
for inp in all_inputs:
|
|
276
|
+
inp.change(
|
|
277
|
+
fn=update_config,
|
|
278
|
+
inputs=all_inputs,
|
|
279
|
+
outputs=[config_state, config_display] + estimator_outs,
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
# ── Preset / Save / Load wiring ───────────────────────
|
|
283
|
+
def apply_preset(name):
|
|
284
|
+
cfg = get_preset(name)
|
|
285
|
+
return [dict(cfg), dict(cfg)] + [gr.update(value=v) for v in values_in_input_order(cfg)]
|
|
286
|
+
|
|
287
|
+
def reset_minimind():
|
|
288
|
+
cfg = get_preset("MiniMind-MoE (default)")
|
|
289
|
+
return [dict(cfg), dict(cfg)] + [gr.update(value=v) for v in values_in_input_order(cfg)]
|
|
290
|
+
|
|
291
|
+
def do_save(cfg, path):
|
|
292
|
+
try:
|
|
293
|
+
abs_path = save_config(cfg or {}, path)
|
|
294
|
+
return f"✅ Saved to `{abs_path}`"
|
|
295
|
+
except Exception as e:
|
|
296
|
+
return f"❌ Save failed: {e}"
|
|
297
|
+
|
|
298
|
+
def do_load(path):
|
|
299
|
+
try:
|
|
300
|
+
cfg = load_config(path)
|
|
301
|
+
vals = values_in_input_order(cfg)
|
|
302
|
+
return [f"✅ Loaded from `{path}`", dict(cfg), dict(cfg)] + [gr.update(value=v) for v in vals]
|
|
303
|
+
except FileNotFoundError:
|
|
304
|
+
return [f"❌ Not found: `{path}`", gr.update(), gr.update()] + [gr.update() for _ in all_inputs]
|
|
305
|
+
except Exception as e:
|
|
306
|
+
return [f"❌ Load failed: {e}", gr.update(), gr.update()] + [gr.update() for _ in all_inputs]
|
|
307
|
+
|
|
308
|
+
apply_preset_btn.click(fn=apply_preset, inputs=[preset_dd], outputs=[config_state, config_display] + all_inputs)
|
|
309
|
+
# Selecting a preset in the dropdown should sync immediately;
|
|
310
|
+
# the explicit "Load Preset" button stays as a re-apply affordance.
|
|
311
|
+
preset_dd.change(fn=apply_preset, inputs=[preset_dd], outputs=[config_state, config_display] + all_inputs)
|
|
312
|
+
reset_btn.click(fn=reset_minimind, outputs=[config_state, config_display] + all_inputs)
|
|
313
|
+
save_btn.click(fn=do_save, inputs=[config_state, cfg_path], outputs=[save_status])
|
|
314
|
+
load_btn.click(fn=do_load, inputs=[cfg_path], outputs=[save_status, config_state, config_display] + all_inputs)
|
|
315
|
+
|
|
316
|
+
total_box.value, active_box.value, vram_box.value, ssd_box.value, kv_box.value, tps_box.value = _estimate(initial_cfg)
|
|
317
|
+
|
|
318
|
+
return config_state, all_inputs, save_dir
|
ui/tabs/data_tab.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ui/tabs/data_tab.py — Dataset inspector.
|
|
3
|
+
|
|
4
|
+
Streams a JSONL file (no full-load) and reports:
|
|
5
|
+
- record count
|
|
6
|
+
- char-length distribution (min / mean / median / p95 / max)
|
|
7
|
+
- tokenized-length distribution if a tokenizer is available
|
|
8
|
+
- unique-record ratio (cheap dedup heuristic via line hash)
|
|
9
|
+
- first ~3 sample previews
|
|
10
|
+
- matplotlib histogram of token lengths
|
|
11
|
+
|
|
12
|
+
The point: before training, the user can verify the corpus is what they
|
|
13
|
+
expect — right field name, sane lengths, low duplication. Catches
|
|
14
|
+
"wrong tokenizer" / "all records empty" / "huge dup ratio" before they
|
|
15
|
+
burn 10 hours of training.
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import hashlib
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import statistics
|
|
23
|
+
|
|
24
|
+
from ui.gradio_compat import gr
|
|
25
|
+
|
|
26
|
+
import chronos.deps # noqa: F401
|
|
27
|
+
from ui.i18n import t, register_translatable
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _extract_text(rec: dict) -> str:
|
|
31
|
+
for k in ("text", "content", "story", "passage", "document", "article"):
|
|
32
|
+
if k in rec and rec[k]:
|
|
33
|
+
return str(rec[k])
|
|
34
|
+
if "conversations" in rec and isinstance(rec["conversations"], list):
|
|
35
|
+
return " ".join(str(c.get("content", c.get("value", ""))) for c in rec["conversations"])
|
|
36
|
+
if "messages" in rec and isinstance(rec["messages"], list):
|
|
37
|
+
return " ".join(str(m.get("content", "")) for m in rec["messages"])
|
|
38
|
+
return " ".join(str(v) for v in rec.values()
|
|
39
|
+
if isinstance(v, (str, int, float)))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _scan(path: str, max_records: int = 50_000):
|
|
43
|
+
"""Stream up to `max_records` lines; return summary dict + length list."""
|
|
44
|
+
if not path or not os.path.exists(path):
|
|
45
|
+
return {"error": f"file not found: {path!r}"}, [], []
|
|
46
|
+
char_lens = []
|
|
47
|
+
samples = []
|
|
48
|
+
seen_hashes = set()
|
|
49
|
+
dup = 0
|
|
50
|
+
n_total = 0
|
|
51
|
+
with open(path, "rb") as f:
|
|
52
|
+
for i, line in enumerate(f):
|
|
53
|
+
if i >= max_records:
|
|
54
|
+
break
|
|
55
|
+
line = line.strip()
|
|
56
|
+
if not line:
|
|
57
|
+
continue
|
|
58
|
+
n_total += 1
|
|
59
|
+
try:
|
|
60
|
+
rec = json.loads(line)
|
|
61
|
+
except json.JSONDecodeError:
|
|
62
|
+
continue
|
|
63
|
+
text = _extract_text(rec)
|
|
64
|
+
char_lens.append(len(text))
|
|
65
|
+
h = hashlib.md5(line).hexdigest()
|
|
66
|
+
if h in seen_hashes:
|
|
67
|
+
dup += 1
|
|
68
|
+
else:
|
|
69
|
+
seen_hashes.add(h)
|
|
70
|
+
if len(samples) < 3:
|
|
71
|
+
samples.append(text[:200])
|
|
72
|
+
if not char_lens:
|
|
73
|
+
return {"error": "no parseable records"}, [], samples
|
|
74
|
+
|
|
75
|
+
summary = {
|
|
76
|
+
"scanned": n_total,
|
|
77
|
+
"parseable": len(char_lens),
|
|
78
|
+
"dup_ratio": round(dup / max(n_total, 1), 4),
|
|
79
|
+
"char_min": min(char_lens),
|
|
80
|
+
"char_mean": round(statistics.mean(char_lens), 1),
|
|
81
|
+
"char_median": int(statistics.median(char_lens)),
|
|
82
|
+
"char_p95": int(sorted(char_lens)[int(0.95 * len(char_lens))]),
|
|
83
|
+
"char_max": max(char_lens),
|
|
84
|
+
"file_size_mb": round(os.path.getsize(path) / (1024 * 1024), 1),
|
|
85
|
+
"note": (f"sampled first {max_records} records"
|
|
86
|
+
if n_total >= max_records else "full scan"),
|
|
87
|
+
}
|
|
88
|
+
return summary, char_lens, samples
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _hist(lens):
|
|
92
|
+
if not lens:
|
|
93
|
+
return None
|
|
94
|
+
try:
|
|
95
|
+
import matplotlib
|
|
96
|
+
matplotlib.use("Agg")
|
|
97
|
+
import matplotlib.pyplot as plt
|
|
98
|
+
except ModuleNotFoundError:
|
|
99
|
+
return None
|
|
100
|
+
|
|
101
|
+
plt.close("all")
|
|
102
|
+
fig, ax = plt.subplots(figsize=(7, 3))
|
|
103
|
+
fig.patch.set_facecolor("#1a1a2e")
|
|
104
|
+
ax.set_facecolor("#16213e")
|
|
105
|
+
ax.hist(lens, bins=40, color="#39d98a", edgecolor="#1a1a2e")
|
|
106
|
+
ax.set_xlabel("chars per record", color="#aaa")
|
|
107
|
+
ax.set_ylabel("frequency", color="#aaa")
|
|
108
|
+
ax.tick_params(colors="#aaa")
|
|
109
|
+
for s in ax.spines.values():
|
|
110
|
+
s.set_edgecolor("#333")
|
|
111
|
+
return fig
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def build_data_tab():
|
|
115
|
+
with gr.Tab(t("tab.data")) as tab:
|
|
116
|
+
register_translatable(tab, "tab.data")
|
|
117
|
+
gr.Markdown(f"### {t('data.title')}")
|
|
118
|
+
|
|
119
|
+
with gr.Row():
|
|
120
|
+
data_path = gr.Textbox(
|
|
121
|
+
label=t("data.path"), scale=4,
|
|
122
|
+
placeholder="/Users/Fona/Downloads/Hybrid_LLM/Dataset/pretrain_t2t_mini.jsonl",
|
|
123
|
+
)
|
|
124
|
+
scan_btn = gr.Button(t("data.scan"), variant="primary", scale=1)
|
|
125
|
+
register_translatable(data_path, "data.path")
|
|
126
|
+
register_translatable(scan_btn, "data.scan")
|
|
127
|
+
|
|
128
|
+
summary_box = gr.JSON(label=t("data.summary"), value={})
|
|
129
|
+
register_translatable(summary_box, "data.summary")
|
|
130
|
+
|
|
131
|
+
preview_box = gr.Textbox(
|
|
132
|
+
label=t("data.preview"), lines=6, interactive=False, value="",
|
|
133
|
+
)
|
|
134
|
+
register_translatable(preview_box, "data.preview")
|
|
135
|
+
|
|
136
|
+
hist_plot = gr.Plot(label=t("data.length_hist"))
|
|
137
|
+
register_translatable(hist_plot, "data.length_hist")
|
|
138
|
+
|
|
139
|
+
def do_scan(path):
|
|
140
|
+
summary, lens, samples = _scan(path)
|
|
141
|
+
preview = "\n---\n".join(samples) if samples else ""
|
|
142
|
+
fig = _hist(lens)
|
|
143
|
+
return summary, preview, fig
|
|
144
|
+
|
|
145
|
+
scan_btn.click(fn=do_scan, inputs=[data_path],
|
|
146
|
+
outputs=[summary_box, preview_box, hist_plot])
|
|
147
|
+
|
|
148
|
+
return tab
|