Первая и третья попытки абсолютно одинаковы и правильны, в то время как второй подход совершенно неверен.
Причина в том, что в Pytorch градиенты нижнего слоя Не"перезаписаны" последующими backward()
звонков, скорее они накапливаются или суммируются.Это делает первый и третий подход идентичными, хотя первый подход может быть предпочтительным, если у вас мало ГП / ОЗУ с малым объемом памяти, поскольку размер пакета 1024 с немедленным вызовом backward() + step()
такой же, как с 8 пакетами размером 128 и 8 backward()
звонки, с одним step()
звонком в конце.
Чтобы проиллюстрировать идею, вот простой пример.Мы хотим, чтобы наш тензор x
был ближайшим к [40,50,60]
одновременно:
x = torch.tensor([1.0],requires_grad=True)
loss1 = criterion(40,x)
loss2 = criterion(50,x)
loss3 = criterion(60,x)
Теперь первый подход: (мы используем tensor.grad
, чтобы получить градиент тока для нашего тензора x
)
loss1.backward()
loss2.backward()
loss3.backward()
print(x.grad)
Это выводит: tensor([-294.])
(РЕДАКТИРОВАТЬ: поместите retain_graph=True
в первых двух backward
вызовах для более сложных вычислительных графов)
Третий подход:
loss = loss1+loss2+loss3
loss.backward()
print(x.grad)
Опять же вывод: tensor([-294.])
2-й подход отличается, потому что мы не вызываем opt.zero_grad
после вызова step()
метода.Это означает, что во всех 3 step
вызовах используются градиенты первого backward
вызова.Например, если 3 потери обеспечивают градиенты 5,1,4
для одного и того же веса, а не 10 (= 5 + 1 + 4), теперь ваш вес будет иметь 5*3+1*2+4*1=21
в качестве градиента.
Я согласен сзаключение, однако, используйте третий подход, если память не проблема.Для дальнейшего чтения: Ссылка 1 , Ссылка 2