機器學習——Logistic迴歸(下)

機器學習——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

發佈了12 篇原創文章 · 獲贊 9 · 訪問量 2716
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章