機器學習——Logistic迴歸(下)
本篇主要對Logistic的梯度上升算法作了一定的改進,重新改成隨機梯度上升算法;接着使用sklearn對一個實例進行了應用示範。
隨機梯度上升算法:
在上一節我們的梯度上升算法中,我們需要遍歷整一個數據集進行迴歸係數的更新:
def gradAscent(dataMatIn,classLabels):
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
m,n =np.shape(dataMatrix)
alpha =0.001
maxCycles = 500
weights=np.ones((n,1))
for k in range(maxCycles):
h=sigmoid(dataMatrix*weights)
error=labelMat-h
weights=weights+alpha*dataMatrix.transpose()*error
return weights.getA()
在小樣本數據這樣是沒有問題的,但是如果數據集太大,所要進行的乘法運算就太多了,因此需要我們每次更新迴歸係數的時候,採用一種新的方法,不採用所有樣本,而一次只用一個樣本點去更新迴歸係數,就減小了運算量,這就是隨機梯度上升:
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
m,n=np.shape(dataMatrix)
weights=np.ones(n)
for j in range(numIter):
dataIndex=list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.01
randIndex=int(random.uniform(0,len(dataIndex)))
h=sigmoid(sum(dataMatrix[randIndex]*weights))
error=classLabels[randIndex]-h
weights=weights+alpha*error*dataMatrix[randIndex]
del(dataIndex[randIndex])
return weights
可以看到這個算法每次的alpha在迭代的時候是會不斷變小的,但不會變成0,保證新數據還有影響,如果要處理的問題是動態的,那麼可以適當加大常數項,確保獲得更大的迴歸係數,另外,在降低alpha的方法中,我們這種方法是不嚴格下降的,避免這種情況的方法是模擬退火方法等,大家可以去了解下;同時這個算法另外的一個改進地方是隨機選取樣本來更新迴歸係數,減少了週期性的波動。實現方法就是每次隨機從列表中選取一個值,然後從列表中刪掉該值,再進行下一次迭代。
我們在上一次的基礎上換上隨機梯度上升算法,可以看到:
import numpy as np
import matplotlib.pyplot as plt
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 函數:
返回sigmoid函數的值
"""
def sigmoid(inx):
return 1.0/(1+np.exp(-inx))
def gradAscent(dataMatIn,classLabels):
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
m,n =np.shape(dataMatrix)
alpha =0.001
maxCycles = 500
weights=np.ones((n,1))
for k in range(maxCycles):
h=sigmoid(dataMatrix*weights)
error=labelMat-h
weights=weights+alpha*dataMatrix.transpose()*error
return weights.getA()
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
m,n=np.shape(dataMatrix)
weights=np.ones(n)
for j in range(numIter):
dataIndex=list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.01
randIndex=int(random.uniform(0,len(dataIndex)))
h=sigmoid(sum(dataMatrix[randIndex]*weights))
error=classLabels[randIndex]-h
weights=weights+alpha*error*dataMatrix[randIndex]
del(dataIndex[randIndex])
return weights
dataMat,labelMat=loadDataSet()
print(gradAscent(dataMat,labelMat))
def plotFit(weights):
dataMat,labelMat = loadDataSet()
dataArr=np.array(dataMat)
n=np.shape(dataMat)[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 = 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('Logistic')
plt.xlabel('X1'); plt.ylabel('X2')
plt.show()
#weights=gradAscent(dataMat,labelMat)
weights=stocGradAscent1(np.array(dataMat),labelMat)
plotFit(weights)
[外鏈圖片轉存失敗(img-gLQ5Yfal-1564728587882)(F:\Python\Logistics_random.png)]
實戰演練:
數據處理:
對於數據中的缺失值,有很多種處理方法:
使用可用特徵的均值來填補缺失值;
使用特殊值來填補缺失值,如-1;
忽略有缺失值的樣本;
使用相似樣本的均值來填補缺失值;
使用另外的機器學習算法預測缺失值;
針對我們使用的分類算法,如果測試集中的一條數據特徵值已經確實,那麼選擇實數0來替換,因爲Logistic回顧中sigmoid(0)=0.5,對於結果沒有任何傾向性影響,如果標籤缺失值,則需要把這個類別數據丟掉,因爲很難確定採用某個合適的值來替換。
在這個章節中,我們使用了已經處理好的數據集進行處理,我們只需要搭建一個Logistic分類器,就可以進行分類:(實驗使用Logistic迴歸來預測患疝氣病的馬的存活問題,數據包含了368個樣本和28個特徵。這種病不一定源自馬的腸胃問題,其他問題也可能引發馬疝病。該數據集中包含了醫院檢測馬疝病的一些指標,有的指標比較主觀,有的指標難以測量,例如馬的疼痛級別。另外需要說明的是,除了部分指標主觀和難以測量外,該數據還存在一個問題,數據集中有30%的值是缺失的。)
我們使用梯度上升的方法對Logistic進行迴歸分類:數據集在我的github上有提供(兩個數據集:一個測試、一個訓練)
import numpy as np
import random
def sigmoid(inX):
return 1.0/(1+np.exp(-inX))
def gradAscent(dataMatIn,classLabels):
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
m,n =np.shape(dataMatrix)
alpha =0.001
maxCycles = 500
weights=np.ones((n,1))
for k in range(maxCycles):
h=sigmoid(dataMatrix*weights)
error=labelMat-h
weights=weights+alpha*dataMatrix.transpose()*error
return weights.getA()
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
m,n=np.shape(dataMatrix)
weights=np.ones(n)
for j in range(numIter):
dataIndex=list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.01
randIndex=int(random.uniform(0,len(dataIndex)))
h=sigmoid(sum(dataMatrix[randIndex]*weights))
error=classLabels[randIndex]-h
weights=weights+alpha*error*dataMatrix[randIndex]
del(dataIndex[randIndex])
return weights
def classifyVector(inX,weights):
prob =sigmoid(sum(inX*weights))
if prob>0.5:
return 1.0
else:
return 0.0
def colicTest():
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainingSet =[]
traininngLabels=[]
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)
traininngLabels.append(float(currLine[-1]))
trainWeights=gradAscent(np.array(trainingSet),traininngLabels)
errorCount = 0
numTestVec=0.0
for line in frTest.readlines():
numTestVec += 1
currLine =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+=1
errorRate=(float(errorCount)/numTestVec)*100
print("測試集錯誤率爲:%.2f%%" % errorRate)
colicTest()
測試集錯誤率爲:29.85%
如果我們使用隨機梯度上升算法:
import numpy as np
import random
def sigmoid(inX):
return 1.0/(1+np.exp(-inX))
def gradAscent(dataMatIn,classLabels):
dataMatrix = np.mat(dataMatIn)
labelMat = np.mat(classLabels).transpose()
m,n =np.shape(dataMatrix)
alpha =0.001
maxCycles = 500
weights=np.ones((n,1))
for k in range(maxCycles):
h=sigmoid(dataMatrix*weights)
error=labelMat-h
weights=weights+alpha*dataMatrix.transpose()*error
return weights.getA()
def stocGradAscent1(dataMatrix,classLabels,numIter=150):
m,n=np.shape(dataMatrix)
weights=np.ones(n)
for j in range(numIter):
dataIndex=list(range(m))
for i in range(m):
alpha = 4/(1.0+j+i)+0.01
randIndex=int(random.uniform(0,len(dataIndex)))
h=sigmoid(sum(dataMatrix[randIndex]*weights))
error=classLabels[randIndex]-h
weights=weights+alpha*error*dataMatrix[randIndex]
del(dataIndex[randIndex])
return weights
def classifyVector(inX,weights):
prob =sigmoid(sum(inX*weights))
if prob>0.5:
return 1.0
else:
return 0.0
def colicTest():
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainingSet =[]
traininngLabels=[]
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)
traininngLabels.append(float(currLine[-1]))
trainWeights=stocGradAscent1(np.array(trainingSet),traininngLabels)
errorCount = 0
numTestVec=0.0
for line in frTest.readlines():
numTestVec += 1
currLine =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+=1
errorRate=(float(errorCount)/numTestVec)*100
print("測試集錯誤率爲:%.2f%%" % errorRate)
colicTest()
測試集錯誤率爲:32.84%
可以看到在數據集比較小時,我們採用梯度上升算法時相對較好的,但是對於數據集較大時,我們使用改進的隨機梯度上升算法則體現出優越性,對應於sklearn,數據集小時使用liblinear,數據集大時使用sag和saga。
sklearn實戰:
對於sklearn的應用,大家可以看一下官方的文檔,對函數有一定的理解就很輕鬆學會了,下面直接給出代碼(對於上述實戰例子):
from sklearn.linear_model import LogisticRegression
def colicTest():
frTrain = open('horseColicTraining.txt')
frTest = open('horseColicTest.txt')
trainingSet =[]
traininngLabels=[]
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)
traininngLabels.append(float(currLine[-1]))
classifier=LogisticRegression(solver='liblinear',max_iter=10).fit(trainingSet,traininngLabels)
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]))
test_accurcy=classifier.score(testSet,testLabels)*100
print("測試準確率率爲:%.2f%%" % test_accurcy)
colicTest()
測試準確率率爲:73.13%
[Finished in 4.5s]
總結:
總的來說,Logistic實現和核心思想是很簡單的,計算代價也不高,但是容易欠擬合,分類精度不高。Logistic迴歸用到了梯度上升方法,我也給出了一些優化的方法,我們需要根據數據集的大小調整使用的方法,同時怎麼去處理數據集也是我們平時經常遇到的問題,綜合考慮這些問題,並試着去調節sklearn的參數,我們會發現更好的分類效果。
持續關注更新:https://github.com/Brian-Liew/Machine-Learning