參考文章1菠红,參考文章2,參考文章3
Logistic Regression Classifier邏輯回歸主要思想就是用最大似然概率方法構(gòu)建出方程柜去,為了最大化方程,利用牛頓梯度上升求解方程參數(shù)拆宛。
優(yōu)點(diǎn):計(jì)算代價(jià)不高嗓奢,易于理解和實(shí)現(xiàn)股耽。
缺點(diǎn):容易欠擬合钳幅,分類精度可能不高。
使用數(shù)據(jù)類型:數(shù)值型和標(biāo)稱型數(shù)據(jù)茬末。
0x01. 引入似然函數(shù)
邏輯回歸跟最大似然關(guān)系很大盖矫,那什么是最大似然呢辈双?
最大似然就是:最大可能嘛~就是根據(jù)已知的數(shù)據(jù)來(lái)推斷最大可能的參數(shù)湃望,比如假設(shè)我們已知高中生的身高符合高斯分布,因此我們隨機(jī)抽樣100個(gè)學(xué)生的身高瞳浦,由于是隨機(jī)抽的废士,因此可以認(rèn)為這些學(xué)生之間是沒(méi)有關(guān)系的官硝,是獨(dú)立的,因此我同時(shí)抽到這100個(gè)學(xué)生的概率就是這100個(gè)樣本的聯(lián)合概率了:
所以,我們就只需要找到一個(gè)參數(shù)θ宣旱,其對(duì)應(yīng)的似然函數(shù)L(θ)最大响鹃,也就是說(shuō)抽到這100個(gè)男生(的身高)概率最大案训。這個(gè)叫做θ的最大似然估計(jì)量强霎,記為:
然后求導(dǎo)并取0随闺,求出參數(shù)就是最佳的參數(shù)值。
0x02. 遷移之邏輯回歸&最大似然
我們從里面抓3個(gè)球龄句,2個(gè)黑球分歇,1個(gè)白球笨使。這時(shí)候硫椰,有人就直接得出了黑球67%,白球占比33%蹄胰。這個(gè)時(shí)候裕寨,其實(shí)這個(gè)人使用了最大似然概率的思想派继,通俗來(lái)講驾窟,當(dāng)黑球是67%的占比的時(shí)候绅络,我們抓3個(gè)球嘁字,出現(xiàn)2黑1白的概率最大纪蜒。我們直接用公式來(lái)說(shuō)明此叠。
假設(shè)黑球占比為P拌蜘,白球?yàn)?-P简卧。于是我們要求解MAX(PP(1-P))烤芦,顯而易見P=67%時(shí)是最有可能得到目前的這個(gè)結(jié)果的(求解方法:對(duì)方程求導(dǎo)构罗,使導(dǎo)數(shù)為0的P值即為最優(yōu)解)
對(duì)比邏輯回歸是不是就是一個(gè)二分類的問(wèn)題遂唧,是不是跟上面的黑白球分類問(wèn)題很像?
假設(shè)我們有n個(gè)獨(dú)立的訓(xùn)練樣本{(x1, y1) ,(x2, y2),…, (xn, yn)}纹烹,y={0, 1}铺呵。那每一個(gè)觀察到的樣本(xi, yi)出現(xiàn)的概率是:就知道了腥寇。注:有xi的時(shí)候觅捆,表示它是第i個(gè)樣本栅炒,下面沒(méi)有做區(qū)分了赢赊,相信你的眼睛是雪亮的),得到:
這時(shí)候,用L(θ)對(duì)θ求導(dǎo)糜俗,得到:
然后我們令該導(dǎo)數(shù)為0悠抹,你會(huì)很失望的發(fā)現(xiàn)楔敌,它無(wú)法解析求解卵凑。不信你就去嘗試一下。所以沒(méi)辦法了伙判,只能借助高大上的迭代來(lái)搞定了澳腹。這里選用了經(jīng)典的梯度下降算法酱塔。
0x03. 優(yōu)化求解 (link)
梯度下降
Gradient descent 又叫 steepest descent危虱,是利用一階的梯度信息找到函數(shù)局部最優(yōu)解的一種方法埃跷,也是機(jī)器學(xué)習(xí)里面最簡(jiǎn)單最常用的一種優(yōu)化方法弥雹。它的思想很簡(jiǎn)單剪勿,和我開篇說(shuō)的那樣,要找最小值酱固,我只需要每一步都往下走(也就是每一步都可以讓代價(jià)函數(shù)小一點(diǎn))运悲,然后不斷的走班眯,那肯定能走到最小值的地方寄纵,例如下圖所示:
但定踱,我同時(shí)也需要更快的到達(dá)最小值啊恃鞋,怎么辦呢?我們需要每一步都找下坡最快的地方肴楷,也就是每一步我走某個(gè)方向赛蔫,都比走其他方法泥张,要離最小值更近媚创。而這個(gè)下坡最快的方向钞钙,就是梯度的負(fù)方向了。
對(duì)logistic Regression來(lái)說(shuō)瘫怜,梯度下降算法新鮮出爐宝磨,如下:
其中,參數(shù)α叫學(xué)習(xí)率别瞭,就是每一步走多遠(yuǎn)蝙寨,這個(gè)參數(shù)蠻關(guān)鍵的墙歪。如果設(shè)置的太多虹菲,那么很容易就在最優(yōu)值附加徘徊掉瞳,因?yàn)槟悴椒ヌ罅恕@缫獜膹V州到上海址愿,但是你的一步的距離就是廣州到北京那么遠(yuǎn)响谓,沒(méi)有半步的說(shuō)法娘纷,自己能邁那么大步失驶,是幸運(yùn)呢枣购?還是不幸呢棉圈?事物總有兩面性嘛分瘾,它帶來(lái)的好處是能很快的從遠(yuǎn)離最優(yōu)值的地方回到最優(yōu)值附近德召,只是在最優(yōu)值附近的時(shí)候上岗,它有心無(wú)力了。但如果設(shè)置的太小敬锐,那收斂速度就太慢了台夺,向蝸牛一樣颤介,雖然會(huì)落在最優(yōu)的點(diǎn)买窟,但是這速度如果是猴年馬月始绍,我們也沒(méi)這耐心啊。所以有的改進(jìn)就是在這個(gè)學(xué)習(xí)率這個(gè)地方下刀子的学赛。我開始迭代是盏浇,學(xué)習(xí)率大绢掰,慢慢的接近最優(yōu)值的時(shí)候滴劲,我的學(xué)習(xí)率變小就可以了班挖。所謂采兩者之精華跋糗健双揪!
梯度下降:
初始化回歸系數(shù)為1
重復(fù)下面步驟直到收斂{
計(jì)算整個(gè)數(shù)據(jù)集的梯度
使用alpha x gradient來(lái)更新回歸系數(shù)
}
返回回歸系數(shù)值
隨機(jī)梯度下降:
初始化回歸系數(shù)為1
重復(fù)下面步驟直到收斂{
對(duì)數(shù)據(jù)集中每個(gè)樣本
計(jì)算該樣本的梯度
使用alpha xgradient來(lái)更新回歸系數(shù)
}
返回回歸系數(shù)值
改進(jìn)的隨機(jī)梯度下降:
初始化回歸系數(shù)為1
重復(fù)下面步驟直到收斂{
對(duì)隨機(jī)遍歷的數(shù)據(jù)集中的每個(gè)樣本
隨著迭代的逐漸進(jìn)行盟榴,減小alpha的值
計(jì)算該樣本的梯度
使用alpha x gradient來(lái)更新回歸系數(shù)
}
返回回歸系數(shù)值
0x04. 另一種解釋 (link)
其實(shí)為某種形式的回歸建立數(shù)學(xué)模型并不是一件容易的事情,經(jīng)過(guò)先烈的曲折探索几莽,得出了一個(gè)神奇的公式章蚣,稱為logit公式:
誒纤垂?看似簡(jiǎn)潔,然而有什么用呢峭沦?里面既沒(méi)有X也沒(méi)有y呀贾虽。。吼鱼。
先等等蓬豁,還記得深度學(xué)習(xí)中經(jīng)常加在神經(jīng)網(wǎng)絡(luò)的頂層來(lái)求后驗(yàn)概率P(y=j|X)的softmax函數(shù)嗎?對(duì)就是下面這個(gè)熟悉的函數(shù):
對(duì)于我們的二分類問(wèn)題來(lái)說(shuō)菇肃,有P(y=0|X)+P(y=1|X)=1地粪,那么如果我們令logit公式中的Q=P(y=0|X)呢琐谤?然后P(y=0|X)用softmax函數(shù)表示呢蟆技?是不是突然被下面推導(dǎo)的過(guò)程和結(jié)果驚呆了!6芳伞V世瘛:
而xTΔw的值不就是反映感知機(jī)模型的輸出嘛!(即xTΔw>0則預(yù)測(cè)類別為正飞蹂,xTΔw<0則預(yù)測(cè)類別為負(fù))
我們?cè)侔褁TΔw整理的好看一點(diǎn)几苍,變成更正常的形式:w·x+b。然后就可以得到下面的結(jié)論3卵啤F薨印!:
這就是我們前面苦苦尋找的邏輯回歸模型惊窖!看刽宪,隨機(jī)變量X與隨機(jī)變量Y的關(guān)系竟然直接納入了一個(gè)模型下面!也就是說(shuō)后驗(yàn)概率直接用隨機(jī)變量X表示了出來(lái)界酒!而不是像貝葉斯定理一樣間接表示后驗(yàn)概率圣拄。
有了上面直接表示的后驗(yàn)概率,于是建立似然函數(shù)毁欣,通過(guò)極大似然估計(jì)來(lái)確定模型的參數(shù)庇谆。因此設(shè):
似然函數(shù)就表示為
對(duì)數(shù)似然函數(shù)即:
也就是本文的“淺入”環(huán)節(jié)的損失函數(shù)啦,原來(lái)是正兒八經(jīng)的一步步推出來(lái)的凭疮!剩下的就交給梯度下降法優(yōu)化出模型參數(shù)吧饭耳!
from numpy import *
filename='...\\testSet.txt' #文件目錄
def loadDataSet(): #讀取數(shù)據(jù)(這里只有兩個(gè)特征)
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,表示方程的常量执解。比如兩個(gè)特征X1,X2寞肖,共需要三個(gè)參數(shù),W1+W2*X1+W3*X2
labelMat.append(int(lineArr[2]))
return dataMat,labelMat
def sigmoid(inX): #sigmoid函數(shù)
return 1.0/(1+exp(-inX))
def gradAscent(dataMat, labelMat): #梯度上升求最優(yōu)參數(shù)
dataMatrix=mat(dataMat) #將讀取的數(shù)據(jù)轉(zhuǎn)換為矩陣
classLabels=mat(labelMat).transpose() #將讀取的數(shù)據(jù)轉(zhuǎn)換為矩陣
m,n = shape(dataMatrix)
alpha = 0.001 #設(shè)置梯度的閥值,該值越大梯度上升幅度越大
maxCycles = 500 #設(shè)置迭代的次數(shù)新蟆,一般看實(shí)際數(shù)據(jù)進(jìn)行設(shè)定觅赊,有些可能200次就夠了
weights = ones((n,1)) #設(shè)置初始的參數(shù),并都賦默認(rèn)值為1琼稻。注意這里權(quán)重以矩陣形式表示三個(gè)參數(shù)吮螺。
for k in range(maxCycles):
h = sigmoid(dataMatrix*weights)
error = (classLabels - h) #求導(dǎo)后差值
weights = weights + alpha * dataMatrix.transpose()* error #迭代更新權(quán)重
return weights
def stocGradAscent0(dataMat, labelMat): #隨機(jī)梯度上升,當(dāng)數(shù)據(jù)量比較大時(shí)欣簇,每次迭代都選擇全量數(shù)據(jù)進(jìn)行計(jì)算规脸,計(jì)算量會(huì)非常大。所以采用每次迭代中一次只選擇其中的一行數(shù)據(jù)進(jìn)行更新權(quán)重熊咽。
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): #遍歷計(jì)算每一行
h = sigmoid(sum(dataMatrix[i] * weights))
error = classLabels[i] - h
weights = weights + alpha * error * dataMatrix[i].transpose()
return weights
def stocGradAscent1(dataMat, labelMat): #改進(jìn)版隨機(jī)梯度上升莫鸭,在每次迭代中隨機(jī)選擇樣本來(lái)更新權(quán)重,并且隨迭代次數(shù)增加横殴,權(quán)重變化越小被因。
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): #隨機(jī)遍歷每一行
alpha=4/(1+j+i)+0.0001 #隨迭代次數(shù)增加,權(quán)重變化越小衫仑。
randIndex=int(random.uniform(0,len(dataIndex))) #隨機(jī)抽樣
h=sigmoid(sum(dataMatrix[randIndex]*weights))
error=classLabels[randIndex]-h
weights=weights+alpha*error*dataMatrix[randIndex].transpose()
del(dataIndex[randIndex]) #去除已經(jīng)抽取的樣本
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()