5.正则化

王哲_UJN_MGG_AI / 2023-09-04 / 原文

import numpy as np
import matplotlib.pyplot as plt
from reg_utils import sigmoid, relu, plot_decision_boundary, initialize_parameters, load_2D_dataset, predict_dec
from reg_utils import compute_cost, predict, forward_propagation, backward_propagation, update_parameters
import sklearn
import sklearn.datasets
import scipy.io
from testCases import *

%matplotlib inline
plt.rcParams['figure.figsize'] = (7.0, 4.0)
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'

##############################################

这段代码看起来是一个Python脚本的开头,它导入了一些必要的库和模块,以及一些自定义的工具函数和测试用例。

  1. import numpy as np: 导入NumPy库,并将其重命名为np,以便在代码中使用更短的别名来引用NumPy函数。

  2. import matplotlib.pyplot as plt: 导入Matplotlib库的pyplot模块,并将其重命名为plt,以便在代码中使用更短的别名来创建和操作图形。

  3. from reg_utils import ...: 这一行导入了自定义的一些函数和工具类,它们似乎与正则化(regularization)有关,包括sigmoid函数、relu函数、绘制决策边界的函数、初始化模型参数的函数、加载数据集的函数、进行预测的函数、计算损失的函数、前向传播和反向传播的函数以及更新模型参数的函数。这些函数很可能在后续的代码中使用。

  4. import sklearnimport sklearn.datasets: 导入scikit-learn库以及其datasets模块,这些库和模块通常用于机器学习任务,可能会在后续的代码中使用。

  5. import scipy.io: 导入SciPy库,SciPy通常用于科学计算和数据分析,它也可能会在后续的代码中使用。

  6. from testCases import *: 这一行导入了一个名为testCases的模块中的所有内容。这通常用于测试代码的正确性,测试用例包含在testCases模块中。

  7. %matplotlib inline: 这是一个Jupyter Notebook魔术命令,用于在Jupyter Notebook中显示Matplotlib图形。如果您不是在Jupyter Notebook中运行代码,可以忽略这一行。

  8. plt.rcParams['figure.figsize'] = (7.0, 4.0): 设置Matplotlib图形的默认大小为7.0 x 4.0英寸。

  9. plt.rcParams['image.interpolation'] = 'nearest': 设置Matplotlib图像的插值方式为最近邻插值,以确保图像在显示时不会模糊。

  10. plt.rcParams['image.cmap'] = 'gray': 设置Matplotlib图像的默认颜色映射为灰度,通常用于显示灰度图像。

这段代码的主要作用是导入必要的库、模块和函数,为后续的代码准备环境。

##############################################

train_X, train_Y, test_X, test_Y = load_2D_dataset()

##############################################

这行代码调用了名为load_2D_dataset()的函数,并将其返回的值分配给四个变量:train_Xtrain_Ytest_Xtest_Y。这些变量是用于训练和测试模型的数据集。

具体来说,这个函数可能的作用是加载一个二维数据集,将其分为训练集和测试集,并返回四个数组,每个数组包含以下内容:

  • train_X: 训练集的输入特征,通常是一个包含多个训练样本的矩阵,其中每行代表一个样本,每列代表一个特征。

  • train_Y: 训练集的标签或输出,与train_X中的样本一一对应,通常是一个包含标签值的向量或矩阵。

  • test_X: 测试集的输入特征,与train_X的结构相同,但包含不同的测试样本。

  • test_Y: 测试集的标签或输出,与test_X中的样本一一对应,通常是一个包含标签值的向量或矩阵。

这种数据集的加载通常用于机器学习任务,其中你需要将模型训练在训练集上,然后在测试集上评估模型的性能。

##############################################

def model(X, Y, learning_rate = 0.3, num_iterations = 30000, print_cost = True, lambd = 0, keep_prob = 1):
grads = {}
costs = []
m = X.shape[1]
layers_dims = [X.shape[0], 20, 3, 1]

parameters = initialize_parameters(layers_dims)

for i in range(0, num_iterations):
if keep_prob == 1:
a3, cache = forward_propagation(X, parameters)
elif keep_prob < 1:
a3, cache = forward_propagation_with_dropout(X, parameters, keep_prob)

if lambd == 0:
cost = compute_cost(a3, Y)
else:
cost = compute_cost_with_regularization(a3, Y, parameters, lambd)

# 防止同时使用L2正则化和dropout。
# 其实是可以同时使用它们的,但是为了教学目的,突出重点,所以单次只允许用其中一个。
assert(lambd == 0 or keep_prob == 1)

if lambd == 0 and keep_prob == 1:
grads = backward_propagation(X, Y, cache)
elif lambd != 0:
grads = backward_propagation_with_regularization(X, Y, cache, lambd)
elif keep_prob < 1:
grads = backward_propagation_with_dropout(X, Y, cache, keep_prob)

parameters = update_parameters(parameters, grads, learning_rate)

if print_cost and i % 10000 == 0:
print("Cost after iteration {}: {}".format(i, cost))
if print_cost and i % 1000 == 0:
costs.append(cost)

plt.plot(costs)
plt.ylabel('cost')
plt.xlabel('iterations (x1,000)')
plt.title("Learning rate =" + str(learning_rate))
plt.show()

return parameters

##############################################

这段代码定义了一个用于构建深度神经网络模型的函数,函数名为 model

  1. def model(X, Y, learning_rate=0.3, num_iterations=30000, print_cost=True, lambd=0, keep_prob=1):: 这是一个函数定义,它接受以下参数:

    • X: 输入特征数据
    • Y: 输出标签数据
    • learning_rate: 学习率,用于控制参数更新的步长,默认值为0.3
    • num_iterations: 迭代次数,指定训练迭代的次数,默认为30000次
    • print_cost: 是否打印成本(损失)信息,默认为True
    • lambd: 正则化超参数,默认为0(没有正则化)
    • keep_prob: dropout的概率,默认为1(没有dropout)
  2. grads = {}: 初始化一个空字典,用于存储梯度信息。

  3. costs = []: 初始化一个空列表,用于存储损失函数的值,以便后续可视化损失。

  4. m = X.shape[1]: 获取训练样本的数量,即X的第二维的大小。

  5. layers_dims = [X.shape[0], 20, 3, 1]: 定义神经网络的层次结构,包括输入层、隐藏层1(20个神经元)、隐藏层2(3个神经元)和输出层(1个神经元)的维度。

  6. parameters = initialize_parameters(layers_dims): 使用 initialize_parameters 函数初始化神经网络的参数。

  7. for i in range(0, num_iterations):: 开始迭代训练模型,迭代次数由 num_iterations 决定。

  8. 根据 keep_problambd 的值,选择相应的前向传播和计算损失的方式。如果 keep_prob 等于1并且 lambd 等于0,则使用普通的前向传播和损失计算方式,否则根据条件选择使用正则化或dropout。

  9. 根据 lambd 的值,选择相应的反向传播方式,同样根据条件选择使用正则化或dropout。

  10. 使用梯度下降法更新参数,更新方式由 update_parameters 函数实现。

  11. 如果 print_cost 为True,并且当前迭代次数是10000的倍数,打印当前损失值。

  12. 如果 print_cost 为True,并且当前迭代次数是1000的倍数,将当前损失值添加到 costs 列表中,以便后续可视化。

  13. 最后,通过Matplotlib绘制损失函数随迭代次数变化的图形,以观察模型的训练过程。

  14. 返回训练好的参数。

这个函数的主要作用是训练深度神经网络模型,并支持正则化和dropout技术,以提高模型的泛化能力。

##############################################

parameters = model(train_X, train_Y)
print("On the training set:")
predictions_train = predict(train_X, train_Y, parameters)
print("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)

##############################################

这段代码用于训练神经网络模型并进行预测。

  1. parameters = model(train_X, train_Y): 调用之前定义的 model 函数来训练神经网络模型。train_X 是训练集的输入特征,train_Y 是训练集的标签。模型训练完成后,返回的 parameters 包含了训练好的神经网络参数。

  2. print("On the training set:"): 打印以下信息,表示接下来要在训练集上进行预测。

  3. predictions_train = predict(train_X, train_Y, parameters): 调用 predict 函数来对训练集进行预测。train_X 是训练集的输入特征,train_Y 是训练集的真实标签,parameters 是之前训练得到的模型参数。函数返回的 predictions_train 是模型对训练集的预测结果。

  4. print("On the test set:"): 打印以下信息,表示接下来要在测试集上进行预测。

  5. predictions_test = predict(test_X, test_Y, parameters): 调用 predict 函数来对测试集进行预测。test_X 是测试集的输入特征,test_Y 是测试集的真实标签,parameters 是之前训练得到的模型参数。函数返回的 predictions_test 是模型对测试集的预测结果。

##############################################

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

##############################################

这段代码用于绘制决策边界图,以可视化模型在训练数据上的表现。

  1. plt.title("Model without regularization"): 设置图的标题为 "Model without regularization",表示这是没有正则化的模型。

  2. axes = plt.gca(): 获取当前图的坐标轴。

  3. axes.set_xlim([-0.75, 0.40]): 设置x轴的坐标范围,限制在-0.75到0.40之间。

  4. axes.set_ylim([-0.75, 0.65]): 设置y轴的坐标范围,限制在-0.75到0.65之间。

  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y.ravel()): 调用 plot_decision_boundary 函数来绘制决策边界。这个函数接受三个参数:

    • 第一个参数是一个lambda函数,它使用训练好的模型参数 parameters 来对输入数据 x.T(转置后的训练数据)进行预测,从而得到决策边界。
    • 第二个参数是训练集的输入特征 train_X
    • 第三个参数是训练集的真实标签 train_Y,使用 ravel() 函数将其转换为一维数组。

这段代码的目的是在图上绘制模型的决策边界,以帮助可视化模型如何对训练数据进行分类。通过调整坐标轴范围和查看决策边界,您可以更好地了解模型的性能。

##############################################

def compute_cost_with_regularization(A3, Y, parameters, lambd):
m = Y.shape[1]
W1 = parameters["W1"]
W2 = parameters["W2"]
W3 = parameters["W3"]

# 获得常规的成本
cross_entropy_cost = compute_cost(A3, Y)

#计算L2尾巴
L2_regularization_cost = lambd * (np.sum(np.square(W1)) + np.sum(np.square(W2)) + np.sum(np.square(W3))) / (2 * m)

cost = cross_entropy_cost + L2_regularization_cost

return cost

##############################################

这段代码定义了一个函数 compute_cost_with_regularization,用于计算带有L2正则化的损失函数。

  1. A3: 是模型的输出,即最后一层的激活值,它代表模型对每个样本的预测概率。

  2. Y: 是训练集的真实标签。

  3. parameters: 是模型的参数,包括权重矩阵和偏置向量。

  4. lambd: 是L2正则化的超参数,控制正则化的强度。

  5. m = Y.shape[1]: 获取训练样本的数量,即Y的第二维大小。

  6. W1, W2, 和 W3:分别是神经网络的权重矩阵,这些权重矩阵包括了不同层次的权重参数。

  7. cross_entropy_cost: 调用 compute_cost 函数计算交叉熵损失,这是常规的损失计算,衡量了模型的预测和真实标签之间的差异。

  8. L2_regularization_cost: 计算L2正则化的损失,这个损失是通过对所有权重矩阵的平方和进行缩放来计算的,它惩罚了权重的大小。

  9. cost = cross_entropy_cost + L2_regularization_cost: 将交叉熵损失和L2正则化损失相加,得到最终的损失值。

  10. 返回 cost,即带有L2正则化的总损失。

这个函数的作用是计算带有L2正则化的损失,正则化有助于减少模型的过拟合问题,使模型更具泛化能力。

##############################################

# 单元测试
A3, Y_assess, parameters = compute_cost_with_regularization_test_case()

print("cost = " + str(compute_cost_with_regularization(A3, Y_assess, parameters, lambd = 0.1)))

##############################################

这段代码看起来是用于进行单元测试的代码,测试 compute_cost_with_regularization 函数的实现是否正确。

  1. A3, Y_assess, parameters = compute_cost_with_regularization_test_case(): 这一行调用了一个名为 compute_cost_with_regularization_test_case 的测试用例函数,并将返回的值分配给三个变量:

    • A3: 模型的输出。
    • Y_assess: 真实标签。
    • parameters: 模型的参数。
  2. compute_cost_with_regularization(A3, Y_assess, parameters, lambd = 0.1): 调用 compute_cost_with_regularization 函数,传入上述获得的模型输出 A3、真实标签 Y_assess、模型参数 parameters 和正则化超参数 lambd(这里设置为0.1)。函数将计算带有L2正则化的损失,并返回损失值。

  3. print("cost = " + str(compute_cost_with_regularization(A3, Y_assess, parameters, lambd = 0.1))): 打印计算得到的带有L2正则化的损失值。

这段代码的主要目的是测试 compute_cost_with_regularization 函数是否正确地计算了带有L2正则化的损失。如果打印的损失值与预期结果一致,那么函数的实现是正确的。这是一种常见的测试方式,用于确保代码的准确性。

##############################################

def backward_propagation_with_regularization(X, Y, cache, lambd):

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) + (lambd * W3) / m
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) + (lambd * W2) / m
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) + (lambd * W1) / m
db1 = 1. / 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_with_regularization 的函数,用于计算带有L2正则化的梯度。

  1. X, Y, cache, lambd: 这个函数接受四个参数,分别是输入特征 X、真实标签 Y、缓存 cache(包含了前向传播过程中的一些中间值),以及正则化超参数 lambd,它控制正则化的强度。

  2. m = X.shape[1]: 获取训练样本的数量,即 X 的第二维大小。

  3. 从缓存 cache 中获取前向传播过程中的中间值,包括:

    • Z1, A1, W1, b1:第一个隐藏层的线性变换、激活值、权重和偏置。
    • Z2, A2, W2, b2:第二个隐藏层的线性变换、激活值、权重和偏置。
    • Z3, A3, W3, b3:输出层的线性变换、激活值、权重和偏置。
  4. 计算输出层的梯度 dZ3,这是预测值 A3 与真实标签 Y 之间的差异。

  5. 计算权重矩阵 W3 的梯度 dW3,考虑了L2正则化的影响,通过添加 (lambd * W3) / m 来惩罚权重的大小。

  6. 计算偏置 b3 的梯度 db3

  7. 计算第二个隐藏层的梯度 dA2,用于反向传播到第二个隐藏层。

  8. 计算第二个隐藏层的线性变换的梯度 dZ2,同时通过乘以 np.int64(A2 > 0) 来考虑ReLU激活函数的导数。

  9. 计算权重矩阵 W2 的梯度 dW2,同样考虑了L2正则化的影响。

  10. 计算偏置 b2 的梯度 db2

  11. 计算第一个隐藏层的梯度 dA1,用于反向传播到第一个隐藏层。

  12. 计算第一个隐藏层的线性变换的梯度 dZ1,同时通过乘以 np.int64(A1 > 0) 来考虑ReLU激活函数的导数。

  13. 计算权重矩阵 W1 的梯度 dW1,同样考虑了L2正则化的影响。

  14. 计算偏置 b1 的梯度 db1

  15. 将所有梯度存储在字典 gradients 中,以便后续的参数更新。

  16. 返回包含所有梯度的 gradients 字典。

这个函数的作用是计算带有L2正则化的梯度,正则化有助于减少模型的过拟合问题,使模型更具泛化能力。

##############################################

这段代码用于进行单元测试,测试 backward_propagation_with_regularization 函数的实现是否正确。

  1. X_assess, Y_assess, cache = backward_propagation_with_regularization_test_case(): 这一行调用了一个名为 backward_propagation_with_regularization_test_case 的测试用例函数,并将返回的值分配给三个变量:

    • X_assess: 用于测试的输入特征。
    • Y_assess: 用于测试的真实标签。
    • cache: 包含了前向传播过程中的中间值,用于后续的反向传播。
  2. grads = backward_propagation_with_regularization(X_assess, Y_assess, cache, lambd=0.7): 调用 backward_propagation_with_regularization 函数,传入测试用的输入特征 X_assess、真实标签 Y_assess、缓存 cache 和正则化超参数 lambd(这里设置为0.7)。函数将计算带有L2正则化的梯度,并返回梯度信息。

  3. print ("dW1 = " + str(grads["dW1"])): 打印第一层权重矩阵 W1 的梯度信息。

  4. print ("dW2 = " + str(grads["dW2"])): 打印第二层权重矩阵 W2 的梯度信息。

  5. print ("dW3 = " + str(grads["dW3"])): 打印输出层权重矩阵 W3 的梯度信息。

这段代码的目的是测试 backward_propagation_with_regularization 函数是否正确地计算了带有L2正则化的梯度。如果打印的梯度信息与预期结果一致,那么函数的实现是正确的。这是一种常见的测试方式,用于确保代码的准确性。

##############################################

parameters = model(train_X, train_Y, lambd=0.7)
print("On the train set:")
predictions_train = predict(train_X, train_Y, parameters)
print("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)

##############################################

这段代码使用带有L2正则化的模型进行训练和预测。

  1. parameters = model(train_X, train_Y, lambd=0.7): 调用 model 函数,传入训练集的输入特征 train_X 和标签 train_Y,以及正则化超参数 lambd(这里设置为0.7)。函数将训练带有L2正则化的神经网络模型,并返回训练好的参数 parameters

  2. print("On the train set:"): 打印以下信息,表示接下来要在训练集上进行预测。

  3. predictions_train = predict(train_X, train_Y, parameters): 调用 predict 函数来对训练集进行预测。train_X 是训练集的输入特征,train_Y 是训练集的真实标签,parameters 是之前训练得到的模型参数。函数返回的 predictions_train 是模型对训练集的预测结果。

  4. print("On the test set:"): 打印以下信息,表示接下来要在测试集上进行预测。

  5. predictions_test = predict(test_X, test_Y, parameters): 调用 predict 函数来对测试集进行预测。test_X 是测试集的输入特征,test_Y 是测试集的真实标签,parameters 是之前训练得到的模型参数。函数返回的 predictions_test 是模型对测试集的预测结果。

通过这些步骤,您使用带有L2正则化的模型对训练集和测试集进行了预测。您可以进一步分析预测结果以评估模型的性能和泛化能力。

##############################################

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

##############################################

这段代码用于绘制带有L2正则化的模型的决策边界图,以可视化模型在训练数据上的表现。

  1. plt.title("Model with L2-regularization"): 设置图的标题为 "Model with L2-regularization",表示这是带有L2正则化的模型。

  2. axes = plt.gca(): 获取当前图的坐标轴。

  3. axes.set_xlim([-0.75, 0.40]): 设置x轴的坐标范围,限制在-0.75到0.40之间。

  4. axes.set_ylim([-0.75, 0.65]): 设置y轴的坐标范围,限制在-0.75到0.65之间。

  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y.ravel()): 调用 plot_decision_boundary 函数来绘制带有L2正则化的模型的决策边界。这个函数接受三个参数:

    • 第一个参数是一个lambda函数,它使用训练好的模型参数 parameters 来对输入数据 x.T(转置后的训练数据)进行预测,从而得到决策边界。
    • 第二个参数是训练集的输入特征 train_X
    • 第三个参数是训练集的真实标签 train_Y,使用 ravel() 函数将其转换为一维数组。

这段代码的目的是在图上绘制带有L2正则化的模型的决策边界,以帮助可视化模型如何对训练数据进行分类。通过调整坐标轴范围和查看决策边界,您可以更好地了解带有L2正则化的模型的性能。

##############################################

def forward_propagation_with_dropout(X, parameters, keep_prob=0.5):

np.random.seed(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)

D1 = np.random.rand(A1.shape[0], A1.shape[1]) # 第一步
D1 = D1 < keep_prob # 第二步
A1 = A1 * D1 # 第三步
A1 = A1 / keep_prob # 第四步

Z2 = np.dot(W2, A1) + b2
A2 = relu(Z2)

D2 = np.random.rand(A2.shape[0], A2.shape[1])
D2 = D2 < keep_prob
A2 = A2 * D2
A2 = A2 / keep_prob

Z3 = np.dot(W3, A2) + b3
A3 = sigmoid(Z3)

cache = (Z1, D1, A1, W1, b1, Z2, D2, A2, W2, b2, Z3, A3, W3, b3)

return A3, cache

##############################################

这段代码定义了一个名为 forward_propagation_with_dropout 的函数,用于实现带有Dropout的前向传播过程。

  1. X, parameters, keep_prob=0.5: 这个函数接受三个参数,分别是输入特征 X、模型参数 parameters,以及 keep_prob,它是Dropout的概率(默认为0.5,即保留50%的神经元)。

  2. np.random.seed(1): 设置随机种子,以确保每次运行时生成的随机数相同,以便复现结果。

  3. 从模型参数 parameters 中获取各层的权重矩阵 W 和偏置向量 b

  4. 计算第一层的线性变换 Z1 和激活值 A1(使用ReLU激活函数)。

  5. Dropout的实现包括四个步骤:

    • 第一步:生成一个与 A1 维度相同的随机矩阵 D1,其中的元素是从均匀分布[0, 1)中随机抽取的值。
    • 第二步:将 D1 中的元素与 keep_prob 比较,生成一个与 A1 维度相同的布尔矩阵 D1,其中元素值为 True 的比例约为 keep_prob
    • 第三步:将 A1D1 相乘,将一部分神经元的输出设置为0,实现了Dropout。
    • 第四步:为了保持激活值的期望不变,对 A1 进行缩放,将除以 keep_prob
  6. 计算第二层的线性变换 Z2 和激活值 A2,同样应用了Dropout。

  7. 计算第三层(输出层)的线性变换 Z3 和激活值 A3,这一层使用了Sigmoid激活函数。

  8. 将计算过程中的中间值存储在 cache 变量中,以便后续的反向传播过程使用。

  9. 返回输出值 A3cache

这个函数的主要作用是在前向传播过程中实现Dropout,以减少神经网络的过拟合问题,提高模型的泛化能力。

##############################################

X_assess, parameters = forward_propagation_with_dropout_test_case()

A3, cache = forward_propagation_with_dropout(X_assess, parameters, keep_prob=0.7)
print ("A3 = " + str(A3))

##############################################

这段代码用于对带有Dropout的前向传播过程进行单元测试,测试 forward_propagation_with_dropout 函数的实现是否正确。

  1. X_assess, parameters = forward_propagation_with_dropout_test_case(): 这一行调用了一个名为 forward_propagation_with_dropout_test_case 的测试用例函数,并将返回的值分配给两个变量:

    • X_assess: 用于测试的输入特征。
    • parameters: 模型的参数。
  2. A3, cache = forward_propagation_with_dropout(X_assess, parameters, keep_prob=0.7): 调用 forward_propagation_with_dropout 函数,传入测试用的输入特征 X_assess、模型参数 parameters,以及Dropout概率 keep_prob(这里设置为0.7)。函数将执行带有Dropout的前向传播过程,计算输出值 A3 和缓存 cache

  3. print ("A3 = " + str(A3)): 打印输出值 A3,即带有Dropout的前向传播的结果。

这段代码的目的是测试 forward_propagation_with_dropout 函数是否正确地实现了Dropout操作,并生成了带有Dropout的前向传播结果。如果打印的输出值与预期结果一致,那么函数的实现是正确的。这是一种常见的测试方式,用于确保代码的准确性。

##############################################

def backward_propagation_with_dropout(X, Y, cache, keep_prob):

m = X.shape[1]
(Z1, D1, A1, W1, b1, Z2, D2, 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)

dA2 = dA2 * D2 # 第一步
dA2 = dA2 / keep_prob # 第二步

dZ2 = np.multiply(dA2, np.int64(A2 > 0))
dW2 = 1. / m * np.dot(dZ2, A1.T)
db2 = 1. / m * np.sum(dZ2, axis=1, keepdims=True)

dA1 = np.dot(W2.T, dZ2)

dA1 = dA1 * D1
dA1 = dA1 / keep_prob

dZ1 = np.multiply(dA1, np.int64(A1 > 0))
dW1 = 1. / m * np.dot(dZ1, X.T)
db1 = 1. / 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_with_dropout 的函数,用于计算带有Dropout的反向传播梯度。

  1. X, Y, cache, keep_prob: 这个函数接受四个参数,分别是输入特征 X、真实标签 Y、缓存 cache(包含了前向传播过程中的一些中间值),以及Dropout的概率 keep_prob

  2. m = X.shape[1]: 获取训练样本的数量,即 X 的第二维大小。

  3. 从缓存 cache 中获取前向传播过程中的中间值,包括:

    • Z1, D1, A1, W1, b1:第一个隐藏层的线性变换、Dropout掩码、激活值、权重和偏置。
    • Z2, D2, A2, W2, b2:第二个隐藏层的线性变换、Dropout掩码、激活值、权重和偏置。
    • Z3, A3, W3, b3:输出层的线性变换、激活值、权重和偏置。
  4. 计算输出层的梯度 dZ3,这是预测值 A3 与真实标签 Y 之间的差异。

  5. 计算权重矩阵 W3 的梯度 dW3

  6. 计算偏置 b3 的梯度 db3

  7. 计算第二层的梯度 dA2,用于反向传播到第二个隐藏层。

  8. Dropout的反向传播包括两个步骤:

    • 第一步:将 dA2D2 相乘,通过Dropout掩码将一部分神经元的梯度置为0。
    • 第二步:为了保持梯度的期望不变,对 dA2 进行缩放,将其除以 keep_prob
  9. 计算第二层的线性变换的梯度 dZ2,同时通过乘以 np.int64(A2 > 0) 来考虑ReLU激活函数的导数。

  10. 计算权重矩阵 W2 的梯度 dW2

  11. 计算偏置 b2 的梯度 db2

  12. 计算第一个隐藏层的梯度 dA1,用于反向传播到第一个隐藏层。

  13. Dropout的反向传播包括两个步骤,类似于第二层:

    • 第一步:将 dA1D1 相乘,通过Dropout掩码将一部分神经元的梯度置为0。
    • 第二步:为了保持梯度的期望不变,对 dA1 进行缩放,将其除以 keep_prob
  14. 计算第一个隐藏层的线性变换的梯度 dZ1,同时通过乘以 np.int64(A1 > 0) 来考虑ReLU激活函数的导数。

  15. 计算权重矩阵 W1 的梯度 dW1

  16. 计算偏置 b1 的梯度 db1

  17. 将所有梯度存储在字典 gradients 中,以便后续的参数更新。

  18. 返回包含所有梯度的 gradients 字典。

这个函数的主要作用是计算带有Dropout的反向传播梯度,Dropout有助于减少模型的过拟合问题,提高模型的泛化能力。

##############################################

X_assess, Y_assess, cache = backward_propagation_with_dropout_test_case()

gradients = backward_propagation_with_dropout(X_assess, Y_assess, cache, keep_prob=0.8)

print ("dA1 = " + str(gradients["dA1"]))
print ("dA2 = " + str(gradients["dA2"]))

##############################################

这段代码用于对带有Dropout的反向传播过程进行单元测试,测试 backward_propagation_with_dropout 函数的实现是否正确。

  1. X_assess, Y_assess, cache = backward_propagation_with_dropout_test_case(): 这一行调用了一个名为 backward_propagation_with_dropout_test_case 的测试用例函数,并将返回的值分配给三个变量:

    • X_assess: 用于测试的输入特征。
    • Y_assess: 用于测试的真实标签。
    • cache: 包含了前向传播过程中的中间值,用于后续的反向传播。
  2. gradients = backward_propagation_with_dropout(X_assess, Y_assess, cache, keep_prob=0.8): 调用 backward_propagation_with_dropout 函数,传入测试用的输入特征 X_assess、真实标签 Y_assess、缓存 cache,以及Dropout的概率 keep_prob(这里设置为0.8)。函数将计算带有Dropout的反向传播梯度,并将梯度信息存储在 gradients 字典中。

  3. print ("dA1 = " + str(gradients["dA1"])): 打印第一个隐藏层的梯度 dA1

  4. print ("dA2 = " + str(gradients["dA2"])): 打印第二个隐藏层的梯度 dA2

这段代码的目的是测试 backward_propagation_with_dropout 函数是否正确地计算了带有Dropout的反向传播梯度。如果打印的梯度信息与预期结果一致,那么函数的实现是正确的。这是一种常见的测试方式,用于确保代码的准确性。

##############################################

parameters = model(train_X, train_Y, keep_prob=0.86, learning_rate=0.3)

print("On the train set:")
predictions_train = predict(train_X, train_Y, parameters)
print("On the test set:")
predictions_test = predict(test_X, test_Y, parameters)

##############################################

这段代码使用带有Dropout的模型进行训练和预测

  1. parameters = model(train_X, train_Y, keep_prob=0.86, learning_rate=0.3): 调用 model 函数,传入训练集的输入特征 train_X 和标签 train_Y,并指定了两个超参数:

    • keep_prob=0.86:这是Dropout的保留概率,表示在训练中保留每个神经元的概率为0.86。
    • learning_rate=0.3:学习率,用于控制参数更新的步长。这里设置为0.3。

    函数将训练带有Dropout的神经网络模型,并返回训练好的参数 parameters

  2. print("On the train set:"): 打印以下信息,表示接下来要在训练集上进行预测。

  3. predictions_train = predict(train_X, train_Y, parameters): 调用 predict 函数来对训练集进行预测。train_X 是训练集的输入特征,train_Y 是训练集的真实标签,parameters 是之前训练得到的模型参数。函数返回的 predictions_train 是模型对训练集的预测结果。

  4. print("On the test set:"): 打印以下信息,表示接下来要在测试集上进行预测。

  5. predictions_test = predict(test_X, test_Y, parameters): 调用 predict 函数来对测试集进行预测。test_X 是测试集的输入特征,test_Y 是测试集的真实标签,parameters 是之前训练得到的模型参数。函数返回的 predictions_test 是模型对测试集的预测结果。

通过这些步骤,您使用带有Dropout的模型对训练集和测试集进行了预测。Dropout有助于减少过拟合问题,提高模型的泛化能力,尤其在训练数据较少时具有重要作用。您可以进一步分析预测结果以评估模型的性能和泛化能力。

##############################################

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

##############################################

这段代码用于绘制带有Dropout的模型的决策边界图,以可视化模型在训练数据上的表现。

  1. plt.title("Model with dropout"): 设置图的标题为 "Model with dropout",表示这是带有Dropout的模型。

  2. axes = plt.gca(): 获取当前图的坐标轴。

  3. axes.set_xlim([-0.75, 0.40]): 设置x轴的坐标范围,限制在-0.75到0.40之间。

  4. axes.set_ylim([-0.75, 0.65]): 设置y轴的坐标范围,限制在-0.75到0.65之间。

  5. plot_decision_boundary(lambda x: predict_dec(parameters, x.T), train_X, train_Y.ravel()): 调用 plot_decision_boundary 函数来绘制带有Dropout的模型的决策边界。这个函数接受三个参数:

    • 第一个参数是一个lambda函数,它使用训练好的模型参数 parameters 来对输入数据 x.T(转置后的训练数据)进行预测,从而得到决策边界。
    • 第二个参数是训练集的输入特征 train_X
    • 第三个参数是训练集的真实标签 train_Y,使用 ravel() 函数将其转换为一维数组。

这段代码的目的是在图上绘制带有Dropout的模型的决策边界,以帮助可视化模型如何对训练数据进行分类。通过调整坐标轴范围和查看决策边界,您可以更好地了解带有Dropout的模型的性能和泛化能力。

##############################################