amarorn commited on
Commit
212ba47
·
1 Parent(s): d91fc67

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

Files changed (2) hide show
  1. Dockerfile +16 -10
  2. 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
- RUN python --version && python3 --version
 
 
 
13
 
 
14
  COPY requirements.txt .
15
- RUN python -m pip install --no-cache-dir --upgrade pip && \
16
- python -m pip install --no-cache-dir -r requirements.txt
17
 
 
18
  COPY train.py /app/train.py
19
  COPY app.py /app/app.py
20
 
21
- # Diretórios explícitos
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
- dataset = load_dataset(DATASET_REPO)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
- system_prompt = (
111
- "Você é um analista de dados sênior realizando uma Análise Exploratória de Dados (EDA) "
112
- "com rigor estatístico, honestidade analítica e pensamento crítico.\n\n"
113
- "Seu objetivo não é gerar insights a qualquer custo, mas avaliar se os dados possuem "
114
- "estrutura informativa, comportamento emergente ou apenas relações estruturais triviais."
115
- )
116
-
117
- instruction = example.get("instruction", "")
118
- input_text = example.get("input", "")
119
- output_text = example.get("output", "")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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": 4,
164
- "per_device_eval_batch_size": 4,
165
  "learning_rate": 3e-05,
166
  "warmup_steps": 100,
167
- "logging_steps": 1, # Para ver métricas rapidamente no TensorBoard
168
- "save_steps": 10, # Salvar frequentemente para ver progresso
169
- "eval_strategy": "steps", # OBRIGATÓRIO para métricas de validação
170
- "eval_steps": 10, # Avaliar frequentemente para ver no TensorBoard
171
  "save_total_limit": 3,
172
  "load_best_model_at_end": True,
173
  "fp16": True,
174
- "gradient_accumulation_steps": 2,
175
- "report_to": ["tensorboard"], # EXPLÍCITO - obrigatório para TensorBoard
 
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