6.梯度检验
import numpy as np
from testCases import *
from gc_utils import sigmoid, relu, dictionary_to_vector, vector_to_dictionary, gradients_to_vector
#########################################################
这段代码看起来导入了一些Python库和模块,以及一些自定义的测试用例和函数。
-
import numpy as np
: 这行代码导入了名为numpy
的Python库,并将其重命名为np
,以便在代码中更方便地使用。NumPy是一个用于科学计算的强大库,它提供了多维数组对象和许多用于操作这些数组的函数。 -
from testCases import *
: 这行代码导入了一个名为testCases
的自定义模块中的所有内容。通常,testCases
模块可能包含一些测试数据和用于测试特定函数或算法的测试用例。 -
from gc_utils import sigmoid, relu, dictionary_to_vector, vector_to_dictionary, gradients_to_vector
: 这行代码从一个名为gc_utils
的自定义模块中导入了一些特定的函数。这些函数包括:sigmoid
: 一个用于计算S形函数(sigmoid函数)的函数。relu
: 一个用于计算修正线性单元(ReLU)函数的函数。dictionary_to_vector
: 用于将字典中的参数转换为向量的函数。vector_to_dictionary
: 用于将向量转换为字典中的参数的函数。gradients_to_vector
: 用于将梯度值转换为向量的函数。
这段代码看起来是为了在后续的代码中使用这些函数和模块的功能。
#########################################################
def forward_propagation(x, theta):
J = np.dot(theta, x)
return J
#########################################################
这是一个名为 forward_propagation
的函数,用于执行前向传播(forward propagation)操作。
-
x
: 这是一个输入向量或矩阵,表示神经网络的输入数据。通常,这是一个包含特征值的向量。 -
theta
: 这是一个参数向量或矩阵,表示神经网络的权重参数。这些参数用于计算前向传播中的预测值。 -
J
: 这是函数的返回值,它表示前向传播的输出或预测结果。具体来说,它是通过计算theta
和x
的点积(dot product)得到的。
在神经网络中,前向传播是用于计算模型的预测输出的过程。这个函数简化了前向传播的实现,只计算了点积,而没有经过激活函数或其他复杂的运算。通常,在神经网络中,前向传播还会涉及到激活函数的应用,但这个函数似乎是一个简化版本,仅用于说明目的。
#########################################################
x, theta = 2, 4
J = forward_propagation(x, theta)
print ("J = " + str(J))
#########################################################
这段代码执行以下步骤:
-
x, theta = 2, 4
: 这一行代码定义了两个变量x
和theta
,并为它们赋值。具体来说,x
被赋值为2,而theta
被赋值为4。 -
J = forward_propagation(x, theta)
: 这一行代码调用了forward_propagation
函数,传递了x
和theta
作为参数。函数将根据传入的参数计算前向传播结果,并将结果赋值给变量J
。 -
print ("J = " + str(J))
: 最后一行代码将计算得到的前向传播结果J
打印到屏幕上。在这里,str(J)
是将J
转换为字符串,以便与字符串 "J = " 连接在一起进行打印。
总结一下,这段代码的目的是计算给定的 x
和 theta
的前向传播结果,并将结果打印到屏幕上。在这个例子中,前向传播结果 J
将是 2 * 4 = 8
,因此屏幕上将打印出 "J = 8"。
#########################################################
# 反向传播
def backward_propagation(x, theta):
dtheta = x
return dtheta
#########################################################
这是一个名为 backward_propagation
的函数,用于执行反向传播(backward propagation)操作。
-
x
: 这是前向传播中使用的输入数据。 -
theta
: 这是前向传播中使用的权重参数。 -
dtheta
: 这是函数的返回值,表示反向传播的输出或梯度。具体来说,它是等于输入参数x
的值。
在神经网络中,反向传播是用于计算梯度的过程,以便优化模型的权重参数。通常,反向传播会计算相对于损失函数的梯度,并使用梯度下降等优化算法来更新参数。
在这个函数中,梯度 dtheta
的计算非常简单,只是将输入 x
直接作为梯度的值返回。这是一个非常简化的示例,通常在实际的神经网络中,梯度计算会更加复杂,涉及到损失函数和激活函数的导数等。
#########################################################
x, theta = 2, 4
dtheta = backward_propagation(x, theta)
print ("dtheta = " + str(dtheta))
#########################################################
这段代码使用了先前定义的 backward_propagation
函数来计算反向传播的梯度,并打印输出。
-
x, theta = 2, 4
: 这一行代码定义了两个变量x
和theta
,并为它们赋值。具体来说,x
被赋值为2,而theta
被赋值为4。 -
dtheta = backward_propagation(x, theta)
: 这一行代码调用了backward_propagation
函数,传递了x
和theta
作为参数。函数将根据传入的参数计算反向传播的梯度,并将结果赋值给变量dtheta
。 -
print ("dtheta = " + str(dtheta))
: 最后一行代码将计算得到的反向传播梯度dtheta
打印到屏幕上。在这里,str(dtheta)
是将dtheta
转换为字符串,以便与字符串 "dtheta = " 连接在一起进行打印。
在这个例子中,反向传播梯度 dtheta
将等于输入 x
的值,即 dtheta = 2
。因此,在屏幕上将打印出 "dtheta = 2"。
总结一下,这段代码的目的是计算给定的 x
和 theta
的反向传播梯度,并将结果打印到屏幕上。
#########################################################
def gradient_check(x, theta, epsilon=1e-7):
# 利用前向传播计算出一个梯度
thetaplus = theta + epsilon
thetaminus = theta - epsilon
J_plus = forward_propagation(x, thetaplus)
J_minus = forward_propagation(x, thetaminus)
gradapprox = (J_plus - J_minus) / (2 * epsilon)
# 利用反向传播也计算出一个梯度
grad = backward_propagation(x, theta)
# 对比两个梯度相差多远
numerator = np.linalg.norm(grad - gradapprox)
denominator = np.linalg.norm(grad) + np.linalg.norm(gradapprox)
difference = numerator / denominator
if difference < 1e-7:
print("反向传播是正确的!")
else:
print("反向传播有问题!")
return difference
#########################################################
这是一个名为 gradient_check
的函数,用于进行梯度检查(gradient checking)。梯度检查是一种用于验证反向传播算法是否正确实现的方法。
-
x
: 这是输入数据,用于进行前向传播和反向传播。 -
theta
: 这是权重参数,用于计算前向传播和反向传播。 -
epsilon
: 这是一个非常小的数,用于计算数值梯度的时候添加到theta
中。它通常被设置为一个很小的正数,例如1e-7
。
函数的主要步骤如下:
-
计算数值梯度 (
gradapprox
):- 使用
theta
和epsilon
来计算thetaplus
和thetaminus
,分别是theta
加上和减去epsilon
。 - 使用
thetaplus
和thetaminus
分别计算两个前向传播的结果J_plus
和J_minus
。 - 计算数值梯度
gradapprox
,它等于(J_plus - J_minus) / (2 * epsilon)
。
- 使用
-
使用反向传播计算真实梯度 (
grad
)。 -
计算两个梯度之间的差异 (
difference
),具体计算如下:numerator
是两个梯度向量之间的欧几里德范数。denominator
是两个梯度向量的欧几里德范数之和。difference
等于numerator / denominator
。
-
如果
difference
小于1e-7
,则认为反向传播实现正确,否则认为反向传播有问题。
最后,函数会返回 difference
,表示数值梯度与真实梯度之间的差异。
这个函数的主要目的是帮助验证反向传播的实现是否正确,因为数值梯度是通过计算前向传播的微小变化来估算的,它应该与反向传播得到的梯度非常接近。
你可以使用这个函数来检查你的神经网络模型的梯度计算是否正确。如果 difference
很小(小于 1e-7
),则反向传播实现正确。
#########################################################
x, theta = 2, 4
difference = gradient_check(x, theta)
print("difference = " + str(difference))
#########################################################
这段代码使用了先前定义的 gradient_check
函数来进行梯度检查,并打印输出梯度的差异。
-
x, theta = 2, 4
: 这一行代码定义了两个变量x
和theta
,并为它们赋值。具体来说,x
被赋值为2,而theta
被赋值为4。 -
difference = gradient_check(x, theta)
: 这一行代码调用了gradient_check
函数,传递了x
和theta
作为参数。函数将执行梯度检查,并将差异结果赋值给变量difference
。 -
print("difference = " + str(difference))
: 最后一行代码将计算得到的差异difference
打印到屏幕上。在这里,str(difference)
是将difference
转换为字符串,以便与字符串 "difference = " 连接在一起进行打印。
梯度检查的结果将显示在屏幕上,告诉你数值梯度与真实梯度之间的差异有多大。如果差异很小(小于 1e-7
),则说明反向传播的实现是正确的。否则,如果差异较大,则可能需要检查反向传播的实现是否存在问题。
总结一下,这段代码的目的是使用 gradient_check
函数来验证给定的 x
和 theta
的梯度计算是否正确,并打印出梯度的差异。
#########################################################
def forward_propagation_n(X, Y, parameters):
m = X.shape[1]
W1 = parameters["W1"]
b1 = parameters["b1"]
W2 = parameters["W2"]
b2 = parameters["b2"]
W3 = parameters["W3"]
b3 = parameters["b3"]
# LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
Z1 = np.dot(W1, X) + b1
A1 = relu(Z1)
Z2 = np.dot(W2, A1) + b2
A2 = relu(Z2)
Z3 = np.dot(W3, A2) + b3
A3 = sigmoid(Z3)
logprobs = np.multiply(-np.log(A3), Y) + np.multiply(-np.log(1 - A3), 1 - Y)
cost = 1. / m * np.sum(logprobs)
cache = (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3)
return cost, cache
#########################################################
这是一个名为 forward_propagation_n
的函数,用于执行神经网络的前向传播操作。
-
X
: 这是输入特征的矩阵,每一列代表一个训练样本,每一行代表一个特征。 -
Y
: 这是与输入样本对应的标签,用于训练神经网络。 -
parameters
: 这是包含神经网络权重和偏差参数的字典,包括W1
,b1
,W2
,b2
,W3
,b3
等参数。
函数的主要步骤如下:
-
获取样本数量
m
,以及各层的权重W
和偏差b
。 -
执行前向传播计算:
- 计算第一层的线性组合
Z1
,并通过激活函数 ReLU 得到A1
。 - 计算第二层的线性组合
Z2
,并通过 ReLU 激活函数得到A2
。 - 计算第三层的线性组合
Z3
,并通过 Sigmoid 激活函数得到A3
。
- 计算第一层的线性组合
-
计算成本(损失)
cost
:- 通过计算交叉熵损失函数来计算
logprobs
。 - 对所有样本求平均,得到总体损失
cost
。
- 通过计算交叉熵损失函数来计算
-
将中间计算结果保存在
cache
中,以便在后续的反向传播中使用。 -
返回
cost
和cache
。
这个函数实现了一个具有三层神经网络的前向传播过程,包括两个隐藏层和一个输出层。它计算出网络的预测结果 A3
和成本 cost
,用于后续的反向传播和训练。
#########################################################
def backward_propagation_n(X, Y, cache):
m = X.shape[1]
(Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
dZ3 = A3 - Y
dW3 = 1. / m * np.dot(dZ3, A2.T)
db3 = 1. / m * np.sum(dZ3, axis=1, keepdims=True)
dA2 = np.dot(W3.T, dZ3)
dZ2 = np.multiply(dA2, np.int64(A2 > 0))
dW2 = 1. / m * np.dot(dZ2, A1.T) * 2 # ~~
db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)
dA1 = np.dot(W2.T, dZ2)
dZ1 = np.multiply(dA1, np.int64(A1 > 0))
dW1 = 1. / m * np.dot(dZ1, X.T)
db1 = 4. / m * np.sum(dZ1, axis=1, keepdims=True) # ~~
gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,
"dA2": dA2, "dZ2": dZ2, "dW2": dW2, "db2": db2,
"dA1": dA1, "dZ1": dZ1, "dW1": dW1, "db1": db1}
return gradients
#########################################################
这是一个名为 backward_propagation_n
的函数,用于执行神经网络的反向传播操作。
-
X
: 这是输入特征的矩阵,每一列代表一个训练样本,每一行代表一个特征。 -
Y
: 这是与输入样本对应的标签,用于训练神经网络。 -
cache
: 这是在前向传播过程中计算得到的中间结果的缓存,包括Z1
,A1
,W1
,b1
,Z2
,A2
,W2
,b2
,Z3
,A3
,W3
,b3
等中间变量。
函数的主要步骤如下:
-
获取样本数量
m
,以及在前向传播中计算得到的各层的中间变量Z
、A
、W
和b
。 -
计算反向传播中的梯度:
- 针对输出层,计算
dZ3
为A3 - Y
,dW3
为输出层权重W3
对应的梯度,db3
为输出层偏差b3
对应的梯度。 - 针对第二隐藏层,计算
dA2
为上一层梯度dZ3
与权重W3
的乘积,然后计算dZ2
为dA2
乘以 ReLU 激活函数的导数(根据A2 > 0
来计算),dW2
为权重W2
对应的梯度,db2
为偏差b2
对应的梯度。 - 针对第一隐藏层,计算
dA1
为上一层梯度dZ2
与权重W2
的乘积,然后计算dZ1
为dA1
乘以 ReLU 激活函数的导数(根据A1 > 0
来计算),dW1
为权重W1
对应的梯度,db1
为偏差b1
对应的梯度。
- 针对输出层,计算
-
将计算得到的梯度保存在字典
gradients
中,以便后续的参数更新。 -
返回梯度字典
gradients
。
这个函数执行了神经网络的反向传播过程,计算了各层的梯度,用于后续的参数更新。梯度计算涉及链式法则和激活函数的导数。这个函数返回一个包含各层梯度的字典。
#########################################################
def gradient_check_n(parameters, gradients, X, Y, epsilon=1e-7):
parameters_values, _ = dictionary_to_vector(parameters)
grad = gradients_to_vector(gradients)
num_parameters = parameters_values.shape[0]
J_plus = np.zeros((num_parameters, 1))
J_minus = np.zeros((num_parameters, 1))
gradapprox = np.zeros((num_parameters, 1))
# 计算gradapprox
for i in range(num_parameters):
thetaplus = np.copy(parameters_values)
thetaplus[i][0] = thetaplus[i][0] + epsilon
J_plus[i], _ = forward_propagation_n(X, Y, vector_to_dictionary(thetaplus))
thetaminus = np.copy(parameters_values)
thetaminus[i][0] = thetaminus[i][0] - epsilon
J_minus[i], _ = forward_propagation_n(X, Y, vector_to_dictionary(thetaminus))
gradapprox[i] = (J_plus[i] - J_minus[i]) / (2 * epsilon)
numerator = np.linalg.norm(grad - gradapprox)
denominator = np.linalg.norm(grad) + np.linalg.norm(gradapprox)
difference = numerator / denominator
if difference > 2e-7:
print("\033[93m" + "反向传播有问题! difference = " + str(difference) + "\033[0m")
else:
print("\033[92m" + "反向传播很完美! difference = " + str(difference) + "\033[0m")
return difference
#########################################################
这是一个名为 gradient_check_n
的函数,用于执行梯度检查以验证反向传播的正确性。这个函数的主要目的是比较数值梯度(通过微小变化计算)和实际梯度(通过反向传播计算)之间的差异。
-
parameters
: 这是包含神经网络权重和偏差参数的字典。 -
gradients
: 这是包含通过反向传播计算得到的梯度的字典。 -
X
: 这是输入特征的矩阵,每一列代表一个训练样本,每一行代表一个特征。 -
Y
: 这是与输入样本对应的标签,用于训练神经网络。 -
epsilon
: 这是一个非常小的数,用于计算数值梯度的时候添加到参数中。通常被设置为一个很小的正数,例如1e-7
。
函数的主要步骤如下:
-
将参数字典
parameters
转换为参数向量parameters_values
,将梯度字典gradients
转换为梯度向量grad
。这是为了方便计算梯度向量的范数。 -
获取参数向量的维度
num_parameters
。 -
初始化一些数组来存储正向传播过程中计算的损失值
J_plus
和J_minus
,以及数值梯度gradapprox
。 -
计算数值梯度
gradapprox
:- 对每个参数进行循环,分别增加和减少
epsilon
后,计算正向传播的损失值J_plus
和J_minus
。 - 计算数值梯度
gradapprox[i]
为(J_plus[i] - J_minus[i]) / (2 * epsilon)
。
- 对每个参数进行循环,分别增加和减少
-
计算梯度的差异:
- 计算
numerator
为数值梯度gradapprox
与实际梯度grad
向量的欧几里德范数差。 - 计算
denominator
为实际梯度grad
向量的欧几里德范数与数值梯度gradapprox
向量的欧几里德范数之和。 - 计算
difference
为numerator / denominator
,表示两者之间的差异。
- 计算
-
比较
difference
与一个阈值(2e-7
):- 如果
difference
大于阈值,说明反向传播可能存在问题,打印警告信息。 - 否则,如果
difference
很小,说明反向传播实现很好,打印成功信息。
- 如果
最后,函数返回 difference
,表示数值梯度与实际梯度之间的差异。
这个函数的主要目的是用于验证反向传播的实现是否正确,通过比较数值梯度和实际梯度的一致性。如果差异小于阈值,则认为反向传播实现正确。
#########################################################
X, Y, parameters = gradient_check_n_test_case()
cost, cache = forward_propagation_n(X, Y, parameters)
gradients = backward_propagation_n(X, Y, cache)
difference = gradient_check_n(parameters, gradients, X, Y)
#########################################################
这段代码执行了梯度检查的过程。
-
X, Y, parameters = gradient_check_n_test_case()
: 这一行代码从名为gradient_check_n_test_case
的测试用例中加载数据,包括输入特征X
、标签Y
和神经网络的参数parameters
。 -
cost, cache = forward_propagation_n(X, Y, parameters)
: 这一行代码调用了forward_propagation_n
函数,使用加载的数据X
、Y
和参数parameters
执行前向传播,并计算损失cost
和缓存cache
。 -
gradients = backward_propagation_n(X, Y, cache)
: 这一行代码调用了backward_propagation_n
函数,使用加载的数据X
、Y
和前向传播的缓存cache
执行反向传播,并计算梯度gradients
。 -
difference = gradient_check_n(parameters, gradients, X, Y)
: 这一行代码调用了gradient_check_n
函数,使用加载的数据X
、Y
、参数parameters
和计算得到的梯度gradients
执行梯度检查,计算数值梯度与实际梯度之间的差异。
最后,difference
变量将包含数值梯度与实际梯度之间的差异,你可以通过检查 difference
的值来判断反向传播是否正确实现。如果 difference
很小(小于 2e-7
),则说明反向传播的实现是正确的。
这个过程是用于验证神经网络中反向传播的关键步骤是否正确实现的重要步骤之一。如果 difference
较大,可能需要检查反向传播的实现以修复问题。
#########################################################