Spaces:
Runtime error
Runtime error
fix: corrigir OOM e configurações de memória
Browse files- Adicionar OMP_NUM_THREADS=1 para evitar erro libgomp
- Configurar PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
- Reduzir batch size de 4 para 2 para economizar memória GPU
- Aumentar gradient_accumulation_steps de 2 para 4
- Adicionar use_cache=False no modelo
- Adicionar configurações de otimização de memória
- Remover código duplicado de TrainingArguments
- Dockerfile +16 -10
- train.py +97 -26
Dockerfile
CHANGED
|
@@ -2,6 +2,8 @@ FROM huggingface/transformers-pytorch-gpu:latest
|
|
| 2 |
|
| 3 |
WORKDIR /app
|
| 4 |
|
|
|
|
|
|
|
| 5 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 6 |
git \
|
| 7 |
python3 \
|
|
@@ -9,25 +11,29 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
| 9 |
python-is-python3 \
|
| 10 |
&& rm -rf /var/lib/apt/lists/*
|
| 11 |
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
| 13 |
|
|
|
|
| 14 |
COPY requirements.txt .
|
| 15 |
-
RUN
|
| 16 |
-
|
| 17 |
|
|
|
|
| 18 |
COPY train.py /app/train.py
|
| 19 |
COPY app.py /app/app.py
|
| 20 |
|
| 21 |
-
#
|
| 22 |
-
RUN mkdir -p /app/logs /app/outputs
|
| 23 |
-
|
| 24 |
-
VOLUME ["/app/logs", "/app/outputs"]
|
| 25 |
-
|
| 26 |
-
EXPOSE 6006
|
| 27 |
-
|
| 28 |
ENV MODEL_NAME=microsoft/Phi-3-mini-4k-instruct
|
| 29 |
ENV DATASET_REPO=beAnalytic/eda-training-dataset
|
| 30 |
ENV OUTPUT_REPO=beAnalytic/eda-llm-model
|
|
|
|
|
|
|
| 31 |
|
|
|
|
|
|
|
|
|
|
| 32 |
CMD ["python", "/app/app.py"]
|
| 33 |
|
|
|
|
| 2 |
|
| 3 |
WORKDIR /app
|
| 4 |
|
| 5 |
+
# Instalar dependências do sistema
|
| 6 |
+
# python-is-python3 cria automaticamente o symlink python -> python3
|
| 7 |
RUN apt-get update && apt-get install -y --no-install-recommends \
|
| 8 |
git \
|
| 9 |
python3 \
|
|
|
|
| 11 |
python-is-python3 \
|
| 12 |
&& rm -rf /var/lib/apt/lists/*
|
| 13 |
|
| 14 |
+
# Verificar que python está disponível (entrypoint do NVIDIA precisa)
|
| 15 |
+
RUN python --version && \
|
| 16 |
+
python3 --version && \
|
| 17 |
+
echo "✅ Python disponível: $(which python)"
|
| 18 |
|
| 19 |
+
# Instalar dependências Python
|
| 20 |
COPY requirements.txt .
|
| 21 |
+
RUN python3 -m pip install --no-cache-dir --upgrade pip && \
|
| 22 |
+
python3 -m pip install --no-cache-dir -r requirements.txt
|
| 23 |
|
| 24 |
+
# Copiar scripts de treinamento
|
| 25 |
COPY train.py /app/train.py
|
| 26 |
COPY app.py /app/app.py
|
| 27 |
|
| 28 |
+
# Configurar variáveis de ambiente padrão (podem ser sobrescritas)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
ENV MODEL_NAME=microsoft/Phi-3-mini-4k-instruct
|
| 30 |
ENV DATASET_REPO=beAnalytic/eda-training-dataset
|
| 31 |
ENV OUTPUT_REPO=beAnalytic/eda-llm-model
|
| 32 |
+
ENV OMP_NUM_THREADS=1
|
| 33 |
+
ENV PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True
|
| 34 |
|
| 35 |
+
# Executar treinamento
|
| 36 |
+
# Usar 'python' (que será o symlink para python3 criado acima)
|
| 37 |
+
# O entrypoint do NVIDIA espera 'python' estar disponível
|
| 38 |
CMD ["python", "/app/app.py"]
|
| 39 |
|
train.py
CHANGED
|
@@ -15,6 +15,7 @@ from transformers import (
|
|
| 15 |
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
|
| 16 |
from transformers import BitsAndBytesConfig
|
| 17 |
from huggingface_hub import login as hf_login, logout as hf_logout
|
|
|
|
| 18 |
import torch
|
| 19 |
import os
|
| 20 |
|
|
@@ -68,7 +69,50 @@ else:
|
|
| 68 |
|
| 69 |
# Carregar dataset
|
| 70 |
print(f"Carregando dataset: {DATASET_REPO}")
|
| 71 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 72 |
|
| 73 |
# Carregar modelo e tokenizer
|
| 74 |
print(f"Carregando modelo: {MODEL_NAME}")
|
|
@@ -88,6 +132,7 @@ model = AutoModelForCausalLM.from_pretrained(
|
|
| 88 |
quantization_config=bnb_config,
|
| 89 |
device_map="auto",
|
| 90 |
trust_remote_code=True,
|
|
|
|
| 91 |
)
|
| 92 |
|
| 93 |
# Preparar modelo para LoRA
|
|
@@ -107,18 +152,49 @@ model = get_peft_model(model, peft_config)
|
|
| 107 |
|
| 108 |
# Formatar prompts
|
| 109 |
def format_prompt(example):
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
|
| 121 |
-
prompt = f"<|system|>\n{system_prompt}\n<|user|>\n{instruction}\n\n{input_text}\n<|assistant|>\n{output_text}<|end|>"
|
| 122 |
return {"text": prompt}
|
| 123 |
|
| 124 |
# Aplicar formatação
|
|
@@ -147,32 +223,27 @@ def tokenize_function(examples):
|
|
| 147 |
train_dataset = train_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 148 |
eval_dataset = eval_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 149 |
|
| 150 |
-
# Criar diretórios manualmente
|
| 151 |
-
os.makedirs("./logs", exist_ok=True)
|
| 152 |
-
os.makedirs("./results", exist_ok=True)
|
| 153 |
-
|
| 154 |
# Configurar argumentos de treinamento
|
| 155 |
# push_to_hub_enabled já foi definido acima durante a autenticação
|
| 156 |
|
| 157 |
# Argumentos base de treinamento
|
| 158 |
-
# NOTA: logging_dir não é suportado em todas as versões do transformers
|
| 159 |
-
# O TensorBoard funcionará com report_to=["tensorboard"] e salvará logs no output_dir
|
| 160 |
training_args_dict = {
|
| 161 |
"output_dir": "./results",
|
| 162 |
"num_train_epochs": 3,
|
| 163 |
-
"per_device_train_batch_size":
|
| 164 |
-
"per_device_eval_batch_size":
|
| 165 |
"learning_rate": 3e-05,
|
| 166 |
"warmup_steps": 100,
|
| 167 |
-
"logging_steps":
|
| 168 |
-
"save_steps":
|
| 169 |
-
"eval_strategy": "steps",
|
| 170 |
-
"eval_steps":
|
| 171 |
"save_total_limit": 3,
|
| 172 |
"load_best_model_at_end": True,
|
| 173 |
"fp16": True,
|
| 174 |
-
"gradient_accumulation_steps":
|
| 175 |
-
"
|
|
|
|
| 176 |
}
|
| 177 |
|
| 178 |
# Adicionar parâmetros do Hub apenas se autenticado
|
|
|
|
| 15 |
from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training
|
| 16 |
from transformers import BitsAndBytesConfig
|
| 17 |
from huggingface_hub import login as hf_login, logout as hf_logout
|
| 18 |
+
from huggingface_hub import login as hf_login
|
| 19 |
import torch
|
| 20 |
import os
|
| 21 |
|
|
|
|
| 69 |
|
| 70 |
# Carregar dataset
|
| 71 |
print(f"Carregando dataset: {DATASET_REPO}")
|
| 72 |
+
try:
|
| 73 |
+
# Tentar carregar o arquivo JSONL expandido diretamente
|
| 74 |
+
print("Carregando dataset_balanceado_messages_expandido.jsonl...")
|
| 75 |
+
dataset_raw = load_dataset(
|
| 76 |
+
DATASET_REPO,
|
| 77 |
+
data_files={"train": "dataset_balanceado_messages_expandido.jsonl"},
|
| 78 |
+
verification_mode="no_checks",
|
| 79 |
+
split="train"
|
| 80 |
+
)
|
| 81 |
+
print(f"✅ Arquivo carregado: {len(dataset_raw)} exemplos")
|
| 82 |
+
|
| 83 |
+
# Dividir em train/validation (80/20)
|
| 84 |
+
print("Dividindo em train/validation (80/20)...")
|
| 85 |
+
dataset_split = dataset_raw.train_test_split(test_size=0.2, seed=42)
|
| 86 |
+
dataset = {
|
| 87 |
+
"train": dataset_split["train"],
|
| 88 |
+
"validation": dataset_split["test"]
|
| 89 |
+
}
|
| 90 |
+
print(f"✅ Dataset preparado: {len(dataset['train'])} exemplos de treino, {len(dataset['validation'])} exemplos de validação")
|
| 91 |
+
|
| 92 |
+
except Exception as e:
|
| 93 |
+
print(f"⚠️ Erro ao carregar arquivo específico: {e}")
|
| 94 |
+
print("Tentando carregar dataset padrão do repositório...")
|
| 95 |
+
try:
|
| 96 |
+
dataset = load_dataset(DATASET_REPO)
|
| 97 |
+
print(f"✅ Dataset padrão carregado")
|
| 98 |
+
if "train" not in dataset:
|
| 99 |
+
# Se não tiver split, criar
|
| 100 |
+
if len(dataset) == 1:
|
| 101 |
+
split_name = list(dataset.keys())[0]
|
| 102 |
+
dataset_raw = dataset[split_name]
|
| 103 |
+
dataset = dataset_raw.train_test_split(test_size=0.2, seed=42)
|
| 104 |
+
dataset = {
|
| 105 |
+
"train": dataset["train"],
|
| 106 |
+
"validation": dataset["test"]
|
| 107 |
+
}
|
| 108 |
+
print(f"✅ Dataset dividido: {len(dataset['train'])} treino, {len(dataset['validation'])} validação")
|
| 109 |
+
except Exception as e2:
|
| 110 |
+
print(f"❌ Erro ao carregar dataset: {e2}")
|
| 111 |
+
raise
|
| 112 |
+
|
| 113 |
+
# Configurar variáveis de ambiente para evitar problemas de memória
|
| 114 |
+
os.environ["OMP_NUM_THREADS"] = "1"
|
| 115 |
+
os.environ.setdefault("PYTORCH_CUDA_ALLOC_CONF", "expandable_segments:True")
|
| 116 |
|
| 117 |
# Carregar modelo e tokenizer
|
| 118 |
print(f"Carregando modelo: {MODEL_NAME}")
|
|
|
|
| 132 |
quantization_config=bnb_config,
|
| 133 |
device_map="auto",
|
| 134 |
trust_remote_code=True,
|
| 135 |
+
use_cache=False,
|
| 136 |
)
|
| 137 |
|
| 138 |
# Preparar modelo para LoRA
|
|
|
|
| 152 |
|
| 153 |
# Formatar prompts
|
| 154 |
def format_prompt(example):
|
| 155 |
+
# Suporta dois formatos: messages (ChatML) ou instruction/input/output
|
| 156 |
+
if "messages" in example:
|
| 157 |
+
# Formato messages (ChatML)
|
| 158 |
+
messages = example["messages"]
|
| 159 |
+
system_content = ""
|
| 160 |
+
user_content = ""
|
| 161 |
+
assistant_content = ""
|
| 162 |
+
|
| 163 |
+
for msg in messages:
|
| 164 |
+
role = msg.get("role", "")
|
| 165 |
+
content = msg.get("content", "")
|
| 166 |
+
if role == "system":
|
| 167 |
+
system_content = content
|
| 168 |
+
elif role == "user":
|
| 169 |
+
user_content = content
|
| 170 |
+
elif role == "assistant":
|
| 171 |
+
assistant_content = content
|
| 172 |
+
|
| 173 |
+
# Se não houver system, usar padrão
|
| 174 |
+
if not system_content:
|
| 175 |
+
system_content = (
|
| 176 |
+
"Você é um analista de dados sênior realizando uma Análise Exploratória de Dados (EDA) "
|
| 177 |
+
"com rigor estatístico, honestidade analítica e pensamento crítico.\n\n"
|
| 178 |
+
"Seu objetivo não é gerar insights a qualquer custo, mas avaliar se os dados possuem "
|
| 179 |
+
"estrutura informativa, comportamento emergente ou apenas relações estruturais triviais."
|
| 180 |
+
)
|
| 181 |
+
|
| 182 |
+
prompt = f"<|system|>\n{system_content}\n<|user|>\n{user_content}\n<|assistant|>\n{assistant_content}<|end|>"
|
| 183 |
+
else:
|
| 184 |
+
# Formato instruction/input/output (legado)
|
| 185 |
+
system_prompt = (
|
| 186 |
+
"Você é um analista de dados sênior realizando uma Análise Exploratória de Dados (EDA) "
|
| 187 |
+
"com rigor estatístico, honestidade analítica e pensamento crítico.\n\n"
|
| 188 |
+
"Seu objetivo não é gerar insights a qualquer custo, mas avaliar se os dados possuem "
|
| 189 |
+
"estrutura informativa, comportamento emergente ou apenas relações estruturais triviais."
|
| 190 |
+
)
|
| 191 |
+
|
| 192 |
+
instruction = example.get("instruction", "")
|
| 193 |
+
input_text = example.get("input", "")
|
| 194 |
+
output_text = example.get("output", "")
|
| 195 |
+
|
| 196 |
+
prompt = f"<|system|>\n{system_prompt}\n<|user|>\n{instruction}\n\n{input_text}\n<|assistant|>\n{output_text}<|end|>"
|
| 197 |
|
|
|
|
| 198 |
return {"text": prompt}
|
| 199 |
|
| 200 |
# Aplicar formatação
|
|
|
|
| 223 |
train_dataset = train_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 224 |
eval_dataset = eval_dataset.map(tokenize_function, batched=True, remove_columns=["text"])
|
| 225 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 226 |
# Configurar argumentos de treinamento
|
| 227 |
# push_to_hub_enabled já foi definido acima durante a autenticação
|
| 228 |
|
| 229 |
# Argumentos base de treinamento
|
|
|
|
|
|
|
| 230 |
training_args_dict = {
|
| 231 |
"output_dir": "./results",
|
| 232 |
"num_train_epochs": 3,
|
| 233 |
+
"per_device_train_batch_size": 2,
|
| 234 |
+
"per_device_eval_batch_size": 2,
|
| 235 |
"learning_rate": 3e-05,
|
| 236 |
"warmup_steps": 100,
|
| 237 |
+
"logging_steps": 10,
|
| 238 |
+
"save_steps": 500,
|
| 239 |
+
"eval_strategy": "steps",
|
| 240 |
+
"eval_steps": 500,
|
| 241 |
"save_total_limit": 3,
|
| 242 |
"load_best_model_at_end": True,
|
| 243 |
"fp16": True,
|
| 244 |
+
"gradient_accumulation_steps": 4,
|
| 245 |
+
"dataloader_pin_memory": False,
|
| 246 |
+
"ddp_find_unused_parameters": False,
|
| 247 |
}
|
| 248 |
|
| 249 |
# Adicionar parâmetros do Hub apenas se autenticado
|