Почему PyTorch стал популярнее TensorFlow?

Ответ

PyTorch получил широкое распространение, особенно в исследованиях и стартапах, из-за своего интуитивного и гибкого дизайна, который лучше соответствует привычкам программистов на Python. Ключевые технические причины:

  • Динамический вычислительный граф (Eager Execution по умолчанию): В PyTorch граф строится на лету. Это позволяет использовать привычные конструкции Python (циклы, условные операторы) прямо внутри модели и отлаживать код стандартными средствами (pdb, print). В TensorFlow 1.x необходимо было сначала статически определить граф через tf.Session, что было громоздко.
  • Python-first подход: API PyTorch ощущается как часть Python. Объектно-ориентированный дизайн (torch.nn.Module) понятен и прост для расширения. Создание кастомных слоев или функций потерь тривиально.
  • Более простой дебаггинг: Так как выполнение происходит построчно, ошибки ведут прямо к проблемной строке кода, а не в недра статического графа.

Пример: Быстрое прототипирование на PyTorch

import torch
import torch.nn as nn
import torch.optim as optim

# Определяем модель за пару строк
class SimpleNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Linear(10, 1)
    def forward(self, x):
        return self.fc(x)

model = SimpleNN()
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# Прямой проход, расчет ошибки, обратное распространение - все ясно и на виду
for data, target in dataloader:
    optimizer.zero_grad()
    output = model(data)          # Граф строится здесь
    loss = criterion(output, target)
    loss.backward()               # Автоматическое дифференцирование
    optimizer.step()
    print(f'Loss: {loss.item()}') # Легко следить за процессом

Хотя TensorFlow 2.0 перенял eager mode, к тому моменту PyTorch уже создал сильное сообщество и стал де-факто стандартом для новых исследований, что обеспечило его экосистеме (библиотеки, предобученные модели) быстрое развитие.

Ответ 18+ 🔞

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

  • Динамический вычислительный граф (Eager Execution по умолчанию): Это, ёпта, главный козырь. В PyTorch граф строится прямо на ходу, как в голове возникает мысль. Хочешь впихнуть обычный питоновский if или for прямо в середину модели? Да пожалуйста! Отлаживать через print или pdb? Легко! А вот в TensorFlow 1.x был просто ёперный театр: надо было сначала этот граф статически собрать через tf.Session, а потом молиться, чтобы он скомпилировался. Овердохуища мороки, честно.

  • Python-first подход: API PyTorch — это как родной диалект для питониста. Весь этот объектно-ориентированный подход с torch.nn.Module — понятен, как дважды два. Создать свой собственный, ебаный, слой или функцию потерь — это раз плюнуть, а не квест на выживание.

  • Более простой дебаггинг: А вот это вообще песня. Код выполняется построчно, поэтому если где-то косяк, то ошибка тебя приведёт прямиком к проблемной строке, а не будет тыкать носом в какую-то абстрактную хуйню из недр статического графа. Доверия ебать ноль было к этим старым подходам.

Смотри, как это выглядит на практике:

import torch
import torch.nn as nn
import torch.optim as optim

# Модель собирается за две секунды, без всякой магии
class SimpleNN(nn.Module):
    def __init__(self):
        super().__init__()
        self.fc = nn.Linear(10, 1)
    def forward(self, x):
        return self.fc(x)

model = SimpleNN()
criterion = nn.MSELoss()
optimizer = optim.SGD(model.parameters(), lr=0.01)

# Всё как на ладони: прямой проход, ошибка, обратное распространение
for data, target in dataloader:
    optimizer.zero_grad()
    output = model(data)          # Граф рождается прямо тут, на лету
    loss = criterion(output, target)
    loss.backward()               # Автодифференцирование делает всё само
    optimizer.step()
    print(f'Loss: {loss.item()}') # И можно спокойно смотреть, как всё учится

Конечно, TensorFlow 2.0 охуел и тоже переполз на eager mode, но было уже поздно. PyTorch к тому моменту уже собрал вокруг себя такое комьюнити, что мама не горюй. Он стал стандартом для всех новых исследований, и вся экосистема — библиотеки, предобученные модели — понеслась развиваться со скоростью света. Вот так, чувак, простота и понятность победили сложность и абстракцию.