1.引言:
看了Stanford的Andrew Ng老師的機器學習公開課中關於Logistic Regression的講解寫下此篇學習筆記總結一下。
2.原理:
按照我們老師的話說,做機器學習無非是三個重點:取模型,選代價函數,找極值
取模型:
找一個合適的預測函數(Andrew Ng的公開課中稱爲hypothesis),一般表示爲h函數,該函數就是我們選取的模型,它用來預測輸入數據的判斷結果。這個過程時非常關鍵的,需要對數據有一定的瞭解或分析,知道或者猜測預測函數的“大概”形式,比如是線性函數還是非線性函數。如果對於一組數據,我們找不到相應的模型來描述,那麼我們無法進行下去。
線性模型就是預測的的形式,這種形式可以用矩陣表示,即Y=θX+C(其中θ,X都爲矩陣)
非線性模型就是出現了x的多次方
選代價函數:代價函數就是能表示樣本的預測值h和實際值y 差值得一個函數,
代價函數有cost函數和J函數兩種說法
cost函數主要是單個樣本的差值,J函數就是對所有樣本的Cost進行求和或取平均值
因爲我們說的代價是所有樣本的,所以J函數纔是我們的代價函數
找極值:顯然,J(θ)函數的值越小表示預測函數越準確(即h函數越準確),所以這一步需要做的是找到能使J(θ)函數的最小的θ值。我們選取的算法幾乎是梯度下降算法或者一些變換形式,可以說沒有梯度下降就沒有現在的深度學習,這是一個神奇的算法。但是缺陷在於只能找到極小值,這要求我們在J函數的選取是要注意J函數只有一個極小值(即要求J函數能收斂)
3.具體實現過程:
找預測函數:
假設我們來預測房價和麪積的關係:
我們可以畫出一條綠色的直線來描述這個關係,這臺直線的函數式即爲,這個即爲我們的假設函數h,爲了區別於實際的房價值,我們把我們通過h函數預測出來的y值稱爲h。
取代價函數:(m是樣本數)
我們肯定希望找到的那條線,距離每個點都很近,最好所有的點上都在這條線上,但是一條直線去擬合所有的點都在這條直線上肯定不現實,所以我們希望這些點儘量離這條直線近一點。
所以我們會想到垂直距離,但是垂直距離的公式比較複雜,所謂我們選取了作爲我們的cost函數,這在圖形上的意義即是豎直距離,當cost函數越小時,這條直線離這個點越近,當cost=0時,點就在直線上。
然後我們再取所有樣本的平均值(再除以2的原因是求導抵消了,這個就相當於常數c,),
那麼我們的總誤差即J函數爲:
找極值:
首先我們要明確找的是什麼,我們要找的即使J函數最小的一組θ值,在這個問題中,即爲θ0,θ1.
所以我們的步驟爲:
1.隨機開始一組theta值,我們一般取0或1之類的
2.利用梯度下降算法改變theta的值直到一個合適的值(即迭代)
展開即爲
首先來講一下梯度下降算法(有一些博客也叫梯度上升,因爲他們求導後把負號號移出去了):
不管我們位於這個什麼位置,只要我們想走到極小點,就要往梯度方向(導數)的反方向走,假如我們一開始在左邊的圓點處,那我們一點一點往右邊滑下去唄,即爲.
α爲步長,這個控制我們每次移動的距離
我們重複這個過程(),就能夠到達極小點附近(而不是最小點)
大多數同學會有疑問,如果越過了最低點怎麼樣:即使越過了最低點,我們對該點求導,發現該店的導數爲正,減去即爲往左邊移動。而且一般α取得合理就不會越過,因爲越到下面導數越小,就像2-1-(1/2)-(1/4)-(1/8)-......-(1/2^n)一樣永遠大於0
接下來講下α的作用和怎麼選取合適的α:
如左圖所示,首先我們隨機選取了θ1爲靠近最小值的圓點,假如我們選取了一個較大的α,那麼我們減去了一個比較大的值,導致我們第一次移動到了最小值的右邊,我們求導發現該點的導數更大,這就導致了我們第二次移動到原來那個點的左邊,然後該點的導數更大,這就導致了每一次迭代不是想更小的移動,而是像更大的移動,這樣就到不了最小值了。
當然α也不是越小越好,太小了,移動的太慢了,迭代500次可能還沒有算出來,一個合適的α迭代100後就很接近極小值了
我們一般選取α爲0.01,但是對於不同的數據,不同的代價函數,這個值會不一樣,我們可以通過30倍的縮放來改變這個值
爲什麼梯度下降算法要求J函數只有一個極小值:
對於以上的J函數,假如我們選取不同的初始值θ0和θ1,那麼我們將會到達不同的極小值,最後我們怎麼確定哪個最小呢,有多少個最小的呢,所以我們在選取J函數的時候就要確保J函數只有一個極小值,即下面這樣的圖形
最後就直接上代碼:
import numpy as np
import pylab
import json
def compute_error(b,m,data):
"""計算代價函數"""
cost=0
x=data[0]
y=data[1]
x=np.array(x)
y=np.array(y)
num=float(len(data))
cost=(m*x+b-y)**2
cost=np.sum(cost,axis=0)
cost=cost/(2*num)
return cost
def optimizer(data,starting_b,starting_m,learning_rate,num_iter):
"""梯度下降算法"""
b=starting_b
m=starting_m
for i in range(num_iter):
b,m=compute_gradient(b,m,data,learning_rate)
if i%100==0:
print(str(i)+" error:"+str(compute_error(b,m,data)))
plot_data(data,b,m)
return [b,m]
def compute_gradient(b_current,m_current,data,learning_rate):
"""每一次的迭代"""
b_gradient = 0
m_gradient = 0
num=float(len(data))
# x=data[:,0]
# y=data[:,1]
x=data[0]
y=data[1]
x=np.array(x)
y=np.array(y)
b_gradient=(m_current*x+b_current-y)*(1/num)#先前無法收斂在於偏導b和m的偏導寫反了
b_gradient=np.sum(b_gradient,axis=0)
m_gradient=(m_current*x+b_current-y)*x*(1/num)
m_gradient=np.sum(m_gradient,axis=0)
m_new=m_current-(learning_rate*m_gradient)
b_new=b_current-(learning_rate*b_gradient)
return [b_new,m_new]
def plot_data(data,b,m):
""""畫曲線"""
x=data[0]
y=data[1]
x=np.array(x)
y=np.array(y)
y_predict = m*x+b
pylab.plot(x,y,'o')#原來的元素
pylab.plot(x,y_predict,'k-')#預測的元素
pylab.show()
def linear_regression():
starting_b=0.0
starting_m=0.0
learning_rate=0.00001#學習步數太小擬合慢,太大就會不能convergence
#數據的大小會決定α,這次數據擴大了100倍,使用α要縮小100倍,標準化也行
num_iter=500
x=[63.030301,25.60160963,84.07966543,33.21034955,80.36386821,83.07007084,73.82586473,91.57007003,10.92153202,84.7031311,24.39083948,61.10565706,56.9543926,87.40964666,71.43630822,66.23807235,12.34436146,93.93842171,14.53165794,78.2440026]
y=[63.87392259,26.29752231,85.14718359,34.06340079,81.33633071,83.85031798,75.08598548,92.85616524,12.43963086,85.56767789,25.40299684,62.18816348,57.7284949,88.24736608,72.85552885,66.93011168,13.14443623,95.47745258,15.50435747,79.81954144]
data=[x,y]
print("first error:"+str(compute_error(starting_b,starting_m,data)))
[b,m]=optimizer(data,starting_b,starting_m,learning_rate,num_iter)
print("last error:"+str(compute_error(b,m,data)))
print(str(m))
print(str(b))
plot_data(data,b,m)
linear_regression()