Logistic回归的一般过程为:
- 收集数据;
- 准备数据:要求是数值型
- 分析数据;
- 训练算法:训练的目的是找到最佳的分类回归系数w和b
- 测试算法;
- 使用:输入数据并基于训练好的回归系数对样本进行分类
基于梯度上升法的优化方法确定回归系数:
w:=w+α▽f(w),其中w是要优化的参数,α是更新步长,▽是梯度。
"""自定义训练数据集""" def loadDataSet(): dataMat=[] #矩阵[x0,x1,x2],x0是b对应的x,值恒为1 labelMat=[] #标签向量 fr=open("testSet.txt") for line in fr.readlines(): lineArr=line.strip().split() #strip()移除字符串头尾指定字符,默认空格;split()通过指定分隔符将字符串切片,默认为空格 dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])]) #因为文件中读入的是字符串,所以将字符串转化为float类型 labelMat.append(int(lineArr[2])) return dataMat,labelMat """定义sigmoid函数""" def sigmoid(inX): return 1.0/(1+exp(-inX)) """梯度上升法""" def gradAscent(dataMatIn,classLabels): dataMatrix=mat(dataMatIn) #用dataMatIn创建特征矩阵 labelMat=mat(classLabels).transpose() #调换矩阵的坐标顺序,对于二维矩阵来说,transpose()就是转置 m,n=shape(dataMatrix) #m是样本数,n是特征数 alpha=0.001 #梯度上升步长 maxCycles=500 #最大迭代次数 weights=ones((n,1)) #权重向量b,初始化为全1 for k in range(maxCycles): h=sigmoid(dataMatrix*weights) #对w1*x1+w2*x2求对数几率回归 error=(labelMat-h) #预测值和真实标签之间的误差 weights=weights+alpha*dataMatrix.transpose()*error #梯度上升更新权重 return weights
解出回归系数之后就是根据系数(权重weights),画出决策边界:
"""画出数据集和Logistic回归的最佳拟合线的函数""" def plotBestFit(weights): dataMat,labelMat=loadDataSet() dataArr=array(dataMat) #将矩阵转化为数组 n=shape(dataMat)[0] #n为样本数 xcord1=[];ycord1=[] xcord2=[];ycord2=[] for i in range(n): #遍历所有样本 if int(labelMat[i])== 1: #如果是正样本 xcord1.append(dataArr[i,1]) #将正样本的x1属性的值加入xcord1 ycord1.append(dataArr[i,2]) #将正样本的y1属性的值加入ycord1 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') #绘制散点图 ax.scatter(xcord2,ycord2,s=30,c='green') x=arange(-3.0,3.0,0.1) y=(-weights[0]-weights[1]*x)/weights[2] #设置sigmoid函数为0,求解X2和X1的关系式,X0=1恒成立 ax.plot(x,y) #画线函数 plt.xlabel('X1') plt.ylabel('X2') plt.show() """测试""" if __name__=='__main__': dataArr,labelMat=loadDataSet() weights=gradAscent(dataArr,labelMat) plotBestFit(weights.getA()) #getA()作用和mat()相反,是将矩阵转化为数组
结果输出为:
优化算法:
梯度上升算法每次更新回归系数都要遍历整个数据集,这对于大容量样本来说是不能接受的。改进的办法是,每次都仅用一个样本点来更新回归系数,该方法称为随机梯度上升。
"""随机梯度上升""" def stocGradAscent0(dataMatrix,classLabels): m,n=shape(dataMatrix) #m是样本数,n是特征数 alpha=0.01 weights=ones(n) for i in range(m): h=sigmoid(sum(dataMatrix[i]*weights)) #随机梯度上升 error=classLabels[i]-h weights=weights+alpha*error*dataMatrix[i] return weights
比较上述两种拟合直线可以发现,随机梯度上升导致了分类器错分了三分之一的样本。然而这种比较是有问题的,因为第一种梯度上升法中所得的结果是在数据集上迭代了500次才得到的。而一个判断优化算法优劣的可靠方法是看它是否收敛,也就是说参数是否达到了稳定值不再不断地变化。
随机梯度上升导致每次迭代是会引发系数的剧烈改变,我们期望算法能避免来回波动,从而收敛到某个值,另外,收敛速度也需要加快。
改进的随机梯度上升算法如下:
"""改进的梯度上升算法""" def stocGradAscent1(dataMatrix,classLabels,numIter=150): m,n=shape(dataMatrix) #m是样本数,n是特征数 weights=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[i]*weights)) #随机梯度上升 error=classLabels[randIndex]-h weights=weights+alpha*error*dataMatrix[randIndex] del(dataIndex[randIndex]) return weights
改进之处:
- alpha在每次迭代时都会调整,可以缓减数据波动或者高频波动,此外alpha会随着迭代次数不断减小但永远不会减小到0
- 通过随机采样来更新回归系数,有利于减少周期性波动。每次随即从列表中选出一个值,然后从列表中删掉该值后再进行下一次迭代。