PowerMachine commited on
Commit
17685dc
·
verified ·
1 Parent(s): 01a6b31

V6.5-7ds cleanup: remove legacy scripts/train_v2.py

Browse files
Files changed (1) hide show
  1. scripts/train_v2.py +0 -440
scripts/train_v2.py DELETED
@@ -1,440 +0,0 @@
1
- #!/usr/bin/env python3
2
- """train_v2.py — Treino BiGRU_T_version V2 (15M params, HW optimizer, multimodal bug-hunt).
3
-
4
- Melhorias vs train_fast.py:
5
- - max_modules=10 (aumento de 4→10, memória permite), d_model=128
6
- - ~14.45M params (objetivo 15M)
7
- - HamiltonianWasserstein optimizer ATIVADO (AdamW + W₂ + repulsão + LR cíclico)
8
- - TimeBudget + aggressive_cleanup (estilo Xavante)
9
- - Teste de multimodalidade (text/image/audio/video encoders) com poucos samples
10
- - Timeout aumentado (1800s total, 900s/epoch)
11
- - Verificação de OOM-Killer antes do treino
12
- - Smoke checks antes do treino principal
13
-
14
- Uso:
15
- export HF_TOKEN=hf_xxx # opcional, para datasets privados
16
- python3 scripts/train_v2.py
17
- """
18
- from __future__ import annotations
19
-
20
- import os
21
- import sys
22
- import time
23
- import json
24
- import logging
25
- import gc
26
- import shutil
27
- from pathlib import Path
28
-
29
- # Adiciona src/ ao path
30
- sys.path.insert(0, str(Path(__file__).parent.parent / "src"))
31
-
32
- # Otimização Xeon
33
- os.environ.setdefault("OMP_NUM_THREADS", "2")
34
- os.environ.setdefault("MKL_NUM_THREADS", "2")
35
- os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
36
-
37
- import torch
38
- torch.set_num_threads(2)
39
-
40
- try:
41
- from bigru_t.utils.xeon_runtime import optimize_xeon_environment
42
- optimize_xeon_environment()
43
- except Exception as e:
44
- logging.warning(f"Could not apply Xeon optimization: {e}")
45
-
46
- from bigru_t import (
47
- UnifiedModel, UnifiedModelConfig, create_unified_model,
48
- BiGRU_T_Trainer, TrainerConfig,
49
- HamiltonianWassersteinOptimizer,
50
- CircularReasoningWasserstein,
51
- BiGRUTGenerator,
52
- aggressive_cleanup, get_rss_mb, TimeBudget,
53
- )
54
- from bigru_t.data.streaming_datasets import stream_dataset
55
-
56
- logging.basicConfig(
57
- level=logging.INFO,
58
- format="%(asctime)s [%(levelname)s] %(message)s",
59
- datefmt="%H:%M:%S",
60
- handlers=[logging.StreamHandler()],
61
- )
62
- logger = logging.getLogger(__name__)
63
-
64
-
65
- def check_oom_killer_risk() -> bool:
66
- """Verifica se há risco de OOM-Killer antes de iniciar treino.
67
-
68
- Checa:
69
- 1. dmesg por ocorrências recentes de OOM-Killer
70
- 2. Memória livre do sistema
71
- 3. Swap disponível
72
-
73
- Returns:
74
- True se seguro para prosseguir, False se risco alto.
75
- """
76
- import subprocess
77
- # 1. dmesg OOM check
78
- try:
79
- result = subprocess.run(
80
- ["dmesg"], capture_output=True, text=True, timeout=5
81
- )
82
- if "Out of memory" in result.stdout or "Killed process" in result.stdout:
83
- logger.warning("⚠️ dmesg mostra OOM-Killer activity recente — risco alto")
84
- # Não aborta, apenas avisa (pode ser histórico antigo)
85
- except (FileNotFoundError, subprocess.TimeoutExpired):
86
- pass # dmesg pode não estar acessível
87
-
88
- # 2. Memória livre do sistema
89
- try:
90
- with open("/proc/meminfo", "r") as f:
91
- meminfo = dict(line.split(":")[1].strip().split()[0:2] for line in f if ":" in line)
92
- free_mb = int(meminfo.get("MemAvailable", "0")) / 1024
93
- total_mb = int(meminfo.get("MemTotal", "0")) / 1024
94
- logger.info(f" Sistema: {total_mb:.0f}MB total, {free_mb:.0f}MB disponível")
95
- if free_mb < 512:
96
- logger.error(f"❌ Memória disponível muito baixa ({free_mb:.0f}MB < 512MB)")
97
- return False
98
- except Exception:
99
- pass
100
-
101
- # 3. Disk free
102
- try:
103
- usage = shutil.disk_usage("/home/z/my-project")
104
- free_gb = usage.free / (1024**3)
105
- logger.info(f" Disk free: {free_gb:.2f}GB")
106
- if free_gb < 1.0:
107
- logger.error(f"❌ Disk livre muito baixo ({free_gb:.2f}GB < 1GB)")
108
- return False
109
- except Exception:
110
- pass
111
-
112
- return True
113
-
114
-
115
- def smoke_test_model(model, tokenizer) -> bool:
116
- """Smoke test rápido: forward + backward + inference."""
117
- logger.info("Running smoke test (forward + backward + generate)...")
118
- try:
119
- import torch
120
- # Forward — COM grad (para permitir backward)
121
- dummy_ids = torch.randint(0, 100, (1, 16), dtype=torch.long)
122
- y_hat, delta = model(dummy_ids, temperature=1.0, use_hypothesis=False)
123
- assert y_hat.shape[0] == 1, f"batch mismatch: {y_hat.shape}"
124
- assert not torch.isnan(y_hat).any(), "NaN in y_hat"
125
- logger.info(f" Forward OK: y_hat {tuple(y_hat.shape)}, delta {tuple(delta.shape)}")
126
-
127
- # Backward
128
- target = torch.tensor([5], dtype=torch.long)
129
- loss = torch.nn.functional.cross_entropy(y_hat, target)
130
- loss.backward()
131
- grad_norm = sum(p.grad.norm().item() ** 2 for p in model.parameters() if p.grad is not None) ** 0.5
132
- logger.info(f" Backward OK: loss={loss.item():.4f}, grad_norm={grad_norm:.4f}")
133
- model.zero_grad()
134
-
135
- # Generate (no grad)
136
- gen = BiGRUTGenerator(model, max_seq_len=64)
137
- with torch.no_grad():
138
- out = gen.generate(dummy_ids, max_new_tokens=5, do_sample=False)
139
- logger.info(f" Generate OK: {dummy_ids.shape} -> {out.shape}")
140
- return True
141
- except Exception as e:
142
- import traceback
143
- logger.error(f" Smoke test FAILED: {e}")
144
- traceback.print_exc()
145
- return False
146
-
147
-
148
- def test_multimodal_encoders() -> dict:
149
- """Testa os encoders multimodais com dados sintéticos (bug detection).
150
-
151
- Não usa datasets reais (apenas poucos samples sintéticos para validar
152
- que os encoders não quebram). Retorna dict com status de cada encoder.
153
-
154
- BUGS ENCONTRADOS E CORRIGIDOS:
155
- 1. TextEncoder dependia de 3 módulos ausentes (attention_multimodal,
156
- embedding_reconfig, gru_hierarchy) — COPIADOS da fonte HF.
157
- 2. AudioEncoder: parâmetro `in_channels` é ignorado pelo Conv1d interno
158
- (sempre usa n_mels=80 como canal) — WORKAROUND: passar [B, 80, T].
159
- 3. VideoEncoder.forward(frames, audio) espera frames=[B,T,C,H,W] (5D),
160
- NÃO [B,C,T,H,W] — corrigido no teste.
161
- 4. ModalRouter.forward(inputs: Dict[str, Tensor]) — espera dict nomeado.
162
- 5. FusionLayer.forward(modality_embs: list) — espera lista de embeddings.
163
- """
164
- logger.info("Testing multimodal encoders (synthetic data, bug detection)...")
165
- results = {}
166
-
167
- try:
168
- from bigru_t.multimodal.text_encoder import TextEncoder
169
- enc = TextEncoder(vocab_size=16384, d_model=128, n_heads=4, n_gru_levels=2, max_seq_len=64)
170
- import torch
171
- x = torch.randint(0, 100, (2, 16), dtype=torch.long)
172
- out = enc(x)
173
- results["text_encoder"] = {"ok": True, "shape": str(tuple(out.shape))}
174
- logger.info(f" TextEncoder OK: {tuple(out.shape)}")
175
- except Exception as e:
176
- results["text_encoder"] = {"ok": False, "error": str(e)}
177
- logger.warning(f" TextEncoder FAIL: {e}")
178
-
179
- try:
180
- from bigru_t.multimodal.image_encoder import ImageEncoder
181
- enc = ImageEncoder(d_model=128, in_channels=3)
182
- import torch
183
- x = torch.randn(2, 3, 16, 16)
184
- out = enc(x)
185
- results["image_encoder"] = {"ok": True, "shape": str(tuple(out.shape))}
186
- logger.info(f" ImageEncoder OK: {tuple(out.shape)}")
187
- except Exception as e:
188
- results["image_encoder"] = {"ok": False, "error": str(e)}
189
- logger.warning(f" ImageEncoder FAIL: {e}")
190
-
191
- try:
192
- from bigru_t.multimodal.audio_encoder import AudioEncoder
193
- # BUG: in_channels é ignorado; Conv1d usa n_mels como canal
194
- enc = AudioEncoder(d_model=128, in_channels=80, n_mels=80)
195
- import torch
196
- # Shape correta: [B, n_mels=80, T_audio]
197
- x = torch.randn(2, 80, 100)
198
- out = enc(x)
199
- results["audio_encoder"] = {"ok": True, "shape": str(tuple(out.shape)),
200
- "note": "in_channels param unused; uses n_mels=80 as channel"}
201
- logger.info(f" AudioEncoder OK: {tuple(out.shape)}")
202
- except Exception as e:
203
- results["audio_encoder"] = {"ok": False, "error": str(e)}
204
- logger.warning(f" AudioEncoder FAIL: {e}")
205
-
206
- try:
207
- from bigru_t.multimodal.video_encoder import VideoEncoder
208
- enc = VideoEncoder(d_model=128, n_frames=4)
209
- import torch
210
- # BUG: frames deve ser [B, T, C, H, W] (5D, T antes de C)
211
- frames = torch.randn(2, 4, 3, 16, 16) # (B=2, T=4, C=3, H=16, W=16)
212
- audio = torch.randn(2, 80, 100) # (B, n_mels, T_audio)
213
- out = enc(frames, audio)
214
- results["video_encoder"] = {"ok": True, "shape": str(tuple(out.shape)),
215
- "note": "frames=[B,T,C,H,W], audio=[B,80,T]"}
216
- logger.info(f" VideoEncoder OK: {tuple(out.shape)}")
217
- except Exception as e:
218
- results["video_encoder"] = {"ok": False, "error": str(e)}
219
- logger.warning(f" VideoEncoder FAIL: {e}")
220
-
221
- try:
222
- from bigru_t.multimodal.modal_router import ModalRouter
223
- from bigru_t.multimodal.fusion_layer import FusionLayer
224
- router = ModalRouter(d_model=128)
225
- fusion = FusionLayer(d_model=128, n_modalities=4)
226
- import torch
227
- # ModalRouter.forward(inputs: Dict[str, Tensor]) — espera dict nomeado
228
- inputs = {
229
- "text": torch.randn(2, 128),
230
- "image": torch.randn(2, 128),
231
- "audio": torch.randn(2, 128),
232
- "video": torch.randn(2, 128),
233
- }
234
- routed = router(inputs)
235
- # FusionLayer.forward(modality_embs: list) — espera lista
236
- embs = [torch.randn(2, 128) for _ in range(4)]
237
- fused = fusion(embs)
238
- results["modal_router_fusion"] = {"ok": True,
239
- "router_shape": str(tuple(routed.shape)),
240
- "fusion_shape": str(tuple(fused.shape))}
241
- logger.info(f" ModalRouter OK: {tuple(routed.shape)} | Fusion OK: {tuple(fused.shape)}")
242
- except Exception as e:
243
- results["modal_router_fusion"] = {"ok": False, "error": str(e)}
244
- logger.warning(f" ModalRouter/Fusion FAIL: {e}")
245
-
246
- return results
247
-
248
-
249
- def main():
250
- logger.info("=" * 70)
251
- logger.info("BiGRU_T_version V2 — 15M params + HW optimizer + multimodal bug-hunt")
252
- logger.info("=" * 70)
253
-
254
- # HF token (do ambiente; será apagado ao final)
255
- hf_token = os.environ.get("HF_TOKEN") or None
256
- if hf_token:
257
- logger.info("HF_TOKEN encontrado no ambiente")
258
- else:
259
- logger.info("HF_TOKEN não fornecido (datasets públicos apenas)")
260
-
261
- # 1. Verificação de OOM-Killer e recursos
262
- logger.info("\n[1/6] Verificando recursos do sistema...")
263
- if not check_oom_killer_risk():
264
- logger.error("Recursos insuficientes. Abortando.")
265
- sys.exit(1)
266
- logger.info(f" RSS atual: {get_rss_mb():.1f}MB")
267
-
268
- # 2. Teste multimodal (bug detection)
269
- logger.info("\n[2/6] Testando multimodalidade (bug hunting)...")
270
- mm_results = test_multimodal_encoders()
271
- mm_ok = sum(1 for v in mm_results.values() if v.get("ok"))
272
- mm_total = len(mm_results)
273
- logger.info(f" Multimodal: {mm_ok}/{mm_total} encoders OK")
274
-
275
- # 3. Config do modelo (12.97M params — max_modules=8, d_model=128)
276
- logger.info("\n[3/6] Criando UnifiedModel (max_modules=8, d_model=128)...")
277
- model_config = UnifiedModelConfig(
278
- vocab_size=16384,
279
- d_model=128,
280
- max_seq_len=32,
281
- pad_token_id=1,
282
- max_modules=8, # base spec do usuário (8)
283
- bigru_hidden=32,
284
- d_transformer=64,
285
- nhead_tu=4,
286
- d_ff_tu=128,
287
- output_dim_u8cell=64,
288
- cache_len=16,
289
- d_cache=128,
290
- nhead_orq=8,
291
- d_ff_orq=256,
292
- trainT_dim=128,
293
- nhead_train=4,
294
- d_ff_train=256,
295
- num_layers_train=2,
296
- hypT_dim=128,
297
- nhead_hyp=4,
298
- d_ff_hyp=256,
299
- num_layers_hyp=2,
300
- num_bits=8,
301
- dropout=0.1,
302
- )
303
- model, _ = create_unified_model(model_config)
304
- params = model.count_parameters()
305
- logger.info(f" params: {params['total']:,} ({params['total_M']:.2f}M)")
306
- logger.info(f" trainable: {params['trainable']:,} ({params['trainable_M']:.2f}M)")
307
- aggressive_cleanup(verbose=True)
308
-
309
- # 4. Tokenizer (usa o pré-treinado do source)
310
- logger.info("\n[4/6] Carregando tokenizer...")
311
- from tokenizers import Tokenizer
312
- tok_path = "/home/z/my-project/source/model_final/tokenizer/tokenizer.json"
313
- if not Path(tok_path).exists():
314
- logger.error(f"Tokenizer não encontrado: {tok_path}")
315
- sys.exit(1)
316
- tokenizer = Tokenizer.from_file(tok_path)
317
- logger.info(f" Tokenizer loaded: vocab={tokenizer.get_vocab_size()}")
318
-
319
- # 5. Smoke test do modelo
320
- logger.info("\n[5/6] Smoke test do modelo...")
321
- if not smoke_test_model(model, tokenizer):
322
- logger.error("Smoke test falhou. Abortando.")
323
- sys.exit(1)
324
- aggressive_cleanup(verbose=True)
325
-
326
- # 6. Carrega datasets (streaming, poucos samples para bug detection)
327
- logger.info("\n[6/6] Carregando datasets (streaming, poucos samples)...")
328
- datasets = [
329
- "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1",
330
- ]
331
- max_samples = 8 # poucos samples (bug detection, testes)
332
- all_samples = []
333
- t0 = time.time()
334
- for ds_name in datasets:
335
- if time.time() - t0 > 180:
336
- logger.warning(f"Timeout carregando datasets após {len(all_samples)} amostras")
337
- break
338
- try:
339
- count_before = len(all_samples)
340
- for sample in stream_dataset(ds_name, max_samples=max_samples, hf_token=hf_token):
341
- all_samples.append(sample)
342
- if len(all_samples) >= max_samples * len(datasets):
343
- break
344
- if time.time() - t0 > 180:
345
- break
346
- logger.info(f" {ds_name}: +{len(all_samples)-count_before} samples")
347
- except Exception as e:
348
- logger.warning(f" {ds_name} falhou: {e}")
349
-
350
- if not all_samples:
351
- logger.error("Nenhuma amostra carregada de nenhum dataset")
352
- sys.exit(1)
353
-
354
- logger.info(f"Total: {len(all_samples)} samples em {time.time()-t0:.1f}s")
355
-
356
- # Split 90/10
357
- import random
358
- random.seed(42)
359
- random.shuffle(all_samples)
360
- split = max(1, int(0.9 * len(all_samples)))
361
- train_samples = all_samples[:split]
362
- val_samples = all_samples[split:] or all_samples[:1]
363
- logger.info(f" train: {len(train_samples)} | val: {len(val_samples)}")
364
-
365
- aggressive_cleanup(verbose=True)
366
-
367
- # Trainer config — HW optimizer + time budget + cleanup agressivo
368
- trainer_config = TrainerConfig(
369
- epochs=2,
370
- datasets=",".join(datasets),
371
- max_samples_per_dataset=max_samples,
372
- max_seq_len=32,
373
- per_device_batch_size=1,
374
- grad_accum=2,
375
- lr=1e-3,
376
- optimizer_type="hamiltonian_wasserstein", # ATIVADO
377
- hw_lr_amp=0.3,
378
- hw_lr_freq=0.01,
379
- hw_sigma_w=1.0,
380
- hw_sigma_rep=0.1,
381
- hw_prune_every_n=0, # pruning off para bug detection
382
- use_hypothesis=True,
383
- stop_grad_hyp=True,
384
- meta_interval=5,
385
- max_total_time_s=1800.0, # 30 min total
386
- max_per_epoch_s=900.0, # 15 min/epoch
387
- cleanup_every_n_steps=5,
388
- log_every=1,
389
- save_temp_every=3, # salvar a cada 3 steps (garante checkpoint mesmo se OOM no fim)
390
- output_dir="/home/z/my-project/BiGRU_T_version/model_final",
391
- temp_dir="/home/z/my-project/BiGRU_T_version/_temp_checkpoints",
392
- keep_temp=False,
393
- ram_threshold_pct=90.0,
394
- disk_min_free_gb=1.0,
395
- loss_patience=30,
396
- )
397
-
398
- trainer = BiGRU_T_Trainer(
399
- model=model,
400
- tokenizer=tokenizer,
401
- config=trainer_config,
402
- train_samples=train_samples,
403
- val_samples=val_samples,
404
- )
405
-
406
- # Treino
407
- result = trainer.train()
408
-
409
- # Salva resultados extras (multimodal + smoke)
410
- extra_report = {
411
- "multimodal_test": mm_results,
412
- "smoke_test": {"passed": True},
413
- "config": {
414
- "max_modules": model_config.max_modules,
415
- "d_model": model_config.d_model,
416
- "bigru_hidden": model_config.bigru_hidden,
417
- "d_transformer": model_config.d_transformer,
418
- "params_total": params["total"],
419
- "params_M": params["total_M"],
420
- "optimizer": "hamiltonian_wasserstein",
421
- },
422
- }
423
- extra_path = Path("/home/z/my-project/BiGRU_T_version/multimodal_report.json")
424
- with open(extra_path, "w") as f:
425
- json.dump(extra_report, f, indent=2, default=str)
426
- logger.info(f"Relatório multimodal salvo: {extra_path}")
427
-
428
- print("\n=== Resultado Final ===")
429
- print(json.dumps(result, indent=2, default=str))
430
-
431
- # Apaga HF token
432
- if "HF_TOKEN" in os.environ:
433
- del os.environ["HF_TOKEN"]
434
- logger.info("HF_TOKEN apagado do ambiente")
435
-
436
- sys.exit(0 if not result.get("killed") else 1)
437
-
438
-
439
- if __name__ == "__main__":
440
- main()