Auto Byte

Science AI

# 如何通过梯度检验帮助实现反向传播

• 右边形式：

•  [J(θ+ϵ)−J(θ)]/ϵ

• 双边形式（见图 2）：

•  [J(θ+ϵ)−J(θ−ϵ)]/2ϵ

• 解析导数：∇_xf(x)=2x ⇒∇_xf(3)=6

• 双边数值导数：[(3+1e−2)^2−(3−1e−2)^2]/[2∗1e−2]=5.999999999999872

• 右边数值导数：[(3+1e−2)^2−3^2]/[1e−2]=6.009999999999849

1. 随机从训练集中抽取一些样本，用来计算数值梯度和解析梯度（不要使用所有训练样本，因为梯度检验运行会很慢）。

2. 初始化参数

3. 计算前向传播和交叉熵损失。

4. 利用写好的反向传播的实现代码计算梯度（解析梯度）。

5. 计算双边形式的数值梯度。

6. 计算数值梯度和解析解梯度的差值。

``````# Loading packages
import sys

import h5py
import matplotlib.pyplot as plt
import numpy as np
from numpy.linalg import norm
import seaborn as sns

sys.path.append("../scripts/")
from coding_neural_network_from_scratch import (initialize_parameters,
L_model_forward,
L_model_backward,
compute_cost)
# Import the data
train_dataset = h5py.File("../data/train_catvnoncat.h5")
X_train = np.array(train_dataset["train_set_x"]).T
y_train = np.array(train_dataset["train_set_y"]).T
X_train = X_train.reshape(-1, 209)
y_train = y_train.reshape(-1, 209)

X_train.shape, y_train.shape
((12288, 209), (1, 209))
``````

``````def dictionary_to_vector(params_dict):"""
Roll a dictionary into a single vector.

Arguments
---------
params_dict : dict
learned parameters.

Returns
-------
params_vector : array
vector of all parameters concatenated.
"""count = 0for key in params_dict.keys():new_vector = np.reshape(params_dict[key], (-1, 1))if count == 0:theta_vector = new_vectorelse:theta_vector = np.concatenate((theta_vector, new_vector))count += 1return theta_vectordef vector_to_dictionary(vector, layers_dims):"""
Unroll parameters vector to dictionary using layers dimensions.

Arguments
---------
vector : array
parameters vector.
layers_dims : list or array_like
dimensions of each layer in the network.

Returns
-------
parameters : dict
dictionary storing all parameters.
"""L = len(layers_dims)parameters = {}k = 0for l in range(1, L):# Create temp variable to store dimension used on each layerw_dim = layers_dims[l] * layers_dims[l - 1]b_dim = layers_dims[l]# Create temp var to be used in slicing parameters vectortemp_dim = k + w_dim# add parameters to the dictionaryparameters["W" + str(l)] = vector[k:temp_dim].reshape(layers_dims[l], layers_dims[l - 1])parameters["b" + str(l)] = vector[temp_dim:temp_dim + b_dim].reshape(b_dim, 1)k += w_dim + b_dimreturn parametersdef gradients_to_vector(gradients):"""
Roll all gradients into a single vector containing only dW and db.

Arguments
---------
storing gradients of weights and biases for all layers: dA, dW, db.

Returns
-------
vector of only dW and db gradients.
``````

``````def forward_prop_cost(X, parameters, Y, hidden_layers_activation_fn="tanh"):"""
Implements the forward propagation and computes the cost.

Arguments
---------
X : 2d-array
input data, shape: number of features x number of examples.
parameters : dict
parameters to use in forward prop.
Y : array
true "label", shape: 1 x number of examples.
hidden_layers_activation_fn : str
activation function to be used on hidden layers: "tanh", "relu".

Returns
-------
cost : float
cross-entropy cost.
"""# Compute forward propAL, _ = L_model_forward(X, parameters, hidden_layers_activation_fn)# Compute costcost = compute_cost(AL, Y)return costdef gradient_check(parameters, gradients, X, Y, layers_dims, epsilon=1e-7,hidden_layers_activation_fn="tanh"):"""
Checks if back_prop computes correctly the gradient of the cost output by
forward_prop.

Arguments
---------
parameters : dict
storing all parameters to use in forward prop.
gradients of weights and biases for all layers: dA, dW, db.
X : 2d-array
input data, shape: number of features x number of examples.
Y : array
true "label", shape: 1 x number of examples.
epsilon :
tiny shift to the input to compute approximate gradient.
layers_dims : list or array_like
dimensions of each layer in the network.

Returns
-------
difference : float
"implementation. The difference is: {}".format(difference))else:print ("\033[32mThere implementation of back-propagation is fine! "+\
"The difference is: {}".format(difference))return difference
``````
``````# Set up neural network architecture
layers_dims = [X_train.shape[0], 5, 5, 1]

# Initialize parameters
parameters = initialize_parameters(layers_dims)

# Randomly selecting 1 example from training data
perms = np.random.permutation(X_train.shape[1])
index = perms[:1]

# Compute forward propagation
AL, caches = L_model_forward(X_train[:, index], parameters, "tanh")

gradients = L_model_backward(AL, y_train[:, index], caches, "tanh")

# Compute difference of numerical and analytical gradients
``````

• 双边形式的数值梯度在逼近解析梯度时效果比单边形式的数值梯度更好。

• 由于梯度检验的运行很慢，因此：

•  进行梯度检验时，只使用一个或少数样本；

•  在确认反向传播的实现代码无误后，训练神经网络时记得取消梯度检验函数的调用。

• 如果使用了 drop-out 策略，（直接进行）梯度检验会失效。可以在进行梯度检验时，将 keep-prob 设置为 1，训练神经网络时，再进行修改。

• 通常采用 e=10e-7 作为检查解析梯度和数值梯度间差值的基准。如果差值小于 10e-7，则反向传播的实现代码没有问题。

• 幸运的是，在诸如 TensorFlow、PyTorch 等深度学习框架中，我们几乎不需要自己实现反向传播，因为这些框架已经帮我们计算好梯度了；但是，在成为一个深度学习工作者之前，动手实现这些算法是很好的练习，可以帮助我们理解其中的原理。

（人工）神经网络是一种起源于 20 世纪 50 年代的监督式机器学习模型，那时候研究者构想了「感知器（perceptron）」的想法。这一领域的研究者通常被称为「联结主义者（Connectionist）」，因为这种模型模拟了人脑的功能。神经网络模型通常是通过反向传播算法应用梯度下降训练的。目前神经网络有两大主要类型，它们都是前馈神经网络：卷积神经网络（CNN）和循环神经网络（RNN），其中 RNN 又包含长短期记忆（LSTM）、门控循环单元（GRU）等等。深度学习是一种主要应用于神经网络帮助其取得更好结果的技术。尽管神经网络主要用于监督学习，但也有一些为无监督学习设计的变体，比如自动编码器和生成对抗网络（GAN）。