1. 程式人生 > >機器學習(7)--梯度下降法(GradientDescent)的簡單實現

機器學習(7)--梯度下降法(GradientDescent)的簡單實現

曾經在  機器學習(1)--神經網路初探  詳細介紹了神經網路基本的演算法,在該文中有一句
weights[i] += 0.2 * layer.T.dot(delta)  #0.2學習效率,應該是一個小於0.5的數,
同時在  tensorflow例項(2)--機器學習初試 篇文章中用tensorflow實現上述的神經網路演算法,該文中也有一句
train=tf.train.GradientDescentOptimizer(0.25).minimize(loss) #梯度下降優化器,0.25學習效率,應該是一個小於0.5的數, 
這兩句裡都有一個學習率的引數,

當神經元越多時,我們的學習率應該越小,同時學習次數應該增加

在這三個案例中,都使用到了學習率,同時在第三例中的結論並不完全,因為這裡的學習率其實和梯度下降法息息相關


本文將通過一個簡單的案例來說演示一組資料在使用梯度下降法後會得到效果,也更容易理解學習率設定差異導致錯誤的原因
至於梯度下降要深入的話數理模型也是好多好多,想深入瞭解的朋友可自行百度一下,
本文會虛擬一組由100個樣本組成的一元一次方程組的資料,並通過matplotlib.pyplot來演示資料變化,
之所以使用一元一次有以下兩個原因,
1、僅使用一次的原因是神經網路使用的都是線性方程,多次的方程也是可以,只是要通過神經網路等方式進行調整,不適合本文的內容

2、使用一元的原因,是為了matplotlib.pyplot顯示的了,本文中的梯度下降函式是可適用於多元線性方程的

# -*- coding:utf-8 -*-
import numpy as np
import tensorflow as tf
import matplotlib.pyplot as plt


#構建資料
#帶有後綴_data的變數是簡單虛擬出來的資料,相當於實際後的樣本資料,
#不含字尾_data的變數就是我們建模時的變數,也就是我們最後的求解
def create_data(a,b):
    '''
    一元一次的方程為y=a*x+b 在構建這個資料時,x_data[:,0]為0-99 代表的是X的坐表,x_data[:,1]為常量1
    ,因為我們後面要用到矩陣乘法和b,相當於留個位給b,用於計算
     y最後np.random.uniform(0,1,[100])*(b/2),產生一個隨機數,
     這樣使得y的資料更加真實也更能看出梯度下降後的效果,以下是x的值,y值根據a,b的值的不同發生變化
    [[ 0.  1.]
     [ 1.  1.]
     [ 2.  1.]
     ...
     [99.  1.]
    '''
    x=np.array([np.arange(100),np.ones(100)]).T
    y = x[:,0]*a + b + np.random.uniform(-1,1,[100])*b
    return x,y


x_data,y_data=create_data(2,25)
weight=np.ones(x_data.shape[1]) #初始化weight,其實這個weight的最後一個相當於b的位置
xt=x_data.T
weightStep=[]#用於存存下降過程中計算結果,後面繪圖需要
for i in range(100000):
    y=np.dot(x_data,weight) #以weight為引數計測試值
    loss=y-y_data   #計算實際值與測試值之間的差額
    #也是cost,並不參於最後計算,這是一種簡單方法loss的一個轉換,由100個數據轉為一個數據
    #簡單的公式推導一下就可以發現,如果y和y_data相等時,cost=0,當然因為我們加了隨機數,等於0是不可能的
    #可以將這個理解為返映測試值y的好壞的一個值,當這個值越小時說明我們的weight質量越好,
    cost=np.sum(loss ** 2) / (2 * x_data.shape[0])   

    gradient=np.dot(xt,loss) /  x_data.shape[0]    # 計算梯度

    #0.0005就是學習率,如果簡單將這個值調大,就會造成weight往無限大的方向擴大,當然太小的話計算次數也需要增加
    #前面說的,當神經元越多時,我們的學習率應該越小,同時學習次數應該增加,,最終原因也在這
    weight=weight-0.0005*gradient 
    if i % 20000==0 or i <10:#每10000次顯示一次結果,之所以還i <10因為前期數值變化明顯,後面變化其實並不大
        print(i,cost,weight)
        weightStep.append(weight)
weightStep.append(weight)


plt.axis([0,110,0,150]) # 用於定義X,Y軸的範圍
plt.scatter(x_data[:,0],y_data,c='b')  # 後面圖中藍色的點為樣本資料
plt.ion()#該命令的作用是plt.show()是不暫停,
plt.show()


pltStep=None
for weight in weightStep:
    if pltStep!=None : pltStep.remove();#如果繪製過了紅點,則清除,進行下一次的繪製
    y=np.dot(x_data,weight)
    print(weight,y_data[0],y[0])
    pltStep=plt.scatter(x_data[:,0],y,c='r')  # 後面圖中紅色的點為計算出來的資料,因為是一元一次,組成的應該是一條直線
    plt.pause(0.6)#暫停0.6秒 
#通過圖案可以看到一開始的紅點直線和原來藍點毫無關聯,但最後,基本中從散點分佈的藍點中間穿過,也證明了我們的梯度降維是有效的