Nivel 1: implementar la retropropagación de la capa totalmente conectada
detalles de la misión
La tarea de este nivel: realizar la retropropagación de la capa totalmente conectada.
información relacionada
Para completar esta tarea, debe dominar:
- Propagación hacia atrás de redes neuronales;
- Propagación hacia atrás para capas totalmente conectadas.
Para conocer el contenido de esta capacitación, consulte el Capítulo 5 del libro "Introducción al aprendizaje profundo: teoría e implementación basada en Python".
Retropropagación de redes neuronales
En la capacitación anterior, aprendimos que la red neuronal calcula el gradiente de cada parámetro a través de la retropropagación, y la idea central de la retropropagación es la regla de derivación de la cadena, a saber:
∂x∂l=∂f(x)∂l⋅∂x∂f(x)
Entonces, dada una red neuronal, ¿cómo funciona la retropropagación? Aquí tomamos una red neuronal de tres capas como ejemplo para explicar la retropropagación de la red neuronal. La siguiente figura muestra la estructura de esta red neuronal simple.
Figura 1 Red neuronal simple de tres capas
Primero, introducimos una notación f(x;W) para representar una capa de red con entrada x y parámetro W. Supongamos que las tres capas de esta red neuronal son f1(x;W1), f2(x;W2), f3(x;W3), y la función de activación después de cada capa es g1(x), g2 (x), g3(x). La función de pérdida utilizada en el entrenamiento de redes es L(x,t), donde x representa la salida de la red y t representa el objetivo. Entonces el proceso de cálculo de esta red se puede expresar como:
y1z1y2z2y3z3l=f1(x;W1)=g1(y1)=f2(z1;W2)=g2(y2)=f3 (z2;W3)=g3(y3)=L(z3,t)
Al hacer retropropagación, primero retropropagamos la función de pérdida:
∂z3∂l=∂z3∂L(z3,t)
Posteriormente, la tercera capa se propaga hacia atrás, y la capa de red anterior se puede deducir de la misma manera:
∂y3∂l∂W3∂l∂z2∂l=∂z3∂l⋅∂y3∂z3=∂z3∂l⋅∂y3∂g3(y3 )=∂y3∂l⋅∂W3∂y3=∂y3∂l⋅∂W3∂f3(z2;W3)=∂y3∂l⋅∂z2 ∂y3=∂y3∂l⋅∂z2∂f3(z2;W3)
De la derivación anterior se puede ver que para una capa de red específica, siempre que se conozca el gradiente de su salida, sus parámetros y el gradiente de entrada solo están relacionados con el cálculo de esta capa y no tienen nada que ver con la capa de red. antes y despues Por lo tanto, siempre que se defina el cálculo de la propagación directa y la propagación inversa para cada capa de red, la red neuronal puede calcular el gradiente de cada parámetro. En este proceso, el cálculo de la red neuronal forma un gráfico de cálculo , que no se ampliará en detalle aquí. Los estudiantes interesados pueden consultar los capítulos 5.1−5.2 del libro de texto.
Propagación hacia atrás de capas completamente conectadas
A continuación, aprendamos la retropropagación de la capa totalmente conectada. Primero, revise el pase hacia adelante de la capa completamente conectada. Una capa completamente conectada con N neuronas de entrada y M neuronas de salida contiene dos conjuntos de parámetros: peso W∈RN×M y sesgo b∈RM, cuya entrada puede considerarse como un vector (columna) N-dimensional x ∈RN, en este tiempo, el cálculo de la propagación directa de la capa completamente conectada se puede expresar como:
y=xTW+b
Suponga primero que ∂l/∂y se conoce de acuerdo con la regla de la cadena. De acuerdo con la definición de la capa completamente conectada, debe ser un vector (columna) de M-dimensional. De acuerdo con la regla de derivación del cálculo matricial, podemos obtener:
∂b∂l∂W∂l∂x∂l=∂y∂l⋅∂b∂y=∂y∂l=∂y∂l⋅∂W∂y=x×( ∂y∂l)T=∂y∂l⋅∂x∂y=W×∂y∂l
Implementación de retropropagación de capa totalmente conectada
El entrenamiento amplía la clase definida en el entrenamiento anterior FullyConnected
, el entrenamiento ya ha dado forward(x)
la implementación, y se ha modificado para satisfacer las necesidades de backpropagation. Debe implementar la función de retropropagación de esta clase backward(dout)
, dout
que es el gradiente de la función de pérdida en relación con la salida de la capa totalmente conectada, es decir, ∂y∂l en la fórmula anterior, que es una forma de (B, M) numpy.ndarray
, y M es la capa completamente conectada el número de canales de salida. Durante la propagación hacia adelante, debido a que la forma de la entrada de la capa totalmente conectada se puede cambiar y registrar en self.original_x_shape
, una vez que se complete el cálculo, restaure el gradiente de la entrada a la forma original. La entrada a la capa completamente conectada se documenta en self.x
. Durante la retropropagación, almacene los gradientes de self.W
y en y devuelva los gradientes de , respectivamente .self.b
self.dW
self.db
x
requisitos de programación
De acuerdo con el indicador, agregue el código entre Begin y End del editor a la derecha para realizar la retropropagación de la capa completamente conectada.
instrucción de prueba
La plataforma probará el código que escriba. El método de prueba es: la plataforma generará aleatoriamente la entrada x
, el peso W
, el sesgo b
y el gradiente de salida dout
, y luego creará una instancia de la clase de acuerdo con su código de implementación FullyConnected
, y luego usará la instancia para realizar propagación hacia adelante primero Calcule y luego realice el cálculo de propagación hacia atrás. Sus respuestas se compararán con las respuestas estándar. Debido a que el cálculo de los números de punto flotante puede tener errores, siempre que el error entre su respuesta y la respuesta estándar no exceda 10−5.
Ejemplo de entrada:
W:
[[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6]]
b:
[0.1, 0.2, 0.3]
x:
[[1, 2],
[3, 4]]
dout:
[[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6]]
Entonces el gradiente correspondiente es:
dx:
[[0.14 0.32]
[0.32 0.77]]
dW:
[[1.3 1.7 2.1]
[1.8 2.4 3. ]]
db:
[0.5 0.7 0.9]
Los resultados anteriores tienen errores de redondeo, que puede ignorar.
¡Comencemos tu misión, te deseo éxito!
Código de implementación:
importar numpy como np
clase totalmente conectado:
def __init__(self, W, b):
r''''
Inicialización de la capa completamente conectada.
Parámetro:
- W: numpy.array, (D_in, D_out)
- b: numpy.array, (D_out)
'''
self.W = W
self.b = b
self.x = Ninguno
self.original_x_shape = Ninguno
self.dW = Ninguno
self.db = Ninguno
def adelante(auto, x):
r''''
El pase hacia adelante de la capa completamente conectada.
Parámetro:
- x: numpy.arreglo, (B, d1, d2, ..., dk)
Devolver:
- y: numpy.matriz, (B, M)
'''
self.original_x_shape = x.forma
x = x.reforma(x.forma[0], -1)
self.x = x
out = np.dot(self.x, self.W) + self.b
regresar
def hacia atrás(self, dout):
r''''
Propagación hacia atrás de capas completamente conectadas
Parámetro:
- dout: numpy.array, (B, M)
Devolver:
- dx: numpy.array, (B, d1, d2, ..., dk) misma forma que self.original_x_shape
Además, es necesario calcular los siguientes resultados:
- self.dW: numpy.array, (N, M) misma forma que self.W
- self.db: numpy.array, (M,)
'''
########## Comenzar ##########
dx = np.dot(dout, self.WT)
self.dW = np.dot(self.xT, dout)
self.db = np.sum(dout, eje=0)
dx = dx.reshape(*self.original_x_shape)
volver dx
########## Fin ##########
Captura de pantalla del código:
Nivel 2: implementar la retropropagación de funciones de activación comunes
detalles de la misión
La tarea de este nivel: realizar la retropropagación de las funciones de activación comunes.
información relacionada
Para completar la tarea de este nivel, debe dominar: retropropagación de funciones de activación comunes.
Para conocer el contenido de esta capacitación, consulte el Capítulo 5.5 del libro "Introducción al aprendizaje profundo: teoría e implementación basada en Python".
Propagación hacia atrás de funciones de activación comunes
En el último paso, aprendimos sobre la retropropagación para capas completamente conectadas. En este pase, vamos un paso más allá y aprendemos sobre la retropropagación para funciones de activación de uso común. Como en el entrenamiento anterior, nos enfocamos principalmente en las dos funciones de activación de sigmoid y ReLU.
1. función de activación sigmoide
En el entrenamiento anterior, aprendimos la propagación directa de la función de activación sigmoidea:
y=sigmoide(x)=1/(1+e−x)
Debido a que la función de activación se calcula elemento por elemento, la retropropagación de la función de activación solo necesita resolverse de acuerdo con la regla de derivación de funciones:
∂x∂y=(1−y)y
Para la función de activación sigmoidea, puede obtener:
∂x∂l=∂y∂l⋅∂x∂y=∂y∂l⋅(1−y)y
2. Función de activación ReLU
En el entrenamiento anterior, aprendimos la propagación directa de la función de activación de ReLU:
y=ReLU(x)=max(0,x)
Se puede ver que hay un punto no diferenciable x=0 en la función de activación de ReLU, y no se puede derivar directamente. En este punto, usamos el subgradiente de ReLU en ese punto:
∂x∂y(0)=0
En cuanto al concepto de subgradiente, no lo introduciré en profundidad aquí, los estudiantes interesados pueden leer libros relacionados. Después de introducir el subgradiente, el gradiente de la función de activación de ReLU se puede expresar completamente como:
∂x∂y={0,x≤01,x>0
Combinado con la fórmula de retropropagación anterior, se puede obtener el método de cálculo de retropropagación de la función de activación ReLU.
Implementación de la función de activación común backpropagation
Para la función de activación sigmoidea, el entrenamiento expande la clase definida en el entrenamiento anterior Sigmoid
, el entrenamiento ha dado forward(x)
la implementación y se ha modificado para satisfacer las necesidades de backpropagation. Debe implementar la función de retropropagación de esta clase backward(dout)
, dout
que es el gradiente de la función de pérdida en relación con la salida sigmoide, es decir, ∂y∂l en la fórmula anterior, que tiene la x
misma forma que la entrada numpy.ndarray
. La salida del pase hacia adelante se registra en self.out
, para que pueda realizar los cálculos de propagación hacia atrás convenientemente. backward(dout)
La función necesita devolver x
el gradiente de la entrada.
Para la función de activación de ReLU, el entrenamiento amplía la clase definida en el entrenamiento anterior ReLU
, el entrenamiento ya ha dado forward(x)
la implementación y se ha modificado para satisfacer las necesidades de propagación hacia atrás. Debe implementar la función de retropropagación de esta clase backward(dout)
, dout
que es el gradiente de la función de pérdida en relación con la salida sigmoide, es decir, ∂y∂l en la fórmula anterior, que tiene la x
misma forma que la entrada numpy.ndarray
. self.mask
Registra si cada entrada es menor o igual a 0 durante la propagación directa, para facilitar el cálculo de la dirección de propagación. backward(dout)
La función necesita devolver x
el gradiente de la entrada.
requisitos de programación
De acuerdo con el aviso, agregue el código entre Begin y End del editor a la derecha para realizar la retropropagación de la función de activación anterior.
instrucción de prueba
La plataforma probará el código que escriba. El método de prueba es: la plataforma generará aleatoriamente x
gradientes de entrada y salida , y luego creará una instancia de / class dout
de acuerdo con su implementación , y luego usará esta instancia para realizar cálculos de propagación hacia adelante, y luego Cálculos de diferencial inverso. Sus respuestas se compararán con las respuestas estándar. Debido a que el cálculo de los números de punto flotante puede tener errores, siempre que el error entre su respuesta y la respuesta estándar no exceda 10−5.Sigmoid
ReLU
Ejemplo de entrada:
# 对于sigmoid激活函数:
x:
[[-1, 0, 1]]
dout:
[[1, 2, 3]]
#对于ReLU激活函数:
x:
[[-1, 0, 1]]
dout:
[[1, 2, 3]]
Entonces el gradiente correspondiente es:
# 对于sigmoid激活函数:
dx:
[[0.20, 0.50, 0.59]]
#对于ReLU激活函数:
dx:
[0, 2, 3]
Los resultados anteriores tienen errores de redondeo, que puede ignorar.
¡Comencemos tu misión, te deseo éxito!
Código de implementación:
importar numpy como np
clase sigmoide:
def __init__(uno mismo):
self.out = Ninguno
def adelante(auto, x):
r''''
Propagación hacia adelante de la función de activación sigmoidea.
Parámetro:
- x: numpy.arreglo, (B, d1, d2, ..., dk)
Devolver:
- y: numpy.arreglo, (B, d1, d2, ..., dk)
'''
salida = 1. / (1. + np.exp(-x))
self.fuera = fuera
regresar
def hacia atrás(self, dout):
r''''
Retropropagación del sigmoide
Parámetro:
- dout: numpy.array, (B, d1, d2, ..., dk)
Devolver:
- dx: numpy.array, (B, d1, d2, ..., dk)
'''
########## Comenzar ##########
dx = dout * (1.0 - self.out) * self.out
volver dx
########## Fin ##########
clase Relu:
def __init__(uno mismo):
self.mask = Ninguno
def adelante(auto, x):
r''''
Propagación directa de la función de activación de ReLU.
Parámetro:
- x: numpy.arreglo, (B, d1, d2, ..., dk)
Devolver:
- y: numpy.arreglo, (B, d1, d2, ..., dk)
'''
self.máscara = (x <= 0)
fuera = x.copiar()
fuera[self.mask] = 0
regresar
def hacia atrás(self, dout):
r''''
Retropropagación de relu
Parámetro:
- dout: numpy.array, (B, d1, d2, ..., dk)
Devolver:
- dx: numpy.array, (B, d1, d2, ..., dk)
'''
########## Comenzar ##########
dout[self.máscara] = 0
dx = salida
volver dx
########## Fin ##########