COMRAD404 / GLOSSARY

Gradient accumulation

Gradient accumulation

Gradient accumulation — накопление градиентов на нескольких mini-batch перед одним шагом optimizer. Техника помогает получить больший effective batch size при ограниченной памяти, но не даёт выигрыша по throughput по сравнению с настоящим большим batch.

TL;DR

Техника обучения, при которой градиенты суммируются на нескольких mini-batch, а оптимизатор обновляется один раз в конце окна накопления.

Gradient accumulation — это техника обучения, при которой модель вычисляет градиенты на нескольких mini-batch подряд, а обновление оптимизатора делает только после завершения окна накопления. Она нужна прежде всего для экономии памяти: вы получаете больший effective batch size, не держа весь большой batch в памяти одновременно.

Английский термин: gradient accumulation. Также встречается: накопление градиента, накопление градиентов; рядом в документации обычно используются выражения mini-batch, micro-batch, effective batch size и gradient_accumulation_steps.

Практический вывод: используйте gradient accumulation, когда желаемый batch size не помещается в память. Не выбирайте его как способ ускорения: документация Hugging Face прямо отмечает, что по сравнению с настоящим большим batch этот приём не улучшает throughput.

Простыми словами

Грубо говоря, это похоже на ситуацию, когда вы не можете занести в лифт все коробки сразу. Вместо одной большой поездки вы делаете несколько маленьких, но итоговое решение принимаете только после того, как собрали весь объём.

В обучении модели роль «коробок» играют маленькие порции данных. Модель обрабатывает их по очереди, складывает вклад каждого прохода в градиенты и только потом делает один шаг оптимизатора.

Как это работает

Технически суть опирается на поведение autograd в PyTorch: градиенты накапливаются в .grad между вызовами backward(). Если вы не обнуляете их сразу, следующий backward добавляет новый вклад к уже посчитанному.

micro-batch 1 -> forward -> backward -> .grad += g1
micro-batch 2 -> forward -> backward -> .grad += g2
micro-batch 3 -> forward -> backward -> .grad += g3
micro-batch 4 -> forward -> backward -> .grad += g4

после окна накопления:
optimizer.step()
optimizer.zero_grad()
  1. Вы берёте первый mini-batch.
  2. Делаете прямой проход и backward().
  3. Градиенты записываются в .grad и не сбрасываются.
  4. Повторяете то же для следующих mini-batch.
  5. Когда число проходов достигло нужного gradient_accumulation_steps, вызываете optimizer.step().
  6. После шага оптимизатора вызываете optimizer.zero_grad(), чтобы следующее окно накопления началось с нуля.

Если упростить пример: batch на устройство равен 8, а gradient_accumulation_steps=4. Тогда обновление весов произойдёт после обработки 32 примеров, хотя в памяти одновременно лежали только 8.

Отдельный нюанс касается mixed precision. В примерах PyTorch AMP сказано, что при gradient accumulation градиенты должны оставаться scaled до конца эффективного batch; если вызвать unscale_ слишком рано, следующие backward смешают scaled и unscaled градиенты. Поэтому unscale_ следует вызывать непосредственно перед step.

Где применяется

  • Когда большой batch не помещается в память. Это основной сценарий: вы хотите обучать модель с более крупным effective batch size, но физическая память не позволяет загрузить его целиком.
  • В Hugging Face Transformers. Официальная документация указывает, что для accumulation используются TrainingArguments с параметрами per_device_train_batch_size и gradient_accumulation_steps.
  • В пользовательских и распределённых циклах обучения. В Hugging Face Accelerate для этого есть Accelerator(gradient_accumulation_steps=...) и контекстный менеджер with accelerator.accumulate(model):.
  • Для проверки эквивалентности с большим batch. В гайде Accelerate показан практический критерий корректности: при правильной настройке запуск с accumulation и запуск без accumulation могут привести к одинаковым итоговым весам.

Практический пример

Допустим, вам нужен effective batch из 32 примеров, но в память помещается только 8. Тогда вы оставляете маленький batch на устройство и откладываете шаг оптимизатора на четыре прохода.

from accelerate import Accelerator

accelerator = Accelerator(gradient_accumulation_steps=4)

for batch in train_dataloader:
    with accelerator.accumulate(model):
        outputs = model(**batch)
        loss = outputs.loss
        accelerator.backward(loss)
        optimizer.step()
        optimizer.zero_grad()

Здесь важна не длина кода, а порядок действий: backward вызывается на каждом mini-batch, а синхронизацию шага берёт на себя documented API Accelerate.

Если вы используете Trainer и собственную функцию потерь, следите за нормализацией loss. В документации Transformers отдельно оговорено деление на num_items_in_batch: для causal language modeling считаются shifted labels, а для других типов потерь — полный tensor labels. Это важный практический момент: неверная нормализация ломает эквивалентность с большим batch даже тогда, когда сам accumulation настроен формально правильно.

Как проверить настройку: возьмите небольшой воспроизводимый прогон и сравните его с вариантом без accumulation, но с тем же итоговым effective batch size. Документация Accelerate использует именно такую проверку и показывает совпадение итоговых весов при корректной реализации.

Чем отличается от обычного mini-batch и большого batch

Подход Когда вызывается optimizer.step() Память Скорость относительно true large batch Что обычно ожидают по результату
Обычный mini-batch без accumulation После каждого mini-batch Ниже Source pack это отдельно не детализирует Другую динамику обновлений, чем у более крупного batch
Gradient accumulation После нескольких mini-batch Ниже, чем у настоящего большого batch Документация Hugging Face не обещает выигрыша по throughput При корректной реализации может совпасть с эквивалентным большим batch
Настоящий большой batch После одного большого batch Выше Базовая точка сравнения Эталон для сравнения с accumulation

Если нужен смежный базовый термин, сначала освежите Backpropagation: именно через него считаются градиенты, которые затем накапливаются.

Ограничения и заблуждения

  • Заблуждение: «это способ ускорить обучение». По официальной документации Hugging Face, accumulation не улучшает throughput по сравнению с настоящим большим batch. Его главная цель — обойти ограничение памяти.
  • Заблуждение: «достаточно несколько раз вызвать backward()». Нет: нужно правильно выбрать момент для optimizer.step() и не забыть optimizer.zero_grad(), иначе градиенты продолжат складываться дальше.
  • Ограничение mixed precision. В AMP нельзя вызывать unscale_ слишком рано: это смешает scaled и unscaled градиенты в одном окне накопления.
  • Ограничение по нормализации loss. Для custom loss и особенно для token-level задач эквивалентность с большим batch зависит от того, как вы считаете количество элементов в batch. Transformers отдельно документирует num_items_in_batch и разницу между causal LM и другими loss.
  • Ограничение по версиям и backend. Семантика accumulation может различаться между версиями библиотек, настройками mixed precision и распределёнными backend’ами. Даже release notes PyTorch фиксируют внутренние изменения, связанные с gradient accumulation nodes, поэтому переносить старые рецепты без проверки рискованно.

Редакционное ограничение: этот материал опирается только на официальный source pack, актуальный на 2026-08-15. Он не покрывает все нюансы конкретных оптимизаторов, а также все частные сценарии DDP, DeepSpeed, FSDP и нестандартных loss-функций, поэтому для production-настройки сверяйтесь с версионной документацией именно вашего стека.

Связанные термины и инструменты

Источники

Вопросы и ответы

Gradient accumulation — это то же самое, что большой batch?

Не совсем. Это способ приблизить обучение с большим effective batch size, не загружая весь batch в память сразу. При корректной реализации результат может совпасть с настоящим большим batch, но документация не обещает выигрыша по throughput.

Нужно ли вызывать zero_grad() после каждого backward()?

Нет, если вы специально накапливаете градиенты. В PyTorch градиенты складываются в .grad, поэтому обнулять их нужно между шагами оптимизатора, а не после каждого backward.

Работает ли gradient accumulation с mixed precision?

Да, но важно не вызывать unscale_ слишком рано. Примеры PyTorch AMP рекомендуют держать градиенты scaled до завершения эффективного batch и делать unscale_ непосредственно перед step.

Как проверить, что accumulation настроен правильно?

Самый надёжный способ из официального гайда Accelerate — сравнить прогон с accumulation и прогон без accumulation при том же effective batch size. При корректной настройке итоговые веса должны совпадать.

Когда лучше использовать Transformers, а когда Accelerate?

Если вы обучаете модель через готовый Trainer, используйте параметры per_device_train_batch_size и gradient_accumulation_steps из Transformers. Если у вас ручной training loop или более сложная схема, удобнее официальный API Accelerate с accumulate().

Источники

SOURCES

Вопросы и ответы

FAQ
Gradient accumulation — это то же самое, что большой batch?

Не совсем. Это способ приблизить обучение с большим effective batch size, не загружая весь batch в память сразу. При корректной реализации результат может совпасть с настоящим большим batch, но документация не обещает выигрыша по throughput.

Нужно ли вызывать zero_grad() после каждого backward()?

Нет, если вы специально накапливаете градиенты. В PyTorch градиенты складываются в .grad, поэтому обнулять их нужно между шагами оптимизатора, а не после каждого backward.

Работает ли gradient accumulation с mixed precision?

Да, но важно не вызывать unscale_ слишком рано. Примеры PyTorch AMP рекомендуют держать градиенты scaled до завершения эффективного batch и делать unscale_ непосредственно перед step.

Как проверить, что accumulation настроен правильно?

Самый надёжный способ из официального гайда Accelerate — сравнить прогон с accumulation и прогон без accumulation при том же effective batch size. При корректной настройке итоговые веса должны совпадать.

Когда лучше использовать Transformers, а когда Accelerate?

Если вы обучаете модель через готовый Trainer, используйте параметры per_device_train_batch_size и gradient_accumulation_steps из Transformers. Если у вас ручной training loop или более сложная схема, удобнее официальный API Accelerate с accumulate().

Читайте также

LINKS