← Назад к вопросам
Расскажите про gradient accumulation
1.0 Junior🔥 151 комментариев
#Глубокое обучение
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Gradient Accumulation в Deep Learning
Gradient Accumulation — техника, позволяющая тренировать модели с большим effective batch size на GPU с ограниченной памятью, путём накопления градиентов нескольких малых батчей перед обновлением весов.
Проблема: Ограничение памяти GPU
Оптимально: batch_size = 256 (32 GB GPU память)
Реальность: GPU память = 8 GB
максимум: batch_size = 64
Проблема: меньше batch_size → хуже обучение
# Проблема
model = BigTransformer() # 1.2B параметров
batch_size = 256 # оптимально
try:
output = model(batch) # CUDA Out of Memory!
except:
# GPU имеет только 8GB, нужно 32GB
pass
Решение: Gradient Accumulation
Идея: вместо одного большого батча, используй несколько малых
Без Gradient Accumulation:
Батч 1 (размер 64) → forward → backward → обновить веса
С Gradient Accumulation (accumulation_steps=4):
Батч 1 (размер 64) → forward → backward → копить градиенты
Батч 2 (размер 64) → forward → backward → копить градиенты
Батч 3 (размер 64) → forward → backward → копить градиенты
Батч 4 (размер 64) → forward → backward → скопить + обновить веса
EFFECTIVE BATCH SIZE = 64 × 4 = 256
Реализация в PyTorch
import torch
from torch import nn, optim
model = BigTransformer()
optimizer = optim.Adam(model.parameters(), lr=1e-4)
accumulation_steps = 4
effective_batch_size = batch_size * accumulation_steps # 256
for epoch in range(num_epochs):
for step, (batch_x, batch_y) in enumerate(train_loader):
# Forward pass
output = model(batch_x)
loss = criterion(output, batch_y)
# Нормализуем loss по accumulation_steps
loss_normalized = loss / accumulation_steps
# Backward pass (накапливаем градиенты)
loss_normalized.backward()
# Обновляем веса каждые accumulation_steps
if (step + 1) % accumulation_steps == 0:
optimizer.step() # Обновить веса
optimizer.zero_grad() # Обнулить градиенты
print(f"Updated weights at step {step}")
С использованием Hugging Face Transformers
from transformers import Trainer, TrainingArguments
training_args = TrainingArguments(
output_dir='./results',
num_train_epochs=3,
per_device_train_batch_size=32, # физический batch size
gradient_accumulation_steps=8, # accumulate 8 times
# Effective batch size = 32 × 8 = 256
learning_rate=5e-5,
warmup_steps=1000,
weight_decay=0.01,
fp16=True, # mixed precision для экономии памяти
)
trainer = Trainer(
model=model,
args=training_args,
train_dataset=train_dataset,
)
trainer.train()
Математически
# Без накопления градиентов
G = ∇L(batch) # gradient for one batch
θ = θ - lr × G # update weights immediately
# С накоплением (accumulation_steps=N)
G_accumulated = 0
for i in range(N):
L_i = loss(model(batch_i), y_i)
G_i = ∇(L_i / N) # нормализуем loss!
G_accumulated += G_i # копим градиент
θ = θ - lr × G_accumulated # обновляем один раз
Важно: теряем loss на N, чтобы получить корректный gradient!
loss_normalized = loss / accumulation_steps
loss_normalized.backward() # правильный градиент
Пример из боевой практики
class TrainingLoop:
def __init__(self, model, batch_size=32, accumulation_steps=8):
self.model = model
self.batch_size = batch_size
self.accumulation_steps = accumulation_steps
self.effective_batch_size = batch_size * accumulation_steps
self.optimizer = optim.Adam(model.parameters())
def train_epoch(self, dataloader):
self.model.train()
total_loss = 0
for step, (inputs, labels) in enumerate(dataloader):
# Forward pass
outputs = self.model(inputs)
loss = F.cross_entropy(outputs, labels)
# Normalize for accumulation
loss_scaled = loss / self.accumulation_steps
# Backward pass
loss_scaled.backward()
total_loss += loss.item()
# Update weights
if (step + 1) % self.accumulation_steps == 0:
# Clip gradients (optional)
torch.nn.utils.clip_grad_norm_(
self.model.parameters(), max_norm=1.0
)
# Update
self.optimizer.step()
self.optimizer.zero_grad()
effective_step = (step + 1) // self.accumulation_steps
print(f"Effective step {effective_step}: "
f"loss={total_loss/effective_step:.4f}")
return total_loss / len(dataloader)
Преимущества и недостатки
✓ Преимущества:
- Используешь большой effective batch size на маленькой GPU
- Лучше обучение (как на большом батче)
- Экономия памяти (обучаешь большие модели)
- Стабильнее训练 (благодаря большему batch size)
✗ Недостатки:
- Медленнее обучение (обновления реже)
- Сложнее отладка (нужно делить loss правильно)
- Может требовать регулировки learning rate
Сравнение
Без накопления (batch_size=256):
Шаг 1: обработка 256 образцов
Шаг 2: обработка 256 образцов
Шаг 3: обработка 256 образцов
Время на 1 обновление: 3 шага
Память: 32 GB
С накоплением (batch_size=64, accumulation_steps=4):
Шаг 1: обработка 64 образцов
Шаг 2: обработка 64 образцов
Шаг 3: обработка 64 образцов
Шаг 4: обработка 64 образцов → обновление
Время на 1 обновление: 4 шага (медленнее)
Память: 8 GB (эффективнее)
Эффективный batch size: 256 (как без накопления!)
Лучшие практики
# 1. Всегда нормализуй loss
loss = loss / accumulation_steps # ✓ правильно
# 2. Обнуляй градиенты правильно
if (step + 1) % accumulation_steps == 0:
optimizer.zero_grad() # обнулить после обновления
# 3. Clip градиенты перед обновлением
if (step + 1) % accumulation_steps == 0:
torch.nn.utils.clip_grad_norm_(model.parameters(), 1.0)
# 4. Мониторь "эффективный" шаг обновления
effective_step = (step + 1) // accumulation_steps
# 5. С mixed precision (AMP)
with autocast():
loss = model(batch)
loss_scaled = loss / accumulation_steps
scaler.scale(loss_scaled).backward() # PyTorch autocast
Резюме
Gradient Accumulation:
- Накопление градиентов несколько батчей
- Эффективный batch size = batch_size × accumulation_steps
- Решает проблему ограничения памяти GPU
- Требует нормализации loss и правильного обнуления градиентов
- Медленнее, но более памяти-эффективно
- Essential для обучения больших моделей на слабых GPU