机器学习--Logistics
Posted zhengyinboke
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了机器学习--Logistics相关的知识,希望对你有一定的参考价值。
摘要
1. Logistic回归分类
2. 梯度下降法
3. 代码实现与解释
Logistic回归
逻辑斯特回归(logistic regression)是一种非常经典的分类方法。其用到的分类函数一般为Sigmoid函数,其函数形式为:
其图形表示如下:
从图中我们可以看到,当z=0时,函数值为0.5。随着z值的增加,对应的函数值将逼近于1;随着z值的减小,函数值将逼近于0。
因此为了实现logistic回归,对于样本,我们可以在每个特征上乘以一个回归系数,然后将所有的结果值相加,将总和带入sigmoid函数中,进而得到一个范围在0~1之间的数值。大于0.5的数据被分到1类,小于0.5的即被分到0类。
二分类问题
对于Logistic回归中的二分类问题,当我们给定数据样本x时,其被分到1类和0类的条件概率分别为:
其中,
那么上式就可改写为:
我们现在需要做的,就是怎么来确定模型中的参数w呢?
模型参数估计
在Logistic回归参数学习中,对于给定的训练数据集T={(x1,y1),(x2,y2),...(xN,yN)},我们用极大似然估计法估计模型参数w,从而得到logistic回归模型。
当我们对L(w)求得极大值,也就得到了参数w的估计值。
这样,问题就变成了以对数似然估计为目标函数的最优化问题。而我们解决这个问题我们一般采用梯度下降法和拟牛顿法。
多分类问题
当然,上述的logistic模型同样可以推广到多分类问题。设Y的取值为{1,2,3,...K},那么回归模型即为:
梯度下降法
梯度下降法可以解决上面对于参数w的优化问题。
梯度下降法是一种迭代算法,通过选取适当的初始值,不断迭代,对参数值不断更新,进行目标函数的极小化,直到收敛。由于负梯度方向是函数值减小最快的方向,所以在迭代的每一步,我们向负梯度方向更新参数值,从而减小函数值。
梯度算法的迭代公式为:
其中:
同理,梯度上升法,就是向梯度方向移动,以求得函数的极大值
随机梯度下降法
在实现梯度下降法时,我们发现在进行梯度下降法的回归系数更新时需要遍历整个数据集,如果样本或者特征过多的话,这种方法计算复杂度太高。
有一种改进的方法,就是一次我们只随机用一个样本点来更新回归系数,那么该方法就叫做随机梯度下降法。
这两种算法在下面的代码部分都有实现,读者可以自行参考。
求解回归系数w的方法除了上面提到的梯度下降法,还有拟牛顿法,想要详细了解的同学可以见《统计学习方法》附录B。
上面一部分,我们系统地从分类函数讲到怎么确定优化目标函数,再到怎么解优化目标函数。
至此我们就完成了Logistic回归原理部分的学习。下面是代码部分。
代码实现
1.回归梯度上升优化算法
程序清单:
1 from numpy import * 2 # 打开文本文件函数 3 def loadDataSet(): 4 dataMat=[];labelMat=[] 5 # 打开文本文件 6 fr=open(‘testSet.txt‘) 7 # 逐行读取 8 for line in fr.readlines(): 9 # 对文本进行处理,处理为一个列表 10 lineArr=line.strip().split() 11 # 加到dataMat中,并把第一个值设为1.0 12 dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])]) 13 # 求得类别标签 14 labelMat.append(int(lineArr[2])) 15 print(len(labelMat)) 16 return dataMat,labelMat 17 18 # 利用sigmoid函数进行计算 19 def sigmoid(inX): 20 return 1.0/(1+exp(-inX)) 21 22 # 梯度上升算法 23 # dataMatIn里存放的是特征,但是第一列都为1.0,实际上为100*3的矩阵 24 # classLabels里存放的是类别标签,1*100的行向量 25 26 def gradAscent(dataMatIn,classLabels): 27 # 转换为Numpy矩阵类型 28 dataMatrix=mat(dataMatIn) 29 # 转化为矩阵类型并求转置 30 labelMat=mat(classLabels).transpose() 31 # 求得矩阵大小 32 m,n=shape(dataMatrix) 33 # alpha是目标移动的步长 34 alpha=0.01 35 # 设置迭代次数 36 maxCycle=500 37 # 权重初始化为1 38 weights=ones((n,1)) 39 for k in range(maxCycle): 40 # 注意,这里h是一个m*1的列向量 41 h=sigmoid(dataMatrix*weights) 42 # 求得误差 43 error=(labelMat-h) 44 # 更新权重 45 weights = weights+alpha*dataMatrix.transpose()*error 46 return weights
2.画出数据集最佳拟合直线
程序清单:
1 # 作图 2 def plotBestFit(): 3 import matplotlib.pyplot as plt 4 dataMat,labelMat=loadDataSet() 5 weights = gradAscent(dataMat,labelMat) 6 dataArr=array(dataMat) 7 # 得到数据的样本数 8 n=shape(dataArr)[0] 9 xcord1=[];ycord1=[] 10 xcord2=[];ycord2=[] 11 # 将样本分成两类,放到列表中 12 for i in range(n): 13 if int(labelMat[i])==1: 14 xcord1.append(dataArr[i,1]);ycord1.append(dataArr[i,2]) 15 else: 16 xcord2.append(dataArr[i,1]);ycord2.append(dataArr[i,2]) 17 fig=plt.figure() 18 ax=fig.add_subplot(111) 19 # 两个种类用不同的颜色表示 20 ax.scatter(xcord1,ycord1,s=30,c=‘red‘,marker=‘s‘) 21 ax.scatter(xcord2,ycord2,s=30,c=‘green‘) 22 # 标注X轴的范围与步长 23 x=arange(-4.0,4.0,0.1) 24 #x = mat(x) 25 # 表示出分界线的方程 26 y=(-weights[0]-weights[1]*x)/weights[2] 27 print(y.shape) 28 ax.plot(x,y.transpose()) 29 # 坐标名称 30 plt.xlabel(‘X1‘);plt.ylabel(‘X2‘) 31 plt.show() 32 33 plotBestFit()
3.随机梯度上升算法
程序清单:
def stocGradAscent(dataMatrix,classLabels): #为便于计算,转为Numpy数组 dataMat=array(dataMatrix) 获取数据集的行数和列数 m,n=shape(dataMatrix) #初始化权值向量各个参数为1.0 weights=ones(n) #设置步长为0.01 alpha=0.01 #循环m次,每次选取数据集一个样本更新参数 for i in range(m): #计算当前样本的sigmoid函数值 h=sigmoid(dataMatrix[i]+weights) #计算当前样本的残差(代替梯度) error=(classLabels[i]-h) #更新权值参数 weights=weights+alpha*error*dataMatrix[i] return weights
4.改进的随机梯度上升算法
我们知道,评判一个优化算法的优劣的可靠方法是看其是否收敛,也就是说参数的值是否达到稳定值。此外,当参数值接近稳定时,仍然可能会出现一些小的周期性的波动。这种情况发生的原因是样本集中存在一些不能正确分类的样本点(数据集并非线性可分),所以这些点在每次迭代时会引发系数的剧烈改变,造成周期性的波动。显然我们希望算法能够避免来回波动,从而收敛到某个值,并且收敛速度也要足够快。
程序清单:
def stocGradAscent1(dataMatrix,classLabels,numIter=150): #将数据集列表转为Numpy数组 #dataMat=array(dataMatrix) #获取数据集的行数和列数 m,n=shape(dataMat) #初始化权值参数向量每个维度均为1 weights=ones(n) #循环每次迭代次数 for j in range(numIter): #获取数据集行下标列表 dataIndex = list(range(m)) #print(dataIndex) #遍历行列表 for i in range(m): #每次更新参数时设置动态的步长,且为保证多次迭代后对新数据仍然具有一定影响 #添加了固定步长0.1 alpha=4/(1.0+j+i)+0.1 #随机获取样本 randomIndex=int(random.uniform(0,len(dataIndex))) #计算当前sigmoid函数值 h=sigmoid(sum(dataMat[randomIndex]*weights)) #计算权值更新 #*********************************************** error=classLabels[randomIndex]-h weights=array(weights)+alpha*error*array(dataMat[randomIndex]) #*********************************************** #选取该样本后,将该样本下标删除,确保每次迭代时只使用一次 del (dataIndex[randomIndex]) return weights stocGradAscent1(dataMat,labelMat,numIter=150)
5.用Logistic回归进行分类
程序清单:
#sigmoid()分类函数 def classifyVector(inX,weights): prob=sigmoid(sum(inX*weights)) # 值如果大于0.5,归为1.0类 if(prob>0.5):return 1.0 else:return 0.0 def colicTest(): # 打开训练集合 frTrain=open(r‘C:UsersAdministratorDesktopMliA‘ ‘MLiA_SourceCodemachinelearninginactionCh05 horseColicTraining.txt‘) # 打开测试集合 frTest=open(r‘C:UsersAdministratorDesktopMliA‘ ‘MLiA_SourceCodemachinelearninginactionCh05horseColicTest.txt‘) # 初始化训练集和标签的列表 trainingSet=[] trainingLabels=[] for line in frTrain.readlines(): # 对训练集的数据格式化处理 currLine=line.strip().split(‘ ‘) lineArr=[] for i in range(21): # 将每一行的特征数据放到lineArr中 lineArr.append(float(currLine[i])) # 再将lineArr作为列表放到trainingSet中 trainingSet.append(lineArr) # 将标签放到trainingLabels中 trainingLabels.append(float(currLine[21])) # 得到训练集的权重 trainWeights=stocGraAscent1(array(trainingSet),trainingLabels,500) errorCount=0;numTestVec=0.0 # 对测试集进行测试 for line in frTest.readlines(): # 计算测试集的个数 numTestVec+=1.0 # 对测试数据进行格式化处理 currLine=line.strip().split(‘ ‘) lineArr=[] for i in range(21): lineArr.append(float(currLine[i])) # 如果学习出来的结果和真实结果不一致,则错误数加一 if(int(classifyVector(array(lineArr),trainWeights))!=int(currLine[21])): errorCount+=1 # 计算错误率 errorRate=(float(errorCount)/numTestVec) print(‘the error rate of this test is %f‘%errorRate) return errorRate # 调用colicTest()函数多次,计算错误率的平均值 def multiTest(): numTests=10 errorSum=0.0 for k in range(numTests): errorSum+=colicTest() print(‘after %d iterations the arrange error rate is:%f‘ % (numTests,errorSum/float(numTests)))
以上是关于机器学习--Logistics的主要内容,如果未能解决你的问题,请参考以下文章
机器学习实战------利用logistics回归预测病马死亡率