python机器学习实战(四)

原文连接:www.cnblogs.com/fydeblog/p/…html

前言

这篇notebook是关于机器学习中logistic回归,内容包括基于logistic回归sigmoid分类,基于最优化方法的最佳系数肯定,从疝气病症预测病马的死亡率。 操做系统:ubuntu14.04 运行环境:anaconda-python2.7-jupyter notebook 参考书籍:机器学习实战和源码 notebook writer ----方阳python

注意事项:在这里说一句,默认环境python2.7的notebook,用python3.6的会出问题,还有个人目录可能跟大家的不同,大家本身跑的时候记得改目录,我会把notebook和代码以及数据集放到结尾的百度云盘,方便大家下载!算法

1. 基于logistic回归和sigmoid函数的分类

首先说说sigmoid函数吧。ubuntu

它的表达式是 g(z) = 1/(1+exp(-x)) ,为直观看出,咱们画画这个函数的曲线。数组

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(-5,5,200)
y = 1./(1+np.exp(-x))

plt.figure()
plt.plot(x,y)
plt.xlabel('x')
plt.ylabel('sigmiod(x)')
plt.show()
复制代码

上面就是sigmiod函数的图形,那么咱们怎么用sigmiod函数进行逻辑回归判决呢?bash

首先观察函数图形,sigmiod函数的y轴被限制在区间(0,1)上,这有利于咱们判决,将线性的无穷范围压缩到这个小范围,当x=0的时候,sigmiod(0) = 0.5, 因而咱们就将0.5看成界限,特征值乘以一个回归系数,而后结果相加,代入到这个sigmiod函数当中,将函数值大于0.5分为1类,小于0.5的分为0类,至此,logistic分类完成。app

2. 基于最优化方法的最佳回归系数肯定

这里说一下,sigmiod函数是为了帮助咱们来判断分类类别,而后与真实类别相比较,算出偏差,而后用梯度上升最小化偏差,获得最佳系数。dom

sigmiod函数的输入记为z,公式:z = w0x0+w1x1+w2x2+...+wnxn (这里0,1,2,...,n都表明下标系数),简单写就是 z = wTx (T表明转置)python2.7

2.1 梯度上升法

梯度上升法的思想:要找到某函数的最大值 ,最好的方法是沿着该函数的梯度方向探寻。机器学习

这是函数f(x,y)的梯度表达式,当要沿x方向移动时,就是对x求偏导;当要沿y方向移动时,就是对y求偏导。其中,函数f(x,y)必需要在待计算的点上可微。

梯度上升算法的迭代公式:w := w+a dw(f(w)) (dw是关于系数w的梯度,a是学习率)

梯度上升法的具体上升过程,如图所示

该公式将一直被迭代执行,直至达到某个中止条件为止,好比迭代次数达到某个指定值或算法达到某个能够容许的偏差范围。

梯度上升算法与梯度降低算法的区别:就是梯度上升的学习率前面的加号变为减号就是梯度降低算法

2.2 训练算法 :使用梯度上升找到最佳参数

梯度上升法的伪代码以下:

每一个回归系数初始化为1
重复R次:
       计算整个数据集的梯度
       使用alpha X gradient更新回归系数
返回回归系数
下面进入梯度上升法的具体实现
复制代码

下面进入梯度上升法的具体实现

def loadDataSet():
    dataMat = []; labelMat = []
    fr = open('testSet.txt')
    for line in fr.readlines():
        lineArr = line.strip().split()
        dataMat.append([1.0, float(lineArr[0]), float(lineArr[1])])
        labelMat.append(int(lineArr[2]))
    return dataMat,labelMat

def sigmoid(inX):
    return 1.0/(1+exp(-inX))

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
复制代码

第一个函数loadDataSet函数就是导入数据集,并进行封装,将特征值封装成三列的多维列表dataMat,label放在labelMat里面 第二个函数不用多说,就是sigmiod函数 第三个函数就是梯度上升的具体算法,dataMatIn就是上面的多维列表dataMat,classlabels就是labelMat,函数首先将输入的数据集和标签所有转化成的numpy矩阵,是为了可以进行矩阵运算和向量运算,maxCycles表明迭代的最大次数,至于参数的迭代还能够看看如下图片,我选的是吴恩达的ppt上,这写的是梯度降低的迭代过程(梯度上升就是反过来,原理同样),可见损失函数的梯度会有逐渐趋于0,这样迭代公式的梯度那项也会趋于0,参数不会有太大的浮动,趋于稳定,偏差最小。

测试一下吧

cd 桌面/machinelearninginaction/Ch05 /home/fangyang/桌面/machinelearninginaction/Ch05 import logRegres dataMat , labelMat = logRegres.loadDataSet() logRegres.gradAscent(dataMat,labelMat)

2.3 分析数据:画出决策边界

咱们获得了最佳系数,便于理解,咱们要画出分隔线,便于咱们观察

代码以下:

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()
复制代码

这个函数先是调用loadDataSet函数将数据集和标签赋给dataMat,labelMat,而后对不一样类别进行不一样的分组,类别1的数据放在xcord1和ycord1,类别2的数据放在xcord2和ycord2,而后分别显示,最后画出输入的权重对应的分隔线,y的求解你可能有疑问,这里说一下,具体表达式是wTx=0,wT是输入权重,x=[x0,x1,x2],其中x0为了方便表示所创建的,值为1,x1就是上述函数的x,x2就是y,这下你就知道为何是那个表达式了吧。

from numpy import * weights = logRegres.gradAscent(dataMat,labelMat) logRegres.plotBestFit(weights.getA()) # the function of getA is used to transform matrix into array

能够看出图中只错分了两到四个点,效果不错。

2.4 训练算法:随机梯度上升

梯度上升算法在每次更新回归系数时都须要遍历整个数据集,当特征的数目很是多的时候,计算量会很是巨大。 一种改进方法是一次仅用一个样本点来更新回归系数, 该方法称为随机梯度上升算法。 因为能够在新样本到来时对分类器进行增量式更新,于是随机梯度上升算法是一个在线学习算法。与在线学习相对应 ,一次处理全部数据被称做是 “批处理” 。

随机梯度上升算法的伪代码以下

全部回归系数初始化为 1
对数据集中每一个样本
          计算该样本的梯度
          使用 alpha x gradient更新回归系数值
返回回归系数值
复制代码

代码以下:

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
复制代码

能够看到 ,随机梯度上升算法与梯度上升算法在代码上很类似,但也有一些区别: 第一 ,后者的变量h和偏差error都是向量 ,而前者则全是数值; 第二 ,前者没有矩阵的转换过程,全部变量的数据类型都是NumPy数组。 这是由于梯度上升算法是遍历全部数据,造成的是全部数据的向量,而随机梯度上升算法每次只用一个样本,因此是单一数值。其余相似

来测试一下效果吧

weights = logRegres.stocGradAscent0(array(dataMat),labelMat) logRegres.plotBestFit(weights)

可见拟合的直线不完美,错分了三分之一的样本,直接比较结果是不公平的,梯度上升算法是在整个数据集中迭代了500次,而随机梯度算法只在整个数据集中迭代了1次,计算量相差不少倍。因此这里还需对随机梯度上升算法进行优化,代码以下

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)))
            h = sigmoid(sum(dataMatrix[randIndex]*weights))
            error = classLabels[randIndex] - h
            weights = weights + alpha * error * dataMatrix[randIndex]
            del(dataIndex[randIndex])
    return weights
复制代码

这里主要改了两点: 第一点增长了alpha动态减小的机制,这样作的缘由是为了保证在屡次迭代以后新数据仍然具备必定的影响。 第二点是经过随机选取样原本更新回归系数,这种方法减小周期性的波动。每次随机从列表中选出一个值,而后从列表中删掉该值,从新迭代 须要注意的是: 若是要处理的问题是动态变化的,那么能够适当加大上述常数项,来确保新的值得到更大的回归系数。

再次运行出来看看

weights = logRegres.stocGradAscent1(array(dataMat),labelMat) logRegres.plotBestFit(weights)

可见分类效果与梯度上升算法差很少,比较一下计算量,梯度上升算法迭代了500次的整个数据集,而随机梯度上升算法迭代了150次就达到相似的效果。

3. 示例:从疝气病症预测病马的死亡率

这个例子是经过马疝病的一些指标,使用logistic回归和随机梯度上升算法来预测病马的生死。

3.1 准备数据:处理数据中的缺失值

马疝病的数据集中有30%的值是缺失的,咱们怎样来解决这个问题呢?

首先咱们要知道,有时候数据是很是昂贵的,扔掉缺失数据和从新获取新的数据都是不可取的,因此咱们采用一些方法来解决这个问题,方法以下:

下面给出了一些可选的作法:

  • 使用可用特征的均值来填补缺失值;
  • 使用特殊值来补缺失值,如 -1;
  • 忽略有缺失值的样本;
  • 使用类似样本的均值添补缺失值;
  • 使用另外的机器学习算法预测缺失值。

预处理阶段的两件事:

第一件事,全部的缺失值必须用一个实数值来替换,由于咱们使用的numpy数据类型不容许包含缺失值。

第二件事,若是在测试数据集中发现了一条数据的类别标签已经缺失,那么咱们的简单作法是将该条数据丢弃。这是由于类别标签与特征不一样,很难肯定采用某个合适的值来替换。

这个例子选实数0来替换全部缺失值,不影响特征系数,若是等于0,对应的参数也被置0,不会更新,还有就是sigmiod(0)=0.5,即对结果的预测不具备任何倾向性,全部用0代替缺失值

3.2 测试算法 :用Logistic回归进行分类

使用Logistic回归方法进行分类并不须要作不少工做,所需作的只是把测试集上每一个特征向量乘以最优化方法得来的回归系数,再将该乘积结果求和,最后输人到sigmiod函数中便可。若是对应的sigmiod值大于0.5就预测类别标签为1 , 不然为0 。

例子的代码以下

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))
复制代码

第一个函数classifyVector,它以回归系数和特征向量做为输入来计算对应的sigmiod值,若是值大于0.5返回1,不然返回0 第二个函数colicTest,先是打开训练集和测试集,而后使用改善后的随机梯度上升算法对训练集进行训练,获得训练参数,而后使用这个训练参数带入到测试集当中,比较这个测试集使用该参数获得的类别和实际类别,算出错误的个数,最终程序是返回错误率 第三个函数就是屡次使用第二个函数,最后打印出平均错误率

logRegres.multiTest()

平均错误率34.7761%,这个错误率还好,毕竟咱们有30%的数据缺失嘛,做者书上调整迭代次数和步长,能够改善错误率,大家能够试一试。

小结

  1. logistic回归的目的是寻找一个非线性函数sigmiod的最佳拟合参数,求解过程能够由最优化算法来完成。在最优化算法中,最经常使用的就是梯度上升算法, 而梯度上升算法又能够简化为随机梯度上升算法。

  2. 随机梯度上升算法与梯度上升算法的效果至关, 但占用更少的计算资源。此 外 ,随机梯度上升是一个在线算法, 它能够在新数据到来时就完成参数更新, 而不须要从新读取整个数据集来进行批处理运算。

百度云连接:pan.baidu.com/s/1cSRMHwvi…


相关文章和视频推荐

圆方圆学院聚集 Python + AI 名师,打造精品的 Python + AI 技术课程。 在各大平台都长期有优质免费公开课,欢迎报名收看。

公开课地址:ke.qq.com/course/3627…

加入python学习讨论群 78486745 ,获取资料,和广大群友一块儿学习。

圆方圆python技术讨论群
圆方圆python技术讨论群
相关文章
相关标签/搜索