1. 程式人生 > >隨機梯度下降的公式實現

隨機梯度下降的公式實現

我們給出一組房子面積,臥室數目以及對應房價資料,如何從資料中找到房價y與面積x1和臥室數目x2的關係?

為了實現監督學習,我們選擇採用自變數x1、x2的線性函式來評估因變數y值,得到:

這裡,sita1、sita2代表自變數x1、x2的權重(weights),sita0代表偏移量。為了方便,我們將評估值寫作h(x),令x0=1,則h(x)可以寫作:

其中n為輸入樣本數的數量。為了得到weights的值,我們需要令我們目前的樣本資料評估出的h(x)儘可能的接近真實y值。我們定義誤差函式(cost function)來表示h(x)和y值相接近的程度:

這裡的係數1/2是為了後面求解偏導數時可以與係數相互抵消。我們的目的是要誤差函式儘可能的小,即求解weights使誤差函式儘可能小。首先,我們隨機初始化weigths,然後不斷反覆的更新weights使得誤差函式減小,直到滿足要求時停止。這裡更新演算法我們選擇梯度下降演算法,利用初始化的weights並且反覆更新weights:

這裡a代表學習率,表示每次向著J最陡峭的方向邁步的大小。為了更新weights,我們需要求出函式J的偏導數。首先計算只有一個數據樣本(x,y)時,如何計算J的偏導數:

對於只含有一組資料的訓練樣本,我們可以得到更新weights的規則為:

擴充套件到多組資料樣本,更新公式為:

稱為批處理梯度下降演算法,這種更新演算法所需要的運算成本很高,尤其是資料量較大時。考慮下面的更新演算法:

該演算法又叫做隨機梯度下降法,這種演算法不停的更新weights,每次使用一個樣本資料進行更新。當資料量較大時,一般使用後者演算法進行更新。

二、梯度下降Python實現

自己建立了一組資料,存為csv格式,如下圖所示:

待訓練資料A、B為自變數,C為因變數。

在寫程式之前,要先匯入我們需要的模組。

import numpy as np
from numpy import genfromtxt

首先將資料讀入Python中,程式如下所示:
dataPath = r"E:\learning\house.csv"
dataSet = genfromtxt(dataPath, delimiter=',')

接下來將讀取的資料分別得到自變數矩陣和因變數矩陣:
def getData(dataSet):
    m, n = np.shape(dataSet)
    trainData = np.ones((m, n))
    trainData[:,:-1] = dataSet[:,:-1]
    trainLabel = dataSet[:,-1]
    return trainData, trainLabel


這裡需要注意的是,在原有自變數的基礎上,需要主觀新增一個均為1的偏移量,即公式中的x0。原始資料的前n-1列再加上新增的偏移量組成自變數trainData,最後一列為因變數trainLabel。
下面開始實現批處理梯度下降演算法:

def batchGradientDescent(x, y, theta, alpha, m, maxIterations):
    xTrains = x.transpose()
    for i in range(0, maxIterations):
        hypothesis = np.dot(x, theta)
        loss = hypothesis - y
        gradient = np.dot(xTrains, loss) / m
        theta = theta - alpha * gradient
    return theta

x為自變數訓練集,y為自變數對應的因變數訓練集;theta為待求解的權重值,需要事先進行初始化;alpha是學習率;m為樣本總數;maxIterations為最大迭代次數;
求解權重過程,初始化batchGradientDescent函式需要的各個引數:
trainData, trainLabel = getData(dataSet)
m, n = np.shape(trainData)
theta = np.ones(n)
alpha = 0.05
maxIteration = 1000
alpha和maxIterations可以更改,之後帶入到batchGradientDescent中可以求出最終權重值。
theta = batchGradientDescent(trainData, trainLabel, theta, alpha, m, maxIteration)
之後我們給出一組資料,需要進行預測,預測函式:
def predict(x, theta):
    m, n = np.shape(x)
    xTest = np.ones((m, n+1))
    xTest[:, :-1] = x
    yPre = np.dot(xTest, theta)
    return yPre

x為待預測值的自變數,thta為已經求解出的權重值,yPre為預測結果
我們給出測試集


對該組資料進行預測,程式如下:

x = np.array([[3.1, 5.5], [3.3, 5.9], [3.5, 6.3], [3.7, 6.7], [3.9, 7.1]])
print predict(x, theta)

輸出結果如下:

[9.49608552  10.19523475  10.89438398  11.59353321  12.29268244]

我們可以更改學習率和迭代次數進行預測結果的對比:
更改學習率由0.05變為0.1時,結果為:
[ 9.49997917  10.19997464  10.89997012  11.59996559  12.29996106]
發現預測結果要由於學習率為0.05時,這說明學習率0.05選擇的偏小,即每一次邁步偏小。
固定學習率為0.05,更改迭代次數為5000時,結果為:
[ 9.5  10.2  10.9  11.6  12.3]

這正是我們想要的預測結果,這說明有限迴圈次數內,迴圈次數越多,越接近真實值。但是也不能無限迴圈下去,需要尋找一個度。

一般達到以下的任意一種情況即可以停止迴圈:
1.權重的更新低於某個閾值;
2.預測的錯誤率低於某個閾值;
3.達到預設的最大迴圈次數;
其中達到任意一種,就停止演算法的迭代迴圈,得出最終結果。
完整的程式如下:

#coding=utf-8
 
import numpy as np
import random
from numpy import genfromtxt
 
def getData(dataSet):
    m, n = np.shape(dataSet)
    trainData = np.ones((m, n))
    trainData[:,:-1] = dataSet[:,:-1]
    trainLabel = dataSet[:,-1]
    return trainData, trainLabel
 
def batchGradientDescent(x, y, theta, alpha, m, maxIterations):
    xTrains = x.transpose()
    for i in range(0, maxIterations):
        hypothesis = np.dot(x, theta)
        loss = hypothesis - y
        # print loss
        gradient = np.dot(xTrains, loss) / m
        theta = theta - alpha * gradient
    return theta
 
def predict(x, theta):
    m, n = np.shape(x)
    xTest = np.ones((m, n+1))
    xTest[:, :-1] = x
    yP = np.dot(xTest, theta)
    return yP
 
dataPath = r"E:\learning\house.csv"
dataSet = genfromtxt(dataPath, delimiter=',')
trainData, trainLabel = getData(dataSet)
m, n = np.shape(trainData)
theta = np.ones(n)
alpha = 0.1
maxIteration = 5000
theta = batchGradientDescent(trainData, trainLabel, theta, alpha, m, maxIteration)
x = np.array([[3.1, 5.5], [3.3, 5.9], [3.5, 6.3], [3.7, 6.7], [3.9, 7.1]])
print predict(x, theta)