Pipeline Parallelism
При конвейерном параллелизме последовательные слои модели размещаются на разных GPU, а мини-пакеты данных проходят через них потоком, благодаря чему можно обучать модель, которая не помещается на одном GPU.
Конвейерный параллелизм — это метод распределённого обучения, при котором нейронную сеть делят по глубине на последовательные группы слоёв, называемые стадиями, размещают каждую стадию на отдельном GPU и передают активации от одной стадии к следующей. Чтобы все GPU были заняты, каждый батч делят на небольшие микробатчи, которые проходят стадии, как детали на сборочной линии. Этот подход также называют межслойным параллелизмом модели. Исследователи Google под руководством Яньпина Хуаня популяризировали подход с микробатчами в ноябре 2018 года с помощью GPipe: они обучили AmoebaNet с 557 миллионами параметров, достигшую точности top-1 84,4% на ImageNet, а также мультиязычный трансформер для машинного перевода с 6 миллиардами параметров и 128 слоями.
Микробатчи и пузырь конвейера#
Если обрабатывать по одному батчу за раз, работает только одна стадия, пока остальные ждут: стадия 2 не может начать работу, пока стадия 1 не завершится, а во время обратного распространения ошибки порядок меняется на обратный. Микробатчи позволяют стадии 1 начать обработку второго микробатча сразу после передачи первого стадии 2. Оставшиеся интервалы простоя, пока конвейер заполняется и опустошается, называют пузырём. Вывод прямого прохода в стиле GPipe для 4 стадий и 6 микробатчей выглядит так:
stages, micro_batches = 4, 6
steps = micro_batches + stages - 1 # time steps until the last micro-batch leaves the last stage
for stage in range(stages):
row = ""
for t in range(steps):
m = t - stage # stage s starts micro-batch m once stage s-1 hands it over
row += f" F{m}" if 0 <= m < micro_batches else " ."
print(f"GPU {stage}:{row}")
# Every stage idles for stages - 1 of the steps, in the forward and again in the backward pass
bubble = (stages - 1) / steps
print(f"Idle fraction: {bubble:.0%}")Результат напоминает лестницу: GPU 0 выполняет прямые проходы F0–F5 на первых шести из девяти шагов, а затем ждёт; GPU 3 ждёт три шага, прежде чем начать F0, а последняя строка выглядит так: Idle fraction: 33%. Доля пузыря равна (число стадий − 1) / (число микробатчей + число стадий − 1), поэтому 24 микробатча при тех же 4 стадиях сокращают её до 11%. Градиенты всех микробатчей суммируются перед единственным шагом оптимизатора — так же, как при накоплении градиентов, поэтому обновление соответствует обучению на полном батче. GPipe также удаляет активации внутри каждой стадии и пересчитывает их во время обратного прохода — это разновидность контрольных точек градиента.
Расписания конвейера#
В более поздних расписаниях те же операции переставляют, чтобы экономить память или уменьшать пузырь:
- 1F1B: Впервые реализовано в системе PipeDream в 2018 году: после заполнения конвейера каждая стадия чередует прямой и обратный проходы одного микробатча. Тогда каждая стадия хранит активации не более чем для такого же числа микробатчей, как всего стадий, а не для всех микробатчей.
- Чередующийся 1F1B: Разбивает работу каждого GPU на несколько более мелких несмежных стадий. Команда Megatron-LM из NVIDIA сообщила о приросте пропускной способности более чем на 10% и обучила модель с 1 триллионом параметров со скоростью 502 петафлопс на 3 072 GPU, достигнув 52% от пиковой производительности.
- Нулевой пузырь: Разделяет обратный проход на градиент входных данных стадии, который нужен предыдущей стадии, и градиент весов, которым можно позже заполнить интервалы простоя. При сопоставимом бюджете памяти пропускная способность оказывается до 23% выше, чем у 1F1B.
PyTorch предоставляет эти режимы как ScheduleGPipe, Schedule1F1B, ScheduleInterleaved1F1B, ScheduleInterleavedZeroBubble и ScheduleDualPipeV в своём API конвейерного параллелизма, который также разбивает модель на стадии. DualPipe разработан для DeepSeek-V3: команда использовала 16 стадий конвейера, чтобы совместить вычисления с передачей данных и уменьшить пузырь.
Применение конвейерного параллелизма#
Стадии обмениваются только активациями на своих границах и только один раз на микробатч, поэтому конвейерный параллелизм лучше переносит медленные соединения между серверами, чем тензорный параллелизм, при котором обмен данными происходит внутри каждого слоя. Поэтому в крупных обучающих запусках эти методы сочетают. Meta обучила Llama 3 405B, используя тензорный параллелизм с коэффициентом 8 внутри каждого сервера с 8 GPU, 16 стадий конвейера между серверами и параллелизм по данным на 64–128 реплик. Сервисы инференса, например vLLM, используют такое же разбиение, когда для большой языковой модели (LLM) требуется больше одного узла. Темп всего конвейера задаёт самая медленная стадия. Кроме того, в языковой модели на первую и последнюю стадии приходятся таблица эмбеддингов и выходной слой, поэтому в Llama 3 каждой из них выделили на один трансформерный слой меньше.
Родственные подходы по-разному разделяют работу:
- Тензорный параллелизм: Делит матрицы весов внутри каждого слоя, поэтому каждый GPU участвует в обработке каждого слоя.
- Контекстный параллелизм: Распределяет длинную входную последовательность между GPU.
- Разделённое обучение: Также делит сеть по глубине, но между организациями, чтобы владелец данных никогда не отправлял исходные данные на сервер, где выполняются последующие слои.
Параллелизм по данным оставляет каждый слой на каждом GPU и вместо этого делит батч. Этого достаточно, если модель помещается на одном устройстве. Так Ultralytics YOLO26 обучается на нескольких GPU: в режиме обучения с device=[0, 1, 2, 3] и batch=64 каждый GPU обрабатывает полную модель обнаружения объектов на 16 изображениях, а PyTorch DistributedDataParallel усредняет градиенты между GPU после каждого обратного прохода.










