版权为吴恩达老师所有,参考Koala_Tree的博客,部分根据自己实践添加
使用google翻译,部分手工翻译
你可能需要的参考资料https://pan.baidu.com/s/1D2lzBvUQ3gF4RzenBeob2w
第3部分:梯度检查
首先导入您需要的库
import numpy as np
from testCases import *
from gc_utils import sigmoid,relu,dictionary_to_vector,vector_to_dictionary,gradients_to_vector
1.梯度检查如何工作?
反向传播计算梯度,其中θ表示模型的参数。J是使用前向传播的和您的损失函数计算。
因为前向传播相对容易实现,所以你确信你做对了,所以你几乎100%确定你是在计算成本J正确。因此,您可以使用您的代码来计算J验证计算的代码
让我们回顾一下导数(或梯度)的定义:
如果你不熟悉符号,这只是一种说法”当ε 真的很小。“
我们知道以下内容:
- 是你想要确保正确计算的东西。
- 你可以计算 和 (在这种情况下θ是一个实数),因为你对J的实施充满信心是正确的。
让我们使用等式和很小的值ε说服你的CEO你的计算代码 是正确的!
2.一维梯度检查
考虑一维线性函数。该模型仅包含单个实值参数θ,并采取x作为输入。
您将实现要计算的代码 J.(.) 及其导数。然后,您将使用梯度检查来确保J的导数计算是正确的。
图1 : 1D线性模型
上图显示了关键的计算步骤:首先从x开始,然后计算函数(“前向传播”)。然后计算导数(“向后传播”)。
练习:为这个简单的函数实现“向前传播”和“向后传播”。即,分别在两个函数中计算(“前向传播”)及其相对于θ的导数(“向后传播”)。
def forward_propagation(x,theta):
J=theta*x
return J
x, theta = 2, 4
J = forward_propagation(x, theta)
print ("J = " + str(J))
练习:现在,实现图1的后向传播步骤(微分计算)。即,计算对于的的导数。无需你去做微积分,你应该得到
def backward_propagation(x,theta):
dtheta=x
return dtheta
x,theta=2,4
dtheta=backward_propagation(x,theta)
print("dtheta = "+str(dtheta))
练习:显示backward_propagation()
函数正确计算梯度,让我们实现梯度检查。
说明:
- 首先使用上面的公式和ε计算“gradapprox”。下面是要遵循的步骤:
- 然后使用向后传播计算梯度,并将结果存储在变量“grad”中
- 最后,使用以下公式计算“gradapprox”和“grad”之间的相对差异:
您将需要3个步骤来计算此公式:
-1'.使用np.linalg.norm(...)
- 2'. 计算分子和分母。您需要两次调用np.linalg.norm(...)。
- 3'.将它们相除。
- 如果这个差异很小(比如小于),你可以非常自信你已经正确计算了你的梯度。否则,梯度计算中可能存在错误。
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("The gradient is correct!")
else:
print("The gradient is wrong!")
return difference
x,theta=2,4
difference=gradient_check(x,theta)
print("difference = "+str(difference))
恭喜,差异小于1。因此,您可以高度确信已正确计算了梯度backward_propagation()
。
现在,在更一般的情况下,您的成本函数J有一个以上的1D输入。当你训练神经网络时,θ实际上由多个矩阵和偏差组成!了解如何使用更高维度的输入进行梯度检查非常重要。我们开始做吧!
3.N维梯度检查
下图描述了检测模型的前向和后向传播。
图2 :深度神经网络
LINEAR - > RELU - > LINEAR - > RELU - > LINEAR - > SIGMOID
我们来看看你的前向传播和后向传播的实现。
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"]
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=relu(Z3)
logprobs=np.multiply(-np.log(A3),Y)+np.multiply(-np.log(1-A3),1-Y)
cost=1.0/m*np.sum(logprobs)
cache=(Z1,A1,W1,b1,Z2,A2,W2,b2,Z3,A3,W3,b3)
return cost,cache
现在,运行向后传播。
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.0/m*np.dot(dZ3,A2.T)
db3=1.0/m*np.sum(dZ3,axis=1,keepdims=True)
dA2=np.dot(W3.T,dZ3)
dZ2=np.multiply(dA2,np.int64(A2>0))
dW2=1.0/m*np.dot(dZ2,A1.T)
db2=1.0/m*np.sum(dZ2,axis=1,keepdims=True)
dA1=np.dot(W2.T,dZ2)
dZ1=np.multiply(dA1,np.int64(A1>0))
dW1=1.0/m*np.dot(dZ1,X.T)
db1=1.0/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
您在检测测试集上获得了一些结果,但您并不是100%确定您的模型。没有人是完美的!让我们实现梯度检查以验证您的渐变是否正确。
梯度检查如何工作?
您想要将“gradapprox”与通过反向传播计算的梯度进行比较。公式仍然是:
但是,θ不再是标量了。这是一个名为“参数”的字典。我们为您实现了一个函数“dictionary_to_vector()
”。它将“参数”字典转换为称为“值”的向量,通过将所有参数(W1,b1,W2,b2,W3,b3)重新变形为向量并将它们连接而获得。
反函数是“ vector_to_dictionary
”,它输出“参数”字典。
图2 : dictionary_to_vector()和vector_to_dictionary()
您将需要在gradient_check_n()中使用这些函数
我们还使用gradients_to_vector()将“gradients”字典转换为矢量“grad”。你不必担心这一点。
练习:实现gradient_check_n()。
说明:这是伪代码,可以帮助您实现渐变检查。
对于num_parameters中的每个i:
- 要计算J_plus[i]
:
1.设置到np.copy(parameters_values)
2.设置到
3.使用forward_propagation_n(x, y, vector_to_dictionary(
))
计算。
- 计算J_minus[i]
:用做同样的事情
- 计算
因此,您得到一个向量gradapprox,其中gradapprox [i]是相对于梯度的近似值parameter_values[i]
。您现在可以将此gradapprox向量与来自反向传播的梯度向量进行比较。就像1D案例一样,计算:
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))
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.0*epsilon)
numerator=np.linalg.norm(grad-gradapprox)
denominator=np.linalg.norm(grad)+np.linalg.norm(gradapprox)
difference=numerator/denominator
if difference>1e-7:
print("There is a mistake in the backward propagation! difference = "+str(difference))
else:
print ("Your backward propagation works perfectly fine! difference = "+str(difference))
return 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)
你能得到梯度检查来宣告你的梯度计算是否正确?
注意
- 渐变检查很慢!用∂逼近梯度计算成本很高。因此,我们不会在训练期间的每次迭代中运行梯度检查。只需几次检查渐变是否正确。
- 至少在我们当前的情况下,渐变检查不适用于dropout。您通常会在没有dropout的情况下运行渐变检查算法,以确保您的backprop是正确的,然后添加dropout。
恭喜,您可以确信您的欺诈检测深度学习模型正常运行!你甚至可以用它来说服你的CEO。:)
你应该从中记住:
- 梯度检查验证了来自反向传播的梯度与梯度的数值近似(使用前向传播计算)之间的接近程度。
- 梯度检查很慢,因此我们不会在每次训练迭代中运行它。您通常只运行它以确保您的代码是正确的,然后将其关闭并使用backprop进行实际的学习过程。