Gradientes para termos de viés na retropropagação


13

Eu estava tentando implementar uma rede neural do zero para entender a matemática por trás dela. Meu problema está completamente relacionado à retropropagação quando adotamos derivada em relação ao viés) e deduzi todas as equações usadas na retropropagação. Agora, toda equação está de acordo com o código da rede neural, exceto a derivada em relação aos vieses.

z1=x.dot(theta1)+b1

h1=1/(1+np.exp(-z1))
z2=h1.dot(theta2)+b2
h2=1/(1+np.exp(-z2))

dh2=h2-y
#back prop

dz2=dh2*(1-dh2)
H1=np.transpose(h1)
dw2=np.dot(H1,dz2)
db2=np.sum(dz2,axis=0,keepdims=True)

Procurei on-line o código e quero saber por que adicionamos a matriz e, em seguida, o escalar db2=np.sum(dz2,axis=0,keepdims=True)é subtraído do viés original, por que não a matriz como um todo é subtraída. Alguém pode me ajudar a dar alguma intuição por trás disso. Se eu der uma derivada parcial da perda em relação ao viés, isso me dará um gradiente superior apenas que é dz2 porque z2=h1.dot(theta2)+b2h1 e teta serão 0 e b2 será 1. Portanto, o termo superior será deixado.

b2+=-alpha*db2

Respostas:


6

O termo tendencioso é muito simples, e é por isso que você geralmente não o vê calculado. De fato

db2 = dz2

Portanto, suas regras de atualização para viés em um único item são:

b2 += -alpha * dz2

e

b1 += -alpha * dz1

Em termos de matemática, se sua perda for J, e você sabe JzEu para um dado neurônio Eu que tem termo tendencioso bEu. . .

JbEu=JzEuzEubEu

e

zEubEu=1 1

Porque zEu=(algo não afetado por bEu)+bEu


Parece que o código que você copiou usa o formulário

db2=np.sum(dz2,axis=0,keepdims=True)

porque a rede foi projetada para processar exemplos em (mini-) lotes e, portanto, você tem gradientes calculados para mais de um exemplo por vez. A soma está compactando os resultados em uma única atualização. Isso seria mais fácil de confirmar se você também mostrasse o código de atualização para pesos.


sim, exatamente isso é o que meus pensamentos eram, porque é assim que matematicamente parece. mas o código que vi eles resumiu a matriz e depois a adicionou ao b1.
User34042

theta1=theta1-alpha*dw1 theta2=theta2-alpha*dw2 ainda não entendi. dessa maneira, o mesmo termo será adicionado a todos os termos diferentes do vetor 'b' que, de outra forma, teriam pesos diferentes para cada um dos termos. isso faria uma diferença significativa para a rede neural atingir mínimos.
User34042

@ user34042: Algo não parece certo para mim - você poderia vincular a fonte da qual obteve esse código? Gostaria de saber se a fonte errou, porque misturou e combinou código de mini-lote com simples descida de gradiente online.
Neil Slater


Eu acho que a fonte está errada. O NN continuará trabalhando com todos os valores de polarização iguais, portanto eles podem não ter percebido. E, como mencionei, você pode realmente usar esse código em um cenário baseado em lote, para que possa ser apenas um erro de recortar e colar.
Neil Slater

10

Eu gostaria de explicar o significado de db2=np.sum(dz2,axis=0,keepdims=True), pois também me confundiu uma vez e não foi respondido.

A derivada de L(perda) wrt bé a derivada a montante multiplicada pelo derivado local :

eub=euZZb

Se tivermos várias amostras Ze Lforem ambas matrizes. b ainda é um vetor.

A derivada local é simplesmente um vetor de uns :

Zb=bW×X+b=1 1

Isso significa que nossa derivada completa é uma multiplicação de matrizes, com a seguinte aparência (por exemplo, 2 amostras com 3 saídas) :

euZ×1 1=[......][1 11 11 1]

Observe que esta é a soma das linhas .

E é db2=np.sum(dz2, axis=0, keepdims=True)daí que vem. É simplesmente uma abreviação para a multiplicação de matrizes das derivadas local e a montante.


0

primeiro, você deve corrigir sua fórmula para o gradiente da função sigmóide.

A primeira derivada da função sigmóide é: (1−σ(x))σ(x)

Sua fórmula para dz2 se tornará: dz2 = (1-h2)*h2 * dh2

Você deve usar a saída da função sigmóide para σ(x)não o gradiente.

Você deve somar o gradiente para o viés, pois esse gradiente vem de muitas entradas únicas (o número de entradas = tamanho do lote). Portanto, devemos acumulá-los para atualizar os vieses da camada 2. No entanto, para os gradientes chegarem à camada 1, uma vez que vêm de muitos nós da camada 2, é necessário somar todo o gradiente para atualizar os vieses e pesos da camada 1 Este caso é diferente da soma dos vieses na camada 2.

Meu implemento para duas camadas totalmente conectadas com as funções de ativação são funções sigmóides:

lr = 1e-3
f = lambda x: 1.0/(1.0 + np.exp(-x))
# pass through layer 1
out_l1 = np.dot(x, W_1) + b_1

out_s1 = f(out_l1)

# pass through layer 2
out_l2 = np.dot(x, W_2) + b_2

out_s2 = f(out_l2)

loss = get_loss(out_s2, y)

# BACKWARD PASS
grad = out_s2 - y

d_h2 = (1 - out_s2) * out_s2 * grad

# Accumulate the gradient come from all examples
d_W2 = out_s1.T.dot(d_h2)
d_b2 = np.sum(d_h2, axis=0, keepdims=True)

# sum of gradient come out from prev node:
grad_1 = np.sum(d_W2.T, axis=0, keepdims=True)
d_h1 = (1 - out_l1) * out_l1 * grad_1

d_W1 = x.T.dot(d_h1)
d_b1 = np.sum(d_h1, axis=0, keepdims=True)

W_1 -= d_W1 * lr
b_1 -= d_b1 * lr

W_2 -= d_W2 * lr
b_2 -= d_b2 * lr
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.