Разбивка пакета в pytorch приводит к разным результатам, почему? - PullRequest
1 голос
/ 18 апреля 2020

Я пытался что-то с пакетной обработкой в ​​pytorch. В моем коде ниже вы можете думать о x как о партии размером 2 (каждый образец - 10-мерный вектор). Я использую x_sep для обозначения первого образца в x.

import torch
import torch.nn as nn

class net(nn.Module):
    def __init__(self):
        super(net, self).__init__()
        self.fc1 = nn.Linear(10,10)

    def forward(self, x):
        x = self.fc1(x)
        return x

f = net()

x = torch.randn(2,10)
print(f(x[0])==f(x)[0])

В идеале f(x[0])==f(x)[0] должен давать тензор со всеми истинными записями. Но вывод на моем компьютере

tensor([False, False,  True,  True, False, False, False, False,  True, False])

Почему это происходит? Это вычислительная ошибка? Или это связано с тем, как пакетная прецессия реализована в pytorch?


Обновление: Я немного упростил код. Вопрос остается прежним.

Мое рассуждение: Я считаю, f(x)[0]==f(x[0]) должно иметь все свои записи True, потому что закон умножения матриц говорит об этом. Давайте подумаем о x как матрице 2x10 и представим линейное преобразование f() в виде матрицы B (игнорируя смещение на мгновение). Тогда f(x)=xB по нашим обозначениям. Закон умножения матриц говорит нам, что xB равен , сначала умножьте две строки на B справа отдельно, а затем сложите две строки вместе . Если перевести обратно к коду, это f(x[0])==f(x)[0] и f(x[1])==f(x)[1].

Даже если мы рассмотрим смещение, каждая строка должна иметь одинаковое смещение, и равенство должно сохраняться.

Также обратите внимание, что здесь не проводится обучение. Следовательно, как инициализируются веса, не должно иметь значения.

1 Ответ

1 голос
/ 18 апреля 2020

TL; DR

Под капотом используется функция с именем addmm, которая имеет некоторые оптимизации и, вероятно, умножает векторы немного по-другому


Я только что понял в чем была проблема real , и я отредактировал ответ.

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

f(x)[0].detach().numpy()
>>>array([-0.5386441 ,  0.4983463 ,  0.07970242,  0.53507525,  0.71045876,
        0.7791027 ,  0.29027492, -0.07919329, -0.12045971, -0.9111403 ],
      dtype=float32)
f(x[0]).detach().numpy()
>>>array([-0.5386441 ,  0.49834624,  0.07970244,  0.53507525,  0.71045876,
        0.7791027 ,  0.29027495, -0.07919335, -0.12045971, -0.9111402 ],
      dtype=float32)
f(x[0]).detach().numpy() == f(x)[0].detach().numpy()
>>>array([ True, False, False,  True,  True,  True, False, False,  True,
   False])

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

После некоторой дополнительной отладки я увидел в линейной функции, которая использует addmm:

def linear(input, weight, bias=None):
    if input.dim() == 2 and bias is not None:
        # fused op is marginally faster
        ret = torch.addmm(bias, input, weight.t())
    else:
        output = input.matmul(weight.t())
    if bias is not None:
        output += bias
    ret = output
return ret

Когда addmm addmm, реализует beta*mat + alpha*(mat1 @ mat2) и предположительно быстрее (см. здесь например).

Кредит Шимону Маске

...