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