You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

微调GPT-2时评估阶段触发CUDA内存不足错误求助

问题:WSL环境下GPT-2微调评估阶段触发CUDA内存溢出

在WSL环境中使用自定义数据集(训练集10000行、验证集1000行)微调GPT-2模型时,训练过程正常,但执行每500步一次的评估阶段时,触发以下CUDA内存不足错误:

torch.cuda.OutOfMemoryError: CUDA out of memory. Tried to allocate 7.72 GiB. GPU 0 has a total capacity of 24.00 GiB of which 13.38 GiB is free. Process 13650 has 17179869184.00 GiB memory in use. Process 48439 has 17179869184.00 GiB memory in use. Process 21062 has 17179869184.00 GiB memory in use. Process 40988 has 17179869184.00 GiB memory in use. Including non-PyTorch memory, this process has 17179869184.00 GiB memory in use. Of the allocated memory 9.14 GiB is allocated by PyTorch, and 187.59 MiB is reserved by PyTorch but unallocated. If reserved but unallocated memory is large try setting PYTORCH_CUDA_ALLOC_CONF=expandable_segments:True to avoid fragmentation.  See documentation for Memory Management  (https://pytorch.org/docs/stable/notes/cuda.html#environment-variables)

复现代码

import torch.utils
import torch.utils.data
from transformers import (
    GPT2LMHeadModel, GPT2Tokenizer, TextDataset, DataCollatorForLanguageModeling, Trainer, TrainingArguments, EarlyStoppingCallback, GPT2Config, get_linear_schedule_with_warmup, TrainerCallback)
import torch, np, GPUtil, os
from sklearn.metrics import accuracy_score

os.environ['PYTORCH_CUDA_ALLOC_CONF'] = 'expandable_segments:True'

BATCH_SIZE = 1
EPOCHS = 100
OUTPUT_DIR = "Models/"
SAVE_STEPS = 1000
EVAL_STEPS = 500
ACCUMULATION_STEPS = 2
EARLY_STOPPING_PATIENCE = 3
DROPOUT = 0.25
MODEL_NAME = f"gpt2-bs{BATCH_SIZE}-ep{EPOCHS}-ss{SAVE_STEPS}-es{EVAL_STEPS}-esp{EARLY_STOPPING_PATIENCE}-drpt-{DROPOUT}"
DATASET_PATH = "trainingData.txt"
VAL_DATASET_PATH = "validationData.txt"
BLOCK_SIZE = 128

# Check if a GPU is available and set the device
device = "cuda" if torch.cuda.is_available() else "cpu"
print("Training on device: {}".format(device))

# Load the pre-trained GPT model and tokenizer
model_name = 'gpt2'  # Change this to a larger model
model = GPT2LMHeadModel.from_pretrained(model_name).to(device)
config = GPT2Config.from_pretrained(model_name, dropout=DROPOUT)
tokenizer = GPT2Tokenizer.from_pretrained(model_name)
print("Loaded model and tokenizer from {}".format(model_name))
print("Transfering model to device: {}".format(device))
model = GPT2LMHeadModel.from_pretrained(model_name, config=config).to(device)
print("Transfer Complete")

# Load the text messages dataset
dataset_path = DATASET_PATH  # Replace with the path to your file
dataset = TextDataset(tokenizer=tokenizer, file_path=dataset_path, block_size=BLOCK_SIZE)
train_dataloader = torch.utils.data.DataLoader(dataset, batch_size=BATCH_SIZE, shuffle=True)
print("Created dataset from {}".format(dataset_path))

# Load the validation dataset
val_dataset_path = "validationData.txt"  # Replace with the path to your file
eval_dataset = TextDataset(tokenizer=tokenizer, file_path=val_dataset_path, block_size=BLOCK_SIZE)
print("Created evaluation dataset from {}".format(val_dataset_path))

# Prepare the data collator for language modeling
data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False)

# Define the optimizer
optimizer = torch.optim.AdamW(model.parameters(), lr=0.1)
# scheduler = torch.optim.lr_scheduler.StepLR(optimizer, step_size=1, gamma=0.95)

# Calculate the total number of training steps
total_steps = len(train_dataloader) * EPOCHS

# Define the training arguments
training_args = TrainingArguments(
    output_dir=OUTPUT_DIR,
    overwrite_output_dir=True,
    num_train_epochs=EPOCHS,
    per_device_train_batch_size=BATCH_SIZE,
    per_device_eval_batch_size=1,
    save_steps=SAVE_STEPS,
    save_total_limit=2,
    evaluation_strategy="steps",
    load_best_model_at_end=True,
    metric_for_best_model="loss",
    weight_decay=0.01,
    learning_rate=1e-6,  # Tune the learning rate
    # gradient_accumulation_steps=ACCUMULATION_STEPS,
    eval_steps=EVAL_STEPS,
    fp16=True,
    # no_cuda=True,
)

# Create the Trainer instance and start training
scheduler = get_linear_schedule_with_warmup(
    optimizer, num_warmup_steps=0, num_training_steps=total_steps
)

def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    predictions = np.argmax(predictions, axis=1)
    loss = torch.nn.functional.cross_entropy(predictions, labels)
    return {"accuracy": accuracy_score(labels, predictions), "loss": loss.item()}

class GPUMonitorCallback(TrainerCallback):
    def on_step_end(self, args, state, control, **kwargs):
        gpu = GPUtil.getGPUs()[0]  # Assuming you have 1 GPU
        print(f"GPU Utilization: {gpu.load*100}%, Memory Usage: {gpu.memoryUsed}, Free Memory: {gpu.memoryFree}, GPU Temperature: {gpu.temperature}°C")

trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=dataset,
    eval_dataset=eval_dataset,
    # callbacks=[GPUMonitorCallback()],
    callbacks=[EarlyStoppingCallback(early_stopping_patience=EARLY_STOPPING_PATIENCE), GPUMonitorCallback()],
    optimizers=(optimizer, scheduler),
    compute_metrics=compute_metrics,
)

print("Created data collator and trainer. Starting training...")

trainer.train().half()

print("Training completed. Saving the model...")

# Save the trained model and tokenizer
model.save_pretrained(MODEL_NAME)
tokenizer.save_pretrained(MODEL_NAME)
print("Model saved to {}".format(MODEL_NAME))

解决思路及代码修改建议

1. 修复重复加载模型的显存浪费

代码中两次加载GPT2LMHeadModel,第一次加载的模型未被释放,会占用额外显存。删除第一次无配置的模型加载,只保留带自定义dropout的加载:

# 删除以下无效代码行
# model = GPT2LMHeadModel.from_pretrained(model_name).to(device)
config = GPT2Config.from_pretrained(model_name, dropout=DROPOUT)
tokenizer = GPT2Tokenizer.from_pretrained(model_name)
print("Loaded model and tokenizer from {}".format(model_name))
print("Transfering model to device: {}".format(device))
model = GPT2LMHeadModel.from_pretrained(model_name, config=config).to(device)

2. 修正优化器与训练参数的冲突

手动定义的optimizer设置了lr=0.1,但TrainingArguments中已经指定learning_rate=1e-6,两者冲突且0.1的学习率过高。建议删除手动定义的优化器和调度器,让Trainer自动生成适配的优化器:

# 删除以下代码块
# optimizer = torch.optim.AdamW(model.parameters(), lr=0.1)
# total_steps = len(train_dataloader) * EPOCHS
# scheduler = get_linear_schedule_with_warmup(
#     optimizer, num_warmup_steps=0, num_training_steps=total_steps
# )

# 创建Trainer时去掉optimizers参数
trainer = Trainer(
    model=model,
    args=training_args,
    data_collator=data_collator,
    train_dataset=dataset,
    eval_dataset=eval_dataset,
    callbacks=[EarlyStoppingCallback(early_stopping_patience=EARLY_STOPPING_PATIENCE), GPUMonitorCallback()],
    compute_metrics=compute_metrics,
)

3. 修复compute_metrics函数的逻辑错误

语言模型的eval_pred中,predictions是形状为[batch_size, seq_len, vocab_size]的logits,原函数的维度处理和损失计算逻辑错误,会生成无效大张量占用显存。修改为:

def compute_metrics(eval_pred):
    predictions, labels = eval_pred
    # 转换为torch张量处理
    predictions = torch.tensor(predictions)
    labels = torch.tensor(labels)
    # 过滤掉pad token(label=-100)
    mask = labels != -100
    predictions = predictions[mask].view(-1, predictions.size(-1))
    labels = labels[mask].view(-1)
    # 计算交叉熵损失
    loss = torch.nn.functional.cross_entropy(predictions, labels)
    # 计算准确率
    preds = torch.argmax(predictions, dim=-1)
    accuracy = (preds == labels).float().mean().item()
    return {"accuracy": accuracy, "loss": loss.item()}

4. 修正训练启动代码的错误

trainer.train()返回的是TrainerState对象,调用.half()无意义,直接删除:

trainer.train()

5. 优化显存使用策略

  • 开启梯度累积:取消TrainingArguments中gradient_accumulation_steps的注释,用小batch模拟大batch效果,减少显存占用:
    gradient_accumulation_steps=ACCUMULATION_STEPS,
    
  • 添加评估前显存清理:自定义回调函数在评估前清理显存碎片:
    class EvalCleanupCallback(TrainerCallback):
        def on_evaluate(self, args, state, control, **kwargs):
            torch.cuda.empty_cache()
    
    # 添加到callbacks列表中
    callbacks=[EarlyStoppingCallback(early_stopping_patience=EARLY_STOPPING_PATIENCE), GPUMonitorCallback(), EvalCleanupCallback()],
    
  • 尝试关闭混合精度:若WSL环境中fp16评估存在兼容性问题,可设置fp16=False

6. 解决WSL显存统计异常问题

错误中显示的进程显存占用数值异常是WSL2的CUDA统计bug,可尝试:

  • 在启动脚本中添加环境变量:export PYTORCH_NO_CUDA_MEMORY_CACHING=1
  • 关闭其他占用GPU的进程,重启WSL后再运行训练

内容的提问来源于stack exchange,提问作者Morris van den Bergh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 18:44:57