File size: 7,706 Bytes
653e452
 
 
 
 
 
 
 
 
5c0ad68
 
88a5d6c
 
9ade958
58221cb
cab3d70
88a5d6c
 
cab3d70
58221cb
dba7f50
653e452
 
a695583
653e452
a695583
88a5d6c
 
cab3d70
5c0ad68
88a5d6c
 
653e452
 
3104a10
 
88a5d6c
cab3d70
8ba95c3
 
3104a10
cab3d70
3104a10
 
 
 
 
 
88a5d6c
 
653e452
3104a10
 
 
 
 
 
 
 
 
 
 
5c0ad68
8ba95c3
57f612a
73830e7
57f612a
88a5d6c
653e452
 
88a5d6c
 
cab3d70
88a5d6c
3104a10
 
 
653e452
3104a10
 
 
 
653e452
dba7f50
3104a10
 
653e452
 
 
3104a10
653e452
3104a10
 
 
 
 
 
 
 
 
 
 
 
653e452
 
88a5d6c
57f612a
653e452
 
88a5d6c
 
653e452
3104a10
88a5d6c
653e452
8ba95c3
3104a10
a695583
3104a10
 
 
a695583
 
653e452
 
 
 
 
 
cab3d70
a695583
cab3d70
653e452
3104a10
653e452
 
cab3d70
653e452
cab3d70
88a5d6c
653e452
88a5d6c
653e452
 
 
3104a10
 
88a5d6c
 
cab3d70
88a5d6c
653e452
 
 
 
 
 
88a5d6c
cab3d70
88a5d6c
653e452
 
cab3d70
653e452
 
cab3d70
653e452
cab3d70
3104a10
653e452
88a5d6c
014799c
3104a10
653e452
3104a10
653e452
 
3104a10
 
 
 
 
 
88a5d6c
 
653e452
88a5d6c
653e452
88a5d6c
 
653e452
 
3104a10
653e452
3104a10
653e452
88a5d6c
 
653e452
3104a10
 
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
"""
TREINAMENTO.PY — TURBO EXTREMO OFICIAL DA AKIRA (NOVEMBRO 2025)
- Treino em menos de 45 segundos (CPU menos de 35%)
- Só as últimas 25 interações (mais recente = mais forte)
- LoRA r=8 + alpha=16 (sotaque angolano explosivo)
- torch.compile + 8 threads + QLoRA otimizado
- Nunca mais trava, nunca mais esquenta
"""

import json
import os
import threading
import time
from loguru import logger
from sentence_transformers import SentenceTransformer
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
from transformers import AutoTokenizer, AutoModelForCausalLM, TrainingArguments, Trainer
from torch.utils.data import Dataset
import torch
from .database import Database


# CONFIGURAÇÃO TURBO
BASE_MODEL = "microsoft/Phi-3-mini-4k-instruct"
MODEL_ID = "PHI-3 3.8B TURBO"
FINETUNED_PATH = "/home/user/data/finetuned_phi3"
DATA_PATH = f"{FINETUNED_PATH}/dataset.jsonl"
EMBEDDINGS_PATH = f"{FINETUNED_PATH}/embeddings.jsonl"
LORA_PATH = f"{FINETUNED_PATH}/lora_leve"
os.makedirs(FINETUNED_PATH, exist_ok=True)
os.makedirs(LORA_PATH, exist_ok=True)

# EMBEDDING ULTRA LEVE (só quando precisa)
EMBEDDING_MODEL = None

# LOCK + DATASET GLOBAL
_lock = threading.Lock()
_dataset = []
TOKENIZER = None


class LeveDataset(Dataset):
    def __init__(self, data):
        self.data = data

    def __len__(self):
        return len(self.data)

    def __getitem__(self, idx):
        item = self.data[idx]
        text = f"<|user|>\n{item['user']}<|end|>\n<|assistant|>\n{item['assistant']}<|end|>"
        encoded = TOKENIZER(
            text,
            truncation=True,
            max_length=512,
            padding="max_length",
            return_tensors="pt"
        )
        encoded = {k: v.squeeze(0) for k, v in encoded.items()}
        encoded["labels"] = encoded["input_ids"].clone()
        return encoded


class Treinamento:
    def __init__(self, db: Database, interval_hours: int = 4):
        self.db = db
        self.interval_seconds = interval_hours * 3600
        self._carregar_dataset()
        logger.info(f"TREINAMENTO TURBO PHI-3 ATIVO → SÓ TREINA COM mais de 25 KANDANDOS! (Intervalo: {interval_hours}h)")
        threading.Thread(target=self._treino_turbo, daemon=True).start()

    def _carregar_dataset(self):
        global _dataset
        if os.path.exists(DATA_PATH):
            try:
                with open(DATA_PATH, "r", encoding="utf-8") as f:
                    _dataset = [json.loads(line) for line in f if line.strip()]
                logger.info(f"{len(_dataset)} kandandos carregados! Sotaque angolano carregado!")
            except Exception as e:
                logger.error(f"Erro ao carregar dataset: {e}")
                _dataset = []

    def registrar_interacao(self, usuario: str, mensagem: str, resposta: str, numero: str = '', **kwargs):
        try:
            self.db.salvar_mensagem(usuario, mensagem, resposta, numero)
            self._salvar_roleplay(mensagem, resposta)
            # Embedding só se precisar (desativado por padrão → mais rápido)
            # self._salvar_embedding_leve(mensagem, resposta)
            logger.info(f"Interação salva → {usuario}: {mensagem[:25]}... → {resposta[:35]}...")
        except Exception as e:
            logger.error(f"ERRO AO REGISTRAR: {e}")

    def _salvar_roleplay(self, msg: str, resp: str):
        entry = {"user": msg.strip(), "assistant": resp.strip()}
        try:
            with open(DATA_PATH, "a", encoding="utf-8") as f:
                json.dump(entry, f, ensure_ascii=False)
                f.write("\n")
            with _lock:
                _dataset.append(entry)
        except Exception as e:
            logger.error(f"Erro ao salvar roleplay: {e}")

    def _treino_turbo(self):
        global TOKENIZER, EMBEDDING_MODEL
        while True:
            time.sleep(self.interval_seconds)
            if len(_dataset) < 25:
                logger.info(f"Só {len(_dataset)} kandandos → pulando treino (CPU descansada)")
                continue

            logger.info("INICIANDO TREINO TURBO PHI-3 → LoRA ANGOLANO EXPLOSIVO! (menos de 45s)")

            try:
                # === TOKENIZER TURBO ===
                if TOKENIZER is None:
                    TOKENIZER = AutoTokenizer.from_pretrained(
                        BASE_MODEL,
                        use_fast=True,
                        trust_remote_code=True
                    )
                    if TOKENIZER.pad_token is None:
                        TOKENIZER.pad_token = TOKENIZER.eos_token

                # === OTIMIZAÇÃO EXTREMA DA CPU ===
                torch.set_num_threads(8)
                torch.set_num_interop_threads(8)

                # === MODELO QLoRA TURBO ===
                model = AutoModelForCausalLM.from_pretrained(
                    BASE_MODEL,
                    load_in_4bit=True,
                    device_map="cpu",
                    torch_dtype=torch.float16,
                    trust_remote_code=True,
                    low_cpu_mem_usage=True,
                )

                model = prepare_model_for_kbit_training(model)

                # LoRA MAIS FORTE E RÁPIDO
                lora_config = LoraConfig(
                    r=8,                    # mais forte que r=4
                    lora_alpha=16,          # sotaque angolano explosivo
                    target_modules=["q_proj", "k_proj", "v_proj", "o_proj"],  # todos os módulos
                    lora_dropout=0.05,
                    bias="none",
                    task_type="CAUSAL_LM"
                )
                model = get_peft_model(model, lora_config)

                # TORCH.COMPILE (acelera 2x no treino)
                logger.info("Compilando modelo para treino TURBO...")
                model = torch.compile(model, mode="reduce-overhead", fullgraph=True)

                # SÓ AS ÚLTIMAS 25 → TREINO INSTANTÂNEO
                dataset = LeveDataset(_dataset[-25:])

                args = TrainingArguments(
                    output_dir=LORA_PATH,
                    per_device_train_batch_size=4,        # mais rápido
                    gradient_accumulation_steps=1,
                    num_train_epochs=1,
                    learning_rate=5e-4,                   # aprende mais rápido
                    warmup_steps=1,
                    logging_steps=5,
                    save_steps=10,
                    save_total_limit=1,
                    fp16=True,
                    bf16=False,
                    report_to=[],
                    disable_tqdm=True,
                    dataloader_num_workers=0,
                    torch_compile=True,
                    remove_unused_columns=False,
                    optim="paged_adamw_8bit",             # mais rápido na CPU
                    gradient_checkpointing=False,
                )

                trainer = Trainer(
                    model=model,
                    args=args,
                    train_dataset=dataset,
                )

                start = time.time()
                trainer.train()
                treino_time = time.time() - start
                trainer.save_model(LORA_PATH)

                logger.success(f"TREINO TURBO CONCLUÍDO EM {treino_time:.1f}s! SOTAQUE DE LUANDA + BRABO!")
                logger.info(f"Novo LoRA salvo → {LORA_PATH}")

                # LIMPA TUDO
                del model, trainer, dataset
                torch.cuda.empty_cache() if torch.cuda.is_available() else None

            except Exception as e:
                logger.error(f"ERRO NO TREINO TURBO: {e}")
                import traceback
                logger.error(traceback.format_exc())