Logistic回归-实现(2)机器学习实战

        上一篇文章我们对logistic回归分类的实现过程大致进行了了解,对书中的梯度上升法实现过程也进行了证明,下面就进入实践环节,看看logistic回归分类的实现过程。


1.训练算法:使用梯度上升找到最优参数

 首先处理的是一些二维数据点的分类情况,数据对应的信息存在TestSet.txt文件中,下面让我们看一下具体的实现过程:

from numpy import *
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]))

	return dataMat,labelMat

def sigmoid(inX):#sigmoid函数
	return 1.0/(1+exp(-inX))

def gradAscent(dataMatIn,classlabels):
	dataMatrix = mat(dataMatIn)#转化为矩阵形式
	labelMat = mat(classlabels).transpose()
	#转化为矩阵形式 transpose为转置 100x1
	m,n = shape(dataMatrix)#提取矩阵的行数,列数
	alpha = 0.001#设置步长alpha
	maxCycles = 500#设置迭代次数
	weights = ones((n,1))#初始化w 均位1,长度与列数一致
	for k in range(maxCycles):#执行梯度优化
		h = sigmoid(dataMatrix*weights)
		error = (labelMat-h)
		weights = weights + alpha *dataMatrix.transpose()*error
	return weights#返回迭代次数内的最优系数w 

这一部分共有三个函数,第一个函数LoadDateSet负责将txt文件中的信息逐行读取,这里解释一下为什么要在X1,X2之前添加一个X0,我们之前讨论的是:

                                                    

这里的X0 = 1 相当于添加了一个修正项常数也作为参数进行最优化,这样完整的参数为:

                                                    

而这里第二个函数sigmoid函数定义了我们之前说的回归方程,第三个函数gradAscent函数就是实现梯度上升的主函数,函数具体的步骤都加了注释,这里就不多解释啦~最后我们运行一下程序,看一下得到的权数矩阵:

dataArr,labelMat = loadDataSet()
weights = gradAscent(dataArr,labelMat)
print(weights)

通过梯度上升得到的权数w:

[[ 4.12414349]
 [ 0.48007329]
 [-0.6168482 ]]

寻找最优参数w大功告成,下面让我们看一下可视化的分类情况。



2.分析数据:画出决策边界

def plotBestFit(weights):
	import matplotlib.pyplot as plt

	dataMat,labelMat = loadDataSet()#导入数据
	dataArr = mat(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')#scatter画点,定义点的类型
	ax.scatter(xcord2,ycord2,s=30,c='green')
	x = arange(-3.0,3.0,0.1)#定义x轴范围
	y = (-weights[0]-weights[1]*x)/weights[2]#根据权数输出y
	ax.plot(x,y)
	plt.xlabel('X1');plt.ylabel('X2')#定义坐标轴名称
	plt.show()

plotBeestFit函数通过上一步梯度上升法得到的最优参数w,画出最优决策边界,并根据原有数据的类别,在figure中分别以红色和绿色标注出来实现可视化,下面运行函数看一下分类效果如何:

plotBestFit(weights.getA())

                           

这个分类效果相当不错,大概只有两到三个点分错,但是每次计算都需要300次乘法(100个点 x 3个权数),所以我们需要对算法稍加改进,从而能够适应更多的数据,避免过大的计算量影响算法实现。



3.改进的训练算法:随机梯度上升

上一节的梯度上升法每更新一次回归系数都要遍历整个数据集,当面对成千上文甚至更多的数据时,这样显然会影响算法学习的效率,所以有了另一种改进方法,即一次只用一个样本点来更新回归系数,该方法为随机梯度上升算法,由于可以在新样本的到来时对分类器进行增量更新,因而随机梯度上升算法算是一个在线学习算法,即来一个样本学一个样本修改一次最优参数,这样就避免了每次都要遍历数集才能得到最优参数,与之对应的每次都遍历整个数据集也叫作批处理。

def stocGradAscent0(dataMatrix,classLabels):#随机梯度上升算法
	m,n = shape(dataMatrix)#提取数据维度
	alpha =0.01#设置步长
	weights = ones(n)
	for i in range(m):#重复R次变为逐点迭代
		h = sigmoid(sum(dataMatrix[i]*weights))#计算预测分类
		error = classLabels[i] - h
		weights = weights + alpha * error * dataMatrix[i]#迭代器
	return weights#返回迭代权数

随机梯度上升与梯度上升的区别就在于:

for k in range(maxCycles) --> for i in range(m)

这里maxCycles是最大循环次数,m是数据集行数,前者是对整个数据集重复最大循环次,后者是对样本中每个数据点执行一次,所以效率得到了大大提升。

看一下最后的分类效果:

dataArr,labelMat = loadDataSet()
weights = stocGradAscent0(array(dataArr),labelMat)
plotBestFit(weights)

                        

相比于之前的分类结果,这个结果不是很完美,但是我们也要考虑算法执行的次数和效率,一个判断优化算法优劣的可靠方法是看他是否收敛,也就是参数是否达到了稳定值,是否会不断变化,因此,除了计算效率需要考虑,参数的快速收敛也是我们希望看到的结果,如果一个分类算法效率很高但参数总是波动,这是我们不希望看到的。



4.基于收敛速度的改进:随机梯度上升算法

def stocGradAscent1(dataMatrix,classLabels,numIter =150):#改进随机梯度上升算法
	m,n = shape(dataMatrix)#提取数据维度
	weights = ones(n)
	for j in range(numIter):    
		dataIndex = list(range(m))
		for i in range(m):#重复R次变为逐点迭代
			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处进行了修改,由固定步长改为根据循环数i,j调整的步长,随着i,j的上升,步长也渐渐变小,这样做的目的是随着迭代次数的增加,最优参数w逐渐接近真实的最优参数之,通过缩小步长可以使w更加接近真实最优参数,从而也减小了参数估计中的周期性波动,还有一处修改就是计算预测分类h时采用了随机生成索引,每次 从预测h时我们都通过randIndex获取数据集的内容,而不是之前的逐行读取,从而降低了周期性波动。下面看一下分类效果。

dataArr,labelMat = loadDataSet()
weights = stocGradAscent1(array(dataArr),labelMat)
plotBestFit(weights)
                        

修改后的随机梯度上升法分类效果与最开始的批处理分类效果相近,但迭代次数大大减小(500-->150),在效率上大大提升,面对更多数据时,显然修改后的随机梯度上升法是更好的选择。

下一篇文章将通过sklearn实现对疝气病特征预测病马的死亡率的例子,并得到不同分类的预测概率,从而更直观的了解logistic回归的分类过程。




猜你喜欢

转载自blog.csdn.net/bit_666/article/details/79775793
今日推荐