За последние 10 месяцев, работая над PyTorch Lightning, команда и я познакомились со многими стилями структурирования кода PyTorch, и мы определили несколько ключевых мест, где мы видим, что люди непреднамеренно создают узкие места.

Мы очень позаботились о том, чтобы PyTorch Lightning не допустил ни одной из этих ошибок для кода, который мы автоматизируем для вас, и даже пытаемся исправить это для пользователей, когда мы их обнаруживаем. Однако, поскольку Lightning - это просто структурированный PyTorch, а вы по-прежнему контролируете весь научный PyTorch, во многих случаях мы мало что можем сделать для пользователя.

Кроме того, если вы не используете Lightning, вы можете случайно внести эти проблемы в свой код.

Чтобы помочь вам тренироваться быстрее, вот 8 советов, о которых вы должны знать, которые могут замедлить ваш код.

Используйте воркеры в DataLoaders

Эту первую ошибку легко исправить. PyTorch позволяет загружать данные сразу по нескольким процессам (документация).

В этом случае PyTorch может обойти блокировку GIL, обработав 8 пакетов, каждый в отдельном процессе. Сколько рабочих нужно использовать? Хорошее практическое правило:

num_worker = 4 * num_GPU

В этом ответе это хорошо обсуждается.

Предупреждение: Обратной стороной является увеличение использования памяти (источник).

Пин-память

Вы знаете, как иногда ваша память графического процессора показывает, что она заполнена, но вы уверены, что ваша модель не так много использует? Эти накладные расходы называются закрепленной памятью. т.е. эта память зарезервирована как тип «рабочего распределения».

Когда вы включаете pinned_memory в DataLoader, он автоматически помещает полученные тензоры данных в закрепленную память и обеспечивает более быструю передачу данных на графические процессоры с поддержкой CUDA (источник).

Это также означает, что вам не следует без надобности звонить:

torch.cuda.empty_cache()

Избегайте передачи ЦП на ГП или наоборот

# bad
.cpu()
.item()
.numpy()

Я вижу интенсивное использование вызовов .item (), .cpu () или .numpy (). Это действительно плохо сказывается на производительности, потому что каждый из этих вызовов передает данные с графического процессора на процессор и резко снижает производительность.

Если вы пытаетесь очистить прикрепленный вычислительный график, используйте вместо него .detach ().

# good
.detach()

Это не передаст память в графический процессор и удалит все вычислительные графики, привязанные к этой переменной.

Создавайте тензоры прямо на графических процессорах

Большинство людей создают тензоры на таких графических процессорах

t = tensor.rand(2,2).cuda()

Однако это сначала создает тензор ЦП, а ЗАТЕМ передает его на графический процессор… это очень медленно. Вместо этого создайте тензор прямо на нужном устройстве.

t = tensor.rand(2,2, device=torch.device('cuda:0'))

Если вы используете Lightning, мы автоматически поместим вашу модель и партию на нужный графический процессор. Но если вы где-то создаете новый тензор внутри своего кода (то есть: образец случайного шума для VAE или что-то в этом роде), тогда вы должны сами поместить тензор.

t = tensor.rand(2,2, device=self.device)

Каждый LightningModule имеет удобный вызов self.device, который работает независимо от того, используете ли вы ЦП, несколько графических процессоров или TPU (то есть: Lightning выберет правильное устройство для этого тензора.

Используйте DistributedDataParallel, а не DataParallel

PyTorch имеет две основные модели для обучения на нескольких графических процессорах. Первый, DataParallel (DP), разделяет пакет между несколькими графическими процессорами. Но это также означает, что модель должна быть скопирована на каждый графический процессор, и после того, как градиенты вычислены на GPU 0, они должны быть синхронизированы с другими графическими процессорами.

Это много дорогих передач GPU! Вместо этого DistributedDataParallel (DDP) создает изолированную копию модели на каждом графическом процессоре (в своем собственном процессе) и делает только часть данных доступной для этого графического процессора. Тогда это похоже на обучение N независимых моделей, за исключением того, что после того, как каждая из них вычисляет градиенты, все они синхронизируют градиенты между моделями ... это означает, что мы передаем данные между графическими процессорами только один раз во время каждого пакета.

В Lightning можно тривиально переключаться между обоими

Trainer(distributed_backend='ddp', gpus=8)
Trainer(distributed_backend='dp', gpus=8)

Обратите внимание, что и PyTorch, и Lightning не рекомендуют использовать DP.

Используйте 16-битную точность

Это еще один способ ускорить обучение, который мы не видим многими людьми. В 16-битном обучении части вашей модели и ваши данные переходят от 32-битных чисел к 16-битным числам. У этого есть несколько преимуществ:

  1. Вы используете половину памяти (что означает, что вы можете удвоить размер пакета и вдвое сократить время обучения).
  2. Некоторые графические процессоры (V100, 2080Ti) обеспечивают автоматическое ускорение (в 3-8 раз быстрее), поскольку они оптимизированы для 16-битных вычислений.

В Lightning легко включить:

Trainer(precision=16)

Примечание. До PyTorch 1.6 вам ТАКЖЕ нужно было установить Nvidia Apex… теперь 16-битная версия является родной для PyTorch. Но если вы используете Lightning, он поддерживает оба и автоматически переключается в зависимости от обнаруженной версии PyTorch.

Профилируйте свой код

Этот последний совет может быть трудно обойтись без Lightning, но вы можете использовать для этого такие вещи, как cprofiler. Однако в Lightning вы можете получить сводку всех вызовов, сделанных во время тренировки, двумя способами:

Во-первых, встроенный базовый профайлер

Trainer(profile=True)

Что дает такой вывод:

или расширенный профилировщик:

profiler = AdvancedProfiler()
trainer = Trainer(profiler=profiler)

который становится очень гранулированным

Полную документацию по профилировщику Lightning можно найти здесь.

Внедрение Lightning в ваш код

PyTorch Lightning - это не что иное, как структурированный PyTorch.

Если вы готовы автоматизировать большинство этих советов (и хорошо протестировать), посмотрите это видео о рефакторинге кода PyTorch в формат Lightning!