初始化

欢迎来到“改善深度神经网络”的第一项作业。

训练神经网络需要指定权重的初始值,而一个好的初始化方法将有助于网络学习。不同的初始化导致的不同结果。好的初始化可以:

  • 加快梯度下降、模型收敛
  • 减小梯度下降收敛过程中训练(和泛化)出现误差的几率

首先,运行以下单元格以加载包和用于分类的二维数据集。

  1. import numpy as np
  2. import matplotlib.pyplot as plt
  3. import sklearn
  4. import sklearn.datasets
  5. from init_utils import sigmoid, relu, compute_loss, forward_propagation, backward_propagation
  6. from init_utils import update_parameters, predict, load_dataset, plot_decision_boundary, predict_dec
  7. %matplotlib inline
  8. plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
  9. plt.rcParams['image.interpolation'] = 'nearest'
  10. plt.rcParams['image.cmap'] = 'gray'
  11. # load image dataset: blue/red dots in circles
  12. train_X, train_Y, test_X, test_Y = load_dataset()


image.png
我们希望分类器将蓝点和红点分开。

1 神经网络模型

你将使用已经实现了的3层神经网络。 下面是你将尝试的初始化方法:

  • 零初始化 :在输入参数中设置initialization = "zeros"
  • 随机初始化 :在输入参数中设置initialization = "random",这会将权重初始化为较大的随机值。
  • He初始化 :在输入参数中设置initialization = "he",这会根据He等人(2015)的论文将权重初始化为按比例缩放的随机值。

说明:请快速阅读并运行以下代码,在下一部分中,你将实现此model()调用的三种初始化方法。

  1. def model(X, Y, learning_rate = 0.01, num_iterations = 15000, print_cost = True, initialization = "he"):
  2. """
  3. Implements a three-layer neural network: LINEAR->RELU->LINEAR->RELU->LINEAR->SIGMOID.
  4. Arguments:
  5. X -- input data, of shape (2, number of examples)
  6. Y -- true "label" vector (containing 0 for red dots; 1 for blue dots), of shape (1, number of examples)
  7. initialization -- flag to choose which initialization to use ("zeros","random" or "he")
  8. Returns:
  9. parameters -- parameters learnt by the model
  10. """
  11. grads = {}
  12. costs = [] # to keep track of the loss
  13. m = X.shape[1] # number of examples
  14. layers_dims = [X.shape[0], 10, 5, 1]
  15. # Initialize parameters dictionary.
  16. if initialization == "zeros":
  17. parameters = initialize_parameters_zeros(layers_dims)
  18. elif initialization == "random":
  19. parameters = initialize_parameters_random(layers_dims)
  20. elif initialization == "he":
  21. parameters = initialize_parameters_he(layers_dims)
  22. # Loop (gradient descent)
  23. for i in range(0, num_iterations):
  24. # Forward propagation: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID.
  25. a3, cache = forward_propagation(X, parameters)
  26. # Loss
  27. cost = compute_loss(a3, Y)
  28. # Backward propagation.
  29. grads = backward_propagation(X, Y, cache)
  30. # Update parameters.
  31. parameters = update_parameters(parameters, grads, learning_rate)
  32. # Print the loss every 1000 iterations
  33. if print_cost and i % 1000 == 0:
  34. print("Cost after iteration {}: {}".format(i, cost))
  35. costs.append(cost)
  36. # plot the loss
  37. plt.plot(costs)
  38. plt.ylabel('cost')
  39. plt.xlabel('iterations (per hundreds)')
  40. plt.title("Learning rate =" + str(learning_rate))
  41. plt.show()
  42. return parameters

2 零初始化

在神经网络中有两种类型的参数要初始化:

  • 权重矩阵 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图2#card=math&code=%28W%5E%7B%5B1%5D%7D%2C%20W%5E%7B%5B2%5D%7D%2C%20W%5E%7B%5B3%5D%7D%2C%20…%2C%20W%5E%7B%5BL-1%5D%7D%2C%20W%5E%7B%5BL%5D%7D%29&id=iO2xL)
  • 偏差向量 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图3#card=math&code=%28b%5E%7B%5B1%5D%7D%2C%20b%5E%7B%5B2%5D%7D%2C%20b%5E%7B%5B3%5D%7D%2C%20…%2C%20b%5E%7B%5BL-1%5D%7D%2C%20b%5E%7B%5BL%5D%7D%29&id=GezSB)

练习:实现以下函数以将所有参数初始化为零。 稍后你会看到此方法会报错,因为它无法“打破对称性”。

  1. # GRADED FUNCTION: initialize_parameters_zeros
  2. def initialize_parameters_zeros(layers_dims):
  3. parameters = {}
  4. L = len(layers_dims) # number of layers in the network
  5. for l in range(1, L):
  6. ### START CODE HERE ### (≈ 2 lines of code)
  7. parameters['W' + str(l)] = np.zeros((layers_dims[l],layers_dims[l-1]))
  8. parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
  9. ### END CODE HERE ###
  10. return parameters
  1. parameters = initialize_parameters_zeros([3,2,1])
  2. print("W1 = " + str(parameters["W1"]))
  3. print("b1 = " + str(parameters["b1"]))
  4. print("W2 = " + str(parameters["W2"]))
  5. print("b2 = " + str(parameters["b2"]))
  1. W1 = [[0. 0. 0.]
  2. [0. 0. 0.]]
  3. b1 = [[0.]
  4. [0.]]
  5. W2 = [[0. 0.]]
  6. b2 = [[0.]]

运行以下代码使用零初始化并迭代15,000次以训练模型。

  1. parameters = model(train_X, train_Y, initialization = "zeros")
  2. print ("On the train set:")
  3. predictions_train = predict(train_X, train_Y, parameters)
  4. print ("On the test set:")
  5. predictions_test = predict(test_X, test_Y, parameters)
  1. Cost after iteration 0: 0.6931471805599453
  2. Cost after iteration 1000: 0.6931471805599453
  3. Cost after iteration 2000: 0.6931471805599453
  4. Cost after iteration 3000: 0.6931471805599453
  5. Cost after iteration 4000: 0.6931471805599453
  6. Cost after iteration 5000: 0.6931471805599453
  7. Cost after iteration 6000: 0.6931471805599453
  8. Cost after iteration 7000: 0.6931471805599453
  9. Cost after iteration 8000: 0.6931471805599453
  10. Cost after iteration 9000: 0.6931471805599453
  11. Cost after iteration 10000: 0.6931471805599455
  12. Cost after iteration 11000: 0.6931471805599453
  13. Cost after iteration 12000: 0.6931471805599453
  14. Cost after iteration 13000: 0.6931471805599453
  15. Cost after iteration 14000: 0.6931471805599453

image.png

  1. On the train set:
  2. Accuracy: 0.5
  3. On the test set:
  4. Accuracy: 0.5

性能确实很差,损失也没有真正降低,该算法的性能甚至不如随机猜测。为什么呢?让我们看一下预测的详细信息和决策边界:

  1. print ("predictions_train = " + str(predictions_train))
  2. print ("predictions_test = " + str(predictions_test))
  1. predictions_train = [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  2. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  3. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  4. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  5. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  6. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  7. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  8. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  9. 0 0 0 0 0 0 0 0 0 0 0 0]]
  10. predictions_test = [[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  11. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
  12. 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]]
  1. plt.title("Model with Zeros initialization")
  2. axes = plt.gca()
  3. axes.set_xlim([-1.5,1.5])
  4. axes.set_ylim([-1.5,1.5])
  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)


image.png
该模型预测的每个示例都为0。

通常,将所有权重初始化为零会导致网络无法打破对称性。 这意味着每一层中的每个神经元都将学习相同的东西,并且你不妨训练每一层【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图6的神经网络,该网络的性能不如线性分类器,例如逻辑回归。

总结:

  • 权重【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图7 应该随机初始化以打破对称性。
  • 将偏差【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图8初始化为零是可以的。只要随机初始化了【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图9,对称性就会被破坏。

3 随机初始化

为了打破对称性,让我们随机设置权重。 在随机初始化之后,每个神经元可以继续学习其输入的不同特征。

练习:实现以下函数,将权重初始化为较大的随机值(按10缩放),并将偏差设为0。 将 `np.random.randn(..,..) 10用于权重,将np.zeros((.., ..))用于偏差。我们使用固定的np.random.seed(..)`,以确保你的“随机”权重与我们的权重匹配。因此,如果运行几次代码后参数初始值始终相同,也请不要疑惑。

  1. # GRADED FUNCTION: initialize_parameters_random
  2. def initialize_parameters_random(layers_dims):
  3. np.random.seed(3) # This seed makes sure your "random" numbers will be the as ours
  4. parameters = {}
  5. L = len(layers_dims) # integer representing the number of layers
  6. for l in range(1, L):
  7. ### START CODE HERE ### (≈ 2 lines of code)
  8. parameters['W' + str(l)] = np.random.randn(layers_dims[l],layers_dims[l-1])*10
  9. parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
  10. ### END CODE HERE ###
  11. return parameters
  1. parameters = initialize_parameters_random([3, 2, 1])
  2. print("W1 = " + str(parameters["W1"]))
  3. print("b1 = " + str(parameters["b1"]))
  4. print("W2 = " + str(parameters["W2"]))
  5. print("b2 = " + str(parameters["b2"]))
  1. W1 = [[ 17.88628473 4.36509851 0.96497468]
  2. [-18.63492703 -2.77388203 -3.54758979]]
  3. b1 = [[0.]
  4. [0.]]
  5. W2 = [[-0.82741481 -6.27000677]]
  6. b2 = [[0.]]

运行以下代码使用随机初始化迭代15,000次以训练模型。

  1. parameters = model(train_X, train_Y, initialization = "random")
  2. print ("On the train set:")
  3. predictions_train = predict(train_X, train_Y, parameters)
  4. print ("On the test set:")
  5. predictions_test = predict(test_X, test_Y, parameters)
  1. Cost after iteration 0: inf
  2. Cost after iteration 1000: 0.6239567039908781
  3. Cost after iteration 2000: 0.5978043872838292
  4. Cost after iteration 3000: 0.563595830364618
  5. Cost after iteration 4000: 0.5500816882570866
  6. Cost after iteration 5000: 0.5443417928662615
  7. Cost after iteration 6000: 0.5373553777823036
  8. Cost after iteration 7000: 0.4700141958024487
  9. Cost after iteration 8000: 0.3976617665785177
  10. Cost after iteration 9000: 0.39344405717719166
  11. Cost after iteration 10000: 0.39201765232720626
  12. Cost after iteration 11000: 0.38910685278803786
  13. Cost after iteration 12000: 0.38612995897697244
  14. Cost after iteration 13000: 0.3849735792031832
  15. Cost after iteration 14000: 0.38275100578285265

image.png

  1. On the train set:
  2. Accuracy: 0.83
  3. On the test set:
  4. Accuracy: 0.86

因为数值舍入,你可能在0迭代之后看到损失为”inf”,我们会在之后用更复杂的数字实现解决此问题。

总之,看起来你的对称性已打破,这会带来更好的结果。 相比之前,模型不再输出全0的结果了。

  1. print (predictions_train)
  2. print (predictions_test)
  1. [[1 0 1 1 0 0 1 1 1 1 1 0 1 0 0 1 0 1 1 0 0 0 1 0 1 1 1 1 1 1 0 1 1 0 0 1
  2. 1 1 1 1 1 1 1 0 1 1 1 1 0 1 0 1 1 1 1 0 0 1 1 1 1 0 1 1 0 1 0 1 1 1 1 0
  3. 0 0 0 0 1 0 1 0 1 1 1 0 0 1 1 1 1 1 1 0 0 1 1 1 0 1 1 0 1 0 1 1 0 1 1 0
  4. 1 0 1 1 0 0 1 0 0 1 1 0 1 1 1 0 1 0 0 1 0 1 1 1 1 1 1 1 0 1 1 0 0 1 1 0
  5. 0 0 1 0 1 0 1 0 1 1 1 0 0 1 1 1 1 0 1 1 0 1 0 1 1 0 1 0 1 1 1 1 0 1 1 1
  6. 1 0 1 0 1 0 1 1 1 1 0 1 1 0 1 1 0 1 1 0 1 0 1 1 1 0 1 1 1 0 1 0 1 0 0 1
  7. 0 1 1 0 1 1 0 1 1 0 1 1 1 0 1 1 1 1 0 1 0 0 1 1 0 1 1 1 0 0 0 1 1 0 1 1
  8. 1 1 0 1 1 0 1 1 1 0 0 1 0 0 0 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 1 0 0 1 1 1
  9. 1 1 1 1 0 0 0 1 1 1 1 0]]
  10. [[1 1 1 1 0 1 0 1 1 0 1 1 1 0 0 0 0 1 0 1 0 0 1 0 1 0 1 1 1 1 1 0 0 0 0 1
  11. 0 1 1 0 0 1 1 1 1 1 0 1 1 1 0 1 0 1 1 0 1 0 1 0 1 1 1 1 1 1 1 1 1 0 1 0
  12. 1 1 1 1 1 0 1 0 0 1 0 0 0 1 1 0 1 1 0 0 0 1 1 0 1 1 0 0]]
  1. plt.title("Model with large random initialization")
  2. axes = plt.gca()
  3. axes.set_xlim([-1.5,1.5])
  4. axes.set_ylim([-1.5,1.5])
  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)


image.png

观察

  • 损失一开始很高是因为较大的随机权重值,对于某些数据,最后一层激活函数sigmoid输出的结果非常接近0或1,并且当该示例数据预测错误时,将导致非常高的损失。当【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图12%20%3D%20%5Clog(0)#card=math&code=%5Clog%28a%5E%7B%5B3%5D%7D%29%20%3D%20%5Clog%280%29&id=OU83o)时,损失达到无穷大。
  • 初始化不当会导致梯度消失/爆炸,同时也会减慢优化算法的速度
  • 训练较长时间的网络,将会看到更好的结果,但是使用太大的随机数进行初始化会降低优化速度

总结

  • 将权重初始化为非常大的随机值效果不佳。
  • 初始化为较小的随机值会更好。重要的问题是:这些随机值应为多小?让我们在下一部分中找到答案!

4 He初始化

最后,让我们尝试一下“He 初始化”,该名称以He等人的名字命名(类似于“Xavier初始化”,但Xavier初始化使用比例因子 sqrt(1./layers_dims[l-1])来表示权重【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图13 ,而He初始化使用sqrt(2./layers_dims[l-1]))。

  1. Xavier初始化:
  1. He初始化

练习:实现以下函数,以He初始化来初始化参数。

提示:此函数类似于先前的initialize_parameters_random(...)。 唯一的不同是,无需将np.random.randn(..,..)乘以10,而是将其乘以【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图20,这是He初始化建议使用的ReLU激活层。

  1. # GRADED FUNCTION: initialize_parameters_he
  2. def initialize_parameters_he(layers_dims):
  3. np.random.seed(3)
  4. parameters = {}
  5. L = len(layers_dims) - 1 # integer representing the number of layers
  6. for l in range(1, L + 1):
  7. ### START CODE HERE ### (≈ 2 lines of code)
  8. parameters['W' + str(l)] = np.random.randn(layers_dims[l],layers_dims[l-1])*np.sqrt(2./layers_dims[l-1])
  9. parameters['b' + str(l)] = np.zeros((layers_dims[l],1))
  10. ### END CODE HERE ###
  11. return parameters
  1. parameters = initialize_parameters_he([2, 4, 1])
  1. W1 = [[ 1.78862847 0.43650985]
  2. [ 0.09649747 -1.8634927 ]
  3. [-0.2773882 -0.35475898]
  4. [-0.08274148 -0.62700068]]
  5. b1 = [[0.]
  6. [0.]
  7. [0.]
  8. [0.]]
  9. W2 = [[-0.03098412 -0.33744411 -0.92904268 0.62552248]]
  10. b2 = [[0.]]

运行以下代码,使用He初始化并迭代15,000次以训练你的模型。

  1. parameters = model(train_X, train_Y, initialization = "he")
  2. print ("On the train set:")
  3. predictions_train = predict(train_X, train_Y, parameters)
  4. print ("On the test set:")
  5. predictions_test = predict(test_X, test_Y, parameters)
  1. Cost after iteration 0: 0.8830537463419761
  2. Cost after iteration 1000: 0.6879825919728063
  3. Cost after iteration 2000: 0.6751286264523371
  4. Cost after iteration 3000: 0.6526117768893805
  5. Cost after iteration 4000: 0.6082958970572938
  6. Cost after iteration 5000: 0.5304944491717495
  7. Cost after iteration 6000: 0.4138645817071794
  8. Cost after iteration 7000: 0.3117803464844441
  9. Cost after iteration 8000: 0.23696215330322562
  10. Cost after iteration 9000: 0.1859728720920684
  11. Cost after iteration 10000: 0.15015556280371808
  12. Cost after iteration 11000: 0.12325079292273551
  13. Cost after iteration 12000: 0.09917746546525937
  14. Cost after iteration 13000: 0.08457055954024283
  15. Cost after iteration 14000: 0.07357895962677366

image.png

  1. On the train set:
  2. Accuracy: 0.9933333333333333
  3. On the test set:
  4. Accuracy: 0.96
  1. plt.title("Model with He initialization")
  2. axes = plt.gca()
  3. axes.set_xlim([-1.5,1.5])
  4. axes.set_ylim([-1.5,1.5])
  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)


image.png
观察

  • 使用He初始化的模型可以在少量迭代中很好地分离蓝色点和红色点。

5 总结

我们已经学习了三种不同类型的初始化方法。对于相同的迭代次数和超参数,三种结果比较为:

Model 测试准确率 评价
零初始化的3层NN 50% 未能打破对称性
随机初始化的3层NN 83% 权重太大
He初始化的3层NN 99% 推荐方法

此作业中应记住的内容

  • 不同的初始化会导致不同的结果
  • 随机初始化用于打破对称性,并确保不同的隐藏单元可以学习不同的东西
  • 不要初始化为太大的值
  • 初始化对于带有ReLU激活的网络非常有效。

    正则化

欢迎来到本周的第二次作业。
深度学习模型具有很高的灵活性和能力,如果训练数据集不够大,将会造成一个严重的问题—过拟合。尽管它在训练集上效果很好,但是学到的网络不能应用到测试集中!

你将学习: 在深度学习模型中使用正则化。

首先导入要使用的包。

  1. # import packages
  2. import numpy as np
  3. import matplotlib.pyplot as plt
  4. from reg_utils import sigmoid, relu, plot_decision_boundary, initialize_parameters, load_2D_dataset, predict_dec
  5. from reg_utils import compute_cost, predict, forward_propagation, backward_propagation, update_parameters
  6. import sklearn
  7. import sklearn.datasets
  8. import scipy.io
  9. from testCases import *
  10. %matplotlib inline
  11. plt.rcParams['figure.figsize'] = (7.0, 4.0) # set default size of plots
  12. plt.rcParams['image.interpolation'] = 'nearest'
  13. plt.rcParams['image.cmap'] = 'gray'

加载二维数据集如下。

  1. train_X, train_Y, test_X, test_Y = load_2D_dataset()


image.png

数据集分析:该数据集含有噪声,但看起来一条将左上半部分(蓝色)与右下半部分(红色)分开的对角线会很比较有效。

你将首先尝试非正则化模型。然后学习如何对其进行正则化,并决定选择哪种模型。

1 非正则化模型

首先,你将尝试不进行任何正则化的模型。然后,你将实现:

  • L2 正则化 函数:compute_cost_with_regularization()backward_propagation_with_regularization()
  • Dropout 函数:forward_propagation_with_dropout()backward_propagation_with_dropout()
  1. def model(X, Y, learning_rate = 0.3, num_iterations = 30000, print_cost = True, lambd = 0, keep_prob = 1):
  2. """
  3. Implements a three-layer neural network: LINEAR->RELU->LINEAR->RELU->LINEAR->SIGMOID.
  4. Arguments:
  5. X -- input data, of shape (input size, number of examples)
  6. Y -- true "label" vector (1 for blue dot / 0 for red dot), of shape (output size, number of examples)
  7. lambd -- regularization hyperparameter, scalar
  8. keep_prob - probability of keeping a neuron active during drop-out, scalar.
  9. Returns:
  10. parameters -- parameters learned by the model. They can then be used to predict.
  11. """
  12. grads = {}
  13. costs = [] # to keep track of the cost
  14. m = X.shape[1] # number of examples
  15. layers_dims = [X.shape[0], 20, 3, 1]
  16. # Initialize parameters dictionary.
  17. parameters = initialize_parameters(layers_dims)
  18. # Loop (gradient descent)
  19. for i in range(0, num_iterations):
  20. # Forward propagation: LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID.
  21. if keep_prob == 1:
  22. a3, cache = forward_propagation(X, parameters)
  23. elif keep_prob < 1:
  24. a3, cache = forward_propagation_with_dropout(X, parameters, keep_prob)
  25. # Cost function
  26. if lambd == 0:
  27. cost = compute_cost(a3, Y)
  28. else:
  29. cost = compute_cost_with_regularization(a3, Y, parameters, lambd)
  30. # Backward propagation.
  31. assert(lambd==0 or keep_prob==1) # it is possible to use both L2 regularization and dropout,
  32. # but this assignment will only explore one at a time
  33. if lambd == 0 and keep_prob == 1:
  34. grads = backward_propagation(X, Y, cache)
  35. elif lambd != 0:
  36. grads = backward_propagation_with_regularization(X, Y, cache, lambd)
  37. elif keep_prob < 1:
  38. grads = backward_propagation_with_dropout(X, Y, cache, keep_prob)
  39. # Update parameters.
  40. parameters = update_parameters(parameters, grads, learning_rate)
  41. # Print the loss every 10000 iterations
  42. if print_cost and i % 10000 == 0:
  43. print("Cost after iteration {}: {}".format(i, cost))
  44. if print_cost and i % 1000 == 0:
  45. costs.append(cost)
  46. # plot the cost
  47. plt.plot(costs)
  48. plt.ylabel('cost')
  49. plt.xlabel('iterations (x1,000)')
  50. plt.title("Learning rate =" + str(learning_rate))
  51. plt.show()
  52. return parameters

让我们在不进行任何正则化的情况下训练模型,并观察训练/测试集的准确性。

  1. parameters = model(train_X, train_Y)
  2. print ("On the training set:")
  3. predictions_train = predict(train_X, train_Y, parameters)
  4. print ("On the test set:")
  5. predictions_test = predict(test_X, test_Y, parameters)

image.png

  1. On the training set:
  2. Accuracy: 0.9478672985781991
  3. On the test set:
  4. Accuracy: 0.915

训练精度为94.8%,而测试精度为91.5%。这是基准模型的表现(你将观察到正则化对该模型的影响)。运行以下代码以绘制模型的决策边界。

  1. plt.title("Model without regularization")
  2. axes = plt.gca()
  3. axes.set_xlim([-0.75,0.40])
  4. axes.set_ylim([-0.75,0.65])
  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)


image.png
非正则化模型显然过度拟合了训练集,拟合了一些噪声点!现在让我们看一下减少过拟合的两种手段。

2 L2正则化

2.1 带有L2正则的正向传播

避免过拟合的标准方法称为 L2正则化,它将损失函数从:

【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图26%7D%5Clog%5Cleft(a%5E%7B%5BL%5D(i)%7D%5Cright)%20%2B%20(1-y%5E%7B(i)%7D)%5Clog%5Cleft(1-%20a%5E%7B%5BL%5D(i)%7D%5Cright)%20%5Clarge%7B)%7D%20%5Ctag%7B1%7D%0A#card=math&code=J%20%3D%20-%5Cfrac%7B1%7D%7Bm%7D%20%5Csum%5Climits_%7Bi%20%3D%201%7D%5E%7Bm%7D%20%5Clarge%7B%28%7D%5Csmall%20%20y%5E%7B%28i%29%7D%5Clog%5Cleft%28a%5E%7B%5BL%5D%28i%29%7D%5Cright%29%20%2B%20%281-y%5E%7B%28i%29%7D%29%5Clog%5Cleft%281-%20a%5E%7B%5BL%5D%28i%29%7D%5Cright%29%20%5Clarge%7B%29%7D%20%5Ctag%7B1%7D%0A&id=JUHFb)

修改到:

【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图27%7D%5Clog%5Cleft(a%5E%7B%5BL%5D(i)%7D%5Cright)%20%2B%20(1-y%5E%7B(i)%7D)%5Clog%5Cleft(1-%20a%5E%7B%5BL%5D(i)%7D%5Cright)%20%5Clarge%7B)%7D%20%7D%5Ctext%7Bcross-entropy%20cost%7D%20%2B%20%5Cunderbrace%7B%5Cfrac%7B1%7D%7Bm%7D%20%5Cfrac%7B%5Clambda%7D%7B2%7D%20%5Csum%5Climits_l%5Csum%5Climits_k%5Csum%5Climits_j%20W%7Bk%2Cj%7D%5E%7B%5Bl%5D2%7D%20%7D%5Ctext%7BL2%20regularization%20cost%7D%20%5Ctag%7B2%7D%0A#card=math&code=J%7Bregularized%7D%20%3D%20%5Csmall%20%5Cunderbrace%7B-%5Cfrac%7B1%7D%7Bm%7D%20%5Csum%5Climits%7Bi%20%3D%201%7D%5E%7Bm%7D%20%5Clarge%7B%28%7D%5Csmall%20y%5E%7B%28i%29%7D%5Clog%5Cleft%28a%5E%7B%5BL%5D%28i%29%7D%5Cright%29%20%2B%20%281-y%5E%7B%28i%29%7D%29%5Clog%5Cleft%281-%20a%5E%7B%5BL%5D%28i%29%7D%5Cright%29%20%5Clarge%7B%29%7D%20%7D%5Ctext%7Bcross-entropy%20cost%7D%20%2B%20%5Cunderbrace%7B%5Cfrac%7B1%7D%7Bm%7D%20%5Cfrac%7B%5Clambda%7D%7B2%7D%20%5Csum%5Climitsl%5Csum%5Climits_k%5Csum%5Climits_j%20W%7Bk%2Cj%7D%5E%7B%5Bl%5D2%7D%20%7D_%5Ctext%7BL2%20regularization%20cost%7D%20%5Ctag%7B2%7D%0A&id=wBGvi)

让我们修改损失并观察结果。

练习:实现compute_cost_with_regularization(),以计算公式(2)的损失。要计算【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图28 ,请使用:

  1. np.sum(np.square(Wl))

请注意,你必须对【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图29【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图30【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图31执行此操作,然后将三个项相加并乘以【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图32

  1. # GRADED FUNCTION: compute_cost_with_regularization
  2. def compute_cost_with_regularization(A3, Y, parameters, lambd):
  3. """
  4. Implement the cost function with L2 regularization. See formula (2) above.
  5. Arguments:
  6. A3 -- post-activation, output of forward propagation, of shape (output size, number of examples)
  7. Y -- "true" labels vector, of shape (output size, number of examples)
  8. parameters -- python dictionary containing parameters of the model
  9. Returns:
  10. cost - value of the regularized loss function (formula (2))
  11. """
  12. m = Y.shape[1]
  13. W1 = parameters["W1"]
  14. W2 = parameters["W2"]
  15. W3 = parameters["W3"]
  16. cross_entropy_cost = compute_cost(A3, Y) # This gives you the cross-entropy part of the cost
  17. ### START CODE HERE ### (approx. 1 line)
  18. L2_regularization_cost = 1./m * lambd/2 * (np.sum(W1**2) + np.sum(W2**2) + np.sum(W3**2))
  19. ### END CODER HERE ###
  20. cost = cross_entropy_cost + L2_regularization_cost
  21. return cost
  1. A3, Y_assess, parameters = compute_cost_with_regularization_test_case()
  2. print("cost = " + str(compute_cost_with_regularization(A3, Y_assess, parameters, lambd = 0.1)))

预期输出:
cost = 1.7864859451590758

2.2 带有L2正则的反向传播

当然,因为你更改了损失,所以还必须更改反向传播! 必须针对新损失函数计算所有梯度。

练习:实现正则化后的反向传播。更改仅涉及dW1,dW2和dW3。对于每一个,你必须添加正则化项的梯度(【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图33%20%3D%20%5Cfrac%7B%5Clambda%7D%7Bm%7D%20W#card=math&code=%5Cfrac%7Bd%7D%7BdW%7D%20%28%20%5Cfrac%7B1%7D%7B2%7D%5Cfrac%7B%5Clambda%7D%7Bm%7D%20%20W%5E2%29%20%3D%20%5Cfrac%7B%5Clambda%7D%7Bm%7D%20W&id=TJeMi))。

  1. # GRADED FUNCTION: backward_propagation_with_regularization
  2. def backward_propagation_with_regularization(X, Y, cache, lambd):
  3. """
  4. Implements the backward propagation of our baseline model to which we added an L2 regularization.
  5. Arguments:
  6. X -- input dataset, of shape (input size, number of examples)
  7. Y -- "true" labels vector, of shape (output size, number of examples)
  8. cache -- cache output from forward_propagation()
  9. lambd -- regularization hyperparameter, scalar
  10. Returns:
  11. gradients -- A dictionary with the gradients with respect to each parameter, activation and pre-activation variables
  12. """
  13. m = X.shape[1]
  14. (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
  15. dZ3 = A3 - Y
  16. ### START CODE HERE ### (approx. 1 line)
  17. dW3 = 1./m * np.dot(dZ3, A2.T) + lambd / m * W3
  18. ### END CODE HERE ###
  19. db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
  20. dA2 = np.dot(W3.T, dZ3)
  21. dZ2 = np.multiply(dA2, np.int64(A2 > 0))
  22. ### START CODE HERE ### (approx. 1 line)
  23. dW2 = 1./m * np.dot(dZ2, A1.T) + lambd / m * W2
  24. ### END CODE HERE ###
  25. db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
  26. dA1 = np.dot(W2.T, dZ2)
  27. dZ1 = np.multiply(dA1, np.int64(A1 > 0))
  28. ### START CODE HERE ### (approx. 1 line)
  29. dW1 = 1./m * np.dot(dZ1, X.T) + lambd / m * W1
  30. ### END CODE HERE ###
  31. db1 = 1./m * np.sum(dZ1, axis=1, keepdims = True)
  32. gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,"dA2": dA2,
  33. "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
  34. "dZ1": dZ1, "dW1": dW1, "db1": db1}
  35. return gradients
  1. X_assess, Y_assess, cache = backward_propagation_with_regularization_test_case()
  2. grads = backward_propagation_with_regularization(X_assess, Y_assess, cache, lambd = 0.7)
  3. print ("dW1 = "+ str(grads["dW1"]))
  4. print ("dW2 = "+ str(grads["dW2"]))
  5. print ("dW3 = "+ str(grads["dW3"]))
  1. dW1 = [[-0.25604646 0.12298827 -0.28297129]
  2. [-0.17706303 0.34536094 -0.4410571 ]]
  3. dW2 = [[ 0.79276486 0.85133918]
  4. [-0.0957219 -0.01720463]
  5. [-0.13100772 -0.03750433]]
  6. dW3 = [[-1.77691347 -0.11832879 -0.09397446]]

预期输出:
dW1 = [[-0.25604646 0.12298827 -0.28297129]
[-0.17706303 0.34536094 -0.4410571 ]]
dW2 = [[ 0.79276486 0.85133918]
[-0.0957219 -0.01720463]
[-0.13100772 -0.03750433]]
dW3 = [[-1.77691347 -0.11832879 -0.09397446]]

现在让我们使用L2正则化【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图34#card=math&code=%28%5Clambda%20%3D%200.7%29&id=rysY9)运行的模型。model()函数将调用:

  • compute_cost_with_regularization代替compute_cost
  • backward_propagation_with_regularization代替backward_propagation
  1. parameters = model(train_X, train_Y, lambd = 0.7)
  2. print ("On the train set:")
  3. predictions_train = predict(train_X, train_Y, parameters)
  4. print ("On the test set:")
  5. predictions_test = predict(test_X, test_Y, parameters)
  1. Cost after iteration 0: 0.6974484493131264
  2. Cost after iteration 10000: 0.26849188732822393
  3. Cost after iteration 20000: 0.2680916337127301

image.png

  1. On the train set:
  2. Accuracy: 0.9383886255924171
  3. On the test set:
  4. Accuracy: 0.93

Nice!测试集的准确性提高到93%。你成功拯救了法国足球队!

模型不再过拟合训练数据了。让我们绘制决策边界看一下。

  1. plt.title("Model with L2-regularization")
  2. axes = plt.gca()
  3. axes.set_xlim([-0.75,0.40])
  4. axes.set_ylim([-0.75,0.65])
  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)


image.png

观察

  • 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图37 的值是你可以调整开发集的超参数。
  • L2正则化使决策边界更平滑。如果【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图38 太大,则也可能“过度平滑”,从而使模型偏差较高。

L2正则化的原理

L2正则化基于以下假设:权重较小的模型比权重较大的模型更简单。因此,通过对损失函数中权重的平方值进行惩罚,可以将所有权重驱动为较小的值。比重太大会使损失过高!这将导致模型更平滑,输出随着输入的变化而变化得更慢。

你应该记住 L2正则化的影响:

  • 损失计算:
    - 正则化条件会添加到损失函数中
  • 反向传播函数:
    - 有关权重矩阵的渐变中还有其他术语
  • 权重最终变小(“权重衰减”):
    - 权重被推到较小的值。

3 Dropout

最后,Dropout是广泛用于深度学习的正则化技术。
它会在每次迭代中随机关闭一些神经元。

要了解Dropout,可以思考这段对话:

  • 朋友:“为什么你需要所有神经元来训练你的网络以分类图像?”。
  • 你:“因为每个神经元都有权重,并且可以学习图像的特定特征/细节/形状。我拥有的神经元越多,模型学习的特征就越丰富!”
  • 朋友:“我知道了,但是你确定你的神经元学习的是不同的特征而不是全部相同的特征吗?”
  • 你:“这是个好问题……同一层中的神经元实际上并不关联。应该绝对有可能让他们学习相同的图像特征/形状/形式/细节…这是多余的。为此应该有一个解决方案。”


在每次迭代中,你以概率【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图39或以概率【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图40(此处为50%)关闭此层的每个神经元。关闭的神经元对迭代的正向和反向传播均无助于训练。
【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图41
图3:Dropout第一和第三隐藏层。
【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图42层:我们平均关闭了40%的神经元。【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图43 层:我们平均关闭了20%的神经元。

当你关闭某些神经元时,实际上是在修改模型。通过Dropout,你的神经元对另一种特定神经元的激活变得不那么敏感,因为另一神经元可能随时关闭。

3.1 带有Dropout的正向传播

练习:实现带有Dropout的正向传播。你正在使用3层的神经网络,并将为第一和第二隐藏层添加Dropout。我们不会将Dropout应用于输入层或输出层。

说明
关闭第一层和第二层中的某些神经元。为此,将执行4个步骤:

  1. 在讲座中,我们讨论了使用np.random.rand()创建与【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图44形状相同的变量【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图45的方法,以随机获取0到1之间的数。在这里,你将使用向量化的实现,创建一个与【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图46的矩阵维度相同的随机矩阵$D^{[1]} = [d^{1} d^{1} … d^{1}] $。
  2. 通过对【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图47中的值进行阈值设置,将【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图48的每个条目设置为0(概率为1-keep_prob)或1(概率为keep_prob)。提示:将矩阵X的所有条目设置为0(如果概率小于0.5)或1(如果概率大于0.5),则可以执行:X = (X < 0.5)。注意0和1分别对应False和True。
  3. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图49设置为【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图50(关闭一些神经元)。你可以将【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图51 视为掩码,这样当它与另一个矩阵相乘时,关闭某些值。
  4. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图52除以keep_prob。通过这样做,你可以确保损失结果仍具有与dropout相同的期望值。(此技术也称为反向dropout)
  1. # GRADED FUNCTION: forward_propagation_with_dropout
  2. def forward_propagation_with_dropout(X, parameters, keep_prob = 0.5):
  3. """
  4. Implements the forward propagation: LINEAR -> RELU + DROPOUT -> LINEAR -> RELU + DROPOUT -> LINEAR -> SIGMOID.
  5. Arguments:
  6. X -- input dataset, of shape (2, number of examples)
  7. parameters -- python dictionary containing your parameters "W1", "b1", "W2", "b2", "W3", "b3":
  8. W1 -- weight matrix of shape (20, 2)
  9. b1 -- bias vector of shape (20, 1)
  10. W2 -- weight matrix of shape (3, 20)
  11. b2 -- bias vector of shape (3, 1)
  12. W3 -- weight matrix of shape (1, 3)
  13. b3 -- bias vector of shape (1, 1)
  14. keep_prob - probability of keeping a neuron active during drop-out, scalar
  15. Returns:
  16. A3 -- last activation value, output of the forward propagation, of shape (1,1)
  17. cache -- tuple, information stored for computing the backward propagation
  18. """
  19. np.random.seed(1)
  20. # retrieve parameters
  21. W1 = parameters["W1"]
  22. b1 = parameters["b1"]
  23. W2 = parameters["W2"]
  24. b2 = parameters["b2"]
  25. W3 = parameters["W3"]
  26. b3 = parameters["b3"]
  27. # LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
  28. Z1 = np.dot(W1, X) + b1
  29. A1 = relu(Z1)
  30. ### START CODE HERE ### (approx. 4 lines) # Steps 1-4 below correspond to the Steps 1-4 described above.
  31. D1 = np.random.rand(len(A1), len(A1[0]))
  32. D1 = D1 < keep_prob
  33. A1 = A1 * D1
  34. A1 = A1 / keep_prob
  35. ### END CODE HERE ###
  36. Z2 = np.dot(W2, A1) + b2
  37. A2 = relu(Z2)
  38. ### START CODE HERE ### (approx. 4 lines)
  39. D2 = np.random.rand(len(A2), len(A2[0]))
  40. D2 = D2 < keep_prob
  41. A2 = A2 * D2
  42. A2 = A2 / keep_prob
  43. ### END CODE HERE ###
  44. Z3 = np.dot(W3, A2) + b3
  45. A3 = sigmoid(Z3)
  46. cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)
  47. return A3, cache
  1. X_assess, parameters = forward_propagation_with_dropout_test_case()
  2. A3, cache = forward_propagation_with_dropout(X_assess, parameters, keep_prob = 0.7)
  3. print ("A3 = " + str(A3))
  1. A3 = [[0.36974721 0.00305176 0.04565099 0.49683389 0.36974721]]

预期输出:
A3 = [[0.36974721 0.00305176 0.04565099 0.49683389 0.36974721]]

3.2 带有dropout的反向传播

练习:实现带有dropout的反向传播。和之前一样,训练一个3层的网络。使用存储在缓存中的掩码【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图53【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图54,添加dropout到第一和第二个隐藏层。

说明
带有dropout的反向传播实现上非常容易。你将必须执行2个步骤:
1.你先前通过在A1上应用掩码【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图55来关闭正向传播过程中的某些神经元。在反向传播中,你将必须将相同的掩码【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图56重新应用于dA1来关闭相同的神经元。
2.在正向传播过程中,你已将A1除以keep_prob。 因此,在反向传播中,必须再次将dA1除以keep_prob(计算的解释是,如果【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图57keep_prob缩放,则其派生的【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图58也由相同的keep_prob缩放)。

  1. # GRADED FUNCTION: backward_propagation_with_dropout
  2. def backward_propagation_with_dropout(X, Y, cache, keep_prob):
  3. """
  4. Implements the backward propagation of our baseline model to which we added dropout.
  5. Arguments:
  6. X -- input dataset, of shape (2, number of examples)
  7. Y -- "true" labels vector, of shape (output size, number of examples)
  8. cache -- cache output from forward_propagation_with_dropout()
  9. keep_prob - probability of keeping a neuron active during drop-out, scalar
  10. Returns:
  11. gradients -- A dictionary with the gradients with respect to each parameter, activation and pre-activation variables
  12. """
  13. m = X.shape[1]
  14. (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3) = cache
  15. dZ3 = A3 - Y
  16. dW3 = 1./m * np.dot(dZ3, A2.T)
  17. db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
  18. dA2 = np.dot(W3.T, dZ3)
  19. ### START CODE HERE ### (≈ 2 lines of code)
  20. dA2 = dA2 * D2
  21. dA2 = dA2 / keep_prob
  22. ### END CODE HERE ###
  23. dZ2 = np.multiply(dA2, np.int64(A2 > 0))
  24. dW2 = 1./m * np.dot(dZ2, A1.T)
  25. db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
  26. dA1 = np.dot(W2.T, dZ2)
  27. ### START CODE HERE ### (≈ 2 lines of code)
  28. dA1 = dA1 * D1
  29. dA1 = dA1 / keep_prob
  30. ### END CODE HERE ###
  31. dZ1 = np.multiply(dA1, np.int64(A1 > 0))
  32. dW1 = 1./m * np.dot(dZ1, X.T)
  33. db1 = 1./m * np.sum(dZ1, axis=1, keepdims = True)
  34. gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,"dA2": dA2,
  35. "dZ2": dZ2, "dW2": dW2, "db2": db2, "dA1": dA1,
  36. "dZ1": dZ1, "dW1": dW1, "db1": db1}
  37. return gradients
  1. X_assess, Y_assess, cache = backward_propagation_with_dropout_test_case()
  2. gradients = backward_propagation_with_dropout(X_assess, Y_assess, cache, keep_prob = 0.8)
  3. print ("dA1 = " + str(gradients["dA1"]))
  4. print ("dA2 = " + str(gradients["dA2"]))

输出:
dA1 = [[ 0.36544439 0. -0.00188233 0. -0.17408748]
[ 0.65515713 0. -0.00337459 0. -0. ]]
dA2 = [[ 0.58180856 0. -0.00299679 0. -0.27715731]
[ 0. 0.53159854 -0. 0.53159854 -0.34089673]
[ 0. 0. -0.00292733 0. -0. ]]

现在让我们使用dropout(keep_prob = 0.86)运行模型。 这意味着在每次迭代中,你都以24%的概率关闭第1层和第2层的每个神经元。

  1. parameters = model(train_X, train_Y, keep_prob = 0.86, learning_rate = 0.3)
  2. print ("On the train set:")
  3. predictions_train = predict(train_X, train_Y, parameters)
  4. print ("On the test set:")
  5. predictions_test = predict(test_X, test_Y, parameters)

image.png

  1. On the train set:
  2. Accuracy: 0.9289099526066351
  3. On the test set:
  4. Accuracy: 0.95

Dropout效果很好!测试精度再次提高(达到95%)!模型并未过拟合训练集,并且在测试集上表现很好。

运行以下代码以绘制决策边界。

  1. plt.title("Model with dropout")
  2. axes = plt.gca()
  3. axes.set_xlim([-0.75,0.40])
  4. axes.set_ylim([-0.75,0.65])
  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y)


image.png

注意

  • 使用dropout时的常见错误是在训练和测试中都使用。你只能在训练中使用dropout(随机删除节点)。
  • 深度学习框架,例如tensorflow, PaddlePaddle, keras或者 caffe 附带dropout层的实现。不需强调-相信你很快就会学习到其中的一些框架。

关dropout你应该记住的事情:

  • dropout是一种正则化技术。
  • 仅在训练期间使用dropout,在测试期间不要使用。
  • 在正向和反向传播期间均应用dropout。
  • 在训练期间,将每个dropout层除以keep_prob,以保持激活的期望值相同。例如,如果keep_prob为0.5,则平均而言,我们将关闭一半的节点,因此输出将按0.5缩放,因为只有剩余的一半对解决方案有所贡献。除以0.5等于乘以2,因此输出现在具有相同的期望值。你可以检查此方法是否有效,即使keep_prob的值不是0.5。

4 结论

这是我们三个模型的结果

模型 训练精度 测试精度
三层神经网络,无正则化 95% 91.50%
具有L2正则化的3层NN 94% 93%
具有dropout的3层NN 93% 95%

请注意,正则化会损害训练集的性能! 这是因为它限制了网络过拟合训练集的能力。 但是,由于它最终可以提供更好的测试准确性,因此可以为你的系统提供帮助。

我们希望你从此笔记本中记住的内容

  • 正则化将帮助减少过拟合。
  • 正则化将使权重降低到较低的值。
  • L2正则化和Dropout是两种非常有效的正则化技术。

梯度检验

学习实现和使用梯度检验,来确定反向传播的实现是正确的。

  1. # Packages
  2. import numpy as np
  3. from testCases import *
  4. from gc_utils import sigmoid, relu, dictionary_to_vector, vector_to_dictionary, gradients_to_vector

1 梯度检验原理

反向传播计算梯度【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图61,其中【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图62表示模型的参数。使用正向传播和损失函数来计算【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图63

由于正向传播相对容易实现,相信你有信心能做到这一点,确定100%计算正确的损失【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图64。为此,你可以使用【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图65来验证代码【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图66

让我们回顾一下导数(或者说梯度)的定义:

【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图67%20-%20J(%5Ctheta%20-%20%5Cvarepsilon)%7D%7B2%20%5Cvarepsilon%7D%20%5Ctag%7B1%7D%0A#card=math&code=%5Cfrac%7B%5Cpartial%20J%7D%7B%5Cpartial%20%5Ctheta%7D%20%3D%20%5Clim_%7B%5Cvarepsilon%20%5Cto%200%7D%20%5Cfrac%7BJ%28%5Ctheta%20%2B%20%5Cvarepsilon%29%20-%20J%28%5Ctheta%20-%20%5Cvarepsilon%29%7D%7B2%20%5Cvarepsilon%7D%20%5Ctag%7B1%7D%0A&id=yi0SM)

如果你还不熟悉”【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图68“表示法,其意思只是“当【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图69值趋向很小时”。

我们知道以下内容:

  • 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图70是你要确保计算正确的对象。
  • 你可以计算【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图71#card=math&code=J%28%5Ctheta%20%2B%20%5Cvarepsilon%29&id=py5pj)和【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图72#card=math&code=J%28%5Ctheta%20-%20%5Cvarepsilon%29&id=Bs6Aw)(在【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图73是实数的情况下),因为要保证【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图74的实现是正确的。

让我们使用方程式(1)和 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图75的一个小值来说服CEO你计算【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图76的代码是正确的!

2 一维梯度检查

思考一维线性函数【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图77%20%3D%20%5Ctheta%20x#card=math&code=J%28%5Ctheta%29%20%3D%20%5Ctheta%20x&id=VMhol),该模型仅包含一个实数值参数 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图78,并以【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图79作为输入。

你将实现代码以计算 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图80#card=math&code=J%28.%29&id=zbQep)及其派生【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图81,然后,你将使用梯度检验来确保【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图82的导数计算正确。

关键的计算步骤:首先从【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图83开始,再评估函数【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图84#card=math&code=J%28x%29&id=tvLXd)(正向传播),然后计算导数【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图85(反向传播)。

  1. # GRADED FUNCTION: forward_propagation
  2. def forward_propagation(x, theta):
  3. # J -- the value of function J, computed using the formula J(theta) = theta * x
  4. J = theta * x
  5. return J

现在,执行图1的反向传播步骤(导数计算)。也就是说,计算【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图86%20%3D%20%5Ctheta%20x#card=math&code=J%28%5Ctheta%29%20%3D%20%5Ctheta%20x&id=Aqh0N) 相对于 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图87的导数,得到【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图88

  1. # GRADED FUNCTION: backward_propagation
  2. def backward_propagation(x, theta):
  3. dtheta = x
  4. return dtheta

练习:为了展示backward_propagation()函数正确计算了梯度【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图89,让我们实施梯度检验。

说明

  • 首先使用梯度的定义计算梯度,即根据上式(1)和【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图90的极小值计算“gradapprox”。步骤:
    1. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图91
    2. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图92
    3. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图93#card=math&code=J%5E%7B%2B%7D%20%3D%20J%28%5Ctheta%5E%7B%2B%7D%29&id=H5keA)
    4. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图94#card=math&code=J%5E%7B-%7D%20%3D%20J%28%5Ctheta%5E%7B-%7D%29&id=Gv35o)
    5. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图95
  • 然后使用反向传播计算梯度,并将结果存储在变量“grad”中
  • 最后,使用以下公式计算“gradapprox”和“grad”之间的相对差:

【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图96

计算norm的代码是np.linalg.norm(x)

  • 如果差异很小(例如小于【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图97),则可以确信正确计算了梯度。否则,梯度计算可能会出错。
  1. # GRADED FUNCTION: gradient_check
  2. def gradient_check(x, theta, epsilon = 1e-7):
  3. """
  4. Implement the backward propagation presented in Figure 1.
  5. Arguments:
  6. x -- a real-valued input
  7. theta -- our parameter, a real number as well
  8. epsilon -- tiny shift to the input to compute approximated gradient with formula(1)
  9. Returns:
  10. difference -- difference (2) between the approximated gradient and the backward propagation gradient
  11. """
  12. # Compute gradapprox using left side of formula (1). epsilon is small enough, you don't need to worry about the limit.
  13. ### START CODE HERE ### (approx. 5 lines)
  14. thetaplus = theta + epsilon # Step 1
  15. thetaminus = theta - epsilon # Step 2
  16. J_plus = forward_propagation(x, thetaplus) # Step 3
  17. J_minus = forward_propagation(x, thetaminus) # Step 4
  18. gradapprox = (J_plus - J_minus) / (2 * epsilon) # Step 5
  19. ### END CODE HERE ###
  20. # Check if gradapprox is close enough to the output of backward_propagation()
  21. ### START CODE HERE ### (approx. 1 line)
  22. grad = backward_propagation(x, theta)
  23. ### END CODE HERE ###
  24. ### START CODE HERE ### (approx. 1 line)
  25. numerator = np.linalg.norm(grad - gradapprox) # Step 1'
  26. denominator = np.linalg.norm(grad) + np.linalg.norm(gradapprox) # Step 2'
  27. difference = numerator / denominator # Step 3'
  28. ### END CODE HERE ###
  29. if difference < 1e-7:
  30. print ("The gradient is correct!")
  31. else:
  32. print ("The gradient is wrong!")
  33. return difference
  1. x, theta = 2, 4
  2. difference = gradient_check(x, theta)
  3. print("difference = " + str(difference))

预期输出:
The gradient is correct!
difference = 2.919335883291695e-10

Nice!差异小于阈值【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图98。因此可以放心,你已经在backward_propagation()中正确计算了梯度。

现在,在更一般的情况下,你的损失函数【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图99具有多个单个1D输入。当你训练神经网络时,【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图100实际上由多个矩阵【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图101组成,并加上偏差【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图102!重要的是要知道如何对高维输入进行梯度检验。我们开始动手吧!

3 N维梯度检验

深层神经网络 LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID

让我们看一下正向传播和反向传播的实现。

  1. def forward_propagation_n(X, Y, parameters):
  2. # retrieve parameters
  3. m = X.shape[1]
  4. W1 = parameters["W1"]
  5. b1 = parameters["b1"]
  6. W2 = parameters["W2"]
  7. b2 = parameters["b2"]
  8. W3 = parameters["W3"]
  9. b3 = parameters["b3"]
  10. # LINEAR -> RELU -> LINEAR -> RELU -> LINEAR -> SIGMOID
  11. Z1 = np.dot(W1, X) + b1
  12. A1 = relu(Z1)
  13. Z2 = np.dot(W2, A1) + b2
  14. A2 = relu(Z2)
  15. Z3 = np.dot(W3, A2) + b3
  16. A3 = sigmoid(Z3)
  17. # Cost
  18. logprobs = np.multiply(-np.log(A3),Y) + np.multiply(-np.log(1 - A3), 1 - Y)
  19. cost = 1./m * np.sum(logprobs)
  20. cache = (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3)
  21. return cost, cache
  1. def backward_propagation_n(X, Y, cache):
  2. m = X.shape[1]
  3. (Z1, A1, W1, b1, Z2, A2, W2, b2, Z3, A3, W3, b3) = cache
  4. dZ3 = A3 - Y
  5. dW3 = 1./m * np.dot(dZ3, A2.T)
  6. db3 = 1./m * np.sum(dZ3, axis=1, keepdims = True)
  7. dA2 = np.dot(W3.T, dZ3)
  8. dZ2 = np.multiply(dA2, np.int64(A2 > 0))
  9. dW2 = 1./m * np.dot(dZ2, A1.T)
  10. db2 = 1./m * np.sum(dZ2, axis=1, keepdims = True)
  11. dA1 = np.dot(W2.T, dZ2)
  12. dZ1 = np.multiply(dA1, np.int64(A1 > 0))
  13. dW1 = 1./m * np.dot(dZ1, X.T)
  14. db1 = 1./m * np.sum(dZ1, axis=1, keepdims = True)
  15. gradients = {"dZ3": dZ3, "dW3": dW3, "db3": db3,
  16. "dA2": dA2, "dZ2": dZ2, "dW2": dW2, "db2": db2,
  17. "dA1": dA1, "dZ1": dZ1, "dW1": dW1, "db1": db1}
  18. return gradients

让我们实现梯度检验以验证你的梯度是否正确。

梯度检验原理

与1和2中一样,你想将“gradapprox”与通过反向传播计算的梯度进行比较。公式仍然是:

【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图103%20-%20J(%5Ctheta%20-%20%5Cvarepsilon)%7D%7B2%20%5Cvarepsilon%7D%20%5Ctag%7B1%7D%0A#card=math&code=%5Cfrac%7B%5Cpartial%20J%7D%7B%5Cpartial%20%5Ctheta%7D%20%3D%20%5Clim_%7B%5Cvarepsilon%20%5Cto%200%7D%20%5Cfrac%7BJ%28%5Ctheta%20%2B%20%5Cvarepsilon%29%20-%20J%28%5Ctheta%20-%20%5Cvarepsilon%29%7D%7B2%20%5Cvarepsilon%7D%20%5Ctag%7B1%7D%0A&id=zV3vT)

但是,【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图104不再是标量。 而是一个叫做“参数”的字典。 我们为你实现了一个函数”dictionary_to_vector()“。它将“参数”字典转换为称为“值”的向量,该向量是通过将所有参数(W1, b1, W2, b2, W3, b3)重塑为向量并将它们串联而获得的。
反函数是“vector_to_dictionary”,它输出回“parameters”字典。
image.png
图2:dictionary_to_vector()和vector_to_dictionary()
你将在 gradient_check_n()中用到这些函数
我们还使用gradients_to_vector()将“gradients”字典转换为向量“grad”。

练习:实现gradient_check_n()。
说明:这是伪代码,可帮助你实现梯度检验。

For each i in num_parameters:

  • 计算J_plus [i]
    1. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图106设为 np.copy(parameters_values)
    2. 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图107 设为【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图108
    3. 使用forward_propagation_n(x, y, vector_to_dictionary( 【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图109 ))计算【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图110
  • 计算J_minus [i]:也是用【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图111
  • 计算【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图112

因此,你将获得向量gradapprox,其中gradapprox[i]是相对于parameter_values[i]的梯度的近似值。现在,你可以将此gradapprox向量与反向传播中的梯度向量进行比较。就像一维情况(步骤1’,2’,3’)一样计算:

【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图113

  1. # GRADED FUNCTION: gradient_check_n
  2. def gradient_check_n(parameters, gradients, X, Y, epsilon = 1e-7):
  3. # Set-up variables
  4. parameters_values, _ = dictionary_to_vector(parameters)
  5. grad = gradients_to_vector(gradients)
  6. num_parameters = parameters_values.shape[0]
  7. J_plus = np.zeros((num_parameters, 1))
  8. J_minus = np.zeros((num_parameters, 1))
  9. gradapprox = np.zeros((num_parameters, 1))
  10. # Compute gradapprox
  11. for i in range(num_parameters):
  12. # Compute J_plus[i]. Inputs: "parameters_values, epsilon". Output = "J_plus[i]".
  13. thetaplus = np.copy(parameters_values)
  14. thetaplus[i][0] = thetaplus[i][0] + epsilon
  15. J_plus[i], _ = forward_propagation_n(X, Y, vector_to_dictionary(thetaplus))
  16. # Compute J_minus[i]. Inputs: "parameters_values, epsilon". Output = "J_minus[i]".
  17. thetaminus = np.copy(parameters_values)
  18. thetaminus[i][0] = thetaminus[i][0] - epsilon
  19. J_minus[i], _ = forward_propagation_n(X, Y, vector_to_dictionary(thetaminus))
  20. # Compute gradapprox[i]
  21. gradapprox[i] = (J_plus[i] - J_minus[i]) / (2.* epsilon)
  22. # Compare gradapprox to backward propagation gradients by computing difference.
  23. numerator = np.linalg.norm(grad - gradapprox)
  24. denominator = np.linalg.norm(grad) + np.linalg.norm(gradapprox)
  25. difference = numerator / denominator
  26. if difference > 1e-7:
  27. print ("\033[93m" + "There is a mistake in the backward propagation! difference = " + str(difference) + "\033[0m")
  28. else:
  29. print ("\033[92m" + "Your backward propagation works perfectly fine! difference = " + str(difference) + "\033[0m")
  30. return difference
  1. X, Y, parameters = gradient_check_n_test_case()
  2. cost, cache = forward_propagation_n(X, Y, parameters)
  3. gradients = backward_propagation_n(X, Y, cache)
  4. difference = gradient_check_n(parameters, gradients, X, Y)

如果根据定义计算的梯度和反向传播计算的梯度差>1e-7,说明backward_propagation_n代码有错误!

注意

  • 梯度检验很慢!用【HW】L2W1-初始化、正则化、dropout、梯度检验 - 图114%20-%20J(%5Ctheta%20-%20%5Cvarepsilon)%7D%7B2%20%5Cvarepsilon%7D#card=math&code=%5Cfrac%7B%5Cpartial%20J%7D%7B%5Cpartial%20%5Ctheta%7D%20%5Capprox%20%20%5Cfrac%7BJ%28%5Ctheta%20%2B%20%5Cvarepsilon%29%20-%20J%28%5Ctheta%20-%20%5Cvarepsilon%29%7D%7B2%20%5Cvarepsilon%7D&id=n2Rxg) 逼近梯度在计算上是很耗费资源的。因此,我们不会在训练期间的每次迭代中都进行梯度检验。只需检查几次梯度是否正确。
  • 至少如我们介绍的那样,梯度检验不适用于dropout。通常,你将运行不带dropout的梯度检验算法以确保你的backprop是正确的,然后添加dropout。

4 总结

  • 梯度检验可验证反向传播的梯度与梯度的数值近似值之间的接近度(使用正向传播进行计算)。
  • 梯度检验很慢,因此我们不会在每次训练中都运行它。通常,你仅需确保其代码正确即可运行它,然后将其关闭并将backprop用于实际的学习过程。