7.mini-batch梯度下降

发布时间 2023-09-28 15:13:18作者: 王哲MGG_AI
import numpy as np
import matplotlib.pyplot as plt
import scipy.io
import math
import sklearn
import sklearn.datasets

from opt_utils import load_params_and_grads, initialize_parameters, forward_propagation, backward_propagation
from opt_utils import compute_cost, predict, predict_dec, plot_decision_boundary, load_dataset
from testCases import *

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

这段代码看起来是一个Python脚本的一部分,它导入了一些常用的Python库,并设置了一些Matplotlib的参数以及自定义的模块。

  1. import语句导入了一些Python库,让我们逐个解释它们的作用:

    • numpy是一个Python库,用于支持大型多维数组和矩阵,以及用于数学运算的函数。
    • matplotlib.pyplot是一个用于绘制图形的Python库。
    • scipy.io是SciPy库的一部分,用于读取和写入各种文件格式的工具。
    • math是Python标准库中的一个数学模块,提供了各种数学函数和常数。
    • sklearn是Scikit-Learn库的缩写,是一个用于机器学习的Python库,包括了许多有用的工具和算法。
  2. 接下来,from关键字与import一起用于导入自定义的模块和函数。具体来说,它导入了以下模块和函数:

    • opt_utils模块包含了一些用于优化算法的工具函数,如加载参数、前向传播、反向传播、计算成本等。
    • testCases模块包含了一些测试用例,用于测试自定义函数的正确性。
  3. %matplotlib inline是一个Jupyter Notebook的魔术命令,用于在Notebook中内联显示Matplotlib图形。

  4. plt.rcParams用于设置Matplotlib的全局参数,这些参数会影响绘图的样式和显示方式。在这里,设置了图形的默认大小、插值方式和颜色映射。

def update_parameters_with_gd(parameters, grads, learning_rate):
    L = len(parameters) // 2 # 获取神经网络的层数。这里除以2是因为字典里面包含了w和b两种参数。

    # 遍历每一层
    for l in range(L):
        # 下面使用l + 1,是因为l是从0开始的,而我们的参数字典是从1开始的
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
        
    return parameters

这段代码是用于执行梯度下降(Gradient Descent)更新参数的函数。

  1. L = len(parameters) // 2:这一行计算了神经网络的层数。因为参数字典中包含了每一层的权重和偏差,所以将参数数量除以2可以得到神经网络的层数。

  2. for l in range(L)::这是一个循环,用于遍历神经网络的每一层。

  3. parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]:这一行代码更新了权重参数 W,用于执行梯度下降的参数更新步骤。具体来说,它从参数字典 parameters 中获取当前层的权重 W,然后减去学习率 learning_rate 乘以当前层的梯度 grads["dW" + str(l + 1)]。这一步的目的是沿着梯度的反方向更新权重,以降低成本函数的值。

  4. parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]:这一行代码更新了偏差参数 b,用法与权重更新类似,但是更新的是偏差参数。

  5. 最后,函数返回更新后的参数字典 parameters,其中包含了更新后的权重和偏差。

这个函数实现了梯度下降的基本步骤,它根据每一层的梯度来更新参数,以最小化神经网络的成本函数。这是训练神经网络的关键步骤之一。

parameters, grads, learning_rate = update_parameters_with_gd_test_case()

parameters = update_parameters_with_gd(parameters, grads, learning_rate)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))

段代码是用于测试之前提供的梯度下降参数更新函数update_parameters_with_gd的功能。

  1. parameters, grads, learning_rate = update_parameters_with_gd_test_case():这一行代码调用了名为update_parameters_with_gd_test_case的测试用例函数,它返回了三个值,分别是初始参数parameters、梯度grads和学习率learning_rate。这些值将用于测试参数更新函数。

  2. parameters = update_parameters_with_gd(parameters, grads, learning_rate):这一行代码调用了梯度下降参数更新函数update_parameters_with_gd,并将初始参数、梯度和学习率传递给该函数。函数将返回更新后的参数,并将其赋值给parameters变量。

  3. print("W1 = " + str(parameters["W1"])):这一行代码打印了第一层权重参数W1的值。

  4. print("b1 = " + str(parameters["b1"])):这一行代码打印了第一层偏差参数b1的值。

  5. print("W2 = " + str(parameters["W2"])):这一行代码打印了第二层权重参数W2的值。

  6. print("b2 = " + str(parameters["b2"])):这一行代码打印了第二层偏差参数b2的值。

def random_mini_batches(X, Y, mini_batch_size = 64, seed = 0):
       
    np.random.seed(seed)            
    m = X.shape[1]  # 获取样本数量
    mini_batches = []
        
    # 第一步: 洗牌训练集
    permutation = list(np.random.permutation(m)) # 这行代码会生成m范围内的随机整数,如果m是3,那么结果可能为[2, 0, 1]
    shuffled_X = X[:, permutation]# 这个代码会将X按permutation列表里面的随机索引进行洗牌。为什么前面是个冒号,因为前面是特征,后面才代表样本数 
    shuffled_Y = Y[:, permutation].reshape((1,m))

    # 第二步: 分割洗牌后的训练集
    num_complete_minibatches = math.floor(m/mini_batch_size) # 获取子训练集的个数(不包括后面不满mini_batch_size的那个子训练集)
    for k in range(0, num_complete_minibatches):
        mini_batch_X = shuffled_X[:,k * mini_batch_size:(k + 1) * mini_batch_size]
        mini_batch_Y = shuffled_Y[:,k * mini_batch_size:(k + 1) * mini_batch_size]
        mini_batch = (mini_batch_X, mini_batch_Y)
        mini_batches.append(mini_batch)
    
    # 出来后面不满mini_batch_size的那个子训练集
    if m % mini_batch_size != 0:
        mini_batch_X = shuffled_X[:,num_complete_minibatches * mini_batch_size:]
        mini_batch_Y = shuffled_Y[:,num_complete_minibatches * mini_batch_size:]

        mini_batch = (mini_batch_X, mini_batch_Y)
        mini_batches.append(mini_batch)
    
    return mini_batches

这段代码定义了一个用于生成随机小批量训练集的函数random_mini_batches

  1. np.random.seed(seed):这一行代码设置了随机数生成器的种子,以确保每次生成的随机数都是可重现的。种子seed是函数的参数,它可以用来在不同的运行中生成相同的随机数。

  2. m = X.shape[1]:这一行代码获取训练集样本的数量,即矩阵X的列数。

  3. permutation = list(np.random.permutation(m)):这一行代码生成一个随机排列的整数列表,范围在0到m-1之间。这个列表将用于对训练集进行洗牌,以确保小批量样本的随机性。

  4. shuffled_X = X[:, permutation]shuffled_Y = Y[:, permutation].reshape((1,m)):这两行代码根据生成的随机排列列表permutation对输入特征矩阵X和标签矩阵Y进行洗牌。shuffled_Xshuffled_Y将包含洗牌后的样本。

  5. num_complete_minibatches = math.floor(m/mini_batch_size):这一行代码计算可以完整容纳在小批量中的子训练集的数量。mini_batch_size是小批量的大小,m是总样本数量,使用math.floor确保得到一个整数。

  6. 接下来的for循环用于分割洗牌后的训练集为小批量子集:

    • mini_batch_Xmini_batch_Y分别表示当前小批量的特征和标签。
    • mini_batch是一个元组,包含了当前小批量的特征和标签,然后将其添加到mini_batches列表中。
  7. 最后的条件语句处理不满足小批量大小的样本,确保这些样本也被包含在最后一个小批量中。

  8. 最后,函数返回一个列表mini_batches,其中包含了洗牌后的小批量子集。

这个函数的目的是创建随机的小批量训练集,以用于训练神经网络。小批量梯度下降(Mini-Batch Gradient Descent)通常比使用整个训练集更高效,特别是在大规模数据集上。

X_assess, Y_assess, mini_batch_size = random_mini_batches_test_case()
mini_batches = random_mini_batches(X_assess, Y_assess, mini_batch_size)

print("第一个mini_batch_X的维度: " + str(mini_batches[0][0].shape))
print("第二个mini_batch_X的维度: " + str(mini_batches[1][0].shape))
print("第三个mini_batch_X的维度: " + str(mini_batches[2][0].shape))
print("第一个mini_batch_Y的维度: " + str(mini_batches[0][1].shape))
print("第二个mini_batch_Y的维度: " + str(mini_batches[1][1].shape)) 
print("第三个mini_batch_Y的维度: " + str(mini_batches[2][1].shape))

这段代码用于测试random_mini_batches函数的功能,以生成随机的小批量训练集,并检查每个小批量的维度。让我解释这段代码的执行过程和输出:

  1. X_assess, Y_assess, mini_batch_size = random_mini_batches_test_case():这一行代码调用了名为random_mini_batches_test_case的测试用例函数,它返回了测试用的输入特征X_assess、标签Y_assess以及小批量大小mini_batch_size

  2. mini_batches = random_mini_batches(X_assess, Y_assess, mini_batch_size):这一行代码调用了random_mini_batches函数,使用测试用的输入特征、标签和小批量大小来生成随机小批量训练集。生成的小批量被存储在mini_batches列表中。

  3. print("第一个mini_batch_X的维度: " + str(mini_batches[0][0].shape)):这一行代码打印了第一个小批量的特征矩阵mini_batch_X的维度。

  4. print("第二个mini_batch_X的维度: " + str(mini_batches[1][0].shape)):这一行代码打印了第二个小批量的特征矩阵mini_batch_X的维度。

  5. print("第三个mini_batch_X的维度: " + str(mini_batches[2][0].shape)):这一行代码打印了第三个小批量的特征矩阵mini_batch_X的维度。

  6. print("第一个mini_batch_Y的维度: " + str(mini_batches[0][1].shape)):这一行代码打印了第一个小批量的标签矩阵mini_batch_Y的维度。

  7. print("第二个mini_batch_Y的维度: " + str(mini_batches[1][1].shape)):这一行代码打印了第二个小批量的标签矩阵mini_batch_Y的维度。

  8. print("第三个mini_batch_Y的维度: " + str(mini_batches[2][1].shape)):这一行代码打印了第三个小批量的标签矩阵mini_batch_Y的维度。

通过执行这段代码,您可以查看生成的随机小批量训练集的维度。这对于确保训练过程中的数据维度一致性非常重要。