初學線性迴歸

線性迴歸是機器學習中的一個非常重要的算法,一般用來進行數據的擬合或者預測,同時還可以用來特徵重要性評估。

線性迴歸一般分爲一元線性迴歸(一個x,一個y)、多元線性迴歸(多個x,一個y):

        例如    :y=a*x+b,這就是一元函數

                      y=a*x1+b*x2+c ,這就是多元線性迴歸


在這裏我只講述一元的線性迴歸,多元的類型,我會在後面提及

首先,我們這裏有一堆數據


所謂一元線性迴歸要做的事情,實際上是去找一條最優直線,能夠以最小的誤差去擬合這些點。

這裏寫圖片描述 

如上圖所示,橫座標表示x,縱座標表示y。我們要找的就是圖中的這條直線。我們要去找到這條直線,大家可以想象,我們肯定希望找到的那條線,距離每個點都很近,最好所有的點上都在這條線上,但是一條直線去擬合所有的點都在這條直線上肯定不現實,所以我們希望這些點儘量離這條直線近一點。即去找每個點和直線的距離 這裏寫圖片描述最小的那條線,一般我們有幾個確定參數的方法:

  • 使得誤差的最大絕對值達到最小
  • 使誤差絕對值的平均值達到最小
  • 使誤差的平方和達到最小

第一種方法中,所確定的參數使得最大誤差達到最小,因而稱爲最大最小問題,也稱爲最優一致逼近問題。這類問題在求解上存在一定的困難

第二種方法中,由於函數關於參數不可微,因此不能用一般的極值方法求最優解

第三中方法中,所確定的參數使得均方差最小,這種逼近稱爲最小二乘逼近,此時成y=mx+b爲最小二乘擬合直線,並且由於函數關於參數連續可微,所以計算上易於處理。在這裏,我們採用第三種方法,那麼誤差(代價函數)可以表示爲這裏寫圖片描述,這裏i表示第i個數據,N表示總的樣本個數。一般我們還會把Loss求和平均,來當作最終的損失,即:

 

怎麼求極值呢?

令每個變量的偏導數爲零,求方程組的解唄,這個是很基礎的高數問題了。 
我們可以得到下面的方程組 
這裏寫圖片描述 
這裏寫圖片描述 
然後就是巴拉巴拉巴拉把m和b求出來,這樣就得到我們要的線性方程了。

梯度下降法

沒有梯度下降就沒有現在的深度學習,這是一個神奇的算法。 
最小二乘法可以一步到位,直接算出m和b,但他是有前提的,具體我有點記不清了,好像是需要滿秩什麼的。梯度下降法和最小二乘不一樣,它通過一步一步的迭代,慢慢的去靠近到那條最優直線。 
最小二乘法裏面我們提到了兩個偏導數,分別爲 
這裏寫圖片描述 
這裏寫圖片描述 
我們要去找Loss這個方程的最小值,最小值怎麼求?按數學的求法就是最小二乘法唄,但是大家可以直觀的想一下,很多地方都會用一個碗來形容,那我也找個碗來解釋吧。 
這裏寫圖片描述 
大家把這個Loss函數想象成這個碗,而我們要求的最小值就是碗底。假設我們現在不能用最小二乘法求極小值,但是我們的計算機的計算能量很強,我們可以用計算量換結果,不管我們位於這個碗的什麼位置,只要我們想去碗底,就要往下走。 
往下走???????? 
這個下不就是往梯度方向走嗎,那我們沿着梯度一點一點滑下去唄,反正計算機不嫌累。梯度不就是上面那兩個公式唄。現在梯度有了,那每次滑多遠呢,一滑劃過頭了不久白算半天了嗎,所以還得定義步長,用來表示每次滑多長。這樣我們就能每次向下走一點點,再定義一個迭代值用來表示滑多少次,這樣我們就能慢慢的一點點的靠近最小值了,不出意外還是能距離最優值很近的。

順便把上面這個梯度下降法實現下

每次向下滑要慢慢滑,就是要個步長,我們定義爲learning_rate,往往很小的一個值。

向下滑動的次數,就是迭代的次數,我定義爲num_iter,相對learning_rate往往很大。

定義好這兩個,我們就可以一邊求梯度,一邊向下滑了。就是去更新m和b。 
這裏寫圖片描述 
這裏寫圖片描述 

我這裏用了加號,很多人會誤以爲梯度下降就要減,但是其實梯度本身是有方向的,所以這裏直接加就可以。

整體代碼如下:

import numpy
import pylab
import math
#生成數據集
def createData():
    dataSet=numpy.loadtxt('DataSet/Data.txt') #讀取Data.txt文件
    dataSet=dataSet.astype(dtype=float)  #轉化成float類型
    return dataSet

def line_regression(dataSet):      #目標函數設爲   y=ax+b
    learn_rate=0.001    #學習速率
    init_a=0     #a初始化爲0
    init_b=0     #b初始化爲0
    iter_count=1000 #迭代次數
    a,b=optimization(dataSet,init_a,init_b,learn_rate,iter_count)  #尋找最優的參數a、b,使得擬合效果最佳
    print(a,b)
    plot_data(dataSet, a, b)

def plot_data(data,a,b):

    #plottting
    x = data[:,0]
    y = data[:,1]
    y_predict = a*x+b
    pylab.plot(x,y,'o')
    #pylab.plot(x,y_predict,'k-')
    pylab.show()


def optimization(dataSet,init_a,init_b,learn_rate,iter_count):
    a=init_a
    b=init_b
    for i in numpy.arange(0,iter_count): #迭代次數
        a,b=compute_gradient(a,b,dataSet,learn_rate)
        if(i%100==0):
            print('iter {0}:error={1}'.format(i, compute_error(a, b, dataSet)))
    return (a,b)

def compute_error(a,b,dataSet):
    sum=0
    for i in numpy.arange(len(dataSet)):
        x=dataSet[i,0]
        y=dataSet[i,1]
        y_predict=a*x+b
        sum+=math.pow(y-y_predict,2)
    return sum/len(dataSet)


def compute_gradient(a,b,dataSet,learn_rata):
    a_gradient = 0 #a的偏導數
    b_gradient = 0 #b的偏導數

    N = float(len(dataSet))
    # Two ways to implement this
    # first way
    for i in range(0,len(dataSet)):  #循環每一對數據,求a,b的偏導數
        x = dataSet[i,0]
        y = dataSet[i,1]

        #computing partial derivations of our error function
        #b_gradient = -(2/N)*sum((y-(m*x+b))^2)
        #m_gradient = -(2/N)*sum(x*(y-(m*x+b))^2)
        a_gradient += -(2 / N) * x * (y - ((a * x) + b))
        b_gradient += -(2/N)*(y-((a*x)+b))

    # Vectorization implementation
    # x = dataSet[:, 0]
    # y = dataSet[:, 1]
    # b_gradient = -(2 / N) * (y - m_current * x - b_current)
    # b_gradient = np.sum(b_gradient, axis=0)
    # m_gradient = -(2 / N) * x * (y - m_current * x - b_current)
    # m_gradient = np.sum(m_gradient, axis=0)
    # update our b and m values using out partial derivations
    #print(a_gradient,b_gradient)
    new_a = a - (learn_rata * a_gradient)  #如果a_gradient>0,說明在a點是遞增的,所以按照梯度下降原理,我們要往相反方向走,所以要減
                                          #如果a_gradient<0,說明在a點是遞減的,所以按照梯度下降原理,要按照這個方向走下去,所以減去一個
                                            #負值,相當於加上一個正值
    new_b = b - (learn_rata * b_gradient)  #原理同上
    return [new_a, new_b]

if __name__=="__main__":
    dataSet=createData()
    line_regression(dataSet)




發佈了34 篇原創文章 · 獲贊 80 · 訪問量 15萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章