深度学习第二课程笔记-第二周分割数据集以及优化梯度下降算法

本文介绍了深度学习中的数据集分割及几种优化梯度下降算法的方法,包括mini-batch梯度下降法、带有动量的梯度下降算法以及Adam算法。通过对比不同算法的效果,展示了Adam算法在训练神经网络方面的高效性。
摘要由CSDN通过智能技术生成

深度学习第二课程笔记-第二周

本文参考何宽所写,点击这里可进入
这部分包括
  1. 分割数据集
  2. 优化梯度下降算法:
     2.1 不使用任何优化算法
     2.2 mini-batch梯度下降法
     2.3 使用具有动量的梯度下降算法
     2.4 使用Adam算法
        想象一下成本函数J ,最小化成本就像找到丘陵的最低点,在训练的每一步中,都会按照某个方向更新参数,以尽可能达到最低点。它类似于最快的下山的路,如下图:
在这里插入图片描述

数据处理
在这里插入图片描述

  • 将X,Y数据按照对应数量进行分割,记为x[t],y[t],表示的数量。对应矩阵维数如图所示。
  • 这里根据我们切割不同,
  • 当我们切割大小size=m,即为整个样本时候,为正常梯度下降Gradient Descent
  • 当我们切割大小size=1,即为单个样本时候,叫做随机梯度下降Stochastic Gradient Descent
  • 当我们切割大小1<size<m,既不是选择全部的数据来学习,也不是选择一个样本来学习,而是把所有的数据集分割为一小块一小块的来学习,它会随机选择一小块(mini-batch),块大小一般为2的n次方倍(64,128,256,512)。为小批量梯度下降 MiNi-Batch Gradient Descent
    我们来看下不同梯度下降的比较:
    在这里插入图片描述
    在这里插入图片描述
  • 在实际中,更好的方法是使用小批量(mini-batch)梯度下降法。一方面,充分利用的GPU的并行性,更一方面,不会让计算时间特别长。

指数加权平均(Exponentially weighted averages)包含动量的梯度下降

       由于小批量梯度下降只看到了一个子集的参数更新,更新的方向有一定的差异,所以小批量梯度下降的路径将“振荡地”走向收敛,使用动量可以减少这些振荡,动量考虑了过去的梯度以平滑更新, 我们将把以前梯度的方向存储在变量v中,从形式上讲,这将是前面的梯度的指数加权平均值。在这里插入图片描述
可以理解为下一次的方向是和上一次的方向有相关性的,受到影响不同,这里影响指的就是权重
在这里插入图片描述

  • 同时注意到beta=0.9 效果是最好的,vdWvdb初值都为零。

RMSProp算法(Root Mean Square prop)

主要作用,加速梯度下降
在这里插入图片描述

Adam算法(Adaptive Moment Estimation )

      Adam算法是训练神经网络中最有效的算法之一,它是RMSProp算法与Momentum算法的结合体
在这里插入图片描述
可以看到这里,将他们结合了起来,并且在beta选取中,一般选取beta1=0.9(dw)beta2=0.999(dw)二次方,ε=1e-8

参数调试

      每个超参数如果设置得不好,都会对训练产生巨大的负面影响,而这些超参数有一个相对重要排名如下图:

  • 学习率alpha动量betaAdam算法中的beta1,beta2,ε层数layers隐藏层数节点的数量hidden units学习衰退率 learning rate decay数据切分大小mini-batch size 等等。
  • 其中学习率alpha是最重要的,其次动量beta隐藏层数节点的数量hidden units数据切分大小mini-batch size ,再者层数layers学习衰退率 learning rate decay。至于Adam算法中的beta1,beta2,ε我们基本保持不变

代码测试
      在我们之前使用的都是最简单的梯度下降,Gradient Descent,核心公式为在这里插入图片描述具体含义不再解释,也不再进行测试。

  • 我们直接来看 mini-batch梯度下降法
    使用mini-batch要经过两个步骤:
      1. 把训练集打乱,但是X和Y依旧是一一对应的,之后,X的第i列是与Y中的第i个标签对应的样本。乱序步骤确保将样本被随机分成不同的小批次。如下图,X和Y的每一列代表一个样本
      在这里插入图片描述
  1. 切分,我们把训练集打乱之后,我们就可以对它进行切分了。这里切分的大小是64,如下图:

在这里插入图片描述
代码如下

def random_mini_batches(X,Y,mini_batch_size=64,seed=0):
    """
    从(X,Y)中创建一个随机的mini-batch列表
    
    参数:
        X - 输入数据,维度为(输入节点数量,样本的数量)
        Y - 对应的是X的标签,【1 | 0】(蓝|红),维度为(1,样本的数量)
        mini_batch_size - 每个mini-batch的样本数量
        
    返回:
        mini-bacthes - 一个同步列表,维度为(mini_batch_X,mini_batch_Y)
        
    """
    
    np.random.seed(seed) #指定随机种子
    m = X.shape[1]
    mini_batches = []
    
    #第一步:打乱顺序
    permutation = list(np.random.permutation(m)) #它会返回一个长度为m的随机数组,且里面的数是0到m-1
    shuffled_X = X[:,permutation]   #将每一列的数据按permutation的顺序来重新排列。
    shuffled_Y = Y[:,permutation].reshape((1,m))
    
    """
    #如果不好理解的话,看一下下面的伪代码,看看X和Y是如何根据permutation来打乱顺序的。
    x = np.array([[1,2,3,4,5,6,7,8,9],
				  [9,8,7,6,5,4,3,2,1]])
    y = np.array([[1,0,1,0,1,0,1,0,1]])
    
    random_mini_batches(x,y)
    permutation= [7, 2, 1, 4, 8, 6, 3, 0, 5]
    shuffled_X= [[8 3 2 5 9 7 4 1 6]
                 [2 7 8 5 1 3 6 9 4]]
    shuffled_Y= [[0 1 0 1 1 1 0 1 0]]
    """
    
    #第二步,分割
    num_complete_minibatches = math.floor(m / mini_batch_size) #把训练集分割成多少份,如果值是99.99,那么返回值是99,剩下的0.99会被舍弃
    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]
        """
      
        #如果不好理解的话单独执行下面的代码,它可以帮你理解一些。
        a = np.array([[1,2,3,4,5,6,7,8,9],
                      [9,8,7,6,5,4,3,2,1],
                      [1,2,3,4,5,6,7,8,9]])
        k=1
        mini_batch_size=3
        print(a[:,1*3:(1+1)*3]) #从第4列到第6列
        '''
        [[4 5 6]
         [6 5 4]
         [4 5 6]]
        '''
        k=2
        print(a[:,2*3:(2+1)*3]) #从第7列到第9列
        '''
        [[7 8 9]
         [3 2 1]
         [7 8 9]]
        '''

        #看一下每一列的数据可能就会好理解一些
        """
        mini_batch = (mini_batch_X,mini_batch_Y)
        mini_batches.append(mini_batch)
    
    #如果训练集的大小刚好是mini_batch_size的整数倍,那么这里已经处理完了
    #如果训练集的大小不是mini_batch_size的整数倍,那么最后肯定会剩下一些,我们要把它处理了
    if m % mini_batch_size != 0:
        #获取最后剩余的部分
        mini_batch_X = shuffled_X[:,mini_batch_size * num_complete_minibatches:]
        mini_batch_Y = shuffled_Y[:,mini_batch_size * num_complete_minibatches:]
        
        mini_batch = (mini_batch_X,mini_batch_Y)
        mini_batches.append(mini_batch)
        
    return mini_batches

包含动量的梯度下降
在这里插入图片描述
先进行初始化

def initialize_velocity(parameters):
    """
    初始化速度,velocity是一个字典:
        - keys: "dW1", "db1", ..., "dWL", "dbL" 
        - values:与相应的梯度/参数维度相同的值为零的矩阵。
    参数:
        parameters - 一个字典,包含了以下参数:
            parameters["W" + str(l)] = Wl
            parameters["b" + str(l)] = bl
    返回:
        v - 一个字典变量,包含了以下参数:
            v["dW" + str(l)] = dWl的速度
            v["db" + str(l)] = dbl的速度
    
    """
    L = len(parameters) // 2 #神经网络的层数
    v = {}
    
    for l in range(L):
        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

初始化完成了,就开始影响梯度的方向,需要使用以下公式进行更新
在这里插入图片描述

def update_parameters_with_momentun(parameters,grads,v,beta,learning_rate):
    """
    使用动量更新参数
    参数:
        parameters - 一个字典类型的变量,包含了以下字段:
            parameters["W" + str(l)] = Wl
            parameters["b" + str(l)] = bl
        grads - 一个包含梯度值的字典变量,具有以下字段:
            grads["dW" + str(l)] = dWl
            grads["db" + str(l)] = dbl
        v - 包含当前速度的字典变量,具有以下字段:
            v["dW" + str(l)] = ...
            v["db" + str(l)] = ...
        beta - 超参数,动量,实数
        learning_rate - 学习率,实数
    返回:
        parameters - 更新后的参数字典
        v - 包含了更新后的速度变量
    """
    L = len(parameters) // 2 
    for l in range(L):
        #计算速度
        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

       需要注意的是速度v vv是用0来初始化的,因此,该算法需要经过几次迭代才能把速度提升上来并开始跨越更大步伐。当beta=0时,该算法相当于是没有使用momentum算法的标准的梯度下降算法。当beta越大的时候,说明平滑的作用越明显。通常0.9是比较合适的值。

Adam算法

Adam算法是训练神经网络中最有效的算法之一,它是RMSProp算法与Momentum算法的结合体。
  1. 计算以前的梯度的指数加权平均值,并将其存储在变量v (偏差校正前)和V(corrected)(偏差校正后)中。
  2. 计算以前梯度的平方的指数加权平均值,并将其存储在变量s (偏差校正前)和S(corrected)(偏差校正后)中。
   3. 根据1和2更新参数。
   在这里插入图片描述
一般选取beta1=0.9(dw)beta2=0.999(dw)二次方,ε=1e-8
初始化参数:

def initialize_adam(parameters):
    """
    初始化v和s,它们都是字典类型的变量,都包含了以下字段:
        - keys: "dW1", "db1", ..., "dWL", "dbL" 
        - values:与对应的梯度/参数相同维度的值为零的numpy矩阵
    
    参数:
        parameters - 包含了以下参数的字典变量:
            parameters["W" + str(l)] = Wl
            parameters["b" + str(l)] = bl
    返回:
        v - 包含梯度的指数加权平均值,字段如下:
            v["dW" + str(l)] = ...
            v["db" + str(l)] = ...
        s - 包含平方梯度的指数加权平均值,字段如下:
            s["dW" + str(l)] = ...
            s["db" + str(l)] = ...
    
    """
    
    L = len(parameters) // 2
    v = {}
    s = {}
    
    for l in range(L):
        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)])
        
        s["dW" + str(l + 1)] = np.zeros_like(parameters["W" + str(l + 1)])
        s["db" + str(l + 1)] = np.zeros_like(parameters["b" + str(l + 1)])
    
    return (v,s)


更新参数

def update_parameters_with_adam(parameters,grads,v,s,t,learning_rate=0.01,beta1=0.9,beta2=0.999,epsilon=1e-8):
    """
    使用Adam更新参数
    
    参数:
        parameters - 包含了以下字段的字典:
            parameters['W' + str(l)] = Wl
            parameters['b' + str(l)] = bl
        grads - 包含了梯度值的字典,有以下key值:
            grads['dW' + str(l)] = dWl
            grads['db' + str(l)] = dbl
        v - Adam的变量,第一个梯度的移动平均值,是一个字典类型的变量
        s - Adam的变量,平方梯度的移动平均值,是一个字典类型的变量
        t - 当前迭代的次数
        learning_rate - 学习率
        beta1 - 动量,超参数,用于第一阶段,使得曲线的Y值不从0开始(参见天气数据的那个图)
        beta2 - RMSprop的一个参数,超参数
        epsilon - 防止除零操作(分母为0)
    
    返回:
        parameters - 更新后的参数
        v - 第一个梯度的移动平均值,是一个字典类型的变量
        s - 平方梯度的移动平均值,是一个字典类型的变量
    """
    L = len(parameters) // 2
    v_corrected = {} #偏差修正后的值
    s_corrected = {} #偏差修正后的值
    
    for l in range(L):
        #梯度的移动平均值,输入:"v , grads , beta1",输出:" v "
        v["dW" + str(l + 1)] = beta1 * v["dW" + str(l + 1)] + (1 - beta1) * grads["dW" + str(l + 1)]
        v["db" + str(l + 1)] = beta1 * v["db" + str(l + 1)] + (1 - beta1) * grads["db" + str(l + 1)]
        
        #计算第一阶段的偏差修正后的估计值,输入"v , beta1 , t" , 输出:"v_corrected"
        v_corrected["dW" + str(l + 1)] = v["dW" + str(l + 1)] / (1 - np.power(beta1,t))
        v_corrected["db" + str(l + 1)] = v["db" + str(l + 1)] / (1 - np.power(beta1,t))
    
        #计算平方梯度的移动平均值,输入:"s, grads , beta2",输出:"s"
        s["dW" + str(l + 1)] = beta2 * s["dW" + str(l + 1)] + (1 - beta2) * np.square(grads["dW" + str(l + 1)])
        s["db" + str(l + 1)] = beta2 * s["db" + str(l + 1)] + (1 - beta2) * np.square(grads["db" + str(l + 1)])
         
        #计算第二阶段的偏差修正后的估计值,输入:"s , beta2 , t",输出:"s_corrected"
        s_corrected["dW" + str(l + 1)] = s["dW" + str(l + 1)] / (1 - np.power(beta2,t))
        s_corrected["db" + str(l + 1)] = s["db" + str(l + 1)] / (1 - np.power(beta2,t))
        
        #更新参数,输入: "parameters, learning_rate, v_corrected, s_corrected, epsilon". 输出: "parameters".
        parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * (v_corrected["dW" + str(l + 1)] / np.sqrt(s_corrected["dW" + str(l + 1)] + epsilon))
        parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * (v_corrected["db" + str(l + 1)] / np.sqrt(s_corrected["db" + str(l + 1)] + epsilon))
    
    return (parameters,v,s)

下面的测试就不放代码了,看下结果:

  • 这是我们的原始数据
    在这里插入图片描述
  • 采用梯度下降
#使用普通的梯度下降
layers_dims = [train_X.shape[0],5,2,1]
parameters = model(train_X, train_Y, layers_dims, optimizer="gd",is_plot=True)
0次遍历整个数据集,当前误差值:0.6907355122911000次遍历整个数据集,当前误差值:0.6852725328462000次遍历整个数据集,当前误差值:0.6470722240723000次遍历整个数据集,当前误差值:0.6195245549974000次遍历整个数据集,当前误差值:0.5765844355955000次遍历整个数据集,当前误差值:0.6072426395976000次遍历整个数据集,当前误差值:0.5294033317687000次遍历整个数据集,当前误差值:0.4607682398598000次遍历整个数据集,当前误差值:0.4655860823999000次遍历整个数据集,当前误差值:0.464517972217
Accuracy: 0.796666666667

在这里插入图片描述
在这里插入图片描述

  • 使用动量的梯度下降
0次遍历整个数据集,当前误差值:0.6907412988351000次遍历整个数据集,当前误差值:0.6853405261272000次遍历整个数据集,当前误差值:0.647144837013000次遍历整个数据集,当前误差值:0.6195943032084000次遍历整个数据集,当前误差值:0.5766650344075000次遍历整个数据集,当前误差值:0.6073238219016000次遍历整个数据集,当前误差值:0.5294761758797000次遍历整个数据集,当前误差值:0.4609361900498000次遍历整个数据集,当前误差值:0.4657800937019000次遍历整个数据集,当前误差值:0.464739596792

在这里插入图片描述
在这里插入图片描述

因为这个例子比较简单,使用动量效果很小

  • Adam优化后的梯度下降
0次遍历整个数据集,当前误差值:0.6905522446111000次遍历整个数据集,当前误差值:0.1855013643862000次遍历整个数据集,当前误差值:0.1508304657533000次遍历整个数据集,当前误差值:0.074454385714000次遍历整个数据集,当前误差值:0.1259591565135000次遍历整个数据集,当前误差值:0.1043444353426000次遍历整个数据集,当前误差值:0.1006763750417000次遍历整个数据集,当前误差值:0.03165203013518000次遍历整个数据集,当前误差值:0.1119727313129000次遍历整个数据集,当前误差值:0.197940071525
Accuracy: 0.94

在这里插入图片描述
在这里插入图片描述

  • 具有动量的梯度下降通常可以有很好的效果,但由于小的学习速率和简单的数据集所以它的影响几乎是轻微的。另一方面,Adam明显优于小批量梯度下降和具有动量的梯度下降。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值