8.动量梯度下降

发布时间 2023-09-28 15:54:09作者: 王哲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程序的一部分,主要包括导入所需的库和模块,以及设置一些Matplotlib的图形参数。

  1. 导入库和模块:

    • numpy:一个用于数值计算的Python库,通常用于处理数组和矩阵操作。
    • matplotlib.pyplot:Matplotlib库的子模块,用于绘制图形和图表。
    • scipy.io:SciPy库的一个模块,用于读取和写入各种文件格式,包括MATLAB文件。
    • math:Python标准库的一部分,提供了数学函数。
    • sklearn:Scikit-Learn库,用于机器学习和数据分析任务。
    • opt_utils:可能是一个自定义模块,包含了一些用于优化的工具函数。
    • testCases:可能是一个自定义模块,包含了一些测试用例。
  2. Matplotlib图形参数设置:

    • %matplotlib inline:这是一个Jupyter Notebook的魔法命令,用于在Notebook中嵌入Matplotlib图形。
    • plt.rcParams['figure.figsize']:设置图形的默认尺寸为(7.0, 4.0)英寸。
    • plt.rcParams['image.interpolation']:设置图像的插值方式为最近邻插值,以使图像更清晰。
    • plt.rcParams['image.cmap']:设置图像的默认颜色映射为灰度。

这段代码的目的是导入必要的库和设置Matplotlib的参数,以便后续的代码能够顺利运行。

# 初始化指数加权平均值字典

def initialize_velocity(parameters):
    
    L = len(parameters) // 2 # 获取神经网络的层数
    v = {}
    
    # 循环每一层
    for l in range(L): 
        # 因为l是从0开始的,所以下面要在l后面加上1
        # zeros_like会返回一个与输入参数维度相同的数组,而且将这个数组全部设置为0
        # 指数加权平均值字典的维度应该是与梯度字典一样的,而梯度字典是与参数字典一样的,所以zeros_like的输入参数是参数字典
        v["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l+1)])
        v["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l+1)])
        
    return v

这段代码定义了一个函数 initialize_velocity(parameters),用于初始化指数加权平均值字典,该字典用于在优化算法中保存参数的历史梯度信息。

  1. L = len(parameters) // 2:计算神经网络的层数,这里假设 parameters 是一个包含神经网络参数的字典,包括权重矩阵 W 和偏置向量 b,每一层都有一个 W 和一个 b

  2. v = {}:创建一个空字典 v,用于存储指数加权平均值。

  3. for l in range(L)::循环遍历神经网络的每一层,l 表示当前层的索引,从 0 到 L-1

  4. v["dW" + str(l + 1)]v["db" + str(l + 1)]:这两行分别初始化权重参数 W 和偏置参数 b 的历史梯度信息。

    • np.zeros_like(parameters["W" + str(l+1)]):使用 np.zeros_like 函数创建一个与当前层的权重矩阵 W 维度相同的全零数组,用于存储权重参数的历史梯度。
    • np.zeros_like(parameters["b" + str(l+1)]):同样,创建一个与当前层的偏置向量 b 维度相同的全零数组,用于存储偏置参数的历史梯度。
  5. 最后,函数返回初始化后的指数加权平均值字典 v,其中包含了每一层权重和偏置参数的历史梯度信息。

这个函数的目的是为了在优化算法(如梯度下降、动量法等)中使用,它会在每次迭代中更新参数的历史梯度信息,以帮助调整学习率或加速收敛。

parameters = initialize_velocity_test_case()

v = initialize_velocity(parameters)
print("v[\"dW1\"] = " + str(v["dW1"]))
print("v[\"db1\"] = " + str(v["db1"]))
print("v[\"dW2\"] = " + str(v["dW2"]))
print("v[\"db2\"] = " + str(v["db2"]))

这段代码用于测试之前定义的 initialize_velocity 函数,以确保它正确初始化了指数加权平均值字典 v

  1. parameters = initialize_velocity_test_case():这一行调用了 initialize_velocity_test_case() 函数,该函数返回了一个包含模拟参数的测试用例字典 parameters。这个测试用例字典包含了权重矩阵 W 和偏置向量 b

  2. v = initialize_velocity(parameters):这一行调用了之前定义的 initialize_velocity 函数,传入参数字典 parameters,并将返回的指数加权平均值字典存储在变量 v 中。

  3. 下面四行代码分别打印了 v 中不同参数的历史梯度信息,以检查是否正确初始化:

    • print("v[\"dW1\"] = " + str(v["dW1"])):打印第一层权重参数 W1 的历史梯度信息。
    • print("v[\"db1\"] = " + str(v["db1"])):打印第一层偏置参数 b1 的历史梯度信息。
    • print("v[\"dW2\"] = " + str(v["dW2"])):打印第二层权重参数 W2 的历史梯度信息。
    • print("v[\"db2\"] = " + str(v["db2"])):打印第二层偏置参数 b2 的历史梯度信息。

这些打印语句用于验证 initialize_velocity 函数是否按预期工作,是否正确初始化了历史梯度信息的字典 v。运行这段代码后,您应该能够看到每个参数的历史梯度信息(全零数组),以确认函数的正确性。

# 使用动量梯度下降算法来更新参数

def update_parameters_with_momentum(parameters, grads, v, beta, learning_rate):

    L = len(parameters) // 2
    
    # 遍历每一层
    for l in range(L):
        
        # 算出指数加权平均值。
        # 下面的beta就相当于我们文章中的k。
        # 看这段代码时应该回想一下我们文章中学到的“一行代码搞定指数加权平均值”的知识点
        v["dW" + str(l + 1)] = beta * v["dW" + str(l + 1)] + (1 - beta) * grads['dW' + str(l + 1)]
        v["db" + str(l + 1)] = beta * v["db" + str(l + 1)] + (1 - beta) * grads['db' + str(l + 1)]
        
        # 用指数加权平均值来更新参数
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * v["dW" + str(l + 1)]
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * v["db" + str(l + 1)]
        
    return parameters, v

这段代码定义了一个名为 update_parameters_with_momentum 的函数,用于使用动量梯度下降算法来更新神经网络的参数。

  1. update_parameters_with_momentum 函数接受以下参数:

    • parameters:一个包含神经网络参数的字典,包括权重矩阵 W 和偏置向量 b
    • grads:一个包含梯度信息的字典,包括权重矩阵的梯度 dW 和偏置向量的梯度 db
    • v:一个包含指数加权平均值的字典,用于存储历史梯度信息。
    • beta:动量参数,通常取值在 0 到 1 之间,控制历史梯度的权重。
    • learning_rate:学习率,用于控制参数更新的步长。
  2. L = len(parameters) // 2:计算神经网络的层数,与参数字典的长度相关。

  3. 循环遍历每一层神经网络,从第 1 层到第 L 层(总共 L 层):

    • 计算权重参数 W 的历史梯度信息:v["dW" + str(l + 1)],使用指数加权平均值的方法更新历史梯度,其中 beta 控制了历史梯度的权重, (1 - beta) 控制了当前梯度的权重。
    • 计算偏置参数 b 的历史梯度信息:v["db" + str(l + 1)],同样使用指数加权平均值的方法更新历史梯度。
  4. 使用指数加权平均值来更新参数:

    • 更新权重参数 Wparameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * v["dW" + str(l + 1)],这里使用了学习率来控制参数的更新步长。
    • 更新偏置参数 bparameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * v["db" + str(l + 1)]
  5. 最后,函数返回更新后的参数字典 parameters 和更新后的历史梯度字典 v

该函数实现了动量梯度下降算法,通过考虑历史梯度信息,可以更稳定地更新参数,有助于加速收敛和避免陷入局部最优解。

parameters, grads, v = update_parameters_with_momentum_test_case()

parameters, v = update_parameters_with_momentum(parameters, grads, v, beta = 0.9, learning_rate = 0.01)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
print("v[\"dW1\"] = " + str(v["dW1"]))
print("v[\"db1\"] = " + str(v["db1"]))
print("v[\"dW2\"] = " + str(v["dW2"]))
print("v[\"db2\"] = " + str(v["db2"]))

这段代码用于测试之前定义的 update_parameters_with_momentum 函数,以确保它正确地使用动量梯度下降算法来更新参数。

  1. parameters, grads, v = update_parameters_with_momentum_test_case():这一行调用了 update_parameters_with_momentum_test_case() 函数,该函数返回了一组测试用例,包括参数字典 parameters、梯度字典 grads 和历史梯度字典 v

  2. parameters, v = update_parameters_with_momentum(parameters, grads, v, beta = 0.9, learning_rate = 0.01):这一行调用了 update_parameters_with_momentum 函数,传入参数字典 parameters、梯度字典 grads、历史梯度字典 v,以及动量参数 beta(设置为0.9)和学习率 learning_rate(设置为0.01)。函数会根据动量梯度下降算法更新参数,并将更新后的参数和历史梯度返回,并将它们分别存储在 parametersv 变量中。

  3. 下面八行代码分别打印了更新后的参数和历史梯度信息,以验证 update_parameters_with_momentum 函数是否按预期工作:

    • print("W1 = " + str(parameters["W1"])):打印第一层权重参数 W1 的更新后值。
    • print("b1 = " + str(parameters["b1"])):打印第一层偏置参数 b1 的更新后值。
    • print("W2 = " + str(parameters["W2"])):打印第二层权重参数 W2 的更新后值。
    • print("b2 = " + str(parameters["b2"])):打印第二层偏置参数 b2 的更新后值。
    • print("v[\"dW1\"] = " + str(v["dW1"])):打印第一层权重参数 W1 的历史梯度信息。
    • print("v[\"db1\"] = " + str(v["db1"])):打印第一层偏置参数 b1 的历史梯度信息。
    • print("v[\"dW2\"] = " + str(v["dW2"])):打印第二层权重参数 W2 的历史梯度信息。
    • print("v[\"db2\"] = " + str(v["db2"])):打印第二层偏置参数 b2 的历史梯度信息。

这些打印语句用于验证动量梯度下降算法是否正确地更新了参数,并且历史梯度信息是否也被正确更新。运行这段代码后,您应该能够看到更新后的参数和历史梯度信息,以确认函数的正确性。