參考文章1,參考文章2,參考文章3
Logistic Regression Classifier邏輯回歸主要思想就是用最大似然概率方法構建出方程,為了最大化方程,利用牛頓梯度上升求解方程參數。
優點:計算代價不高,易于理解和實現。
缺點:容易欠擬合,分類精度可能不高。
使用數據類型:數值型和標稱型數據。
0x01. 引入似然函數
邏輯回歸跟最大似然關系很大,那什么是最大似然呢?
最大似然就是:最大可能嘛~就是根據已知的數據來推斷最大可能的參數,比如假設我們已知高中生的身高符合高斯分布,因此我們隨機抽樣100個學生的身高,由于是隨機抽的,因此可以認為這些學生之間是沒有關系的,是獨立的,因此我同時抽到這100個學生的概率就是這100個樣本的聯合概率了:
所以,我們就只需要找到一個參數θ,其對應的似然函數L(θ)最大,也就是說抽到這100個男生(的身高)概率最大。這個叫做θ的最大似然估計量,記為:
然后求導并取0,求出參數就是最佳的參數值。
0x02. 遷移之邏輯回歸&最大似然
我們從里面抓3個球,2個黑球,1個白球。這時候,有人就直接得出了黑球67%,白球占比33%。這個時候,其實這個人使用了最大似然概率的思想,通俗來講,當黑球是67%的占比的時候,我們抓3個球,出現2黑1白的概率最大。我們直接用公式來說明。
假設黑球占比為P,白球為1-P。于是我們要求解MAX(PP(1-P)),顯而易見P=67%時是最有可能得到目前的這個結果的(求解方法:對方程求導,使導數為0的P值即為最優解)
對比邏輯回歸是不是就是一個二分類的問題,是不是跟上面的黑白球分類問題很像?
假設我們有n個獨立的訓練樣本{(x1, y1) ,(x2, y2),…, (xn, yn)},y={0, 1}。那每一個觀察到的樣本(xi, yi)出現的概率是:就知道了。注:有xi的時候,表示它是第i個樣本,下面沒有做區分了,相信你的眼睛是雪亮的),得到:
這時候,用L(θ)對θ求導,得到:
然后我們令該導數為0,你會很失望的發現,它無法解析求解。不信你就去嘗試一下。所以沒辦法了,只能借助高大上的迭代來搞定了。這里選用了經典的梯度下降算法。
0x03. 優化求解 (link)
梯度下降
Gradient descent 又叫 steepest descent,是利用一階的梯度信息找到函數局部最優解的一種方法,也是機器學習里面最簡單最常用的一種優化方法。它的思想很簡單,和我開篇說的那樣,要找最小值,我只需要每一步都往下走(也就是每一步都可以讓代價函數小一點),然后不斷的走,那肯定能走到最小值的地方,例如下圖所示:
但,我同時也需要更快的到達最小值啊,怎么辦呢?我們需要每一步都找下坡最快的地方,也就是每一步我走某個方向,都比走其他方法,要離最小值更近。而這個下坡最快的方向,就是梯度的負方向了。
對logistic Regression來說,梯度下降算法新鮮出爐,如下:
其中,參數α叫學習率,就是每一步走多遠,這個參數蠻關鍵的。如果設置的太多,那么很容易就在最優值附加徘徊,因為你步伐太大了。例如要從廣州到上海,但是你的一步的距離就是廣州到北京那么遠,沒有半步的說法,自己能邁那么大步,是幸運呢?還是不幸呢?事物總有兩面性嘛,它帶來的好處是能很快的從遠離最優值的地方回到最優值附近,只是在最優值附近的時候,它有心無力了。但如果設置的太小,那收斂速度就太慢了,向蝸牛一樣,雖然會落在最優的點,但是這速度如果是猴年馬月,我們也沒這耐心啊。所以有的改進就是在這個學習率這個地方下刀子的。我開始迭代是,學習率大,慢慢的接近最優值的時候,我的學習率變小就可以了。所謂采兩者之精華啊!
梯度下降:
初始化回歸系數為1
重復下面步驟直到收斂{
計算整個數據集的梯度
使用alpha x gradient來更新回歸系數
}
返回回歸系數值
隨機梯度下降:
初始化回歸系數為1
重復下面步驟直到收斂{
對數據集中每個樣本
計算該樣本的梯度
使用alpha xgradient來更新回歸系數
}
返回回歸系數值
改進的隨機梯度下降:
初始化回歸系數為1
重復下面步驟直到收斂{
對隨機遍歷的數據集中的每個樣本
隨著迭代的逐漸進行,減小alpha的值
計算該樣本的梯度
使用alpha x gradient來更新回歸系數
}
返回回歸系數值
0x04. 另一種解釋 (link)
其實為某種形式的回歸建立數學模型并不是一件容易的事情,經過先烈的曲折探索,得出了一個神奇的公式,稱為logit公式:
誒?看似簡潔,然而有什么用呢?里面既沒有X也沒有y呀。。。
先等等,還記得深度學習中經常加在神經網絡的頂層來求后驗概率P(y=j|X)的softmax函數嗎?對就是下面這個熟悉的函數:
對于我們的二分類問題來說,有P(y=0|X)+P(y=1|X)=1,那么如果我們令logit公式中的Q=P(y=0|X)呢?然后P(y=0|X)用softmax函數表示呢?是不是突然被下面推導的過程和結果驚呆了!!!:
而xTΔw的值不就是反映感知機模型的輸出嘛!(即xTΔw>0則預測類別為正,xTΔw<0則預測類別為負)
我們再把xTΔw整理的好看一點,變成更正常的形式:w·x+b。然后就可以得到下面的結論!!!:
這就是我們前面苦苦尋找的邏輯回歸模型!看,隨機變量X與隨機變量Y的關系竟然直接納入了一個模型下面!也就是說后驗概率直接用隨機變量X表示了出來!而不是像貝葉斯定理一樣間接表示后驗概率。
有了上面直接表示的后驗概率,于是建立似然函數,通過極大似然估計來確定模型的參數。因此設:
似然函數就表示為
對數似然函數即:
也就是本文的“淺入”環節的損失函數啦,原來是正兒八經的一步步推出來的!剩下的就交給梯度下降法優化出模型參數吧!
from numpy import *
filename='...\\testSet.txt' #文件目錄
def loadDataSet(): #讀取數據(這里只有兩個特征)
dataMat = []
labelMat = []
fr = open(filename)
for line in fr.readlines():
lineArr = line.strip().split()
dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])]) #前面的1,表示方程的常量。比如兩個特征X1,X2,共需要三個參數,W1+W2*X1+W3*X2
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
def sigmoid(inX): #sigmoid函數
return 1.0/(1+exp(-inX))
def gradAscent(dataMat, labelMat): #梯度上升求最優參數
dataMatrix=mat(dataMat) #將讀取的數據轉換為矩陣
classLabels=mat(labelMat).transpose() #將讀取的數據轉換為矩陣
m,n = shape(dataMatrix)
alpha = 0.001 #設置梯度的閥值,該值越大梯度上升幅度越大
maxCycles = 500 #設置迭代的次數,一般看實際數據進行設定,有些可能200次就夠了
weights = ones((n,1)) #設置初始的參數,并都賦默認值為1。注意這里權重以矩陣形式表示三個參數。
for k in range(maxCycles):
h = sigmoid(dataMatrix*weights)
error = (classLabels - h) #求導后差值
weights = weights + alpha * dataMatrix.transpose()* error #迭代更新權重
return weights
def stocGradAscent0(dataMat, labelMat): #隨機梯度上升,當數據量比較大時,每次迭代都選擇全量數據進行計算,計算量會非常大。所以采用每次迭代中一次只選擇其中的一行數據進行更新權重。
dataMatrix=mat(dataMat)
classLabels=labelMat
m,n=shape(dataMatrix)
alpha=0.01
maxCycles = 500
weights=ones((n,1))
for k in range(maxCycles):
for i in range(m): #遍歷計算每一行
h = sigmoid(sum(dataMatrix[i] * weights))
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i].transpose()
return weights
def stocGradAscent1(dataMat, labelMat): #改進版隨機梯度上升,在每次迭代中隨機選擇樣本來更新權重,并且隨迭代次數增加,權重變化越小。
dataMatrix=mat(dataMat)
classLabels=labelMat
m,n=shape(dataMatrix)
weights=ones((n,1))
maxCycles=500
for j in range(maxCycles): #迭代
dataIndex=[i for i in range(m)]
for i in range(m): #隨機遍歷每一行
alpha=4/(1+j+i)+0.0001 #隨迭代次數增加,權重變化越小。
randIndex=int(random.uniform(0,len(dataIndex))) #隨機抽樣
h=sigmoid(sum(dataMatrix[randIndex]*weights))
error=classLabels[randIndex]-h
weights=weights+alpha*error*dataMatrix[randIndex].transpose()
del(dataIndex[randIndex]) #去除已經抽取的樣本
return weights
def plotBestFit(weights): #畫出最終分類的圖
import matplotlib.pyplot as plt
dataMat,labelMat=loadDataSet()
dataArr = array(dataMat)
n = shape(dataArr)[0]
xcord1 = []; ycord1 = []
xcord2 = []; ycord2 = []
for i in range(n):
if int(labelMat[i])== 1:
xcord1.append(dataArr[i,1])
ycord1.append(dataArr[i,2])
else:
xcord2.append(dataArr[i,1])
ycord2.append(dataArr[i,2])
fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(xcord1, ycord1, s=30, c='red', marker='s')
ax.scatter(xcord2, ycord2, s=30, c='green')
x = arange(-3.0, 3.0, 0.1)
y = (-weights[0]-weights[1]*x)/weights[2]
ax.plot(x, y)
plt.xlabel('X1')
plt.ylabel('X2')
plt.show()
def main():
dataMat, labelMat = loadDataSet()
weights=gradAscent(dataMat, labelMat).getA()
plotBestFit(weights)
if __name__=='__main__':
main()