Как правильно обновить веса в PyTorch? - PullRequest
0 голосов
/ 30 января 2019

Я пытаюсь реализовать градиентный спуск с помощью PyTorch в соответствии с этой схемой , но не могу понять, как правильно обновлять веса.Это просто игрушечный пример с 2 линейными слоями с 2 узлами в скрытом слое и одним выходом.

Скорость обучения = 0,05;целевой выход = 1

https://hmkcode.github.io/ai/backpropagation-step-by-step/

Вперед

Назад

Мой код следующий:

    import torch
    import torch.nn as nn
    import torch.nn.functional as F
    import torch.optim as optim

    class MyNet(nn.Module):

    def __init__(self):
         super(MyNet, self).__init__()
         self.linear1 = nn.Linear(2, 2,  bias=None)
         self.linear1.weight = torch.nn.Parameter(torch.tensor([[0.11, 0.21], [0.12, 0.08]]))
         self.linear2 = nn.Linear(2, 1,  bias=None)
         self.linear2.weight = torch.nn.Parameter(torch.tensor([[0.14, 0.15]]))

    def forward(self, inputs):
         out = self.linear1(inputs)
         out = self.linear2(out)
         return out

    losses = []
    loss_function = nn.L1Loss()
    model = MyNet()
    optimizer = optim.SGD(model.parameters(), lr=0.05)
    input = torch.tensor([2.0,3.0])
    print('weights before backpropagation = ',   list(model.parameters()))
    for epoch in range(1):
       result = model(input )
       loss = loss_function(result , torch.tensor([1.00],dtype=torch.float))
       print('result = ', result)
       print("loss = ",   loss)
       model.zero_grad()
       loss.backward()
       print('gradients =', [x.grad.data  for x in model.parameters()] )
       optimizer.step()
       print('weights after backpropagation = ',   list(model.parameters())) 

Результат следующий:

    weights before backpropagation =  [Parameter containing:
    tensor([[0.1100, 0.2100],
            [0.1200, 0.0800]], requires_grad=True), Parameter containing:
    tensor([[0.1400, 0.1500]], requires_grad=True)]

    result =  tensor([0.1910], grad_fn=<SqueezeBackward3>)
    loss =  tensor(0.8090, grad_fn=<L1LossBackward>)

    gradients = [tensor([[-0.2800, -0.4200], [-0.3000, -0.4500]]), 
                 tensor([[-0.8500, -0.4800]])]

    weights after backpropagation =  [Parameter containing:
    tensor([[0.1240, 0.2310],
            [0.1350, 0.1025]], requires_grad=True), Parameter containing:
    tensor([[0.1825, 0.1740]], requires_grad=True)]

Передача Значения:

2x0.11 + 3*0.21=0.85 ->  
2x0.12 + 3*0.08=0.48 -> 0.85x0.14 + 0.48*0.15=0.191 -> loss =0.191-1 = -0.809  

Обратный проход : давайте вычислим w5 и w6 (веса выходного узла)

w = w - (prediction-target)x(gradient)x(output of previous node)x(learning rate)  
w5= 0.14 -(0.191-1)*1*0.85*0.05= 0.14 + 0.034= 0.174  
w6= 0.15 -(0.191-1)*1*0.48*0.05= 0.15 + 0.019= 0.169 

В моем примере Torch не умножает потери на производную, поэтому мы получаем неправильные веса после обновления.Для выходного узла мы получили новые веса w5, w6 [0,1825, 0,1740], когда он должен быть [0,174, 0,169]

Перемещение назад для обновления первого веса выходного узла (w5), которое нам нужно вычислить: (цель прогноза) x (градиент) x (выход предыдущего узла) x (скорость обучения) = - 0,809 * 1 * 0,85 * 0,05 = -0,034.Обновленный вес w5 = 0,14 - (- 0,034) = 0,174.Но вместо этого вычислили новый вес = 0,1825.Забыл умножить на (прогноз-цель) = - 0,809.Для выходного узла мы получили градиенты -0,8500 и -0,4800.Но нам все еще нужно умножить их на потерю 0,809 и коэффициент обучения 0,05, прежде чем мы сможем обновить вес.

Как правильно это сделать?Должны ли мы передать «убыток» в качестве аргумента функции backward () следующим образом: loss.backward (loss).

Кажется, это исправить.Но я не смог найти ни одного примера в документации.

1 Ответ

0 голосов
/ 31 января 2019

Вы должны использовать .zero_grad() с оптимизатором, поэтому optimizer.zero_grad(), а не потери или модель, как это предлагается в комментариях (хотя модель в порядке, но она не ясна или не читаема IMO).

За исключением этоговаши параметры обновляются нормально, поэтому ошибка не на стороне PyTorch.

На основе предоставленных вами значений градиента:

gradients = [tensor([[-0.2800, -0.4200], [-0.3000, -0.4500]]), 
             tensor([[-0.8500, -0.4800]])]

Давайте умножим их все на вашу скорость обучения (0,05):

gradients_times_lr = [tensor([[-0.014, -0.021], [-0.015, -0.0225]]), 
                      tensor([[-0.0425, -0.024]])]

Наконец, давайте применим обычный SGD (тета - = градиент * lr), чтобы получить точно такие же результаты, как в PyTorch:

parameters = [tensor([[0.1240, 0.2310], [0.1350, 0.1025]]),
              tensor([[0.1825, 0.1740]])]

То, что вы сделали, взялоградиенты, рассчитанные PyTorch и умноженные на результат предыдущего узла и , это не так, как он работает! .

Что вы сделали:

w5= 0.14 -(0.191-1)*1*0.85*0.05= 0.14 + 0.034= 0.174  

Что должно быть сделано (с использованием результатов PyTorch):

w5 = 0.14 - (-0.85*0.05) = 0.1825

Нет умножения предыдущего узла, это делается за сценой (это то, что делает .backprop() - вычисляет правильные градиенты для всех узлов), нет необходимостиумножьте их на предыдущие.

Если вы хотите рассчитать их вручную, вы должны начать с убытка (с дельтой, равной единице) и обратно до конца ( здесь не используют скорость обучения,это другая история! ).

После того, как все они рассчитаны, вы можете умножить каждый вес на скорость обучения оптимизаторов (или любую другую формулу по этому вопросу, например, Momentum), и после этого вы получитеправильное обновление.

Как рассчитать backprop

Скорость обучения не является частью обратного распространения, оставьте это в покое, пока вы не вычислите все градиенты (это смешивает отдельные алгоритмы вместе,процедуры оптимизации и обратного распространения).

1.Производная от общей ошибки по выходным данным

Ну, я не знаю, почему вы используете Средняя абсолютная ошибка (в то время как в учебнике это Средняя квадратичная ошибка ),и поэтому оба эти результата различаются.Но пойдем с твоим выбором.

Производная от |y_true - y_pred |wrt для y_pred равно 1 , поэтому ЭТО не то же самое, что потеря.Измените на MSE , чтобы получить равные результаты (здесь, производная будет (1/2 * y_pred - y_true), но мы обычно умножаем MSE на два, чтобы удалить первое умножение).

В случае MSE вы умножаете значение потери, но оно полностью зависит от функции потерь (было немного прискорбно, что использованное вами руководство не указало на это).

2.Производная полной ошибки по w5

Скорее всего, вы могли бы пойти отсюда, но ... Производная полной ошибки по w5 - это результат h1 (в данном случае 0,85).Мы умножаем его на производную от общей ошибки по выводу (это 1!) И получаем 0,85, как это сделано в PyTorch.Та же идея относится и к w6.

Я серьезно советую вам не путать скорость обучения с backprop, вы усложняете свою жизнь (а с IMO backprop, это нелегко, довольно нелогично), иэто две разные вещи (не могу не подчеркнуть, что достаточно одной).

Этот источник хорош, более пошаговый, с немного более сложной сетевой идеей (включая активации), так что вы можете лучше понять, если вы пройдете через все это.

Кроме того, если вы действительно заинтересованы (и вам, кажется, интересны), чтобы узнать больше подробностей об этом, рассчитайте поправки на вес для других оптимизаторов (скажем, нестеров), чтобы вы знали, почему мы должны сохранятьэти идеи разделились.

...