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
- 通过随机采样来更新回归系数,有利于减少周期性波动。每次随即从列表中选出一个值,然后从列表中删掉该值后再进行下一次迭代。
以上就是Logisitic回归模型的构造方法以及参数求解。构造出模型函数后就可以根据模型函数对样本进行分类。