File size: 8,026 Bytes
d855e28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
#!/usr/bin/env python3
"""train.py — Treino de bug-detection para BiGRU_T_version (2 épocas).

Especificações do usuário atendidas:
- 2 épocas fixas (não mudar sem permissão)
- Sem dados inventados (apenas datasets do repo + HF públicos)
- Monitorar RAM, disco, loss, perplexidade
- Kill-switch: matar se loss não diminuir ou RAM explodir
- Estados temporários salvos durante treino e APAGADOS ao final
- Parâmetros do modelo aprendidos automaticamente (sem inserção manual)
- Ambiente otimizado para Xeon (AVX512, OpenMP, MKL)
- HF_TOKEN apagado ao final (se usado para datasets privados)

Uso:
    export HF_TOKEN="hf_xxx"  # opcional para datasets públicos
    python scripts/train.py \\
        --datasets CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1,Madras1/corpus-ptbr-v2 \\
        --max-samples 30 \\
        --epochs 2
"""
from __future__ import annotations

import argparse
import logging
import os
import sys
import time
from pathlib import Path

# Adiciona src/ ao path
sys.path.insert(0, str(Path(__file__).parent.parent / "src"))

# Otimização Xeon (deve vir ANTES de importar torch)
os.environ.setdefault("OMP_NUM_THREADS", "2")
os.environ.setdefault("MKL_NUM_THREADS", "2")
os.environ.setdefault("TOKENIZERS_PARALLELISM", "false")
os.environ.setdefault("TORCH_NUM_THREADS", "2")

import torch
torch.set_num_threads(2)

# Tenta otimizar ambiente Xeon (do módulo reaproveitado)
try:
    from bigru_t.utils.xeon_runtime import optimize_xeon_environment
    optimize_xeon_environment()
except Exception as e:
    logging.warning(f"Could not apply Xeon optimization: {e}")

from bigru_t import (
    UnifiedModel, UnifiedModelConfig, create_unified_model,
    BiGRU_T_Trainer, TrainerConfig,
)
from bigru_t.data.streaming_datasets import stream_dataset

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    datefmt="%H:%M:%S",
    handlers=[logging.StreamHandler()],
)
logger = logging.getLogger(__name__)


def load_tokenizer(path: str):
    """Carrega tokenizer BBPE 16K."""
    from tokenizers import Tokenizer
    return Tokenizer.from_file(path)


def load_samples(
    datasets: str,
    max_samples_per_dataset: int,
    hf_token: str | None,
) -> list:
    """Carrega amostras via streaming (reaproveita streaming_datasets_v13_9)."""
    samples = []
    datasets_list = [d.strip() for d in datasets.split(",") if d.strip()]
    logger.info(f"Loading samples from {len(datasets_list)} datasets...")
    for ds_name in datasets_list:
        ds_start = time.time()
        ds_count = 0
        try:
            for sample in stream_dataset(
                ds_name,
                max_samples=max_samples_per_dataset,
                hf_token=hf_token,
            ):
                samples.append(sample)
                ds_count += 1
                if ds_count >= max_samples_per_dataset:
                    break
                if time.time() - ds_start > 300:
                    logger.warning(f"  timeout em {ds_name} após {ds_count} amostras")
                    break
        except Exception as e:
            logger.error(f"Erro carregando {ds_name}: {e}")
            continue
        logger.info(f"  {ds_name}: {ds_count} amostras em {time.time()-ds_start:.1f}s")
    return samples


def main():
    parser = argparse.ArgumentParser(description="BiGRU_T_version — Bug-detection training (2 epochs)")
    parser.add_argument(
        "--datasets",
        type=str,
        default="CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1,Madras1/corpus-ptbr-v2,dominguesm/restore-punctuation-ptbr-dataset",
        help="Lista separada por vírgulas de datasets HF",
    )
    parser.add_argument("--max-samples", type=int, default=30, help="Amostras POR dataset (default 30)")
    parser.add_argument("--epochs", type=int, default=2, help="Nº de épocas (default 2)")
    parser.add_argument("--batch-size", type=int, default=1)
    parser.add_argument("--grad-accum", type=int, default=4)
    parser.add_argument("--lr", type=float, default=1e-3)
    parser.add_argument("--max-seq-len", type=int, default=64)
    parser.add_argument("--output-dir", type=str, default="/home/z/my-project/BiGRU_T_version/model_final")
    parser.add_argument("--temp-dir", type=str, default="/home/z/my-project/BiGRU_T_version/_temp_checkpoints")
    parser.add_argument(
        "--tokenizer-path",
        type=str,
        default="/home/z/my-project/source/model_final/tokenizer/tokenizer.json",
    )
    parser.add_argument("--no-hypothesis", action="store_true", help="Desativar hipótese (Lema 3)")
    parser.add_argument("--keep-temp", action="store_true", help="Manter checkpoints temporários")
    parser.add_argument("--smoke-test", action="store_true", help="Smoke test: 5 samples, 1 epoch")
    args = parser.parse_args()

    if args.smoke_test:
        logger.info("SMOKE TEST MODE — 5 samples/dataset, 1 epoch")
        args.max_samples = 5
        args.epochs = 1
        args.grad_accum = 2
        args.datasets = "CEIA-POSITIVO/ultrachat_br_clustred_balanced_v1"

    # HF token (opcional para datasets públicos)
    hf_token = os.environ.get("HF_TOKEN") or None

    # Configuração do modelo
    model_config = UnifiedModelConfig(
        vocab_size=16384,
        d_model=128,
        max_seq_len=args.max_seq_len,
        # input_dim é automaticamente = d_model via __post_init__
        # Para bug-detection, reduzimos max_modules (64 é caro)
        max_modules=8,  # 8 módulos em vez de 64
        bigru_hidden=32,
        d_transformer=64,
        nhead_tu=4,
        d_ff_tu=128,
        output_dim_u8cell=64,
        cache_len=16,
        d_cache=128,
        nhead_orq=8,
        d_ff_orq=256,
        trainT_dim=128,
        nhead_train=4,
        d_ff_train=256,
        num_layers_train=2,
        hypT_dim=128,
        nhead_hyp=4,
        d_ff_hyp=256,
        num_layers_hyp=2,
        num_bits=8,
        dropout=0.1,
    )

    # Cria modelo
    logger.info("Criando UnifiedModel...")
    model, _ = create_unified_model(model_config)
    params = model.count_parameters()
    logger.info(f"  params: {params['total']:,} ({params['total_M']:.2f}M)")

    # Carrega tokenizer
    tokenizer = load_tokenizer(args.tokenizer_path)

    # Carrega datasets
    samples = load_samples(args.datasets, args.max_samples, hf_token)
    n = len(samples)
    if n == 0:
        logger.error("Nenhuma amostra carregada — abortando")
        sys.exit(1)
    logger.info(f"Total: {n} amostras carregadas")

    # Split 90/10 treino/val
    import random
    random.seed(42)
    random.shuffle(samples)
    split = max(1, int(0.9 * n))
    train_samples = samples[:split]
    val_samples = samples[split:] or samples[:1]  # fallback: 1 amostra
    logger.info(f"  train: {len(train_samples)} | val: {len(val_samples)}")

    # Configuração do trainer
    trainer_config = TrainerConfig(
        epochs=args.epochs,
        datasets=args.datasets,
        max_samples_per_dataset=args.max_samples,
        max_seq_len=args.max_seq_len,
        per_device_batch_size=args.batch_size,
        grad_accum=args.grad_accum,
        lr=args.lr,
        use_hypothesis=not args.no_hypothesis,
        output_dir=args.output_dir,
        temp_dir=args.temp_dir,
        keep_temp=args.keep_temp,
    )

    # Trainer
    trainer = BiGRU_T_Trainer(
        model=model,
        tokenizer=tokenizer,
        config=trainer_config,
        train_samples=train_samples,
        val_samples=val_samples,
    )

    # Treina
    result = trainer.train()

    # Print resultado
    import json
    print("\n=== Resultado Final ===")
    print(json.dumps(result, indent=2, default=str))

    # Apaga HF token do ambiente (especificação do usuário)
    if "HF_TOKEN" in os.environ:
        del os.environ["HF_TOKEN"]
        logger.info("HF_TOKEN apagado do ambiente")

    # Exit code: 0 se OK, 1 se morto pelo kill-switch
    sys.exit(0 if not result.get("killed") else 1)


if __name__ == "__main__":
    main()