机器学习实战(五)——Logistic 回归
文章目錄
- Logistic 回歸
- 5.2 基于最優(yōu)化方法的最佳回歸系數(shù)確定
- 5.2.1 梯度上升法
- 5.3 python實(shí)戰(zhàn)
- 5.3.1 查看數(shù)據(jù)集分布情況
- 5.3.2 訓(xùn)練
- 5.3.3 繪制決策邊界
- 5.4 改進(jìn)的隨機(jī)梯度上升算法
- 5.4.1 隨機(jī)梯度上升算法代碼:
- 5.4.2 回歸系數(shù)與迭代次數(shù)的關(guān)系
- 5.5 示例:從疝氣病癥預(yù)測病馬的死亡率
- 5.5.1 準(zhǔn)備數(shù)據(jù)
- 5.5.2 使用python構(gòu)建Logistic回歸分類器
- 5.6 使用sklearn構(gòu)建Logistic回歸分類器
- 5.6.1 函數(shù)說明
- 5.6.2 使用sklearn構(gòu)建分類器
- 5.7 Logistic算法總結(jié)
- 5.7.1 優(yōu)缺點(diǎn)
- 5.8 系統(tǒng)介紹
- 5.8.1 邏輯斯蒂分布
- 5.8.2 二項邏輯回歸
- 5.8.3 邏輯回歸的正則化
- 5.8.4 為什么邏輯回歸比線性回歸好
- 5.8.5 其他降低過擬合的方法
- 5.8.6 LR和SVM的聯(lián)系與區(qū)別
Logistic 回歸
假設(shè)有一些數(shù)據(jù)點(diǎn),我們用一條直線對這些點(diǎn)進(jìn)行擬合(稱為最佳擬合直線),這個擬合的過程就叫做回歸。
Logistic 回歸的主要思想:根據(jù)現(xiàn)有數(shù)據(jù)對分類邊界線建立回歸公式,以此進(jìn)行分類。
回歸:回歸一詞源于最佳擬合,表示要找到最佳擬合參數(shù)集。
訓(xùn)練分類器:尋找最佳擬合參數(shù),使用的是最優(yōu)化算法。
###5.1 基于Logistic回歸和Sigmoid函數(shù)的分類
-
Logistic回歸是回歸的一種方法,它利用的是Sigmoid函數(shù)閾值在[0,1]這個特性。
-
Logistic回歸進(jìn)行分類的主要思想是:根據(jù)現(xiàn)有數(shù)據(jù)對分類邊界線建立回歸公式,以此進(jìn)行分類。其實(shí),Logistic本質(zhì)上是一個基于條件概率的判別模型(Discriminative Model)。
Logistic回歸的函數(shù)是sigmoid函數(shù),公式如下:
δ(z)=11+e?z\delta(z)=\frac{1}{1+e^{-z}}δ(z)=1+e?z1?
不同坐標(biāo)尺度下的兩條曲線圖,當(dāng)x為0時,sigmoid函數(shù)值為0.5,隨x的增大,函數(shù)值接近1,隨著x的減小,函數(shù)值接近0。
如何實(shí)現(xiàn)Logistic回歸分類器:
在每個特征上都乘以一個回歸系數(shù),然后將所有的結(jié)果值相加,將這個總和帶入sigmoid函數(shù)中,進(jìn)而得到一個范圍在0~1中的數(shù)值,任何大于0.5的數(shù)據(jù)被分入1類,小于0.5的被分入0類,所以Logistic回歸也可以被看成一種概率估計。
確定了分類器的函數(shù)形式之后,要確定最佳回歸系數(shù)
5.2 基于最優(yōu)化方法的最佳回歸系數(shù)確定
假設(shè)Sigmoid函數(shù)的輸入記為zzz,由下面的公式得出:
z=w0x0+w1x1+w2x2+...+wnxnz=w_0x_0+w_1x_1+w_2x_2+...+w_nx_nz=w0?x0?+w1?x1?+w2?x2?+...+wn?xn?如果采用向量的寫法,上述公式可以寫成z=wTxz=w^Txz=wTx,它表示將這兩個數(shù)值向量對應(yīng)元素相乘然后全部加起來即得到zzz值,其中的向量xxx是分類器的輸入數(shù)據(jù),向量www也就是我們要找到的最佳參數(shù)(系數(shù)),從而使得分類器盡可能的精確,為了尋找該最佳參數(shù),需要使用一些優(yōu)化理論的知識。
5.2.1 梯度上升法
基本思想:
要找到某函數(shù)的最大值,最好的方法是沿著該函數(shù)的梯度方向探尋,函數(shù)f(x,y)f(x,y)f(x,y)的梯度?\nabla?為:
?f(x,y)=[?f(x,y)?x?f(x,y)?y]\nabla f(x,y)= \left[ \begin{matrix} \frac{\partial f(x,y)}{\partial x} \\ \frac{\partial f(x,y)}{\partial y} \end{matrix} \right]?f(x,y)=[?x?f(x,y)??y?f(x,y)??]
梯度意味著要沿x的方向移動?f(x,y)?x\frac {\partial f(x,y)}{\partial x}?x?f(x,y)?,沿y的方向移動?f(x,y)?y\frac {\partial f(x,y)}{\partial y}?y?f(x,y)?,其中,f(x,y)f(x,y)f(x,y)必須要在待計算的點(diǎn)上有定義并且可微。
舉例如下:
上圖中的梯度上升算法沿梯度的方向移動了一步,可以看到,梯度算子總是指向函數(shù)值增長最快的方向,移動量的大小稱為步長,記作α\alphaα,用向量來表示的話,梯度算法的迭代公式如下:
w:=w+α?wf(w)w:=w+\alpha \nabla _wf(w)w:=w+α?w?f(w)
該公式一直被迭代執(zhí)行,直到達(dá)到某個停止條件為止,比如迭代次數(shù)達(dá)到某個指定值或算法達(dá)到某個可以允許的誤差范圍。
舉例說明:求函數(shù)極大值
""" 函數(shù)說明:梯度上升法測試函數(shù)求函數(shù)f(x)=-x^2 + 4x的極大值""" def Gradient_Ascent_test():def f_prime(x_old):return -2*x_old+4x_old=-1x_new=0alpha=0.01presision=0.00000001while abs(x_new-x_old)>presision:x_old=x_newx_new=x_old+alpha*f_prime(x_old)print(x_new)if __name__=='__main__':Gradient_Ascent_test()結(jié)果:
1.999999515279857結(jié)果已經(jīng)很接近真實(shí)值2了,這一個過程就是梯度上升算法。
5.3 python實(shí)戰(zhàn)
使用testSet.txt數(shù)據(jù)集,數(shù)據(jù)如下:
該數(shù)據(jù)有兩維特征,最后一列為分類標(biāo)簽,根據(jù)標(biāo)簽的不同,對其進(jìn)行分類
5.3.1 查看數(shù)據(jù)集分布情況
import matplotlib.pylab as plt import numpy as np""" 函數(shù)說明:加載數(shù)據(jù)""" def loadDataSet():# 創(chuàng)建數(shù)據(jù)列表dataMat=[]# 創(chuàng)建標(biāo)簽列表labelMat=[]# 打開文件fr=open('testSet.txt')# 逐行讀取for line in fr.readlines():# 去回車,放入列表lineArr=line.strip().split()# 添加數(shù)據(jù)dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])# 添加標(biāo)簽labelMat.append(int(lineArr[2]))# 關(guān)閉文件fr.close()return dataMat,labelMat""" 函數(shù)說明:繪制數(shù)據(jù)集 """ def plotDataSet():#加載數(shù)據(jù)集dataMat,labelMat=loadDataSet()#轉(zhuǎn)化成numpy的arraydataArr=np.array(dataMat)#數(shù)據(jù)個數(shù)n=np.shape(dataMat)[0]#正樣本xcord1=[];ycord1=[]#負(fù)樣本xcord2=[];ycord2=[]#根據(jù)數(shù)據(jù)集標(biāo)簽進(jìn)行分類for i in range(n):#1為正樣本if int(labelMat[i])==1:xcord1.append(dataArr[i,1])ycord1.append(dataArr[i,2])#0為負(fù)樣本else:xcord2.append(dataArr[i,1])ycord2.append(dataArr[i,2])fig=plt.figure()#添加subplotax=fig.add_subplot(111)#繪制正樣本ax.scatter(xcord1,ycord1,s=20,c='red',marker='s',alpha=.5)#繪制負(fù)樣本ax.scatter(xcord2,ycord2,s=20,c='green',alpha=.5)plt.title('DataSet')plt.xlabel('x')plt.ylabel('y')plt.show()if __name__=='__main__':plotDataSet()結(jié)果:
上圖為數(shù)據(jù)的分布情況,假設(shè)Sigmoid函數(shù)的輸入為z,則z=w0+w1x1+w2x2z=w_0+w_1x_1+w_2x_2z=w0?+w1?x1?+w2?x2?,即可將數(shù)據(jù)分隔開。其中x0x_0x0?為全1向量,x1x_1x1?為數(shù)據(jù)集的第一列數(shù)據(jù),x2x_2x2?為數(shù)據(jù)集的第二列數(shù)據(jù)。
令z=0z=0z=0,則0=w0+w1x1+w2x20=w_0+w_1x_1+w_2x_20=w0?+w1?x1?+w2?x2?,橫坐標(biāo)為x1x_1x1?,縱坐標(biāo)為x2x_2x2?,未知參數(shù)w0、w1、w2w_0、w_1、w_2w0?、w1?、w2?也就是需要求的回歸系數(shù)。
5.3.2 訓(xùn)練
import matplotlib.pylab as plt import numpy as np""" 函數(shù)說明:加載數(shù)據(jù)""" def loadDataSet():# 創(chuàng)建數(shù)據(jù)列表dataMat=[]# 創(chuàng)建標(biāo)簽列表labelMat=[]# 打開文件fr=open('testSet.txt')# 逐行讀取for line in fr.readlines():# 去回車,放入列表lineArr=line.strip().split()# 添加數(shù)據(jù)dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])# 添加標(biāo)簽labelMat.append(int(lineArr[2]))# 關(guān)閉文件fr.close()return dataMat,labelMat""" 函數(shù)說明:sigmoid函數(shù)""" def sigmoid(inX):return 1.0/(1+np.exp(-inX))""" 函數(shù)說明:梯度上升算法""" def gradAscent(dataMatIn,classLabels):#轉(zhuǎn)化成numpy的matdataMatrix=np.mat(dataMatIn)# 轉(zhuǎn)化成numpy的mat,并進(jìn)行轉(zhuǎn)置labelMat=np.mat(classLabels).transpose()#返回dataMatrix的大小,m為行,n為列m,n=np.shape(dataMatrix)#移動步長,也就是學(xué)習(xí)速率,控制參數(shù)更新的速度alpha=0.001#最大迭代次數(shù)maxCycles=500weights=np.ones((n,1))for k in range(maxCycles):#梯度上升矢量化公式h=sigmoid(dataMatrix*weights)error=labelMat-hweights=weights+alpha*dataMatrix.transpose()*error#將矩陣轉(zhuǎn)化為數(shù)組,并返回權(quán)重參數(shù)return weights.getA()if __name__=='__main__':dataMat,labelMat=loadDataSet()print(gradAscent(dataMat,labelMat))結(jié)果:
[[ 4.12414349][ 0.48007329][-0.6168482 ]]輸出結(jié)果即為回歸系數(shù)[w0,w1,w2][w_0,w_1,w_2][w0?,w1?,w2?]
通過求解參數(shù),可以確定不同類別數(shù)據(jù)之間的分割線,畫出決策邊界。
5.3.3 繪制決策邊界
繪制邊界程序如下:
import matplotlib.pylab as plt import numpy as np""" 函數(shù)說明:加載數(shù)據(jù)""" def loadDataSet():# 創(chuàng)建數(shù)據(jù)列表dataMat=[]# 創(chuàng)建標(biāo)簽列表labelMat=[]# 打開文件fr=open('testSet.txt')# 逐行讀取for line in fr.readlines():# 去回車,放入列表lineArr=line.strip().split()# 添加數(shù)據(jù)dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])# 添加標(biāo)簽labelMat.append(int(lineArr[2]))# 關(guān)閉文件fr.close()return dataMat,labelMat""" 函數(shù)說明:sigmoid函數(shù)""" def sigmoid(inX):return 1.0/(1+np.exp(-inX))""" 函數(shù)說明:梯度上升算法""" def gradAscent(dataMatIn,classLabels):#轉(zhuǎn)化成numpy的matdataMatrix=np.mat(dataMatIn)# 轉(zhuǎn)化成numpy的mat,并進(jìn)行轉(zhuǎn)置labelMat=np.mat(classLabels).transpose()#返回dataMatrix的大小,m為行,n為列m,n=np.shape(dataMatrix)#移動步長,也就是學(xué)習(xí)速率,控制參數(shù)更新的速度alpha=0.001#最大迭代次數(shù)maxCycles=500weights=np.ones((n,1))for k in range(maxCycles):#梯度上升矢量化公式h=sigmoid(dataMatrix*weights)error=labelMat-hweights=weights+alpha*dataMatrix.transpose()*error#將矩陣轉(zhuǎn)化為數(shù)組,并返回權(quán)重參數(shù)return weights.getA()""" 函數(shù)說明:繪制數(shù)據(jù)集""" def plotBestFit(weights):#加載數(shù)據(jù)集dataMat,labelMat=loadDataSet()dataArr=np.array(dataMat)#數(shù)據(jù)個數(shù)n=np.shape(dataMat)[0]#正樣本xcord1=[]ycord1=[]#負(fù)樣本xcord2=[]ycord2=[]#根據(jù)數(shù)據(jù)集標(biāo)簽進(jìn)行分類for i in range(n):#1為正樣本if int(labelMat[i])==1:xcord1.append(dataArr[i,1])ycord1.append(dataArr[i,2])#0為負(fù)樣本else:xcord2.append(dataArr[i,1])ycord2.append(dataArr[i,2])fig = plt.figure()# 添加subplotax = fig.add_subplot(111)# 繪制正樣本ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)# 繪制負(fù)樣本ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)x=np.arange(-3.0,3.0,0.1)y=(-weights[0]-weights[1]*x)/weights[2]ax.plot(x,y)plt.title('BestFit')plt.xlabel('X1')plt.ylabel('X2')plt.show()if __name__=='__main__':dataMat,labelMat=loadDataSet()weights=gradAscent(dataMat,labelMat)plotBestFit(weights)結(jié)果:
上圖分類結(jié)果較為可觀,但是計算量很大。
總結(jié):
Logistic回歸的目的是尋找一個非線性函數(shù)Sigmoid的最佳擬合參數(shù),求解過程可以由最優(yōu)化算法完成。
5.4 改進(jìn)的隨機(jī)梯度上升算法
假設(shè),我們使用的數(shù)據(jù)集一共有100個樣本。那么,dataMatrix就是一個1003的矩陣。每次計算h的時候,都要計算dataMatrixweights這個矩陣乘法運(yùn)算,要進(jìn)行1003次乘法運(yùn)算和1002次加法運(yùn)算。同理,更新回歸系數(shù)(最優(yōu)參數(shù))weights時,也需要用到整個數(shù)據(jù)集,要進(jìn)行矩陣乘法運(yùn)算。總而言之,該方法處理100個左右的數(shù)據(jù)集時尚可,但如果有數(shù)十億樣本和成千上萬的特征,那么該方法的計算復(fù)雜度就太高了。因此,需要對算法進(jìn)行改進(jìn),我們每次更新回歸系數(shù)(最優(yōu)參數(shù))的時候,能不能不用所有樣本呢?一次只用一個樣本點(diǎn)去更新回歸系數(shù)(最優(yōu)參數(shù))?這樣就可以有效減少計算量了,這種方法就叫做隨機(jī)梯度上升算法。
5.4.1 隨機(jī)梯度上升算法代碼:
def stocGradAscent1(dataMatrix, classLabels, numIter=150):#返回dataMatrix的大小。m為行數(shù),n為列數(shù)。m,n = np.shape(dataMatrix) #參數(shù)初始化weights = np.ones(n) for j in range(numIter): dataIndex = list(range(m))#降低alpha的大小,每次減小1/(j+i)。for i in range(m): alpha = 4/(1.0+j+i)+0.01 #隨機(jī)選取樣本 randIndex = int(random.uniform(0,len(dataIndex))) #選擇隨機(jī)選取的一個樣本,計算h h = sigmoid(sum(dataMatrix[randIndex]*weights)) #計算誤差 error = classLabels[randIndex] - h #更新回歸系數(shù) weights = weights + alpha * error * dataMatrix[randIndex] #刪除已經(jīng)使用的樣本del(dataIndex[randIndex]) #返回return weights改進(jìn)之處:
(一)
alpha在每次迭代的時候都會進(jìn)行調(diào)整,雖然每次都會減小,但是不會減小到0,因?yàn)榇颂幱谐?shù)項。
如果需要處理的問題是動態(tài)變化的,那么可以適當(dāng)加大上述常數(shù)項,來確保新的值獲得更大的回歸系數(shù)。
alpha減少的過程中,每次減小1/(j+i)1/(j+i)1/(j+i),其中jjj為迭代次數(shù),iii為樣本點(diǎn)的下標(biāo)。
(二)
更新回歸系數(shù)時,只使用一個樣本點(diǎn),并且選擇的樣本點(diǎn)是隨機(jī)的,每次迭代不使用已經(jīng)用過的樣本點(diǎn),減小了計算量,并且保證了回歸效果。
改進(jìn)的隨機(jī)梯度上升算法:
import matplotlib.pylab as plt import numpy as np import random """ 函數(shù)說明:加載數(shù)據(jù)""" def loadDataSet():# 創(chuàng)建數(shù)據(jù)列表dataMat=[]# 創(chuàng)建標(biāo)簽列表labelMat=[]# 打開文件fr=open('testSet.txt')# 逐行讀取for line in fr.readlines():# 去回車,放入列表lineArr=line.strip().split()# 添加數(shù)據(jù)dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])# 添加標(biāo)簽labelMat.append(int(lineArr[2]))# 關(guān)閉文件fr.close()return dataMat,labelMat""" 函數(shù)說明:sigmoid函數(shù)""" def sigmoid(inX):return 1.0/(1+np.exp(-inX))""" 函數(shù)說明:梯度上升算法""" def gradAscent(dataMatIn,classLabels):#轉(zhuǎn)化成numpy的matdataMatrix=np.mat(dataMatIn)# 轉(zhuǎn)化成numpy的mat,并進(jìn)行轉(zhuǎn)置labelMat=np.mat(classLabels).transpose()#返回dataMatrix的大小,m為行,n為列m,n=np.shape(dataMatrix)#移動步長,也就是學(xué)習(xí)速率,控制參數(shù)更新的速度alpha=0.001#最大迭代次數(shù)maxCycles=500weights=np.ones((n,1))for k in range(maxCycles):#梯度上升矢量化公式h=sigmoid(dataMatrix*weights)error=labelMat-hweights=weights+alpha*dataMatrix.transpose()*error#將矩陣轉(zhuǎn)化為數(shù)組,并返回權(quán)重參數(shù)return weights.getA()""" 函數(shù)說明:繪制數(shù)據(jù)集""" def plotBestFit(weights):#加載數(shù)據(jù)集dataMat,labelMat=loadDataSet()dataArr=np.array(dataMat)#數(shù)據(jù)個數(shù)n=np.shape(dataMat)[0]#正樣本xcord1=[]ycord1=[]#負(fù)樣本xcord2=[]ycord2=[]#根據(jù)數(shù)據(jù)集標(biāo)簽進(jìn)行分類for i in range(n):#1為正樣本if int(labelMat[i])==1:xcord1.append(dataArr[i,1])ycord1.append(dataArr[i,2])#0為負(fù)樣本else:xcord2.append(dataArr[i,1])ycord2.append(dataArr[i,2])fig = plt.figure()# 添加subplotax = fig.add_subplot(111)# 繪制正樣本ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)# 繪制負(fù)樣本ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)x=np.arange(-3.0,3.0,0.1)y=(-weights[0]-weights[1]*x)/weights[2]ax.plot(x,y)plt.title('BestFit')plt.xlabel('X1')plt.ylabel('X2')plt.show()""" 函數(shù)說明:改進(jìn)的隨機(jī)梯度上升算法""" def stocGradAscent1(dataMatix,classLabels,numIter=500):m,n=np.shape(dataMatix)weights=np.ones(n)for j in range(numIter):dataIndex=list(range(m))for i in range(m):#降低alpha的大小alpha=4/(1.0+j+i)+0.01#隨機(jī)選取樣本randIndex=int(random.uniform(0,len(dataIndex)))#選擇隨機(jī)選取的樣本,計算hh=sigmoid(sum(dataMatix[randIndex]*weights))#計算誤差error=classLabels[randIndex]-h#更新回歸系數(shù)weights=weights+alpha*error*dataMatix[randIndex]#刪除已經(jīng)使用的樣本del(dataIndex[randIndex])return weightsif __name__=='__main__':dataMat,labelMat=loadDataSet()weights=gradAscent(dataMat,labelMat)plotBestFit(weights)結(jié)果:
5.4.2 回歸系數(shù)與迭代次數(shù)的關(guān)系
import matplotlib.pylab as plt import numpy as np import random from matplotlib.font_manager import FontProperties""" 函數(shù)說明:加載數(shù)據(jù)""" def loadDataSet():# 創(chuàng)建數(shù)據(jù)列表dataMat=[]# 創(chuàng)建標(biāo)簽列表labelMat=[]# 打開文件fr=open('testSet.txt')# 逐行讀取for line in fr.readlines():# 去回車,放入列表lineArr=line.strip().split()# 添加數(shù)據(jù)dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])# 添加標(biāo)簽labelMat.append(int(lineArr[2]))# 關(guān)閉文件fr.close()return dataMat,labelMat""" 函數(shù)說明:sigmoid函數(shù)""" def sigmoid(inX):return 1.0/(1+np.exp(-inX))""" 函數(shù)說明:梯度上升算法""" def gradAscent(dataMatIn,classLabels):#轉(zhuǎn)化成numpy的matdataMatrix=np.mat(dataMatIn)# 轉(zhuǎn)化成numpy的mat,并進(jìn)行轉(zhuǎn)置labelMat=np.mat(classLabels).transpose()#返回dataMatrix的大小,m為行,n為列m,n=np.shape(dataMatrix)#移動步長,也就是學(xué)習(xí)速率,控制參數(shù)更新的速度alpha=0.01#最大迭代次數(shù)maxCycles=500weights=np.ones((n,1))weights_array = np.array([])for k in range(maxCycles):#梯度上升矢量化公式h=sigmoid(dataMatrix*weights)error=labelMat-hweights=weights+alpha*dataMatrix.transpose()*errorweights_array = np.append(weights_array, weights)weights_array = weights_array.reshape(maxCycles, n)#將矩陣轉(zhuǎn)化為數(shù)組,并返回權(quán)重參數(shù)return weights.getA(),weights_array""" 函數(shù)說明:繪制數(shù)據(jù)集""" def plotBestFit(weights):#加載數(shù)據(jù)集dataMat,labelMat=loadDataSet()dataArr=np.array(dataMat)#數(shù)據(jù)個數(shù)n=np.shape(dataMat)[0]#正樣本xcord1=[]ycord1=[]#負(fù)樣本xcord2=[]ycord2=[]#根據(jù)數(shù)據(jù)集標(biāo)簽進(jìn)行分類for i in range(n):#1為正樣本if int(labelMat[i])==1:xcord1.append(dataArr[i,1])ycord1.append(dataArr[i,2])#0為負(fù)樣本else:xcord2.append(dataArr[i,1])ycord2.append(dataArr[i,2])fig = plt.figure()# 添加subplotax = fig.add_subplot(111)# 繪制正樣本ax.scatter(xcord1, ycord1, s=20, c='red', marker='s', alpha=.5)# 繪制負(fù)樣本ax.scatter(xcord2, ycord2, s=20, c='green', alpha=.5)x=np.arange(-3.0,3.0,0.1)y=(-weights[0]-weights[1]*x)/weights[2]ax.plot(x,y)plt.title('BestFit')plt.xlabel('X1')plt.ylabel('X2')plt.show()""" 函數(shù)說明:改進(jìn)的隨機(jī)梯度上升算法""" def stocGradAscent1(dataMatix,classLabels,numIter=150):m,n=np.shape(dataMatix)weights=np.ones(n)weights_array=np.array([])for j in range(numIter):dataIndex=list(range(m))for i in range(m):#降低alpha的大小alpha=4/(1.0+j+i)+0.01#隨機(jī)選取樣本randIndex=int(random.uniform(0,len(dataIndex)))#選擇隨機(jī)選取的樣本,計算hh=sigmoid(sum(dataMatix[randIndex]*weights))#計算誤差error=classLabels[randIndex]-h#更新回歸系數(shù)weights=weights+alpha*error*dataMatix[randIndex]#添加回歸系數(shù)到數(shù)組中weights_array=np.append(weights_array,weights,axis=0)#刪除已經(jīng)使用的樣本del(dataIndex[randIndex])#改變維度weights_array=weights_array.reshape(numIter*m,n)#返回return weights,weights_array""" 函數(shù)說明:繪制回歸系數(shù)與迭代次數(shù)的關(guān)系Parameters:weights_array1 - 回歸系數(shù)數(shù)組1weights_array2 - 回歸系數(shù)數(shù)組2 Returns:無 Author:Jack Cui Blog:http://blog.csdn.net/c406495762 Zhihu:https://www.zhihu.com/people/Jack--Cui/ Modify:2017-08-30 """ def plotWeights(weights_array1,weights_array2):#設(shè)置漢字格式font = FontProperties(fname=r"c:\windows\fonts\simsun.ttc", size=14)#將fig畫布分隔成1行1列,不共享x軸和y軸,fig畫布的大小為(13,8)#當(dāng)nrow=3,nclos=2時,代表fig畫布被分為六個區(qū)域,axs[0][0]表示第一行第一列fig, axs = plt.subplots(nrows=3, ncols=2,sharex=False, sharey=False, figsize=(20,10))x1 = np.arange(0, len(weights_array1), 1)#繪制w0與迭代次數(shù)的關(guān)系axs[0][0].plot(x1,weights_array1[:,0])axs0_title_text = axs[0][0].set_title(u'梯度上升算法:回歸系數(shù)與迭代次數(shù)關(guān)系',FontProperties=font)axs0_ylabel_text = axs[0][0].set_ylabel(u'W0',FontProperties=font)plt.setp(axs0_title_text, size=20, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=20, weight='bold', color='black')#繪制w1與迭代次數(shù)的關(guān)系axs[1][0].plot(x1,weights_array1[:,1])axs1_ylabel_text = axs[1][0].set_ylabel(u'W1',FontProperties=font)plt.setp(axs1_ylabel_text, size=20, weight='bold', color='black')#繪制w2與迭代次數(shù)的關(guān)系axs[2][0].plot(x1,weights_array1[:,2])axs2_xlabel_text = axs[2][0].set_xlabel(u'迭代次數(shù)',FontProperties=font)axs2_ylabel_text = axs[2][0].set_ylabel(u'W1',FontProperties=font)plt.setp(axs2_xlabel_text, size=20, weight='bold', color='black')plt.setp(axs2_ylabel_text, size=20, weight='bold', color='black')x2 = np.arange(0, len(weights_array2), 1)#繪制w0與迭代次數(shù)的關(guān)系axs[0][1].plot(x2,weights_array2[:,0])axs0_title_text = axs[0][1].set_title(u'改進(jìn)的隨機(jī)梯度上升算法:回歸系數(shù)與迭代次數(shù)關(guān)系',FontProperties=font)axs0_ylabel_text = axs[0][1].set_ylabel(u'W0',FontProperties=font)plt.setp(axs0_title_text, size=20, weight='bold', color='black')plt.setp(axs0_ylabel_text, size=20, weight='bold', color='black')#繪制w1與迭代次數(shù)的關(guān)系axs[1][1].plot(x2,weights_array2[:,1])axs1_ylabel_text = axs[1][1].set_ylabel(u'W1',FontProperties=font)plt.setp(axs1_ylabel_text, size=20, weight='bold', color='black')#繪制w2與迭代次數(shù)的關(guān)系axs[2][1].plot(x2,weights_array2[:,2])axs2_xlabel_text = axs[2][1].set_xlabel(u'迭代次數(shù)',FontProperties=font)axs2_ylabel_text = axs[2][1].set_ylabel(u'W1',FontProperties=font)plt.setp(axs2_xlabel_text, size=20, weight='bold', color='black')plt.setp(axs2_ylabel_text, size=20, weight='bold', color='black')plt.show()if __name__ == '__main__':dataMat, labelMat = loadDataSet()weights1,weights_array1 = stocGradAscent1(np.array(dataMat), labelMat)weights2,weights_array2 = gradAscent(dataMat, labelMat)plotWeights(weights_array1, weights_array2)改進(jìn)的隨機(jī)梯度上升算法,隨機(jī)選取樣本點(diǎn),所以每次運(yùn)行結(jié)果不同,但大體趨勢相同。
改進(jìn)的隨機(jī)梯度上升算法收斂效果更好:
我們一共有100個樣本點(diǎn),改進(jìn)的隨機(jī)梯度上升算法迭代次數(shù)為150。而上圖顯示15000次迭代次數(shù)的原因是,使用一次樣本就更新一下回歸系數(shù)。因此,迭代150次,相當(dāng)于更新回歸系數(shù)150*100=15000次。簡而言之,迭代150次,更新1.5萬次回歸參數(shù)。從上圖左側(cè)的改進(jìn)隨機(jī)梯度上升算法回歸效果中可以看出,其實(shí)在更新2000次回歸系數(shù)的時候,已經(jīng)收斂了。相當(dāng)于遍歷整個數(shù)據(jù)集20次的時候,回歸系數(shù)已收斂。訓(xùn)練已完成。
再讓我們看看上圖右側(cè)的梯度上升算法回歸效果,梯度上升算法每次更新回歸系數(shù)都要遍歷整個數(shù)據(jù)集。從圖中可以看出,當(dāng)?shù)螖?shù)為300多次的時候,回歸系數(shù)才收斂。湊個整,就當(dāng)它在遍歷整個數(shù)據(jù)集300次的時候已經(jīng)收斂好了。
改進(jìn)的隨機(jī)梯度上升算法,在遍歷數(shù)據(jù)集的第20次開始收斂。而梯度上升算法,在遍歷數(shù)據(jù)集的第300次才開始收斂。
5.5 示例:從疝氣病癥預(yù)測病馬的死亡率
數(shù)據(jù)報告368個樣本和28個特征,數(shù)據(jù)集包含了馬疝病的一些指標(biāo),有的指標(biāo)比較主觀,有的指標(biāo)難以測量,例如馬的疼痛級別。
處理部分指標(biāo)主觀和難以測量外,該數(shù)據(jù)還存在一個問題,數(shù)據(jù)集中有30%的值是缺失的,首先要解決數(shù)據(jù)缺失問題,再用Logistic回歸來預(yù)測病馬的生死。
5.5.1 準(zhǔn)備數(shù)據(jù)
若機(jī)器上的某個傳感器損壞導(dǎo)致一個特征無效時該怎么辦?它們是否還可用?答案是肯定的。因?yàn)橛袝r候數(shù)據(jù)相當(dāng)昂貴,扔掉和重新獲取都是不可取的,所以必須采用一些方法來解決這個問題。下面給出了一些可選的做法:
- 使用可用特征的均值來填補(bǔ)缺失值;
- 使用特殊值來填補(bǔ)缺失值,如-1;
- 忽略有缺失值的樣本;
- 使用相似樣本的均值添補(bǔ)缺失值;
- 使用另外的機(jī)器學(xué)習(xí)算法預(yù)測缺失值。
數(shù)據(jù)預(yù)處理:
- 如果測試集中一條數(shù)據(jù)的特征值已經(jīng)確實(shí),那么我們選擇實(shí)數(shù)0來替換所有缺失值,因?yàn)楸疚氖褂肔ogistic回歸。因此這樣做不會影響回歸系數(shù)的值。sigmoid(0)=0.5,即它對結(jié)果的預(yù)測不具有任何傾向性。
- 如果測試集中一條數(shù)據(jù)的類別標(biāo)簽已經(jīng)缺失,那么我們將該類別數(shù)據(jù)丟棄,因?yàn)轭悇e標(biāo)簽與特征不同,很難確定采用某個合適的值來替換。
保存為兩個文件:horseColicTest.txt和horseColicTraining.txt
5.5.2 使用python構(gòu)建Logistic回歸分類器
使用Logistic回歸方法進(jìn)行分類并不需要做很多工作,所需做的只是把測試集上每個特征向量乘以最優(yōu)化方法得來的回歸系數(shù),再將乘積結(jié)果求和,最后輸入到Sigmoid函數(shù)中即可。如果對應(yīng)的Sigmoid值大于0.5就預(yù)測類別標(biāo)簽為1,否則為0。
import numpy as np import random""" 函數(shù)說明:sigmoid函數(shù) """ def sigmoid(inX):return 1.0/(1+np.exp(-inX))""" 函數(shù)說明:改進(jìn)的梯度上升算法""" def stocGradAscent1(dataMatix,classLabels,numIter=500):m,n=np.shape(dataMatix)weights=np.ones(n)for j in range(numIter):dataIndex=list(range(m))for i in range(m):#降低alpha的大小alpha=4/(1.0+j+i)+0.01#隨機(jī)選取樣本randIndex=int(random.uniform(0,len(dataIndex)))#選擇隨機(jī)選取的樣本,計算hh=sigmoid(sum(dataMatix[randIndex]*weights))#計算誤差error=classLabels[randIndex]-h#更新回歸系數(shù)weights=weights+alpha*error*dataMatix[randIndex]#刪除已經(jīng)使用的樣本del(dataIndex[randIndex])return weights""" 函數(shù)說明:使用python寫的Logistic分類器做預(yù)測""" def colicTest():frTrain=open('horseColicTraining.txt')frTest=open('horseColicTest.txt')trainingSet=[]trainingLabels=[]for line in frTrain.readlines():currLine = line.strip().split('\t')lineArr = []for i in range(len(currLine) - 1):lineArr.append(float(currLine[i]))trainingSet.append(lineArr)trainingLabels.append(float(currLine[-1]))# 使用改進(jìn)的隨即上升梯度訓(xùn)練trainWeights = stocGradAscent1(np.array(trainingSet), trainingLabels, 500)errorCount = 0numTestVec = 0.0for line in frTest.readlines():numTestVec += 1.0currLine = line.strip().split('\t')lineArr = []for i in range(len(currLine) - 1):lineArr.append(float(currLine[i]))if int(classifyVector(np.array(lineArr), trainWeights)) != int(currLine[-1]):errorCount += 1errorRate = (float(errorCount) / numTestVec) * 100 # 錯誤率計算print("測試集錯誤率為: %.2f%%" % errorRate)""" 函數(shù)說明:分類函數(shù)""" def classifyVector(inX, weights):prob = sigmoid(sum(inX*weights))if prob > 0.5: return 1.0else: return 0.0if __name__ == '__main__':colicTest()結(jié)果:
測試集錯誤率為: 28.36%錯誤率較高,運(yùn)行時間較長,且每次的錯誤率不穩(wěn)定,原因是數(shù)據(jù)集本身有30%的數(shù)據(jù)缺失,另一個原因是改進(jìn)的隨機(jī)梯度上升算法,因?yàn)閿?shù)據(jù)集本身就很小,所以用隨機(jī)梯度上升不太合適。
使用梯度上升算法:
import matplotlib.pylab as plt import numpy as np import random """ 函數(shù)說明:加載數(shù)據(jù)""" def loadDataSet():# 創(chuàng)建數(shù)據(jù)列表dataMat=[]# 創(chuàng)建標(biāo)簽列表labelMat=[]# 打開文件fr=open('testSet.txt')# 逐行讀取for line in fr.readlines():# 去回車,放入列表lineArr=line.strip().split()# 添加數(shù)據(jù)dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])# 添加標(biāo)簽labelMat.append(int(lineArr[2]))# 關(guān)閉文件fr.close()return dataMat,labelMat""" 函數(shù)說明:sigmoid函數(shù)""" def sigmoid(inX):return 1.0/(1+np.exp(-inX))""" 函數(shù)說明:梯度上升算法""" def gradAscent(dataMatIn,classLabels):#轉(zhuǎn)化成numpy的matdataMatrix=np.mat(dataMatIn)# 轉(zhuǎn)化成numpy的mat,并進(jìn)行轉(zhuǎn)置labelMat=np.mat(classLabels).transpose()#返回dataMatrix的大小,m為行,n為列m,n=np.shape(dataMatrix)#移動步長,也就是學(xué)習(xí)速率,控制參數(shù)更新的速度alpha=0.001#最大迭代次數(shù)maxCycles=500weights=np.ones((n,1))for k in range(maxCycles):#梯度上升矢量化公式h=sigmoid(dataMatrix*weights)error=labelMat-hweights=weights+alpha*dataMatrix.transpose()*error#將矩陣轉(zhuǎn)化為數(shù)組,并返回權(quán)重參數(shù)return weights.getA()""" 函數(shù)說明:使用python寫的Logistic分類器做預(yù)測""" def colicTest():frTrain=open('horseColicTraining.txt')frTest=open('horseColicTest.txt')trainingSet=[]trainingLabels=[]for line in frTrain.readlines():currLine = line.strip().split('\t')lineArr = []for i in range(len(currLine) - 1):lineArr.append(float(currLine[i]))trainingSet.append(lineArr)trainingLabels.append(float(currLine[-1]))# 使用改進(jìn)的隨即上升梯度訓(xùn)練trainWeights = gradAscent(np.array(trainingSet), trainingLabels)errorCount = 0numTestVec = 0.0for line in frTest.readlines():numTestVec += 1.0currLine = line.strip().split('\t')lineArr = []for i in range(len(currLine) - 1):lineArr.append(float(currLine[i]))if int(classifyVector(np.array(lineArr), trainWeights[:,0])) != int(currLine[-1]):errorCount += 1errorRate = (float(errorCount) / numTestVec) * 100 # 錯誤率計算print("測試集錯誤率為: %.2f%%" % errorRate)""" 函數(shù)說明:分類函數(shù)""" def classifyVector(inX, weights):prob = sigmoid(sum(inX*weights))if prob > 0.5: return 1.0else: return 0.0if __name__ == '__main__':colicTest()結(jié)果:
測試集錯誤率為: 29.85%結(jié)論:
使用梯度上升法時間更短,且錯誤率更穩(wěn)定
使用兩個不同算法的場合:
1)數(shù)據(jù)集較小時,采用梯度上升算法
2)數(shù)據(jù)集較大時,采用隨機(jī)梯度上升算法
5.6 使用sklearn構(gòu)建Logistic回歸分類器
官方文檔
5.6.1 函數(shù)說明
class sklearn.linear_model.LogisticRegression(penalty=’l2’, dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, random_state=None, solver=’liblinear’, max_iter=100, multi_class=’ovr’, verbose=0, warm_start=False, n_jobs=1)該函數(shù)有14個參數(shù),參數(shù)說明如下:
-
penalty:懲罰項,str類型,可選參數(shù)為l1和l2,默認(rèn)為l2。用于指定懲罰項中使用的規(guī)范。newton-cg、sag和lbfgs求解算法只支持L2規(guī)范。L1G規(guī)范假設(shè)的是模型的參數(shù)滿足拉普拉斯分布,L2假設(shè)的模型參數(shù)滿足高斯分布,所謂的范式就是加上對參數(shù)的約束,使得模型更不會過擬合(overfit),但是如果要說是不是加了約束就會好,這個沒有人能回答,只能說,加約束的情況下,理論上應(yīng)該可以獲得泛化能力更強(qiáng)的結(jié)果。
-
dual:對偶或原始方法,bool類型,默認(rèn)為False。對偶方法只用在求解線性多核(liblinear)的L2懲罰項上。當(dāng)樣本數(shù)量>樣本特征的時候,dual通常設(shè)置為False。
-
tol:停止求解的標(biāo)準(zhǔn),float類型,默認(rèn)為1e-4。就是求解到多少的時候,停止,認(rèn)為已經(jīng)求出最優(yōu)解。
-
c:正則化系數(shù)λ的倒數(shù),float類型,默認(rèn)為1.0。必須是正浮點(diǎn)型數(shù)。像SVM一樣,越小的數(shù)值表示越強(qiáng)的正則化。
-
fit_intercept:是否存在截距或偏差,bool類型,默認(rèn)為True。
-
intercept_scaling:僅在正則化項為”liblinear”,且fit_intercept設(shè)置為True時有用。float類型,默認(rèn)為1。
-
class_weight:用于標(biāo)示分類模型中各種類型的權(quán)重,可以是一個字典或者balanced字符串,默認(rèn)為不輸入,也就是不考慮權(quán)重,即為None。
-
如果選擇輸入的話,可以選擇balanced讓類庫自己計算類型權(quán)重,或者自己輸入各個類型的權(quán)重。舉個例子,比如對于0,1的二元模型,我們可以定義class_weight={0:0.9,1:0.1},這樣類型0的權(quán)重為90%,而類型1的權(quán)重為10%。
-
如果class_weight選擇balanced,那么類庫會根據(jù)訓(xùn)練樣本量來計算權(quán)重。某種類型樣本量越多,則權(quán)重越低,樣本量越少,則權(quán)重越高。
-
當(dāng)class_weight為balanced時,類權(quán)重計算方法如下:n_samples / (n_classes *
-
np.bincount(y))。n_samples為樣本數(shù),n_classes為類別數(shù)量,np.bincount(y)會輸出每個類的樣本數(shù),例如y=[1,0,0,1,1],則np.bincount(y)=[2,3]。
-
那么class_weight有什么作用呢? 在分類模型中,我們經(jīng)常會遇到兩類問題:
-
第一種是誤分類的代價很高。比如對合法用戶和非法用戶進(jìn)行分類,將非法用戶分類為合法用戶的代價很高,我們寧愿將合法用戶分類為非法用戶,這時可以人工再甄別,但是卻不愿將非法用戶分類為合法用戶。這時,我們可以適當(dāng)提高非法用戶的權(quán)重。
-
第二種是樣本是高度失衡的,比如我們有合法用戶和非法用戶的二元樣本數(shù)據(jù)10000條,里面合法用戶有9995條,非法用戶只有5條,如果我們不考慮權(quán)重,則我們可以將所有的測試集都預(yù)測為合法用戶,這樣預(yù)測準(zhǔn)確率理論上有99.95%,但是卻沒有任何意義。這時,我們可以選擇balanced,讓類庫自動提高非法用戶樣本的權(quán)重。提高了某種分類的權(quán)重,相比不考慮權(quán)重,會有更多的樣本分類劃分到高權(quán)重的類別,從而可以解決上面兩類問題。
-
random_state:隨機(jī)數(shù)種子,int類型,可選參數(shù),默認(rèn)為無,僅在正則化優(yōu)化算法為sag,liblinear時有用。
-
solver:優(yōu)化算法選擇參數(shù),只有五個可選參數(shù),即newton-cg,lbfgs,liblinear,sag,saga。默認(rèn)為liblinear。solver參數(shù)決定了我們對邏輯回歸損失函數(shù)的優(yōu)化方法,有四種算法可以選擇,分別是:
-
liblinear:使用了開源的liblinear庫實(shí)現(xiàn),內(nèi)部使用了坐標(biāo)軸下降法來迭代優(yōu)化損失函數(shù)。
-
lbfgs:擬牛頓法的一種,利用損失函數(shù)二階導(dǎo)數(shù)矩陣即海森矩陣來迭代優(yōu)化損失函數(shù)。
-
newton-cg:也是牛頓法家族的一種,利用損失函數(shù)二階導(dǎo)數(shù)矩陣即海森矩陣來迭代優(yōu)化損失函數(shù)。
-
sag:即隨機(jī)平均梯度下降,是梯度下降法的變種,和普通梯度下降法的區(qū)別是每次迭代僅僅用一部分的樣本來計算梯度,適合于樣本數(shù)據(jù)多的時候。
-
saga:線性收斂的隨機(jī)優(yōu)化算法的的變重。
總結(jié):
-
liblinear適用于小數(shù)據(jù)集,而sag和saga適用于大數(shù)據(jù)集因?yàn)樗俣雀臁?/p>
-
對于多分類問題,只有newton-cg,sag,saga和lbfgs能夠處理多項損失,而liblinear受限于一對剩余(OvR)。啥意思,就是用liblinear的時候,如果是多分類問題,得先把一種類別作為一個類別,剩余的所有類別作為另外一個類別。一次類推,遍歷所有類別,進(jìn)行分類。
-
newton-cg,sag和lbfgs這三種優(yōu)化算法時都需要損失函數(shù)的一階或者二階連續(xù)導(dǎo)數(shù),因此不能用于沒有連續(xù)導(dǎo)數(shù)的L1正則化,只能用于L2正則化。而liblinear和saga通吃L1正則化和L2正則化。
-
同時,sag每次僅僅使用了部分樣本進(jìn)行梯度迭代,所以當(dāng)樣本量少的時候不要選擇它,而如果樣本量非常大,比如大于10萬,sag是第一選擇。但是sag不能用于L1正則化,所以當(dāng)你有大量的樣本,又需要L1正則化的話就要自己做取舍了。要么通過對樣本采樣來降低樣本量,要么回到L2正則化。
-
從上面的描述,大家可能覺得,既然newton-cg, lbfgs和sag這么多限制,如果不是大樣本,我們選擇liblinear不就行了嘛!錯,因?yàn)閘iblinear也有自己的弱點(diǎn)!我們知道,邏輯回歸有二元邏輯回歸和多元邏輯回歸。對于多元邏輯回歸常見的有one-vs-rest(OvR)和many-vs-many(MvM)兩種。而MvM一般比OvR分類相對準(zhǔn)確一些。郁悶的是liblinear只支持OvR,不支持MvM,這樣如果我們需要相對精確的多元邏輯回歸時,就不能選擇liblinear了。也意味著如果我們需要相對精確的多元邏輯回歸不能使用L1正則化了。
-
max_iter:算法收斂最大迭代次數(shù),int類型,默認(rèn)為10。僅在正則化優(yōu)化算法為newton-cg, sag和lbfgs才有用,算法收斂的最大迭代次數(shù)。
-
multi_class:分類方式選擇參數(shù),str類型,可選參數(shù)為ovr和multinomial,默認(rèn)為ovr。ovr即前面提到的one-vs-rest(OvR),而multinomial即前面提到的many-vs-many(MvM)。如果是二元邏輯回歸,ovr和multinomial并沒有任何區(qū)別,區(qū)別主要在多元邏輯回歸上。
-
OvR和MvM有什么不同?
-
OvR的思想很簡單,無論你是多少元邏輯回歸,我們都可以看做二元邏輯回歸。具體做法是,對于第K類的分類決策,我們把所有第K類的樣本作為正例,除了第K類樣本以外的所有樣本都作為負(fù)例,然后在上面做二元邏輯回歸,得到第K類的分類模型。其他類的分類模型獲得以此類推。
-
MvM則相對復(fù)雜,這里舉MvM的特例one-vs-one(OvO)作講解。如果模型有T類,我們每次在所有的T類樣本里面選擇兩類樣本出來,不妨記為T1類和T2類,把所有的輸出為T1和T2的樣本放在一起,把T1作為正例,T2作為負(fù)例,進(jìn)行二元邏輯回歸,得到模型參數(shù)。我們一共需要T(T-1)/2次分類。
-
可以看出OvR相對簡單,但分類效果相對略差(這里指大多數(shù)樣本分布情況,某些樣本分布下OvR可能更好)。而MvM分類相對精確,但是分類速度沒有OvR快。如果選擇了ovr,則4種損失函數(shù)的優(yōu)化方法liblinear,newton-cg,lbfgs和sag都可以選擇。但是如果選擇了multinomial,則只能選擇newton-cg, lbfgs和sag了。
-
verbose:日志冗長度,int類型。默認(rèn)為0。就是不輸出訓(xùn)練過程,1的時候偶爾輸出結(jié)果,大于1,對于每個子模型都輸出。
-
warm_start:熱啟動參數(shù),bool類型。默認(rèn)為False。如果為True,則下一次訓(xùn)練是以追加樹的形式進(jìn)行(重新使用上一次的調(diào)用作為初始化)。
-
n_jobs:并行數(shù)。int類型,默認(rèn)為1。1的時候,用CPU的一個內(nèi)核運(yùn)行程序,2的時候,用CPU的2個內(nèi)核運(yùn)行程序。為-1的時候,用所有CPU的內(nèi)核運(yùn)行程序。
LogisticRegression的方法:
5.6.2 使用sklearn構(gòu)建分類器
代碼如下:
from sklearn.linear_model import LogisticRegression""" 函數(shù)說明:使用sklearn構(gòu)建logistics分類器""" def colicSklearn():frTrain=open('horseColicTraining.txt')frTest=open(('horseColicTest.txt'))trainingSet=[]trainingLabels=[]testSet=[]testLabels=[]for line in frTrain.readlines():currLine=line.strip().split('\t')lineArr=[]for i in range(len(currLine)-1):lineArr.append(float(currLine[i]))trainingSet.append(lineArr)trainingLabels.append(float(currLine[-1]))for line in frTest.readlines():currLine=line.strip().split('\t')lineArr=[]for i in range(len(currLine)-1):lineArr.append(float(currLine[i]))testSet.append(lineArr)testLabels.append(float(currLine[-1]))classifier=LogisticRegression(solver='liblinear',max_iter=10).fit(trainingSet,trainingLabels)test_accuracy=classifier.score(testSet,testLabels)*100print('正確率:%f%%' % test_accuracy)if __name__=='__main__':colicSklearn()結(jié)果:
正確率:73.134328%更改solver參數(shù),設(shè)置為sag,使用隨機(jī)平均梯度下降法,可以看到正確率提高,但是出現(xiàn)了警告:
說明算法還沒有收斂,可以將迭代次數(shù)改為5000,再次運(yùn)行:
classifier=LogisticRegression(solver='sag',max_iter=5000).fit(trainingSet,trainingLabels)結(jié)果:
5.7 Logistic算法總結(jié)
5.7.1 優(yōu)缺點(diǎn)
- 優(yōu)點(diǎn):實(shí)現(xiàn)簡單,易于理解,計算代價不高,速度快,存儲資源低
- 缺點(diǎn):容易欠擬合,分類精度不高,一般只能處理兩分類問題,必須借助softmax才能實(shí)現(xiàn)多分類問題,且前提是必須線性可分。
5.8 系統(tǒng)介紹
5.8.1 邏輯斯蒂分布
設(shè)X是連續(xù)隨機(jī)變量,X服從邏輯斯蒂分布是指X具有下列的分布函數(shù)和密度函數(shù):
上式中,μ表示位置參數(shù),γ>0 為形狀參數(shù)。
邏輯斯蒂分布函數(shù)就是一個sigmoid曲線,以(μ,12)(\mu,\frac{1}{2})(μ,21?)為中心對稱,形狀參數(shù)γ\gammaγ的值越小,曲線在中心附近增長越快。
5.8.2 二項邏輯回歸
邏輯回歸是一種二分類模型,由條件概率分布P(Y∣X)P(Y|X)P(Y∣X)表示,形式就是參數(shù)化的邏輯斯蒂分布。這里自變量X的取值為實(shí)數(shù),因變量Y為0或1,二項LR的條件概率如下:
也就是說,輸出Y=1 的對數(shù)幾率是由輸入x的線性函數(shù)表示的模型,這就是 邏輯回歸模型。當(dāng) w?x的值越接近正無窮,P(Y=1|x) 概率值也就越接近1。
實(shí)際上,對數(shù)似然損失在單個數(shù)據(jù)點(diǎn)上的定義為:
如果取整個數(shù)據(jù)集上的平均對數(shù)似然損失,我們恰好可以得到:
接下來就是對L(w) 求極大值(也可認(rèn)為是求J(w) 的最小值),得到w 的估計值。邏輯回歸學(xué)習(xí)中通常采用的方法是梯度下降法 和 牛頓法。
5.8.3 邏輯回歸的正則化
當(dāng)模型參數(shù)過多時,會產(chǎn)生過擬合問題,正則化是通過在經(jīng)驗(yàn)風(fēng)險上加一個正則化項,來懲罰過大的參數(shù)來防止過擬合。
正則化是符合奧卡姆剃刀(Occam’s razor)原理的:在所有可能選擇的模型中,能夠很好地解釋已知數(shù)據(jù)并且十分簡單的才是最好的模型。
最右邊的圖就是overfitting,參數(shù)過多,導(dǎo)致在訓(xùn)練集上的效果過于優(yōu)秀,喪失了一般性,總而言之f(x)多項式的N特別的大,因?yàn)樾枰峁┑奶卣鞫?#xff0c;或者提供的測試用例中我們使用到的特征非常多(一般而言,機(jī)器學(xué)習(xí)的過程中,很多特征是可以被丟棄掉的)。典型的做法就是在優(yōu)化目標(biāo)中加入正則項:
p=1或者2,表示L 1 范數(shù)和 L 2 范數(shù),這兩者還是有不同效果的。
通過懲罰過大的參數(shù)來防止過擬合,也就是使得w向量中項的個數(shù)最小,所以要損失函數(shù)和正則項同時最小,最終是讓兩者之和最小。
一、L0范數(shù)與L1范數(shù)
L0范數(shù):向量中非0元素的個數(shù),如果用L0范數(shù)來規(guī)范化一個參數(shù)矩陣的話,就是希望w的大部分元素都是0,也就是希望參數(shù)w是稀疏的。但是L0范數(shù)難以優(yōu)化求解,故基本不用。
L1范數(shù):向量中各個元素的絕對值之和,也叫“稀疏規(guī)則算子(Lasso Regularization)”,比L0具有更好的優(yōu)化求解特性。
為什么要讓參數(shù)稀疏化呢?
(1)特征選擇:
稀疏規(guī)則和效果好的一個關(guān)鍵原因在于它能夠?qū)崿F(xiàn)特征的自動選擇,一般來說,xi的大部分元素(也就是特征)都是和最終的輸出yi沒有關(guān)系或者不提供任何信息的,在最小化目標(biāo)函數(shù)的時候考慮xi這些額外的特征,雖然可以獲得更小的訓(xùn)練誤差,但在預(yù)測新的樣本時,這些沒用的信息反而會被考慮,從而干擾了對正確yi的預(yù)測。稀疏規(guī)則化算子的引入就是為了完成特征自動選擇的光榮使命,它會學(xué)習(xí)地去掉這些沒有信息的特征,也就是把這些特征對應(yīng)的權(quán)重置為0。
(2)可解釋性:
另一個青睞于稀疏的理由是,模型更容易解釋。例如患某種病的概率是y,然后我們收集到的數(shù)據(jù)x是1000維的,也就是我們需要尋找這1000種因素到底是怎么影響患上這種病的概率的。假設(shè)我們這個是個回歸模型:y=w1x1+w2x2+…+w1000x1000+b(當(dāng)然了,為了讓y限定在[0,1]的范圍,一般還得加個Logistic函數(shù))。通過學(xué)習(xí),如果最后學(xué)習(xí)到的w就只有很少的非零元素,例如只有5個非零的wi,那么我們就有理由相信,這些對應(yīng)的特征在患病分析上面提供的信息是巨大的,決策性的。也就是說,患不患這種病只和這5個因素有關(guān),那醫(yī)生就好分析多了
二、L2范數(shù)
除了L1范數(shù),還有一種更受寵幸的規(guī)則化范數(shù)是L2范數(shù): ∣∣W∣∣2||W||_2∣∣W∣∣2?。它也不遜于L1范數(shù),它有兩個美稱,在回歸里面,有人把有它的回歸叫“嶺回歸”(Ridge Regression),有人也叫它“權(quán)值衰減weight decay”。這用的很多吧,因?yàn)樗膹?qiáng)大功效是改善機(jī)器學(xué)習(xí)里面一個非常重要的問題:過擬合。
L2范數(shù)是指向量各元素的平方和然后求平方根。我們讓L2范數(shù)的規(guī)則項||W||2最小,可以使得W的每個元素都很小,都接近于0,但與L1范數(shù)不同,它不會讓它等于0,而是接近于0,這里是有很大的區(qū)別的哦。而越小的參數(shù)說明模型越簡單,越簡單的模型則越不容易產(chǎn)生過擬合現(xiàn)象,因?yàn)閰?shù)小,對結(jié)果的影響就小了。
L2正則化是在目標(biāo)函數(shù)中增加所有權(quán)重參數(shù)的平方和,使得所有的w盡可能的趨于0但不為0,因?yàn)檫^擬合的時候,擬合函數(shù)需要顧忌每一個點(diǎn),最終形成的擬合函數(shù)波動很大, 在某些小區(qū)間里,函數(shù)值的變化很距離,也就是w非常大,所以,L2正則化就很大程度上抑制了w變化的速率,也就是權(quán)重變化太快的趨勢。
通過L2范數(shù),我們可以實(shí)現(xiàn)了對模型空間的限制,從而在一定程度上避免了過擬合。
因此,一句話總結(jié)就是:L1會趨向于產(chǎn)生少量的特征,而其他的特征都是0,而L2會選擇更多的特征,這些特征都會接近于0。Lasso在特征選擇時候非常有用,而Ridge就只是一種規(guī)則化而已。
為了簡單,上圖只考慮了w 為二維(w 1 ,w 2 ) 的情況。彩色等高線是(w 1 ,w 2 ) ;而左邊黑色矩形 ||w|| 1 <C 和右邊的圓形 ||w|| 2 <C 是約束條件;相交的黑點(diǎn)就是最優(yōu)解發(fā)生的地方。兩者的區(qū)別可以從圖中看出來,L 1 正則化(左圖)傾向于使參數(shù)變?yōu)?,因此能產(chǎn)生稀疏解。而 L 2 使 w 接近0;
來自網(wǎng)頁:
L1范數(shù): 為x向量各個元素絕對值之和。
L2范數(shù): 為x向量各個元素平方和的1/2次方,L2范數(shù)又稱Euclidean范數(shù)或者Frobenius范數(shù)
Lp范數(shù): 為x向量各個元素絕對值p次方和的1/p次方.
在支持向量機(jī)學(xué)習(xí)過程中,L1范數(shù)實(shí)際是一種對于成本函數(shù)求解最優(yōu)的過程,因此,L1范數(shù)正則化通過向成本函數(shù)中添加L1范數(shù),使得學(xué)習(xí)得到的結(jié)果滿足稀疏化,從而方便人類提取特征。
L1范數(shù)可以使權(quán)值稀疏,方便特征提取。
L2范數(shù)可以防止過擬合,提升模型的泛化能力。
L1和L2的差別:為什么一個讓絕對值最小,一個讓平方最小,會有那么大的差別呢?看導(dǎo)數(shù)一個是1一個是w便知, 在靠進(jìn)零附近, L1以勻速下降到零, 而L2則完全停下來了. 這說明L1是將不重要的特征(或者說, 重要性不在一個數(shù)量級上)盡快剔除, L2則是把特征貢獻(xiàn)盡量壓縮最小但不至于為零. 兩者一起作用, 就是把重要性在一個數(shù)量級(重要性最高的)的那些特征一起平等共事(簡言之, 不養(yǎng)閑人也不要超人)。
L1和L2正則先驗(yàn)分別服從什么分布。機(jī)器學(xué)習(xí) ML基礎(chǔ) 易
面試中遇到的,L1和L2正則先驗(yàn)分別服從什么分布,L1是拉普拉斯分布,L2是高斯分布。
先驗(yàn)就是優(yōu)化的起跑線, 有先驗(yàn)的好處就是可以在較小的數(shù)據(jù)集中有良好的泛化性能,當(dāng)然這是在先驗(yàn)分布是接近真實(shí)分布的情況下得到的了,從信息論的角度看,向系統(tǒng)加入了正確先驗(yàn)這個信息,肯定會提高系統(tǒng)的性能。
對參數(shù)引入L2正則化,相當(dāng)于引入高斯正態(tài)先驗(yàn)分布
對參數(shù)引入L1正則化,相當(dāng)于引入拉普拉斯先驗(yàn)分布
從上面兩圖可以看出, L2先驗(yàn)趨向零周圍, L1先驗(yàn)趨向零本身。
5.8.4 為什么邏輯回歸比線性回歸好
雖然邏輯回歸能夠用于分類,不過其本質(zhì)還是線性回歸。它僅在線性回歸的基礎(chǔ)上,在特征到結(jié)果的映射中加入了一層sigmoid函數(shù)(非線性)映射,即先把特征線性求和,然后使用sigmoid函數(shù)來預(yù)測。
這主要是由于線性回歸在整個實(shí)數(shù)域內(nèi)敏感度一致,而分類范圍,需要在[0,1]之內(nèi)。而邏輯回歸就是一種減小預(yù)測范圍,將預(yù)測值限定為[0,1]間的一種回歸模型,其回歸方程與回歸曲線如下圖所示。邏輯曲線在z=0時,十分敏感,在z>>0或z<<0處,都不敏感,將預(yù)測值限定為(0,1)。
5.8.5 其他降低過擬合的方法
隨機(jī)失活:dropout
讓神經(jīng)元以超參數(shù)p的概率被激活(也就是1-p的概率被設(shè)置為0), 每個w因此隨機(jī)參與, 使得任意w都不是不可或缺的, 效果類似于數(shù)量巨大的模型集成。
逐層歸一化
這個方法給每層的輸出都做一次歸一化(網(wǎng)絡(luò)上相當(dāng)于加了一個線性變換層), 使得下一層的輸入接近高斯分布. 這個方法相當(dāng)于下一層的w訓(xùn)練時避免了其輸入以偏概全, 因而泛化效果非常好.
提前終止:early stopping
理論上可能的局部極小值數(shù)量隨參數(shù)的數(shù)量呈指數(shù)增長, 到達(dá)某個精確的最小值是不良泛化的一個來源. 實(shí)踐表明, 追求細(xì)粒度極小值具有較高的泛化誤差。這是直觀的,因?yàn)槲覀兺ǔM覀兊恼`差函數(shù)是平滑的, 精確的最小值處所見相應(yīng)誤差曲面具有高度不規(guī)則性, 而我們的泛化要求減少精確度去獲得平滑最小值, 所以很多訓(xùn)練方法都提出了提前終止策略. 典型的方法是根據(jù)交叉叉驗(yàn)證提前終止: 若每次訓(xùn)練前, 將訓(xùn)練數(shù)據(jù)劃分為若干份, 取一份為測試集, 其他為訓(xùn)練集, 每次訓(xùn)練完立即拿此次選中的測試集自測. 因?yàn)槊糠荻加幸淮螜C(jī)會當(dāng)測試集, 所以此方法稱之為交叉驗(yàn)證. 交叉驗(yàn)證的錯誤率最小時可以認(rèn)為泛化性能最好, 這時候訓(xùn)練錯誤率雖然還在繼續(xù)下降, 但也得終止繼續(xù)訓(xùn)練了.
5.8.6 LR和SVM的聯(lián)系與區(qū)別
1、LR和SVM都可以處理分類問題,且一般都用于處理線性二分類問題(在改進(jìn)的情況下可以處理多分類問題)
2、兩個方法都可以增加不同的正則化項,如l1、l2等等。所以在很多實(shí)驗(yàn)中,兩種算法的結(jié)果是很接近的。
區(qū)別:
1、LR是參數(shù)模型,SVM是非參數(shù)模型。
2、從目標(biāo)函數(shù)來看,區(qū)別在于邏輯回歸采用的是logistical loss,SVM采用的是hinge loss,這兩個損失函數(shù)的目的都是增加對分類影響較大的數(shù)據(jù)點(diǎn)的權(quán)重,減少與分類關(guān)系較小的數(shù)據(jù)點(diǎn)的權(quán)重。
3、SVM的處理方法是只考慮support vectors,也就是和分類最相關(guān)的少數(shù)點(diǎn),去學(xué)習(xí)分類器。而邏輯回歸通過非線性映射,大大減小了離分類平面較遠(yuǎn)的點(diǎn)的權(quán)重,相對提升了與分類最相關(guān)的數(shù)據(jù)點(diǎn)的權(quán)重。
4、邏輯回歸相對來說模型更簡單,好理解,特別是大規(guī)模線性分類時比較方便。而SVM的理解和優(yōu)化相對來說復(fù)雜一些,SVM轉(zhuǎn)化為對偶問題后,分類只需要計算與少數(shù)幾個支持向量的距離,這個在進(jìn)行復(fù)雜核函數(shù)計算時優(yōu)勢很明顯,能夠大大簡化模型和計算。
5、logic 能做的 svm能做,但可能在準(zhǔn)確率上有問題,svm能做的logic有的做不了。
總結(jié)
以上是生活随笔為你收集整理的机器学习实战(五)——Logistic 回归的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 数独技巧——唯一矩形排除法的几种情况
- 下一篇: 机器学习实战(六)——支持向量机