天天看點

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

前言

       這一篇部落格是在上一篇部落格的基礎上對網絡訓練使用梯度下降法進行改進,加快訓練的速度,主要從兩方面進行改進:(1)對資料集進行分割,以部分資料集為機關進行梯度下降;(2)分别采用momentum和Adam算法進行梯度下降,加快收斂速度。

上一篇部落格:采用L2正則化和随機删除節點的L層神經網絡

       最後,如果有寫得不對的地方,希望大家不吝賜教,謝謝!

一、主要任務:(1)對資料集進行分割     (2)采用momentum算法進行梯度下降     (3)采用Adam算法進行梯度下降

二、整個流程:構造資料集

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

用随機數初始化w和b參數

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

前向傳播

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

損失函數

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

反向傳播

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

優化(分割資料

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

梯度下降

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

含momentum的梯度下降

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

含Adam的梯度下降)

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

預測

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

模型

三、引用的包有:

import numpy as np
import matplotlib.pyplot as plt
import sklearn.datasets as sd
import math
           

四、需要準備的函數

激活函數:relu函數和輸出層的sigmoid函數

#輸出層激活函數
def sigmoid(z):
    
    s=1/(1+np.exp(-z))
    
    return s

#隐藏層激活函數
def relu(z):
    
    s=np.maximum(0,z)
    
    return s
           

1、構造資料集

#構造資料集
def load_datasets(is_plot=False):
    
    np.random.seed(1)
    train_x,train_y=sd.make_moons(n_samples=300,noise=.2)
    
    if is_plot==True:
        plt.scatter(train_x[:,0],train_x[:,1],c=train_y,s=40,cmap=plt.cm.Spectral)
        
    train_x=train_x.T
    train_y=train_y.reshape(1,train_y.shape[0])
    
    print('train_x_shape:',train_x.shape)
    print('train_y_shape:',train_y.shape)
    
    return train_x,train_y
           

資料集大小:

train_x_shape: (2, 300)

train_y_shape: (1, 300)

2、用随機數初始化w和b參數

#用随機數初始化w和b
def init_w_b(net_dims):
    
    L=len(net_dims)
    np.random.seed(2)
    parms={}
    
    for i in range(1,L):
        
        parms['W'+str(i)]=np.random.randn(net_dims[i],net_dims[i-1])
        parms['b'+str(i)]=np.zeros(shape=(net_dims[i],1))
        
    return parms
           

 3、前向傳播

      這次網絡設計的是一個三層的網絡

#前向傳播
def forward(X,parms):
    
    forward_data={}
    
    W1=parms['W1']
    b1=parms['b1']
    W2=parms['W2']
    b2=parms['b2']
    W3=parms['W3']
    b3=parms['b3']
    
    z1=np.dot(W1,X)+b1
    a1=relu(z1)
    z2=np.dot(W2,a1)+b2
    a2=relu(z2)
    z3=np.dot(W3,a2)+b3
    a3=sigmoid(z3)
    
    forward_data={'W1':W1,
                  'b1':b1,
                  'z1':z1,
                  'a1':a1,
                  'W2':W2,
                  'b2':b2,
                  'z2':z2,
                  'a2':a2,
                  'W3':W3,
                  'b3':b3,
                  'z3':z3,
                  'a3':a3}
    
    return forward_data
           

4、損失函數

#損失函數
def cost_function(Y,forward_data):
    
    a3=forward_data['a3']
    m=Y.shape[1]
    
    cost=(-1/m)*np.sum(Y*np.log(a3)+(1-Y)*np.log(1-a3))
    
    return cost
           

5、反向傳播

#反向傳播
def backward(forward_data,X,Y):
    
    m=Y.shape[1]
    
    dZ3=forward_data['a3']-Y
    dW3=(1/m)*np.dot(dZ3,forward_data['a2'].T)
    db3=(1/m)*np.sum(dZ3,axis=1,keepdims=True)
    
    dZ2=np.dot(forward_data['W3'].T,dZ3)*np.int64(forward_data['a2']>0)
    dW2=(1/m)*np.dot(dZ2,forward_data['a1'].T)
    db2=(1/m)*np.sum(dZ2,axis=1,keepdims=True)
    
    dZ1=np.dot(forward_data['W2'].T,dZ2)*np.int64(forward_data['a1']>0)
    dW1=(1/m)*np.dot(dZ1,X.T)
    db1=(1/m)*np.sum(dZ1,axis=1,keepdims=True)
    
    grads={'dZ3':dZ3,
           'dW3':dW3,
           'db3':db3,
           'dZ2':dZ2,
           'dW2':dW2,
           'db2':db2,
           'dZ1':dZ1,
           'dW1':dW1,
           'db1':db1}
    
    return grads
           

6、優化

(1)分割資料集

1)先打亂train_x的順序,即一共有m個資料,原始的排列是:0,1,2,,,m-1,現在随機打亂順序,train_y對應地也會和train_x随機順序一樣。

#第一步:打亂順序
    permutation =list(np.random.permutation(m))
    X_random=X[:,permutation]
    Y_random=Y[:,permutation].reshape(1,m)
           

2)切分資料,把m個資料按照一定大小進行切分,如每一個大小為mini_batch_size=64,即一共分成(m/mini_batch_size)組,例如:

第一組:first_mini_batch=X_random[:,0:mini_batch_size]

第二組:sceond_mini_batch=X_random[:,mini_batch_size:2*mini_batch_size]

,,,

 整個過程如下:

#先進行分割資料
def random_mini_batches(X,Y,mini_batches_size=64):
    
    np.random.randn(3)
    m=Y.shape[1]
    mini_batches=[]
    
    #第一步:打亂順序
    permutation =list(np.random.permutation(m))
    X_random=X[:,permutation]
    Y_random=Y[:,permutation].reshape(1,m)
    
    #第二步:分割
    num_competed=math.floor(m/mini_batches_size)
    
    for i in range(num_competed):
        
        mini_batch_x=X_random[:,i*mini_batches_size:(i+1)*mini_batches_size]
        mini_batch_y=Y_random[:,i*mini_batches_size:(i+1)*mini_batches_size]
        mini_batch=(mini_batch_x,mini_batch_y)
        mini_batches.append(mini_batch)
        
    if m%mini_batches_size!=0:
        
        mini_batch_x=X_random[:,num_competed*mini_batches_size:]
        mini_batch_y=Y_random[:,num_competed*mini_batches_size:]
        mini_batch=(mini_batch_x,mini_batch_y)
        mini_batches.append(mini_batch)
        
    return mini_batches
           

(2)梯度下降

【momentum&Adam】對梯度下降法進行優化,加快訓練速度
【momentum&Adam】對梯度下降法進行優化,加快訓練速度
#1、梯度下降
def updata_w_b(parms,grads,learning_rate,net_dims):
    
    L=len(net_dims)
    
    for i in range(1,L):
        
        parms['W'+str(i)]=parms['W'+str(i)]-learning_rate*grads['dW'+str(i)]
        parms['b'+str(i)]=parms['b'+str(i)]-learning_rate*grads['db'+str(i)]
        
    return parms
           

在進行momentum和Adam梯度下降時,先對算法中需要用到的v和s變量進行初始化

#初始化momentun和Adam算法中需要用到的v和s變量
def init_v_s(net_dims,parms):
    
    L=len(net_dims)
    v={}
    s={}
    for i in range(1,L):
        
        v['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
        v['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
        
        s['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
        s['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
        
    return v,s
           

(3)含momentum的梯度下降

算法本身主要利用前面的梯度進行綜合求和平均,來進行平滑,防止波動,每次求和個數:

【momentum&Adam】對梯度下降法進行優化,加快訓練速度
【momentum&Adam】對梯度下降法進行優化,加快訓練速度

  其中一般

【momentum&Adam】對梯度下降法進行優化,加快訓練速度
【momentum&Adam】對梯度下降法進行優化,加快訓練速度
#2、包含動量的梯度下降
def update_w_b_momentum(parms,v,grads,net_dims,learning_rate,beta=0.9):
    
    L=len(net_dims)
        
    for i in range(1,L):
        
        v['dW'+str(i)]=beta*v['dW'+str(i)]+(1-beta)*grads['dW'+str(i)]
        v['db'+str(i)]=beta*v['db'+str(i)]+(1-beta)*grads['db'+str(i)]
        
        parms['W'+str(i)]=parms['W'+str(i)]-learning_rate*v['dW'+str(i)]
        parms['b'+str(i)]=parms['b'+str(i)]-learning_rate*v['db'+str(i)]
        
    return parms,v
           

(4)含Adam算法的梯度下降

先介紹下RMSprop算法:這個算法主要是加快梯度下降的前進速度,減緩梯度左右波動的速度

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

   其中一般

【momentum&Adam】對梯度下降法進行優化,加快訓練速度
【momentum&Adam】對梯度下降法進行優化,加快訓練速度

Adam算法是将momentum和RMSprop進行結合起來

【momentum&Adam】對梯度下降法進行優化,加快訓練速度
【momentum&Adam】對梯度下降法進行優化,加快訓練速度

偏差校正:

【momentum&Adam】對梯度下降法進行優化,加快訓練速度
【momentum&Adam】對梯度下降法進行優化,加快訓練速度

update:

【momentum&Adam】對梯度下降法進行優化,加快訓練速度

    其中一般

【momentum&Adam】對梯度下降法進行優化,加快訓練速度
#3、Adam算法
def update_w_b_adam(parms,grads,v,s,t,net_dims,learning_rate,beta1=0.9,beta2=0.999,e=1e-8):
    
    L=len(net_dims)
    v_corr={}
    s_corr={}
    
    for i in range(1,L):
        
        v_corr['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
        v_corr['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
        
        s_corr['dW'+str(i)]=np.zeros_like(parms['W'+str(i)])
        s_corr['db'+str(i)]=np.zeros_like(parms['b'+str(i)])
        
    for i in range(1,L):
        
        v['dW'+str(i)]=beta1*v['dW'+str(i)]+(1-beta1)*grads['dW'+str(i)]
        v['db'+str(i)]=beta1*v['db'+str(i)]+(1-beta1)*grads['db'+str(i)]
        
        s['dW'+str(i)]=beta2*s['dW'+str(i)]+(1-beta2)*np.square(grads['dW'+str(i)])
        s['db'+str(i)]=beta2*s['db'+str(i)]+(1-beta2)*np.square(grads['db'+str(i)])
        
        v_corr['dW'+str(i)]=v['dW'+str(i)]/(1-np.power(beta1,t))
        v_corr['db'+str(i)]=v['db'+str(i)]/(1-np.power(beta1,t))
        
        s_corr['dW'+str(i)]=s['dW'+str(i)]/(1-np.power(beta2,t))
        s_corr['db'+str(i)]=s['db'+str(i)]/(1-np.power(beta2,t))
        
        parms['W'+str(i)]=parms['W'+str(i)]-learning_rate*v_corr['dW'+str(i)]/(np.sqrt(s_corr['dW'+str(i)])+e)
        parms['b'+str(i)]=parms['b'+str(i)]-learning_rate*v_corr['db'+str(i)]/(np.sqrt(s_corr['db'+str(i)])+e)
        
    return parms,v,s
           

7、預測

#預測
def predict(X,parms,Y):
    
    forward_data=forward(X,parms)
    a3=forward_data['a3']
    m=Y.shape[1]
    y_predict=np.zeros(shape=(1,m))
    
    for i in range(m):
        
        if a3[0,i]>0.5:
            y_predict[0,i]=1
        else:
            y_predict[0,i]=0
            
    print('Accuracy:'+str(np.mean((y_predict[0,:]==Y[0,:]))))
    
    return y_predict
           

8、模型

首先網絡結構:net_dims={0:n,1:5,2:2,3:1},其中n=train_x.shape[0]

#模型
def mole(train_x,train_y,choice,learning_rate,mini_batches_size=64,iterations=5000,
         beta=0.9,beta1=0.9,beta2=0.999,e=1e-8,print_flag=False,is_plot=False):
    
        n=train_x.shape[0]
        net_dims={0:n,1:5,2:2,3:1}
        t=0
        costs=[]
        
        parms=init_w_b(net_dims)
        v,s=init_v_s(net_dims,parms)
        
        for i in range(iterations):
            
            mini_batches=random_mini_batches(train_x,train_y,mini_batches_size)
            
            for mini_batch in mini_batches:
                
                #選擇一個batch
                (mini_batch_x,mini_batch_y)=mini_batch
                
                #前向傳播
                forward_data=forward(mini_batch_x,parms)
                
                #損失函數
                cost=cost_function(mini_batch_y,forward_data)
                
                #反向傳播
                grads=backward(forward_data,mini_batch_x,mini_batch_y)
                
                #更新參數
                if choice=='gd': #梯度下降
                    
                    parms=updata_w_b(parms,grads,learning_rate,net_dims)
                    
                elif choice=='momentum': #包含動量的梯度下降
                    
                    parms,v=update_w_b_momentum(parms,v,grads,net_dims,learning_rate,beta)
                    
                elif choice=='adam': #使用Adam的梯度下降
                    
                    t=t+1
                    parms,v,s=update_w_b_adam(parms,grads,v,s,t,net_dims,learning_rate,beta1,beta2,e)
                    
            if i%100==0:
                
                costs.append(cost)
                
            if (print_flag) and (i%1000==0):
                
                print('疊代次數:%d,損失函數:%f' % (i,cost))
                
        if is_plot==True:
            
            plt.plot(costs)
            plt.ylabel('costs')
            plt.xlabel('epochs(per 100)')
            plt.title('learning_rate='+str(learning_rate))
            plt.show()
            
        return parms
           

九、結果

(1)梯度下降

plt.figure(1)
train_x,train_y=load_datasets(is_plot=True)

plt.figure(2)
choice='gd'
parms=mole(train_x,train_y,choice,learning_rate=0.0007,mini_batches_size=64,iterations=10000,beta=0.9,
         beta1=0.9,beta2=0.999,e=1e-8,print_flag=True,is_plot=True)
y_predict=predict(train_x,parms,train_y)
           

疊代次數:0,損失函數:0.591491

疊代次數:1000,損失函數:0.437251

疊代次數:2000,損失函數:0.406010

疊代次數:3000,損失函數:0.466082

疊代次數:4000,損失函數:0.498481

疊代次數:5000,損失函數:0.436827

疊代次數:6000,損失函數:0.414368

疊代次數:7000,損失函數:0.364876

疊代次數:8000,損失函數:0.372451

疊代次數:9000,損失函數:0.344451

Accuracy:0.8566666666666667 
【momentum&Adam】對梯度下降法進行優化,加快訓練速度

                                                                                                圖1 梯度下降

(2)含momentum的梯度下降

plt.figure(1)
train_x,train_y=load_datasets(is_plot=True)

plt.figure(2)
choice='momentum'
parms=mole(train_x,train_y,choice,learning_rate=0.0007,mini_batches_size=64,iterations=10000,beta=0.9,
         beta1=0.9,beta2=0.999,e=1e-8,print_flag=True,is_plot=True)
y_predict=predict(train_x,parms,train_y)
           

疊代次數:0,損失函數:0.591578

疊代次數:1000,損失函數:0.437270

疊代次數:2000,損失函數:0.406018

疊代次數:3000,損失函數:0.466111

疊代次數:4000,損失函數:0.498487

疊代次數:5000,損失函數:0.436937

疊代次數:6000,損失函數:0.414402

疊代次數:7000,損失函數:0.364930

疊代次數:8000,損失函數:0.372556

疊代次數:9000,損失函數:0.344508

Accuracy:0.8566666666666667 
【momentum&Adam】對梯度下降法進行優化,加快訓練速度

                                                                               圖2 含momentum的梯度下降法

注:由于小的學習速率和小的資料集,是以導緻和普通的梯度下降效果差不多。

(3)含Adam的梯度下降

plt.figure(1)
train_x,train_y=load_datasets(is_plot=True)

plt.figure(2)
choice='adam'
parms=mole(train_x,train_y,choice,learning_rate=0.0007,mini_batches_size=64,iterations=10000,beta=0.9,
         beta1=0.9,beta2=0.999,e=1e-8,print_flag=True,is_plot=True)
y_predict=predict(train_x,parms,train_y)
           

疊代次數:0,損失函數:0.590683

疊代次數:1000,損失函數:0.143360

疊代次數:2000,損失函數:0.072868

疊代次數:3000,損失函數:0.072864

疊代次數:4000,損失函數:0.162808

疊代次數:5000,損失函數:0.042045

疊代次數:6000,損失函數:0.107865

疊代次數:7000,損失函數:0.058769

疊代次數:8000,損失函數:0.043267

疊代次數:9000,損失函數:0.101692

Accuracy:0.9666666666666667 
【momentum&Adam】對梯度下降法進行優化,加快訓練速度

                                                                                    圖3 含Adam的梯度下降法

繼續閱讀