Как приведенный ниже код точно обратный, как градиент передается от выхода (1) ко всем входам? - PullRequest
0 голосов
/ 29 сентября 2019

Я пытаюсь изучать входы и выходы обратного распространения.Я понял до того момента, когда я продвигаю нейронную сеть.Однако я не понимаю, как именно код передает градиенты обратно на входы.Например, я не понимаю, как self.u0.grad для sg0 (объект класса SigmoidGate) передается обратно в self.utop.grad addg1 (объект класса AddGate).Это моя проблема.

Моя цель - понять, как градиенты передаются с выхода (Примечание: градиент всегда начинается с +1 в конце, чтобы начать цепочку. Это (по умолчанию) тяга в цепи кего значение увеличилось.) вплоть до входов.

Я попытался отладить скрипт Python, как показано ниже.Мои выводы: Примечание: Самость содержит u0, u1 и utop.Все 3 переменные содержат значение и градиент.1) Переменные self одинаковы, когда я взглянул на все остальные переменные класса.Например, в настоящее время я отлаживаю в mulg1.backward ().Когда я сравниваю собственные переменные класса MultGate (): и класса AddGate ():.Они одинаковы, и их адреса памяти тоже равны.

Адрес памяти класса MultGate (): равен <<strong> main .MultGate объект в 0x0000019681754400>.Адрес памяти класса AddGate (): <<strong> main .MultGate объект в 0x0000019681754400>.

Почему это происходит?Как переменные могут быть разделены между классами?

2) Как значение self.u0.grad sig0 передается в self.utop.grad addg1.Обе переменные указывают на одну и ту же ячейку памяти?

Пожалуйста, дайте мне понимание этой проблемы.

# Referenced from:
# Tutorial: http://karpathy.github.io/neuralnets/ 
# Code : https://gist.github.com/bmobear/29e29da38a2593c9bcb0d0a434e57022

import math


class Unit:
    def __init__(self, value, grad):
        self.value = value
        self.grad = grad


class MultGate:
    def forward(self, u0, u1):
        self.u0 = u0  # this is a Unit by itself
        self.u1 = u1  # this is a Unit by itself
        self.utop = Unit(u0.value*u1.value, 0.0)
        return self.utop  # return a Unit object

    def backward(self):
        self.u0.grad += self.u1.value*self.utop.grad
        self.u1.grad += self.u0.value*self.utop.grad


class AddGate:
    def forward(self, u0, u1):
        self.u0 = u0
        self.u1 = u1
        self.utop = Unit(u0.value+u1.value, 0.0)
        return self.utop

    def backward(self):
        self.u0.grad += 1*self.utop.grad
        self.u1.grad += 1*self.utop.grad


class SigmoidGate:
    def sigmoid(self, x):
        return 1.0/(1+math.exp(-x))

    def forward(self, u0):
        self.u0 = u0

        # print("self.u0 in sigmoid gate is " + str(self.u0.value))
        # print("self.sigmoid(u0.value) is " + str(self.sigmoid(u0.value)))

        self.utop = Unit(self.sigmoid(u0.value), 0.0)
        return self.utop

    def backward(self):
        shankar = self.sigmoid(self.u0.value)
        # print(self.u0.value) ==> 2
        # print("s in sigmoid backward function is " + str(s))
        # # s = 0.8807970779778823
        self.u0.grad += (shankar*(1 - shankar))*self.utop.grad
        # print("self.u0.grad is "+str(self.u0.grad))
        # # self.u0.grad is 0.10499358540350662


# example time
a = Unit(1.0, 0.0)
b = Unit(2.0, 0.0)
c = Unit(-3.0, 0.0)
x = Unit(-1.0, 0.0)
y = Unit(3.0, 0.0)

mulg0 = MultGate()
mulg1 = MultGate()
addg0 = AddGate()
addg1 = AddGate()
sg0 = SigmoidGate()


def forwardNeuron():
    ax = mulg0.forward(a, x)  # ax is a Unit object, a * x = -1
    by = mulg1.forward(b, y)  # by is a Unit object, b * y = 6
    ax_p_by = addg0.forward(ax, by)  # ax_p_by is a Unit object; a*x + b*y = 5
    ax_p_by_p_c = addg1.forward(ax_p_by, c)
    return sg0.forward(ax_p_by_p_c)  # returns a Unit object


s = forwardNeuron()  # s is sg0.forward(ax_p_by_p_c) where sg0 is Sigmoid Gate
# before backpropagating
# print("forward pass 0 is : "+str(s.value) + " gradient is " + str(s.grad))

# backward pass
# call all gates in reverse order
# after backpropagating
s.grad = 1.0  # set the gradient from 0 to 1.0
print("forward pass 0 is : "+str(s.value) + " gradient is " + str(s.grad))

sg0.backward()    # writes gradient into ax_p_by_p_c
addg1.backward()  # writes gradients into ax_p_by and c
addg0.backward()  # writes gradients into ax and by
mulg1.backward()  # writes gradients into b and y
mulg0.backward()  # writes gradients into a and x
print("backward pass ", [a.grad, b.grad, c.grad, x.grad, y.grad])


step_size = 0.01
a.value += step_size*a.grad  # a.grad is -0.105
b.value += step_size*b.grad  # b.grad is 0.315
c.value += step_size*c.grad  # c.grad is 0.105
x.value += step_size*x.grad  # x.grad is 0.105
y.value += step_size*y.grad  # y.grad is 0.210
s = forwardNeuron()
print("forward pass 1 is : "+str(s.value))

Ожидаемый и правильный вывод

...