Какой тип графа вычислений используется в PyTorch — динамический или статический
Что проверяют
Знаете ли вы, как PyTorch считает градиенты, а не просто вызубрили слово «динамический». Ответ из одного слова засчитают, но дальше спросят, что это даёт на практике и куда делся статический граф, если он нужен для скорости и деплоя.
Как отвечать
PyTorch строит граф по ходу выполнения, это подход define-by-run. Во время прямого прохода autograd записывает операции над тензорами с requires_grad=True, каждый результат хранит ссылку на создавшую его операцию в grad_fn, и loss.backward() идёт по этой цепочке назад. После обратного прохода граф освобождается, повторный вызов требует retain_graph=True.
Что это даёт:
- обычный Python внутри
forward:if, циклы, разная длина последовательности от батча к батчу, разветвление по входу; - отладка привычными средствами:
printиpdbработают прямо между слоями, стектрейс указывает на строку модели; - простое изменение архитектуры: новую ветку можно попробовать без пересборки.
Обязательно покажите вторую половину картины. Динамика мешает оптимизировать вычисления заранее, поэтому в ветке 2.x есть torch.compile: TorchDynamo перехватывает байт-код, собирает из него граф, ставит guard-условия и передаёт всё Inductor, который склеивает операции и генерирует ядра. Там, где перехватить не получилось, возникает graph break и кусок исполняется обычным образом. Для выкладки модели служит torch.export, а torch.jit.script и trace сейчас считаются уходящим путём. Сравнение просится само: TF1 требовал сначала описать граф и запускать его в сессии, TF2 работает eagerly и включает статику через tf.function, то есть оба фреймворка сошлись к схеме «динамика по умолчанию, компиляция по требованию».
Где спотыкаются
Считают, что динамический граф значит медленный. С torch.compile и слитыми ядрами разрыв со статическими графами сильно меньше, чем принято думать.
Собирают лоссы в список без .item() или .detach(). Вместе с числом сохраняется весь граф, и память кончается на середине эпохи.
Думают, что torch.compile компилирует один раз навсегда. Смена формы входа или новая ветка кода запускают перекомпиляцию, а лечится это dynamic=True и стабильными размерами батча.