Redes neurais Back Propagation do zero!
Mar 01 2023
Este artigo é inspirado por Andrej Karpathy, eu recomendo fortemente que você veja a lista de reprodução abaixo. Como é a explicação mais detalhada passo a passo de Back Propagation e treinamento de redes neurais.
Este artigo é inspirado por Andrej Karpathy , eu recomendo fortemente que você veja a lista de reprodução abaixo.
Como é a explicação mais detalhada passo a passo de Back Propagation e treinamento de redes neurais.
Retropropagação:
- É o método de cálculo do gradiente da função de perda em relação aos pesos da rede neural.
- A retropropagação é uma forma de calcular gradientes de expressões por meio da aplicação recursiva da regra da cadeia.
- O que isso faz ? É usado para ajustar os pesos das redes neurais, o que, por sua vez, diminui as perdas.
- O conceito de derivada é super importante na propagação reversa.
- Significa a inclinação de uma função em relação a alguma variável.
- Em termos leigos, a derivada mede o efeito de aumentar uma variável em um valor muito pequeno (ex: 0,001), resultando em aumento ou diminuição no valor da função.
- Se a inclinação for positiva, representa que a variável tem efeito positivo (crescente) na função resultante.
- Se a inclinação for negativa, representa que a variável tem efeito negativo (decrescente) na função resultante.
- No caso da retropropagação, as variáveis seriam pesos e vieses e a função seria a função de perda.
- Em suma, define a influência dos pesos na função de perda, que por sua vez ajuda a ajustar os pesos de forma a diminuir a resultante da função de perda
def f(x):
return 3*(x**2)-(4*x)+5
# Derivative with respect to x is given by
df_dx = (f(x+h)-f(x))/h
w1=Value(2.13,label='w1')
x1=Value(3,label='x1')
w2=Value(3,label='w2')
x2=Value(1,label='x2')
b = Value(5);b.label='b'
##modeling perceptron
w1x1=w1*x1 ;w1x1.label='w1*x1'
w2x2=w2*x2 ;w2x2.label='w2*x2'
w1x1w2x2 = w1x1+w2x2 ; w1x1w2x2.label='(w1*x1)+(w2*x2)'
z = w1x1w2x2+b ; n.label='z'
y = n.relu() ; y.label='y'
- Agora vamos retropropagar através do neurônio.
- Na configuração de rede neural típica, realmente nos concentramos no cálculo da derivada em relação aos pesos... porque nos concentramos em alterar os pesos como parte da otimização.
- Também por uma questão de simplicidade, estamos pegando um único neurônio, mas em redes neurais teríamos muitos neurônios interconectados entre si.
- Eventualmente, haveria uma função de perda que mede a perda da rede neural e voltaríamos a propagar em relação à função de perda para diminuí-la.
- O ponto crucial da retropropagação é a regra da cadeia da derivada.
- Exemplo de regra da cadeia: se um carro viaja duas vezes mais rápido que uma bicicleta e uma bicicleta viaja 4x vezes em comparação com um homem caminhando, como podemos comparar a velocidade do carro com a velocidade de um homem caminhando? Seria 4x2 = 8 vezes mais rápido em comparação com o homem andando.
- Exemplo de regra da cadeia pode ser visto abaixo:
# set some inputs
x = -2; y = 5; z = -4
# perform the forward pass
q = x + y # q becomes 3
f = q * z # f becomes -12
# perform the backward pass (backpropagation) in reverse order:
# first backprop through f = q * z
dfdz = q # df/dz = q, so gradient on z becomes 3
dfdq = z # df/dq = z, so gradient on q becomes -4
dqdx = 1.0
dqdy = 1.0
# now backprop through q = x + y
dfdx = dfdq * dqdx # The multiplication here is the chain rule!
dfdy = dfdq * dqdy
class Value:
""" stores a single scalar value and its gradient """
def __init__(self, data, _children=(), _op='',label=''):
self.data = data
self.grad = 0
# internal variables used for autograd graph construction
self._backward = lambda: None
self._prev = set(_children)
self._op = _op # the op that produced this node, for graphviz / debugging / etc
self.label=''
def __add__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data + other.data, (self, other), '+')
def _backward():
self.grad += out.grad
other.grad += out.grad
out._backward = _backward
return out
def __sub__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data - other.data, (self, other), '-')
def _backward():
self.grad += out.grad
other.grad += out.grad
out._backward = _backward
return out
def __mul__(self, other):
other = other if isinstance(other, Value) else Value(other)
out = Value(self.data * other.data, (self, other), '*')
def _backward():
self.grad += other.data * out.grad
other.grad += self.data * out.grad
out._backward = _backward
return out
def __pow__(self, other):
assert isinstance(other, (int, float)), "only supporting int/float powers for now"
out = Value(self.data**other, (self,), f'**{other}')
def _backward():
self.grad += (other * self.data**(other-1)) * out.grad
out._backward = _backward
return out
def relu(self):
out = Value(0 if self.data < 0 else self.data, (self,), 'ReLU')
def _backward():
self.grad += (out.data > 0) * out.grad
out._backward = _backward
return out
def backward(self):
# topological order all of the children in the graph
topo = []
visited = set()
def build_topo(v):
if v not in visited:
visited.add(v)
for child in v._prev:
build_topo(child)
topo.append(v)
build_topo(self)
# go one variable at a time and apply the chain rule to get its gradient
self.grad = 1
for v in reversed(topo):
v._backward()
- A porta de adição sempre pega o gradiente em sua saída e o distribui igualmente para todas as suas entradas, independentemente de quais foram seus valores durante a passagem para frente.
self.grad += out.grad
other.grad += out.grad
# out is o/p node gradient wrt to loss
# self and other are inputs for which slope have to calculated wrt to loss
self.grad += other.data * out.grad
other.grad += self.data * out.grad
# here we can observe that data of i/p values are swtiched
self.grad += (out.data > 0) * out.grad
def cal_loss(actual_val):
""" Forward pass"""
x1=Value(3,label='x1')
x2=Value(1,label='x2')
w1x1=w1*x1 ;w1x1.label='w1*x1'
w2x2=w2*x2 ;w2x2.label='w2*x2'
w1x1w2x2 = w1x1+w2x2 ; w1x1w2x2.label='(w1*x1)+(w2*x2)'
z = w1x1w2x2+b ;z.label='z'
ypred = z.relu() ; ypred.label='y'
# Calculating the losses
l = (ypred-actual_val)**2; l.label='loss'
print(ypred.data)
return l
- Depois de calcular as perdas, vamos propagar de volta e calcular a quantidade de influência de cada peso para a função de perda.
actual_val=1000
for i in range(10): # number of iteration
l= cal_loss(actual_val) # we will recalculate losses w.r.t new weights
l.backward() #we will calculate the gradient w.r.t loss func wit new weights
w1.data=w1.data-0.01*w1.grad # updation of weights to decrease the losses
w2.data=w2.data-0.01*w2.grad
#>>> ypred getting updated for each iteration of forward pass and backward pass
ypred=14.39
211.512
369.16960000000006
495.29568000000006
596.196544
676.9172352000002
741.4937881600001
793.1550305280001
834.4840244224
867.54721953792
Christopher Nolan uma vez se arrependeu de ter lido o 'roteiro de Pulp Fiction' de Quentin Tarantino





































![O que é uma lista vinculada, afinal? [Parte 1]](https://post.nghiatu.com/assets/images/m/max/724/1*Xokk6XOjWyIGCBujkJsCzQ.jpeg)