Pytorch: RFC: добавьте флаг torch.deterministic для принудительного применения детерминированных алгоритмов

Созданный на 18 дек. 2018  ·  67Комментарии  ·  Источник: pytorch/pytorch

🚀 Особенность

Мы должны добавить глобальную переменную, чтобы PyTorch использовал побитовые детерминированные алгоритмы. Сумит предлагает добавить флаг в подпакет torch.experimental , потому что мы не уверены в некоторых деталях.

Мотивация

Поразрядный детерминизм между запусками иногда полезен для отладки. Однако для некоторых операций сложно написать эффективные детерминированные алгоритмы.

Подача

Когда torch.experimental.deterministic равно False (по умолчанию), PyTorch должен использовать самый быстрый алгоритм, доступный для данной операции. Когда torch.experimental.deterministic равно True , PyTorch должен использовать только детерминированные алгоритмы. PyTorch должен выдать предупреждение, если у нас нет детерминированного алгоритма, доступного для данной операции, а torch.experimental.deterministic равно True .

cuDNN

У нас уже есть флаг torch.backends.cudnn.deterministic для управления выбором алгоритма cuDNN. Мы должны оставить этот флаг на данный момент и ограничить cuDNN детерминированными алгоритмами, если значение torch.backends.cudnn.deterministic или torch.experimental.deterministic равно True.

Нецелевые

Мы стремимся только к поразрядному детерминированию между запусками на машинах с одинаковой архитектурой и конфигурацией. Например, даже когда torch.experimental.deterministic имеет значение True, мы не стремимся к побитовому детерминизму, когда изменяется любое из следующих условий:

  • Версия PyTorch
  • Архитектура ЦП (например, x86 с AVX против ARM)
  • Архитектура графического процессора (например, AMD против NVIDIA или P100 против V100)
  • Зависимости библиотек (например, OpenBLAS против MKL)
  • Количество потоков OpenMP

Предложения по реализации

Я предлагаю добавить эту функцию в два этапа. Первый шаг - добавить флаг torch.backends.cudnn.deterministic и добавить предупреждения для любых недетерминированных операций. Второй шаг - добавить детерминированные реализации для недетерминированных операций.

В документации PyTorch есть неполный список недетерминированных операций.

Открытые вопросы

Как torch.experimental.deterministic взаимодействовать с семенем ГСЧ? Должен ли он устанавливать начальное число по умолчанию, если не было задано начальное число вручную? Должен ли он выдавать предупреждение, если ручной посев не был установлен?

Копия @ezyang @gchanan @ zou3519

feature high priority determinism internals triaged

Самый полезный комментарий

Привет, я хочу поговорить о плане в будущем для torch.deterministic . Нам нужно ответить на несколько вопросов высокого уровня:

  1. Какова семантика torch.deterministic ? Чего ожидает пользователь? Действительно ли максимальное усилие полезно для пользователя? Если это бесполезно, лучше ли определить torch.deterministic с точки зрения того, какие операции он контролирует?
  2. Теперь, когда у нас есть флаг torch.deterministic , имеет ли смысл полностью исключить аргумент deterministic= keyword из общедоступного API ( bmm , я смотрю на вас).
  3. Каким будет конец этой работы? Над какой частью этого вы (@kurtamohler) собираетесь работать по сравнению с общим сообществом, и когда мы подойдем к концу вашего пребывания здесь, как будет выглядеть разумное состояние?

Начиная с (1), текущая документация для torch.deterministic гласит:

     r"""Sets a global flag to force all operations to use a deterministic
    implementation if available. If an operation that does not have a
    deterministic implementation is called while this setting is True, the
    operation will throw a RuntimeError.

    Note that deterministic operations tend to have worse performance than
    non-deterministic operations.

Хотя это может быть верно для возможного конечного состояния, это неточно представляет текущую ситуацию, когда многие операции не были проверены, и для любой данной модели мы не знаем, действительно ли torch.deterministic будет делать то, что он говорит на жестяной банке и сделает вашу модель детерминированной / выдаст ошибку при нажатии nondet. Таким образом, наша реализация содержит ошибки в отношении этой семантики, и в обозримом будущем они останутся ошибочными. Это не лучшее состояние для жизни.

Мы могли бы изменить документацию torch.deterministic, чтобы исправить это. Некоторые возможные изменения:

  • torch.deterministic - лучший вариант, но, пожалуйста, сообщайте об ошибках, если вы видите, что он не обнаруживает недетерминизма
  • torch.deterministic переключает поведение этих операторов (а затем дает исчерпывающий список операторов, которые он переключает)

Второй пункт приводит к (2): если torch.deterministic теперь существует как способ переключения детерминизма, гораздо менее важно поддерживать детерминизм непосредственно в пользовательском API. Так что нам, вероятно, не стоило добавлять аргумент deterministic в bmm. Мы могли бы рассмотреть возможность раскрытия внутренней функции, если вы хотите что-то переключить напрямую, но deterministic не должно быть доступно непосредственно в самой функции.

Что вы думаете? Я думаю, что изменение документации - это, вероятно, самый простой способ встать на устойчивый путь. Есть и другие детали, например, как заполнить исчерпывающий список, но эта семантика, вероятно, имеет больше смысла, чем «идеальная» семантика, которая на самом деле не будет истинной.

cc @gchanan @mruberry

Все 67 Комментарий

Это мой большой палец вверх. Проблема будет в первую очередь в том, как на самом деле развернуть это повсюду в кодовой базе; нет ничего хуже, если заявить, что мы детерминированы, но втайне это не так :)

Я полностью за это, и мой подход заключался бы в том, чтобы отмечать операции и ошибки, когда детерминированный режим включен, а мы знаем, что это не так.

Я считаю, что ошибки в недетерминированных операциях слишком суровы. Предупреждение кажется более плавным

Я думаю, что по умолчанию должен быть throw, но я думаю, мы могли бы поддерживать там многозначное свойство (недетерминированное - это нормально, предупреждать, бросать).

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

Ошибка, предупреждение, правильная документация ...
Последнее обязательно.
Предупреждение или ошибка? Пойду с ошибкой.

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

Спасибо за взвешивание. В конце концов, главное усилие для троичного флага - это сам флаг, и это несложно.
Я добавлю флаг в Context.h и добавлю (через служебную функцию) AT_ERROR и AT_CHECK.

Привет,
Есть новости об этом флаге?
Детерминизм имеет решающее значение.
По моему опыту, текущая версия позволяет детерминировать более одного графического процессора с точностью до 1e-16 , используя фиксированные начальные числа. Обратите внимание, что бесконечно малая разница может усиливаться и приводить к расхождению результатов.

Пожалуйста, рассмотрите также случай multigpu (по крайней мере, для фиксированного K gpus поведение должно быть детерминированным. Я могу достичь некоторого детерминизма, который время от времени выходит из строя по причине, которую я пока не понимаю (используя nightly build 1.2.0.dev20190616 ).). Я борюсь с этим прямо сейчас ( 1 , 2 ).

Спасибо!

@ t-vi ты активно над этим работаешь?

Я не хочу мешать тебе это делать.

@ t-vi Извините, если я не понял, я не планирую над этим работать :). Просто пытался понять, активно ли это кто-то делает.

Спустя почти год проблема недетерминированной интерполяции все еще не решена.

Надеюсь, сообщество добавит эту функцию :)

Может быть, детерминированная интерполяция очень поможет пользователям.

~ На самом деле я еще не рекламировал его, но, учитывая, что, похоже, интерес пользователей больше, чем выделенные ресурсы разработчика, у меня он указан как проект, за который вы можете проголосовать на моей странице спонсорства github, когда я его настрою.
Я совершенно уверен, что мы сможем добиться хороших результатов к концу года, и интерполяция, безусловно, является одной из вещей, которые у меня есть план, как исправить (похоже на псевдокод для складки, который я где-то в проблемах), но просто не Не занимаю лидирующие позиции в моем собственном списке приоритетов. ~
Оказалось не интересно.

детерминированная интерполяция будет огромным подспорьем. ссылка на сайт

Приоритет повышения, особенно для CUDA, на основе отзывов пользователей

Рад, что починили, спасибо!

@ t-vi, честно говоря, я не думаю, что «повышение приоритета» эквивалентно «исправлено» :).

С нетерпением жду решений!

Колсбери упомянул, что одна из главных причин появления детерминированных алгоритмов заключается не в том, что детерминизм на самом деле является проблемой, а в том, что вы можете исключить его, если включите это;)

Как torch.experimental.deterministic взаимодействовать с семенем ГСЧ? Должен ли он устанавливать начальное число по умолчанию, если не было задано начальное число вручную? Должен ли он выдавать предупреждение, если ручной посев не был установлен?

Я бы посоветовал не устанавливать начальное число, если оно не было установлено пользователем. Во-первых, потому что он объединяет два интерфейса, в которых нет необходимости (я думаю, пользователи, которым небезразличен детерминизм, очень хорошо поймут ГСЧ). Что еще более важно, это очень сложно сделать надежно; можно использовать ГСЧ в многопроцессорных / многопоточных приложениях, иметь другие подклассы torch.Generator , также использовать numpy.random и т. д.

Не уверен в предупреждении, только если есть разумное место для его установки (например, вы затем заставляете семя перед determinism=True а не в том же модуле / функции, где используется ГСЧ?).

Мне просто любопытно, что когда я устанавливаю torch.backends.cudnn.deterministic=True , оператор интерполяции по-прежнему не может быть детерминированным. Интерполяция pytorch не использует cudnn?

Может и нет. Вы можете nvprof выполнить интерполяцию, чтобы проверить наверняка.

Мне интересно, следует ли нам продолжать предоставлять аргументы deterministic в вызовах функций после реализации torch.experimental.deterministic . Возможно, нам стоит, потому что пользователь может предпочесть детерминизм для некоторых операций и скорость для других операций.

Если мы сохраним аргументы, то что произойдет, если torch.experimental.deterministic и флаг функции deterministic противостоят друг другу. Если torch.experimental.deterministic = True означает "использовать детерминизм во всех случаях независимо от того, что" или должно означать "использовать детерминизм в качестве значения по умолчанию, но если аргумент deterministic указан в вызове функции, тогда используйте этот параметр для конкретного вызова функции ". Другими словами, как обращаться с приведенным ниже кодом? Кто-нибудь знает, как флаг torch.backends.cudnn.deterministic действует в подобной ситуации?

torch.experimental.deterministic = True
torch.some_operation(deterministic=False)

@kurtamohler Хороший вопрос. Я думаю, что самое простое решение - сделать это bool? deterministic=None , а затем интерпретировать None как "уважать torch.experimental.deterministic ", а в противном случае использовать именно то, что запросил пользователь.

У нас вроде бы похожая ситуация со сверткой, но способ, которым это было сделано, заключался в том, что есть convolution без аргумента benchmark , а затем _convolution с явным ориентир.

Я думаю, что любое из этих решений было бы приемлемым; однако подход свертки имеет дополнительное преимущество, заключающееся в том, что внутренний флаг deterministic попадает в видимый для пользователя API (если они не используют внутренний API).

Каково обоснование «Я хочу быть детерминированным везде, но не в этом конкретном операторе»? Действительно ли это должно быть достаточно распространенным вариантом использования, чтобы гарантировать добавление дополнительных входных данных для многих наших операторов (и большинства сложных)? ИМО было бы лучше предоставить менеджеров контекста для переключения детерминизма.

@apaszke , да, я думаю, вы правы, что было бы лучше просто использовать диспетчеры контекста для переключения детерминизма. Я бы не сказал, что мы должны добавлять аргумент deterministic к каким-либо операторам, но у некоторых операторов он уже есть. Было бы лучше удалить их все и сломать BC, или лучше оставить их и позволить им переопределить torch.experimental.deterministic ?

Я бы сказал, что мы должны удалить его или, по крайней мере, сделать его закрытым (например, префикс подчеркивания или sth).

Мне интересно, закрыта ли детерминированная функция интерполяции и не будет ли реализована?

Нет, мы поддаемся детерминированным версиям ВСЕХ функций в PyTorch

@ezyang какая версия pytorch имеет детерминированную функцию F.interpolate? это начиная с pytorch 1.6? или он доступен в последней стабильной версии (1.5)? или мне нужно загрузить и установить Pytorch из исходников?

Я был бы рад начать работать над этим

Вышеупомянутый коммит только добавляет флаг, он пока не влияет на какие-либо операции. Я был бы признателен, если бы кто-нибудь мог потратить несколько минут, чтобы взглянуть на это и сообщить мне, если я сделал что-то неправильно или можно ли что-то улучшить до сих пор. Я основывал это на том, как реализовано torch.backends.cudnn.deterministic .

Это выглядит нормально, но я чувствую, что внутреннее именование не должно включать экспериментальное (поскольку, якобы, когда-нибудь вы захотите сделать его не экспериментальным, и это не должно включать переименование всех битов реализации!)

@ezyang , да в этом есть смысл, я переименую.

Я добавил torch.experimental.deterministic_error_level , аналогично тому, что сделал @ t-vi в своей предыдущей работе над этой проблемой. deterministic_error_level управляет поведением ошибок / предупреждений, если deterministic == True и данная функция не имеют детерминированной реализации. Может иметь значение 2 (ошибка), 1 (предупреждение) или 0 (без звука).

Если пользователь устанавливает для него любое другое значение, я хочу вызвать исключение времени выполнения python, которое можно уловить. Обычно я бы использовал TORCH_CHECK() для такого поведения, но в этом случае исключение невозможно уловить, и я не уверен, почему. Вот ссылка на TORCH_CHECK() :

Вот что происходит, когда эта проверка терпит неудачу:

>>> import torch
>>> try:
...     torch.experimental.deterministic_error_level=50
... except:
...     print('exception caught')
... 
terminate called after throwing an instance of 'c10::Error'
  what():  error level 50 is invalid, must be one of 0: None, 1: Warn, or 2: Error
Exception raised from longToErrorLevel at ../aten/src/ATen/Context.cpp:85 (most recent call first):
frame #0: c10::Error::Error(c10::SourceLocation, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) + 0x58 (0x7f53e2cc0878 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libc10.so)
frame #1: at::Context::longToErrorLevel(long) + 0x122 (0x7f53f6d61a82 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libtorch_cpu.so)
frame #2: THPModule_setDeterministicErrorLevel(_object*, _object*) + 0x31 (0x7f53fb5625d1 in /work/kurtamohler/development/pytorch-deterministic-flag/torch/lib/libtorch_python.so)
<omitting python frames>
frame #23: __libc_start_main + 0xe7 (0x7f5432d62b97 in /lib/x86_64-linux-gnu/libc.so.6)

Aborted (core dumped)

Если кто-нибудь знает, как я могу это исправить, дайте мне знать.

@kurtamohler THPModule_setDeterministicErrorLevel отсутствует макрос HANDLE_TH_ERRORS / END_ HANDLE_TH_ERRORS? Они необходимы, чтобы перехватить исключение C ++ и преобразовать его в возврат ошибки Python.

Ах вот и все, спасибо @colesbury!

Я начинаю добавлять недетерминированное предупреждение всем вызывающим atomicAdd . Я заметил, что некоторые абоненты используют atomicAdd в определенных случаях. Например, adaptive_avg_pool3d_backward используется, только если (isizeW%osizeW != 0) || (isizeH%osizeH != 0) || (isizeT%osizeT != 0) истинно. Следует ли мне предупреждать только в этих случаях и пытаться передать их в сообщении об ошибке, или было бы хорошо просто предупреждать каждый раз, когда эти функции вызываются, независимо от того, используется ли atomicAdd конечном итоге?

Вероятно, это проще реализовать и легче понять, если вы безоговорочно бдительны.

@ngimel , я думал о том, как использовать CUBLAS_WORKSPACE_CONFIG для обеспечения детерминированного использования потока, и я думаю, что есть два основных подхода, которые следует рассмотреть.

Если кто-то использует одну из затронутых версий CUDA (на данный момент 10.2 или выше) и вызывается torch.set_deterministic(True) , используйте std::getenv чтобы убедиться, что CUBLAS_WORKSPACE_CONFIG либо :16:8 или :4096:8 . Если нет, выполните либо (1), либо (2):

  1. Выдает сообщение об ошибке, сообщающее пользователю, что нужно правильно установить переменную.

  2. Автоматически установить переменную с помощью putenv ( _putenv в Windows). Однако с этим связаны некоторые дальнейшие дизайнерские решения. Должны ли мы выбрать :16:8 (более низкая производительность, но меньшее использование памяти) или :4096:8 (более высокая производительность, но большее использование памяти)? Кроме того, если пользователь установит для переменной какое-либо другое недетерминированное значение, нам придется либо отслеживать исходное значение и восстанавливать его, если вызывается torch.set_deterministic(False) , либо мы можем выдать ошибку, сообщающую пользователю, что им нужно отключить переменную или какую-то другую схему.

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

У вас есть твердое мнение о том, какой вариант лучше? Вариант (2), вероятно, будет более удобным для пользователя, но, возможно, менее прозрачным, чем вариант (1).

Я не знаю, повлияет ли на самом деле установка переменной во время работы приложения

Чтобы ответить на этот вопрос, установка переменной среды внутри скрипта pytorch, похоже, не влияет на детерминизм потока CUDA. Я изменил скрипт с https://github.com/pytorch/pytorch/issues/39849, чтобы он запускался несколько раз и сравнивал статистику обучения для проверки недетерминированного поведения. Он пытается установить CUBLAS_WORKSPACE_CONFIG=:4096:8 для обеспечения детерминированного использования потока: https://github.com/kurtamohler/pytorch-perf-test-scripts/blob/master/nondeterministic_alert/cuda_stream_nondeterminism.py

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

$ python cuda_stream_nondeterminism.py 
Before setting var: not deterministic
After setting var: not deterministic
After restoring old var: not deterministic

Но запуск его с переменной окружения, установленной вне сценария, делает его детерминированным:

$ CUBLAS_WORKSPACE_CONFIG=:4096:8 python cuda_stream_nondeterminism.py 
Before setting var: possibly deterministic
After setting var: possibly deterministic
After restoring old var: possibly deterministic

Обратите внимание, что он печатает «возможно детерминированный», потому что я запускаю функцию обучения только 5 раз, и это возможно, даже если поведение не совсем детерминированное.

Возможно, если бы я мог повторно инициализировать поток cuda, это заставило бы его учитывать измененную переменную CUBLAS_WORKSPACE_CONFIG . Я хотел бы попробовать это, но я не знаю, как и возможно ли это сделать во время выполнения. Если кто-то знает, дайте мне знать.

Я узнал, что могу создать и использовать новый поток с:

with  torch.cuda.stream(torch.cuda.Stream()):

Но новый поток не учитывает измененную настройку переменной среды. Я также нашел torch.cuda.init() , но, к сожалению, это невозможно, если cuda уже был инициализирован.

Поэтому, если мы не придумаем, что еще попробовать, похоже, что мы, вероятно, не сможем автоматически изменить конфигурацию рабочего пространства, поэтому нам, возможно, придется выдать ошибку, предлагая пользователю установить ее.

Да, установка переменной среды после инициализации контекста cuda не имеет никакого эффекта, поэтому, к сожалению, это решение типа «все или ничего». Сообщение об ошибке, говорящее пользователю установить его, звучит разумно.

В настоящее время не похоже, что можно проверить версию CUDA из скомпилированного файла, отличного от nvcc, поэтому я считаю, что мне придется добавить это в aten/src/ATen/cuda/detail/CUDAHooks.h (проверка версии cuDNN является частью этого интерфейса) . Если кто-то знает лучше, дайте мне знать.

Приведенная выше фиксация добавляет ошибку. Но теперь мне нужно разобраться, что делать с модульными тестами. Есть две проблемы:

  • Чтобы проверить, что ошибка возникает в правильном случае (cuda> = 10.2 и CUBLAS_WORKSPACE_CONFIG не задано должным образом), инфраструктура тестирования должна иметь возможность автоматически изменять переменную среды перед запуском теста.
  • Чтобы убедиться, что существующие torch.set_deterministic тесты не ломаются, нам нужно будет автоматически правильно установить CUBLAS_WORKSPACE_CONFIG . Мы могли бы просто установить эту переменную по умолчанию во всех заданиях CI, которые используют cuda> = 10.2.

Я обнаружил, что могу установить переменные среды из скрипта python, а затем перезагрузить модуль torch, чтобы он соблюдал новое значение:

>>> import torch
>>> torch.set_deterministic(True)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/work/kurtamohler/development/pytorch-deterministic-flag-cuda-env-var/torch/__init__.py", line 306, in set_deterministic
    _C._set_deterministic(d)
RuntimeError: To enable deterministic behavior with CUDA >= 10.2, you must set environment variable CUBLAS_WORKSPACE_CONFIG=:4096:8 or CUBLAS_WORKSPACE_CONFIG=:16:8. For more information, go to https://docs.nvidia.com/cuda/cublas/index.html#cublasApi_reproducibility
>>> import os
>>> os.environ['CUBLAS_WORKSPACE_CONFIG'] = ':4096:8'
>>> from importlib import reload
>>> torch = reload(torch)
>>> torch.set_deterministic(True)

Я не знаю, заставит ли перезагрузка torch CUDA учитывать это изменение, но, по крайней мере, это дает нам возможность модульного теста для сообщения об ошибке. Хотя я должен спросить, может ли быть проблема с перезагрузкой модуля torch внутри модульного теста?

РЕДАКТИРОВАТЬ: Оказывается, мне не нужно перезагружать факел, чтобы он увидел измененную переменную среды. Кроме того, перезагрузка после изменения переменной не влияет на время выполнения CUDA.

Вышеупомянутая фиксация решает все проблемы, о которых я упоминал в моем предыдущем комментарии. Я добавил декоратор, чтобы обернуть любой тест API, который вызывает torch.set_deterministic() , временно устанавливая CUBLAS_WORKSPACE_CONFIG=:4096:8 только при необходимости. Он также восстанавливает детерминированный флаг и настройки CUBLAS_WORKSPACE_CONFIG до того, что было до запуска теста.

Я понял, что в документе о воспроизводимости упоминается, что для детерминированного поведения CuDNN требуется:

torch.backends.cudnn.deterministic = True
torch.backends.cudnn.benchmark = False

Кто-нибудь в этой теме знает, что такое benchmark и почему torch.backends.cudnn.deterministic = True недостаточно?

Мы могли бы захотеть принудительно отключить benchmark если torch.is_deterministic() == True . Другими словами, вместо передачи ctx.benchmarkCuDNN() непосредственно в at::_convolution() , возможно, в этой строке должно быть ctx.benchmarkCuDNN() && !ctx.deterministic() : https://github.com/pytorch/pytorch/blob/ главный / ATEN / SRC / ATen / native / Convolution.cpp # L602

Если мы не внесем это изменение, похоже, что люди, использующие set_deterministic и CuDNN, должны будут сделать это:

torch.set_deterministic(True)
torch.backends.cudnn.benchmark = False

Это означает, что одна set_deterministic() не покрывает все, что, на мой взгляд, сбивает с толку.

коп @ezyang @colesbury @ t-vi @ngimel

При обнаружении новой конфигурации свертки benchmark=True запускает все доступные реализации cudnn и выбирает самую быструю, кэшируя выбранную реализацию, поэтому все последующие вызовы свертки с теми же параметрами будут использовать ее. Итак, если для deterministic также установлено значение True результаты будут детерминированными, пока сохраняется этот кеш, то есть пока вы находитесь в одном процессе. Если есть реализации с близким временем выполнения, в следующий раз, когда вы запустите процесс и снова запустите тестирование, другая реализация может выиграть, и результаты (хотя и детерминированные в смысле, описанном выше) будут отличаться от предыдущего запуска. Итак, чтобы гарантировать детерминизм между прогонами, вам нужно отключить тестирование производительности.

Я понимаю. Так что, возможно, для некоторых приложений имеет значение только внутрипроцессный детерминизм, а не кросс-процессный детерминизм, поэтому люди могут счесть полезным иметь возможность использовать бенчмаркинг, если они установят torch.set_deterministic(True) . В этом случае мне не следует менять текущее поведение. Пока я обновляю документацию, чтобы прояснить это, я не вижу в этом проблемы.

Я сделал вики-страницу, чтобы помочь участникам PyTorch добавить поддержку для torch.set_deterministic() : https://github.com/pytorch/pytorch/wiki/How-to-support-%60torch.set_deterministic ()% 60-in- PyTorch-операторы

Любые улучшения приветствуются.

Кроме того, я не был уверен, должен ли раздел «Неподдерживаемые в настоящее время функции» быть в этой вики, или это было бы лучше как новая проблема с github (страница вики могла бы ссылаться на него). Есть ли у кого-нибудь предпочтения?

Привет, я хочу поговорить о плане в будущем для torch.deterministic . Нам нужно ответить на несколько вопросов высокого уровня:

  1. Какова семантика torch.deterministic ? Чего ожидает пользователь? Действительно ли максимальное усилие полезно для пользователя? Если это бесполезно, лучше ли определить torch.deterministic с точки зрения того, какие операции он контролирует?
  2. Теперь, когда у нас есть флаг torch.deterministic , имеет ли смысл полностью исключить аргумент deterministic= keyword из общедоступного API ( bmm , я смотрю на вас).
  3. Каким будет конец этой работы? Над какой частью этого вы (@kurtamohler) собираетесь работать по сравнению с общим сообществом, и когда мы подойдем к концу вашего пребывания здесь, как будет выглядеть разумное состояние?

Начиная с (1), текущая документация для torch.deterministic гласит:

     r"""Sets a global flag to force all operations to use a deterministic
    implementation if available. If an operation that does not have a
    deterministic implementation is called while this setting is True, the
    operation will throw a RuntimeError.

    Note that deterministic operations tend to have worse performance than
    non-deterministic operations.

Хотя это может быть верно для возможного конечного состояния, это неточно представляет текущую ситуацию, когда многие операции не были проверены, и для любой данной модели мы не знаем, действительно ли torch.deterministic будет делать то, что он говорит на жестяной банке и сделает вашу модель детерминированной / выдаст ошибку при нажатии nondet. Таким образом, наша реализация содержит ошибки в отношении этой семантики, и в обозримом будущем они останутся ошибочными. Это не лучшее состояние для жизни.

Мы могли бы изменить документацию torch.deterministic, чтобы исправить это. Некоторые возможные изменения:

  • torch.deterministic - лучший вариант, но, пожалуйста, сообщайте об ошибках, если вы видите, что он не обнаруживает недетерминизма
  • torch.deterministic переключает поведение этих операторов (а затем дает исчерпывающий список операторов, которые он переключает)

Второй пункт приводит к (2): если torch.deterministic теперь существует как способ переключения детерминизма, гораздо менее важно поддерживать детерминизм непосредственно в пользовательском API. Так что нам, вероятно, не стоило добавлять аргумент deterministic в bmm. Мы могли бы рассмотреть возможность раскрытия внутренней функции, если вы хотите что-то переключить напрямую, но deterministic не должно быть доступно непосредственно в самой функции.

Что вы думаете? Я думаю, что изменение документации - это, вероятно, самый простой способ встать на устойчивый путь. Есть и другие детали, например, как заполнить исчерпывающий список, но эта семантика, вероятно, имеет больше смысла, чем «идеальная» семантика, которая на самом деле не будет истинной.

cc @gchanan @mruberry

@ zou3519 также пересекается с Q на https://github.com/pytorch/pytorch/pull/38683#issuecomment -662590937

Я рад, что вы задали эти вопросы @ezyang , @ zou3519 и @mruberry. Я согласен с тем, что написанная мною документация является ложным представлением текущего состояния.

Мне нравится идея исчерпывающего перечисления всех функций, на которые влияет torch.set_deterministic() , чтобы мы не лгали пользователю. Спасибо, что добавили это в 1.6.0, @ zou3519.

Я согласен с тем, что мы не должны предлагать параметр deterministic качестве прямых аргументов функции.

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

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

  1. Детерминированный
  2. Недетерминированный по умолчанию, но имеет поддержку детерминированного флага (ошибка или альтернативная реализация)
  3. Недетерминированный и не поддерживает детерминированный флаг

Конечно, в идеальном случае полностью исключить категорию 3, и тогда списка функций категории 2 будет достаточно. Однако функции категории 3 будут по-прежнему существовать в течение значительного периода времени (или, возможно, навсегда, если не все участники осведомлены о проблеме детерминизма, или фиксация случайно удаляет детерминизм для функции и т. Д.). Таким образом, даже если у нас есть исчерпывающий список всех функций категории 2, у пользователя нет простого способа узнать, является ли функция, которая не отображается в списке, детерминированной или нет (может быть категория 1 или 3). Например, torch.add не отображается в списке, так как же пользователь узнает, что он детерминирован?

Возможно, мы могли бы также подумать о ведении списка функций категории 3. Но ведение этих списков вручную было бы очень сложно по многим причинам, поэтому мне интересно, можем ли мы несколько автоматизировать это. Мы потенциально могли бы настроить задание CI, которое запускает тесты детерминизма для всех функций. Невозможно на 100% индуктивно доказать, что функция детерминирована, а недетерминированная функция может иногда давать один и тот же результат несколько раз, если нам не повезет. Но чем чаще мы запускаем эти тесты, тем увереннее мы сможем понять, к какой категории относится каждая функция.

Также возникает вопрос, как наиболее эффективно донести до пользователя все, что мы знаем и не знаем о каждой функции и каждой платформе. Возможно, мы могли бы составить таблицу всех функций категорий 2 и 3 на каждой платформе. Было бы хорошо, если бы тесты детерминизма могли автоматически проверять правильность этой таблицы.

Просто мозговой штурм, может быть, эти идеи сложнее, чем они того стоят. Более прагматичный план мог бы быть значительно более устойчивым, даже менее идеальным.

Является ли torch.add детерминированным?

import torch
n = 512
device = 'cuda'
a = torch.arange(n**3, device=device, dtype=torch.float32)
a = a.reshape((n, n, n))
b = torch.arange(n**3, device=device, dtype=torch.float32)
b = b.reshape((n, n, n))
out_zero = torch.zeros((n, n, n), device=device)
out_zero = out_zero.set_(out_zero.storage(), storage_offset=0, size=a.size(), stride=(1,1,1))
out_one = torch.zeros((n, n, n), device=device)
out_one = out_one.set_(out_one.storage(), storage_offset=0, size=a.size(), stride=(1,1,1))

torch.add(a, b, out=out_zero)
torch.add(a, b, out=out_one)
(out_zero == out_one).all()
: tensor(False, device='cuda:0')

Вероятно, нам следует задокументировать, что перекрывающиеся тензоры нарушают любой контракт детерминизма, который мы собираемся использовать.

Список операций, на которые распространяется флаг "детерминизма", звучит неплохо. Однако, немного отступив назад, кажется, что мы действительно говорим о двух вещах:

  • Запрос детерминированных версий операций, если они доступны ( use_deterministic ?)
  • Предупреждение, если операция недетерминированная

Флаг для первого дела кажется простым. Однако второй вариант немного сложнее. Меня беспокоит, что трудно сказать, являются ли операции математических библиотек, таких как oneDNN, cuDNN и MAGMA, детерминированными, особенно между версиями и оборудованием. У тебя есть идея, как лучше всего решить эту проблему, @kurtamohler? Может быть, мы могли бы предупредить обо всех встроенных недетерминированных операциях, а также предупредить, когда были сделаны вызовы математической библиотеки? Предупреждение один раз на процесс не должно быть таким навязчивым.

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

(Третья обсуждаемая вещь - лучший способ представить детерминированный выбор алгоритма (через глобальный флаг или как kwargs для функций), но я думаю, что мы можем отложить это обсуждение до тех пор, пока мы не определим план для флага (ов)?)

Я думаю, мы не должны позволять идеальному быть здесь врагом хорошего. Я не знаю, когда было на 100% безопасно использовать самоперекрывающиеся тензоры с PyTorch, и у меня сложилось впечатление, что их используют не обычные люди.

На форумах у меня сложилось впечатление, что большинство людей удивляются тому, что они запускают что-то дважды и получают от этого разные градиенты, чаще всего из-за одной из собственных функций PyTorch, использующих atomicAdd.
Если мы получаем предупреждения об этом, мы рассмотрели большинство случаев, которые интересуют людей. Что-то вроде того, что кажется половиной, на самом деле происходит от апскейлинга в обратном направлении.

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

Я не знаю, когда было на 100% безопасно использовать самоперекрывающиеся тензоры с PyTorch, и у меня сложилось впечатление, что их используют не обычные люди.

Да, и любые программы, которые это делают, могут быть классифицированы как ошибочные. Я просто имел в виду, что мы должны быть осторожны при документировании любого контракта, который мы придумываем для этих флагов.

Я думаю, мы должны четко заявить, что это лучший вариант для внешних библиотек и что мы добавляем предупреждения по мере того, как узнаем о проблемах ...

В документе может быть сказано что-то вроде «вызовы математической библиотеки, которые заведомо недетерминированы ...»?

Я согласен с @ t-vi (и мне очень нравится наблюдение, что половина заявленного недетерминизма увеличивается в обратном направлении). В частности, я думаю, что состояние, в котором у нас есть частично задокументированные функции, которые, как известно, недетерминированы (или даже частично задокументированы некоторые функции как детерминированные), строго лучше, чем состояние, в котором мы вообще не даем никаких указаний - ключевой момент не заявлять о поддержке того, чего мы не делаем! Я согласен с тем, что подумать о том, как можно провести тестирование на детерминизм, полезно, но я думаю, что это ортогональное действие для пометки API, которые явно недетерминированы.

Поскольку вокруг витало много идей, позвольте мне поделиться своими мыслями о некоторых из них:

  1. «Возможно, мы могли бы подумать о ведении списка функций категории 3». Похоже, это большая работа. Я думаю, это, вероятно, того стоит только для функций, где мы явно сделали некоторые приспособления для детерминизма (скорее всего, функции, поддерживающие детерминированный флаг)
  2. «Мы могли бы потенциально создать задание CI, которое запускает тесты детерминизма для всех функций». Я думаю, что что-то подобное нужно делать с большой осторожностью, потому что по самой своей природе он проверяет что-то недетерминированное, а это означает, что сам тест на детерминизм «нестабилен» (иногда проходит, а другие - нет). . Наши инструменты отчетности CI не очень хорошо справляются с такими ситуациями.
  3. «Второй вариант, однако, немного сложнее. Я беспокоюсь, что трудно сказать, являются ли операции математических библиотек, таких как oneDNN, cuDNN и MAGMA, детерминированными, особенно для разных версий и оборудования». Мы должны сделать все возможное. Во многих случаях математическая библиотека явно указывает в своей документации, что они детерминированы или нет, и мы должны просто правдиво сообщать о том, что говорится в документации.
  4. «Может быть, мы могли бы предупредить обо всех встроенных недетерминированных операциях, а также предупредить, когда были сделаны вызовы математической библиотеки?» Я не думаю, что мы должны это делать. Когда мы предупреждаем о недетерминизме, это должно быть потому, что недетерминизм происходит, а не потому, что он МОЖЕТ иметь место. Если вы переусердствуете, люди начнут игнорировать предупреждения.

Я не думаю, что нам следует беспокоиться о кросс-версии / аппаратном детерминизме - удачи с этим.

Когда мы предупреждаем о недетерминизме, это должно быть потому, что недетерминизм происходит, а не потому, что он МОЖЕТ иметь место. Если вы переусердствуете, люди начнут игнорировать предупреждения.

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

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

Конечно, но пользователь может просто не вовлекать нас в свои недетерминированные махинации, и тогда, конечно, вы не ожидаете, что его тогда предупредят;)

Я считаю, что мы можем закрыть эту проблему сейчас, поскольку API флагов существует и хорошо документирован.

@kurtamohler Отличная работа. Спасибо.

Означает ли это, что мы могли бы использовать torch.manual_seed(111) для установки всего детерминированного, включая операцию interpolation ?

Нет. Взгляните на примечание " Воспроизводимость / случайность" .
Пока что у нас есть инфраструктура, отмечены известные источники недетерминизма и значительно улучшена документация, чтобы вы могли знать, что происходит.
Если вы попадете в недетерминированные операции, вам все равно не повезло, но теперь разумнее над этим поработать.

В частности, кажется, что интерполяцию можно сделать детерминированной, написав не такое уж сложное ядро ​​для обратного.

@ t-vi Привет, теперь, когда выпущен pytorch 1.7, обновлено ли ядро ​​обратной интерполяции?

Таким образом, ядра CUDA с повышением частоты дискретизации и обратно лежат в aten/src/ATen/native/cuda/UpSample* . Команда grep предполагает, что у линейных, билинейных и кубических есть недетерминированные обратные направления (у них есть предупреждающий маркер), а у ближайших - нет.
Хотя @kurtamohler было бы куда лучше спросить.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги