Так что я думаю, вы ожидаете нулевой результат. Приглядевшись повнимательнее, вы увидите, что он совсем рядом. При делении чисел в двоичной системе (компьютер) часто возникают ошибки округления.
Давайте посмотрим на ваш пример с добавленным дополнительным print-statement:
a = torch.tensor(np.random.randn(), requires_grad=True)
loss = 1/a
loss.backward()
print(a.grad, (-1/(a**2)))
print(a.grad - (-1/(a**2)))
Поскольку вы используете случайный ввод, вывод, конечно, тоже случайный.
(так что вы не получите те же самые цифры, просто повторите этот эксперимент, и у вас будут похожие примеры).
Иногда в результате вы получите ноль. Но это было не так в вашем первоначальном примере:
tensor(-0.9074) tensor(-0.9074, grad_fn=<MulBackward>)
tensor(5.9605e-08, grad_fn=<ThSubBackward>)
Вы видите, хотя оба отображаются как одно и то же число, но они отличаются одним из последних знаков после запятой. Вот почему вы получаете эту очень маленькую разницу при вычитании обоих.
Эта проблема, как и общая проблема компьютеров, просто некоторые дроби имеют большое или бесконечное количество знаков после запятой, а памяти вашего компьютера нет. Так что в какой-то момент они обрываются.
Итак, то, что вы испытываете здесь, на самом деле является отсутствием точности. А точность зависит от используемого типа числовых данных (например, torch.float32
или torch.float64
).
Дополнительную информацию также можно найти здесь:
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
Но это не относится к PyTorch или около того, вот пример Python:
print(29/100*100)
Результат:
28.999999999999996
Изменить:
Как отметил @HOANG GIANG, изменение уравнения на -(1/a)*(1/a) работает хорошо, и результат равен нулю. Вероятно, это так, потому что вычисление, выполненное для вычисления градиента, очень похоже (или то же самое) на -(1/a)*(1/a) в этом случае. Следовательно, он использует те же ошибки округления, поэтому разница равна нулю.
Итак, вот еще один более подходящий пример, чем приведенный выше. Несмотря на то, что -(1/x)*(1/x) математически эквивалентен -1/x^2, он не всегда совпадает при вычислении на компьютере. , в зависимости от значения x:
import numpy as np
print('e1 == e2','x value', '\t'*2, 'round-off error', sep='\t')
print('='*70)
for i in range(10):
x = np.random.randn()
e1 = -(1/x)*(1/x)
e2 = (-1/(x**2))
print(e1 == e2, x, e1-e2, sep='\t\t')
Выход:
e1 == e2 x value round-off error
======================================================================
True 0.2934154339948173 0.0
True -1.2881863891014191 0.0
True 1.0463038021843876 0.0
True -0.3388766143622498 0.0
True -0.6915415747192347 0.0
False 1.3299049850551317 1.1102230246251565e-16
True -1.2392046539563553 0.0
False -0.42534236747121645 8.881784197001252e-16
True 1.407198823994324 0.0
False -0.21798652132356966 3.552713678800501e-15
Несмотря на то, что ошибка округления кажется немного меньше (я пробовал разные случайные значения, и редко ошибка округления встречалась более чем в двух случаях из десяти), но все равно уже есть небольшие отличия при простом расчете 1/x:
import numpy as np
print('e1 == e2','x value', '\t'*2, 'round-off error', sep='\t')
print('='*70)
for i in range(10):
x = np.random.randn()
# calculate 1/x
result = 1/x
# apply inverse function
reconstructed_x = 1/result
# mathematically this should be the same as x
print(x == reconstructed_x, x, x-reconstructed_x, sep='\t\t')
Выход:
e1 == e2 x value round-off error
======================================================================
False 0.9382823115235075 1.1102230246251565e-16
True -0.5081217386356917 0.0
True -0.04229436058156134 0.0
True 1.1121100294357302 0.0
False 0.4974618312372863 -5.551115123125783e-17
True -0.20409933212316553 0.0
True -0.6501652554924282 0.0
True -3.048057937738731 0.0
True 1.6236075700470816 0.0
True 0.4936926651641918 0.0
13.11.2018