Download src/03_train.py from BeaMorais/Seeds: direct link, hf CLI and curl.
- Browser
- Download file 13.3 kB
-
https://huggingface.co/spaces/BeaMorais/Seeds/resolve/main/src/03_train.py
- Command line
-
hf download hf://spaces/BeaMorais/Seeds/src/03_train.py
-
curl -L -o 03_train.py https://huggingface.co/spaces/BeaMorais/Seeds/resolve/main/src/03_train.py
13.3 kB
| import json | |
| import os | |
| import random | |
| import subprocess | |
| import sys | |
| import numpy as np | |
| import torch | |
| from datasets import Dataset | |
| from transformers import ( | |
| AutoTokenizer, | |
| AutoModelForSeq2SeqLM, | |
| DataCollatorForSeq2Seq, | |
| Seq2SeqTrainer, | |
| Seq2SeqTrainingArguments, | |
| set_seed, | |
| ) | |
| # ============================================================ | |
| # CONFIGURAÇÕES | |
| # ============================================================ | |
| MODEL_NAME = "google/byt5-small" | |
| TRAIN_FILE = "data/train.jsonl" | |
| VALIDATION_FILE = "data/validation.jsonl" | |
| # Pasta diferente para não sobrescrever os testes anteriores | |
| OUTPUT_DIR = "models/seeds-byt5-overfit-v2" | |
| FINAL_MODEL_DIR = "models/seeds-byt5-overfit-v2-final" | |
| # ------------------------------------------------------------ | |
| # TESTE CONTROLADO DE MEMORIZAÇÃO | |
| # ------------------------------------------------------------ | |
| MAX_TRAIN_EXAMPLES = 20 | |
| MAX_VALIDATION_EXAMPLES = 20 | |
| # Os exemplos selecionados são curtos, então é possível aumentar | |
| # o limite sem sofrer com truncamento. | |
| MAX_SOURCE_LENGTH = 256 | |
| MAX_TARGET_LENGTH = 256 | |
| NUM_EPOCHS = 10 | |
| LEARNING_RATE = 1e-4 | |
| TRAIN_BATCH_SIZE = 1 | |
| EVAL_BATCH_SIZE = 1 | |
| # Cada exemplo gera uma atualização do otimizador. | |
| GRADIENT_ACCUMULATION_STEPS = 1 | |
| SEED = 42 | |
| # ============================================================ | |
| # FUNÇÕES AUXILIARES | |
| # ============================================================ | |
| def carregar_jsonl(caminho): | |
| with open(caminho, "r", encoding="utf-8") as arquivo: | |
| return [json.loads(linha) for linha in arquivo] | |
| def garantir_dataset(): | |
| if os.path.exists(TRAIN_FILE) and os.path.exists(VALIDATION_FILE): | |
| return | |
| print("Arquivos de dataset não encontrados.") | |
| print("Executando automaticamente o script 02_prepare_dataset.py...") | |
| resultado = subprocess.run( | |
| [sys.executable, "src/02_prepare_dataset.py"], | |
| capture_output=True, | |
| text=True, | |
| ) | |
| print(resultado.stdout) | |
| if resultado.returncode != 0: | |
| print(resultado.stderr) | |
| raise RuntimeError( | |
| "Não foi possível preparar o dataset automaticamente." | |
| ) | |
| def preparar_exemplos(): | |
| train_data = carregar_jsonl(TRAIN_FILE) | |
| validation_data = carregar_jsonl(VALIDATION_FILE) | |
| print(f"Treino completo: {len(train_data)} exemplos") | |
| print(f"Validação completa: {len(validation_data)} exemplos") | |
| # -------------------------------------------------------- | |
| # Selecionado os exemplos mais curtos. | |
| # | |
| # Isso torna o teste de memorização mais limpo: | |
| # o modelo não precisa lidar com frases enormes nem | |
| # truncamento. | |
| # -------------------------------------------------------- | |
| train_data = sorted( | |
| train_data, | |
| key=lambda item: len(item["source"]) + len(item["target"]) | |
| )[:MAX_TRAIN_EXAMPLES] | |
| validation_data = sorted( | |
| validation_data, | |
| key=lambda item: len(item["source"]) + len(item["target"]) | |
| )[:MAX_VALIDATION_EXAMPLES] | |
| print(f"Treino selecionado: {len(train_data)} exemplos") | |
| print(f"Validação selecionada: {len(validation_data)} exemplos") | |
| print("\nExemplos selecionados para treino:") | |
| for indice, exemplo in enumerate(train_data[:5], start=1): | |
| print( | |
| f"{indice}. " | |
| f"source_chars={len(exemplo['source'])}, " | |
| f"target_chars={len(exemplo['target'])}" | |
| ) | |
| print(f" EN: {exemplo['source']}") | |
| print(f" MBC: {exemplo['target']}") | |
| print() | |
| return train_data, validation_data | |
| def criar_dataset(exemplos): | |
| return Dataset.from_list( | |
| [ | |
| { | |
| "source": exemplo["source"], | |
| "target": exemplo["target"], | |
| } | |
| for exemplo in exemplos | |
| ] | |
| ) | |
| # ============================================================ | |
| # PREPARAÇÃO | |
| # ============================================================ | |
| print("=" * 70) | |
| print("SEEDS — TESTE FINAL DE MEMORIZAÇÃO") | |
| print("=" * 70) | |
| garantir_dataset() | |
| set_seed(SEED) | |
| random.seed(SEED) | |
| np.random.seed(SEED) | |
| torch.manual_seed(SEED) | |
| train_data, validation_data = preparar_exemplos() | |
| train_dataset = criar_dataset(train_data) | |
| validation_dataset = criar_dataset(validation_data) | |
| # ============================================================ | |
| # TOKENIZER | |
| # ============================================================ | |
| print("\nCarregando tokenizer...") | |
| tokenizer = AutoTokenizer.from_pretrained( | |
| MODEL_NAME | |
| ) | |
| # ============================================================ | |
| # MODELO | |
| # ============================================================ | |
| print("Carregando modelo...") | |
| model = AutoModelForSeq2SeqLM.from_pretrained( | |
| MODEL_NAME | |
| ) | |
| # Evita incompatibilidade entre cache e | |
| # gradient checkpointing. | |
| model.config.use_cache = False | |
| # Evita que o Transformers tente amarrar novamente | |
| # shared.weight e lm_head.weight. | |
| model.config.tie_word_embeddings = False | |
| # ============================================================ | |
| # TOKENIZAÇÃO | |
| # ============================================================ | |
| def preprocess_function(exemplos): | |
| prompts = [ | |
| f"English: {texto}\nMacushi:" | |
| for texto in exemplos["source"] | |
| ] | |
| targets = exemplos["target"] | |
| # Entrada | |
| model_inputs = tokenizer( | |
| prompts, | |
| max_length=MAX_SOURCE_LENGTH, | |
| truncation=True, | |
| ) | |
| # Saída esperada | |
| labels = tokenizer( | |
| text_target=targets, | |
| max_length=MAX_TARGET_LENGTH, | |
| truncation=True, | |
| ) | |
| model_inputs["labels"] = labels["input_ids"] | |
| return model_inputs | |
| print("\nTokenizando dataset de treino...") | |
| tokenized_train = train_dataset.map( | |
| preprocess_function, | |
| batched=True, | |
| remove_columns=train_dataset.column_names, | |
| ) | |
| print("Tokenizando dataset de validação...") | |
| tokenized_validation = validation_dataset.map( | |
| preprocess_function, | |
| batched=True, | |
| remove_columns=validation_dataset.column_names, | |
| ) | |
| # ============================================================ | |
| # VERIFICAÇÃO DOS TAMANHOS | |
| # ============================================================ | |
| print("\nComprimentos dos primeiros exemplos tokenizados:") | |
| for indice in range(min(5, len(tokenized_train))): | |
| print( | |
| f"Exemplo {indice + 1}: " | |
| f"source={len(tokenized_train[indice]['input_ids'])}, " | |
| f"target={len(tokenized_train[indice]['labels'])}" | |
| ) | |
| # ============================================================ | |
| # DATA COLLATOR | |
| # ============================================================ | |
| data_collator = DataCollatorForSeq2Seq( | |
| tokenizer=tokenizer, | |
| model=model, | |
| padding=True, | |
| ) | |
| # ============================================================ | |
| # ARGUMENTOS DE TREINAMENTO | |
| # ============================================================ | |
| training_args = Seq2SeqTrainingArguments( | |
| output_dir=OUTPUT_DIR, | |
| # -------------------------------------------------------- | |
| # ÉPOCAS | |
| # -------------------------------------------------------- | |
| num_train_epochs=NUM_EPOCHS, | |
| # -------------------------------------------------------- | |
| # BATCH | |
| # -------------------------------------------------------- | |
| per_device_train_batch_size=TRAIN_BATCH_SIZE, | |
| per_device_eval_batch_size=EVAL_BATCH_SIZE, | |
| # -------------------------------------------------------- | |
| # GRADIENT ACCUMULATION | |
| # -------------------------------------------------------- | |
| # Cada exemplo gera uma atualização. | |
| gradient_accumulation_steps=GRADIENT_ACCUMULATION_STEPS, | |
| # -------------------------------------------------------- | |
| # LEARNING RATE | |
| # -------------------------------------------------------- | |
| learning_rate=LEARNING_RATE, | |
| # Mantem o learning rate constante durante o teste. | |
| lr_scheduler_type="constant", | |
| warmup_steps=0, | |
| # Não queremos regularização adicional nesse teste. | |
| weight_decay=0.0, | |
| # -------------------------------------------------------- | |
| # LOGS | |
| # -------------------------------------------------------- | |
| logging_steps=1, | |
| # -------------------------------------------------------- | |
| # AVALIAÇÃO E CHECKPOINTS | |
| # -------------------------------------------------------- | |
| eval_strategy="epoch", | |
| save_strategy="epoch", | |
| save_total_limit=2, | |
| # Não é preciso gerar durante cada avaliação. | |
| # A geração será feita manualmente no final. | |
| predict_with_generate=False, | |
| # -------------------------------------------------------- | |
| # MEMÓRIA | |
| # -------------------------------------------------------- | |
| gradient_checkpointing=True, | |
| use_cpu=True, | |
| # Não enviar métricas para serviços externos. | |
| report_to="none", | |
| seed=SEED, | |
| ) | |
| # ============================================================ | |
| # TRAINER | |
| # ============================================================ | |
| trainer = Seq2SeqTrainer( | |
| model=model, | |
| args=training_args, | |
| train_dataset=tokenized_train, | |
| eval_dataset=tokenized_validation, | |
| processing_class=tokenizer, | |
| data_collator=data_collator, | |
| ) | |
| # ============================================================ | |
| # TREINAMENTO | |
| # ============================================================ | |
| print("\n" + "=" * 70) | |
| print("INICIANDO TREINAMENTO") | |
| print("=" * 70) | |
| print(f"Exemplos de treino: {len(tokenized_train)}") | |
| print(f"Épocas: {NUM_EPOCHS}") | |
| print( | |
| "Atualizações por época: " | |
| f"aproximadamente {len(tokenized_train)}" | |
| ) | |
| print( | |
| "Atualizações totais: " | |
| f"aproximadamente {len(tokenized_train) * NUM_EPOCHS}" | |
| ) | |
| print(f"Learning rate: {LEARNING_RATE}") | |
| print( | |
| "Gradient accumulation: " | |
| f"{GRADIENT_ACCUMULATION_STEPS}" | |
| ) | |
| train_result = trainer.train() | |
| print("\nTreinamento concluído.") | |
| print("\nMétricas finais de treino:") | |
| print(train_result.metrics) | |
| # ============================================================ | |
| # SALVAMENTO | |
| # ============================================================ | |
| print("\nSalvando modelo final...") | |
| trainer.save_model( | |
| FINAL_MODEL_DIR | |
| ) | |
| tokenizer.save_pretrained( | |
| FINAL_MODEL_DIR | |
| ) | |
| print( | |
| f"Modelo salvo em: {FINAL_MODEL_DIR}" | |
| ) | |
| # ============================================================ | |
| # RECARREGAMENTO | |
| # ============================================================ | |
| print("\nRecarregando modelo salvo...") | |
| final_tokenizer = AutoTokenizer.from_pretrained( | |
| FINAL_MODEL_DIR | |
| ) | |
| final_model = AutoModelForSeq2SeqLM.from_pretrained( | |
| FINAL_MODEL_DIR | |
| ) | |
| final_model.config.use_cache = True | |
| print("Modelo recarregado com sucesso.") | |
| # ============================================================ | |
| # GERAÇÃO | |
| # ============================================================ | |
| def gerar_traducao(texto, num_beams): | |
| prompt = f"English: {texto}\nMacushi:" | |
| inputs = final_tokenizer( | |
| prompt, | |
| return_tensors="pt", | |
| truncation=True, | |
| max_length=MAX_SOURCE_LENGTH, | |
| ) | |
| with torch.no_grad(): | |
| output_ids = final_model.generate( | |
| **inputs, | |
| max_length=MAX_TARGET_LENGTH, | |
| num_beams=num_beams, | |
| early_stopping=True, | |
| # Evita repetições muito óbvias. | |
| no_repeat_ngram_size=3, | |
| ) | |
| return final_tokenizer.decode( | |
| output_ids[0], | |
| skip_special_tokens=True, | |
| ) | |
| # ============================================================ | |
| # TESTE DE MEMORIZAÇÃO | |
| # ============================================================ | |
| print("\n" + "=" * 70) | |
| print("TESTE DE MEMORIZAÇÃO") | |
| print("=" * 70) | |
| print( | |
| "\nO modelo será testado nos próprios exemplos " | |
| "que foram utilizados durante o treinamento." | |
| ) | |
| print( | |
| "Isso NÃO é uma avaliação de generalização." | |
| ) | |
| print( | |
| "O objetivo aqui é verificar se o modelo consegue " | |
| "simplesmente aprender os pares apresentados." | |
| ) | |
| for indice, exemplo in enumerate( | |
| train_data[:5], | |
| start=1 | |
| ): | |
| print("\n" + "-" * 70) | |
| print(f"EXEMPLO {indice}") | |
| print( | |
| f"\nEN esperado:\n" | |
| f"{exemplo['source']}" | |
| ) | |
| print( | |
| f"\nMBC esperado:\n" | |
| f"{exemplo['target']}" | |
| ) | |
| # -------------------------------------------------------- | |
| # GREEDY | |
| # -------------------------------------------------------- | |
| traducao_gulosa = gerar_traducao( | |
| exemplo["source"], | |
| num_beams=1, | |
| ) | |
| # -------------------------------------------------------- | |
| # BEAM SEARCH | |
| # -------------------------------------------------------- | |
| traducao_beam = gerar_traducao( | |
| exemplo["source"], | |
| num_beams=4, | |
| ) | |
| print( | |
| f"\nGerado — greedy:\n" | |
| f"{traducao_gulosa}" | |
| ) | |
| print( | |
| f"\nGerado — beam 4:\n" | |
| f"{traducao_beam}" | |
| ) | |
| # ============================================================ | |
| # FIM | |
| # ============================================================ | |
| print("\n" + "=" * 70) | |
| print("TESTE FINALIZADO") | |
| print("=" * 70) | |
| print( | |
| "\nSe o modelo ainda não conseguir reproduzir " | |
| "os próprios exemplos de treinamento, " | |
| "vamos considerar este teste suficiente " | |
| "para abandonar essa abordagem e partir " | |
| "para outra estratégia." | |
| ) |