本文介绍logistic回归,和改进算法随机logistic回归,及一个病马是否可以治愈的案例。例子中涉及了数据清洗工作,缺失值的处理。
一 引言
1 sigmoid函数,这个非线性函数十分重要,f(z) = 1 / (1 + e^(-z) ), 画图如下:
这个函数可以很好的把数轴上的值映射到0,1区间,所以很好的解决了分类问题。下面是代码:
def sigmoid(inX): return 1.0/(1+exp(-inX))
2 梯度上升法是我们常用的最优化方法,公式。就是说沿这梯度方向迭代,alpha是步长,控制收敛速度;delta是对各个变量的偏微分;
def gradAscent(dataMatIn, classLabels): dataMatrix = mat(dataMatIn) #convert to NumPy matrix labelMat = mat(classLabels).transpose() #convert to NumPy matrix m,n = shape(dataMatrix) alpha = 0.001 maxCycles = 500 weights = ones((n,1)) for k in range(maxCycles): #heavy on matrix operations h = sigmoid(dataMatrix*weights) #matrix mult error = (labelMat - h) #vector subtraction weights = weights + alpha * dataMatrix.transpose()* error #matrix mult return weights
所有的数据集迭代500次,步长是0.001,目的是确认参数weights,weights会在500次左右收敛,误差较小。
二 逻辑回归算法
思路:根据梯度上升发,求出了最优化的参数weights,带入logistics分类器,y = (-weights[0]-weights[1]*x)/weights[2],预测测试样本即可;
inX*weights大于0.5,则分类到1,否则分类到0;主意,inX和weights都是向量;
def classifyVector(inX, weights): prob = sigmoid(sum(inX*weights)) if prob > 0.5: return 1.0 else: return 0.0
下面python代码主要利用了matplotlib这个包,模仿matlab画出了图;
def plotBestFit(weights): import matplotlib.pyplot as plt dataMat,labelMat=loadDataSet() dataArr = array(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‘) ax.scatter(xcord2, ycord2, s=30, c=‘green‘) x = arange(-3.0, 3.0, 0.1) y = (-weights[0]-weights[1]*x)/weights[2] ax.plot(x, y) plt.xlabel(‘X1‘); plt.ylabel(‘X2‘); plt.show()
效果还不错,只有两个实例分错了;
三 logistics改进:随机梯度上升
改进算法1:
上面的logistics缺陷:每次迭代都要遍历所有的数据集样本,这样迭代500次1000000个样本的数据集,压力很大;
提出增量的方法,每遍历一个样本就修改一次weighs;
def stocGradAscent0(dataMatrix, classLabels): m,n = shape(dataMatrix) alpha = 0.01 weights = ones(n) #initialize to all ones for i in range(m): h = sigmoid(sum(dataMatrix[i]*weights)) error = classLabels[i] - h weights = weights + alpha * error * dataMatrix[i] return weights
可以看出效果不如非增量的,这是可以接受的,因为增量的只遍历了一遍所有样本,时间上快了很多。
改进算法2:
上面的改进算法1,虽然在迭代15000次后三个参数均趋于收敛,但很明显x1 x2参数存在波动(原因是有些样本点不能正确分类,数据集也不是全线性不可分的所以误差很大),为了避免这种波动,提出了改进算法2;
改进点:
1迭代150次改进的增量算法1,这样还是比迭代200次全集时间要约简不少,并且准确率不低;
2步长alpha不是固定的,这样可以开始收敛速度大,后来越来越准确的时候收敛速度慢点,后面加0.0001是防止alpha为0。步长为0原地不动迭代就没有意义;
3为了避免上图的波动情况,随机选取样本点训练,然后再原数据集中删除,避免重复使用;下图可以看出,选用随机点可以避免周期性波动,波动确实可以变小;
def stocGradAscent1(dataMatrix, classLabels, numIter=150): m,n = shape(dataMatrix) weights = ones(n) #initialize to all ones for j in range(numIter): dataIndex = range(m) for i in range(m): alpha = 4/(1.0+j+i)+0.0001 #apha decreases with iteration, does not randIndex = int(random.uniform(0,len(dataIndex)))#go to 0 because of the constant h = sigmoid(sum(dataMatrix[randIndex]*weights)) error = classLabels[randIndex] - h weights = weights + alpha * error * dataMatrix[randIndex] del(dataIndex[randIndex]) return weights
分类结果:
四 实例: 病马治愈问题
先说明下,数据清洗是必要的工作,确实值是我们经常需要处理的;
缺失值的解决方案:
1使用特征的均值;
2使用-1等特殊值填充;
3删除有缺失值的样本;不推荐,有的数据的获得是不可恢复的
4使用相似样本的值来填充;
5使用其它算法计算确实值,比如kmeans等;
若样本的类属性缺失,监督学习中一般采用直接删除的方法;
主函数是multiTest(),原理和上面一样,整体过程如下:
训练分类器,获得线性分类器的参数weighs;
对测试样本应用分类器,classifyVector方法返回0或1;
统计错误率;
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 = []; trainingLabels = [] for line in frTrain.readlines(): currLine = line.strip().split(‘\t‘) lineArr =[] for i in range(21): lineArr.append(float(currLine[i])) trainingSet.append(lineArr) trainingLabels.append(float(currLine[21])) trainWeights = stocGradAscent1(array(trainingSet), trainingLabels, 1000) errorCount = 0; numTestVec = 0.0 for line in frTest.readlines(): numTestVec += 1.0 currLine = line.strip().split(‘\t‘) 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 def multiTest(): numTests = 10; errorSum=0.0 for k in range(numTests): errorSum += colicTest() print "after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests))
五 总结
1 logistic分类器只适用于数值属性,不能处理非数值型数据集;
2 logistic分类器的目的是寻找一个非线性函数sigmoid的最佳拟合参数;求解过程用到了最优化方法梯度上升法;