当前位置: 代码迷 >> GIS >> MachineLearning—Logistic Regression(2)
  详细解决方案

MachineLearning—Logistic Regression(2)

热度:502   发布时间:2016-05-05 06:03:34.0
MachineLearning—Logistic Regression(二)

    本篇博文主要接着上一篇博文Logistic Regression(一)介绍逻辑回归的应用,其中包括具体实现时用到的梯度上升算法,随机梯度上升算法,改进随机梯度上升算法,最后还有一个具体的应用小例子;本文内容主要结合《机器学习实战》这本书,以较为细致的方式一步一步讲解逻辑回归的实现,代码为Python(江湖人称“拍神”)版;


    回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类。这里的“ 回归” 一词源于最佳拟合,表示要找到最佳拟合参数集,其背后的数学分析将在下一部分介绍。训练分类器时的做法就是寻找最佳拟合参数,使用的是最优化算法;

    我们可以在每个特征上都乘以一个回归系数,然后把所有的结果值相加,将这个总和代人8丨 81110丨 ?1函数中,进而得到一个范围在0? 1之间的数值; 

    z = w0x0 +w1x1 + w2x2+ ? ? ? + wnxn

    梯度上升算法已经在上一篇的博文中进行了详细的推导解析,这里就不再过度推导介绍了;我们依然要记住梯度算子总是指向函数值增长最快的方向;还有就是梯度上升算法用来求函数的最大值,而梯度下降算法用来求函数的最小值。


这里我们有100个样本点数据,每个数据有两个特征X1,X2;最后一列是标签分类

<span style="font-family:Microsoft YaHei;">from numpy import *def loadDataSet():    dataMat = []; labelMat = []                      //首先定义两个数组,一个装数据一个装标签    fr = open('testSet.txt')                              //打开文件    for line in fr.readlines():        lineArr = line.strip().<strong>split()</strong>        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])         //X1,X2特征,前面加上X0=1        labelMat.append(int(lineArr[2]))    return dataMat,labelMat                           //返回100*3 100 数组def sigmoid(inX):    return 1.0/(1+exp(-inX))                            //返回g(z) def<strong> gradAscent</strong>(dataMatIn, classLabels):                                             //定义梯度上升的函数    dataMatrix = mat(dataMatIn)                     #convert to NumPy matrix转化为矩阵    labelMat = mat(classLabels).transpose()  #convert to NumPy matrix<strong>转化为竖着的形式</strong>    m,n = shape(dataMatrix)    alpha = 0.001    maxCycles = 500    weights = ones((n,1))                       //3行1列的全1矩阵 初始化权值全为1    for k in range(maxCycles):              #heavy on matrix operations  500次迭代        h = sigmoid(dataMatrix*weights)     //θX的计算  100*3  3*1  最后带入sigmoid        error = (labelMat - h)                           //误差计算 即公式中的y-h  注意h是100*1的矩阵        weights = weights + alpha * <strong>dataMatrix.transpose()</strong>* error         //使权值沿着error的方向更新迭代  注意data进行了转置    return weights                            //n个权值,θj</span>


最后得出权值矩阵为


如何画出决策边界

<span style="font-family:Microsoft YaHei;"> def plotBestFit(weights):    import matplotlib.pyplot as plt    dataMat,labelMat=loadDataSet()    dataArr = array(dataMat)                              //转化为数组的形式    n = shape(dataArr)[0]         //shape返回数组的大小,(100,3)的形式    n=100    xcord1 = []; ycord1 = []      //1类    xcord2 = []; ycord2 = []      //0类    for i in range(n):        if int(labelMat[i])== 1:            xcord1.append(dataArr[i,1]); ycord1.append(dataArr[i,2])       //如果属于1类         X1,Y1样本点坐标        else:            xcord2.append(dataArr[i,1]); ycord2.append(dataArr[i,2])        //0类                   x2,y2样本点坐标    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)        //x坐标范围    y = (-weights[0]-weights[1]*x)/weights[2]                //由0=W0X0+W1X1+W2X2得出X2的表达式,也就是纵坐标Y    ax.plot(x, y)    plt.xlabel('X1'); plt.ylabel('X2');    plt.show()</span>





随机梯度上升算法:

我们发现这种批处理方法计算量往往比较大,每次都要计算所有的m个样本点同时还有所有的n个特征(或者说θ参数系数,特征的个数),所以我们引入随机梯度上升算法来简化计算量;随机梯度上升算法的特点是一次仅用一个样本点来更新回归系数;


def stocGradAscent0(dataMatrix, classLabels):    m,n = shape(dataMatrix)    alpha = 0.01    weights = ones(n)   #初始化权值都为1,3*1    for i in range(m):   //m个样本点        h = sigmoid(sum(<strong>dataMatrix[i]</strong>*weights))      //每次使用一个样本点来更新权值        error = <strong>classLabels[i]</strong> - h        weights = weights + alpha * error * <strong>dataMatrix[i]</strong>    return weights


对比图我们发现此时的分界线并不完美,错误分类大约三分之一,我们将代码修改让其重复迭代200次后效果会更好;


产生波动现象的原因是存在一些不能正确分类的样本点(数据集并非线性可分),在每次迭代时会引发系数的剧烈改变;



一种改进的随机梯度上升算法:

<span style="font-family:Microsoft YaHei;">def stocGradAscent1(dataMatrix, classLabels, numIter=150):    m,n = shape(dataMatrix)    weights = ones(n)   #initialize to all ones    for j in range(numIter):           //150次迭代        dataIndex = range(m)        for i in range<strong>(m)</strong>:            //m次随机选取            alpha = 4/(1.0+j+i)+0.0001    </span>
<span style="font-family:Microsoft YaHei;">                  #alpha每次迭代和每次的样本点都会调整,即调整步幅越来越小从而避免波动  但不会最后变为0,因为0.0001为了数据始终具有影响             randIndex = int(random.uniform(0,len(dataIndex)))    #实际上alpha并非严格下降,比如j=1,i=200 j=2,i=100这个时候步幅是增大的            h = sigmoid(sum(dataMatrix<strong>[randIndex]</strong>*weights))      //通过随机选取避免周期性波动            error = classLabels<strong>[randIndex]</strong> - h            weights = weights + alpha * error * dataMatrix<strong>[randIndex]</strong>            del(dataIndex<strong>[randIndex]</strong>)                             //要删掉当前的index避免重复选到同一个样本点    return weights</span>


该方法比采用固定alpha的方法收敛速度更快,且分隔线达到了与Gradientascent()差不多的效果,但是所使用的计算量更少;




一个小例子:预测马的存活与否

数据是22列,即有21个特征,最后一列为样本标签;(一点数据清洗的问题,将缺失的特征置为0,缺失的标签舍去整个样本点)

<span style="font-family:Microsoft YaHei;">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)         //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 errorRatedef 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))        </span>

运行结果:





  相关解决方案