python3.5.2之《机器学习实战》之Logistic回归——python3.5.2+Eclipse4.5.2+pydev5.2.0+Geany——自己配合书看代码

这里就有一些麻烦的地方:为什么梯度是:error*y*x

这其中是用到了likelihood的但是证明实在太麻烦了,我引用一下台北大学林轩田老师的证明方法:

证明这里其实就出来了,这个梯度结合一下PLA,发现如下

这就是 : 学习效率 * error(是否等于标注) * y * n。下面是代码

#coding=gbk
from numpy import *
'''
Created on 2018年8月1日

@author: 张怡
'''
from statsmodels.sandbox.nonparametric.tests.ex_smoothers import weights

'''
    logistic回归进行分类的主要思想是:根据现有数据对分类边界线建立回归公式,以此进行分类。
    逻辑斯蒂回归主要针对的是0-1数值,二分类预测,用到的函数是sigmoid函数,傻子都应该记住的一个函数
    当该值大于0.5归类为1,否则归类为0,这样就完成了二分类的任务。
    所以也算是一个概率估计
    
二.关于最佳回归系数的确定
    w就是我们要拟合的最佳参数,
    1,。梯度上升法、
    2.使用梯度上升发寻找最佳参数
'''
#预处理数据
def loadDataSet():
    #创建两个列表
    dataMat = []; labelMat = []
    #打开文本数据集
    fr = open('testSet.txt')
    #遍历文本的每一行
    for line in fr.readlines():
        #对当前行去除收尾空格,并按空格进行分离
        lineArr = line.strip().split()
        #讲每一行的两个特征x1,x2,加上x0=1,组成列表并添加到数据集列表中
        dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
        #将当前行的标签添加到标签列表中
        labelMat.append(int(lineArr[2]))
    #返回数据列表、标签列表
    return dataMat,labelMat

#定义sigmoid函数
def sigmoid(inx):
    return 1/(1+exp(-inx))

#梯度上升法更新最有拟合参数
#@dataMatIn : 数据集
#@classLabels : 数据标签
def gradAscent(dataMatIn , classLabels):
    #将数据集列表转换为Numpy矩阵
    dataMatrix = mat(dataMatIn)
    #将数据集标签列表转换Numpy矩阵,并转置
    labelMat = mat(classLabels).transpose()
    #获取数据集矩阵的行数和猎术
    m,n = shape(dataMatrix)
    #学习步长
    alpha = 0.001
    #最大迭代次数
    maxCycles = 500
    #初始化权值参数向量的每个维度均为1.0
    #是个向量,毕竟维度嘛
    weights = ones((n,1))
    #循环迭代次数
    for k in range(maxCycles):
        #求当前的sigmoid函数预测概率
        #每次都要遍历数据一次
        h = sigmoid(dataMatrix*weights)
        #*******************************************
        #此处计算真是类别和预测类别的差值
        #对logistic回归函数的对数释然函数的参数项求偏导
        error = (labelMat-h)
        #更新权值参数s
        #梯度是对参数进行求偏导数
        #w  =  w + x * error(y)这是梯度上升
        #梯度下降就是减法,但是PLA中是w=w-y*x
        weights = weights+alpha*dataMatrix.transpose()*error
    return weights

#还可以通过matpotlib画出决策的边界。集体代码是
def plotBestFit(wei):
    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()  

'''
    我们知道梯度上升法每次更新回归系数都需要遍历整个数据集,当样本数量较小时,该方法尚可,但是当样本数据集非常大且特征非常多时,那么随机梯度下降法的计算复杂度就会特别高。一种改进的方法是一次仅用一个样本点来更新回归系数,即岁集梯度上升法。由于可以在新样本到来时对分类器进行增量式更新,因此随机梯度上升法是一个在线学习算法。随机梯度上升法可以写成如下伪代码:
所有回归系数初始化为1
    随机梯度上升是一个在线学习算法,与在线学习相对应,一次处理所有数据被称作是’批处理‘

对数据集每个样本
  计算该样本的梯度
  使用alpha*gradient更新回顾系数值
 返回回归系数值
 
 前者对参数采用精确解析的方式,计算时间场但模型性能较高,后者采用随机梯度上升估算模型参数,计算时间短但产出的模型性能略低
'''
def stocGradAscent(dataMatrix , classLabels):
    #获取数据集的行数和列数
    m,n = shape(dataMatrix)
    #为了方便计算转换为numpy数组,也就是矩阵
    dataMat = mat(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
    


'''
    评判一个优化算法的优劣的可靠方法是看其是否收敛,也就是说参数的值是否达到稳定值。此外,当参数值接近稳定时,仍然可能会出现一些小的周期性的波动。这种情况发生的原因是样本集中存在一些不能正确分类的样本点(数据集并非线性可分),所以这些点在每次迭代时会引发系数的剧烈改变,造成周期性的波动。显然我们希望算法能够避免来回波动,从而收敛到某个值,并且收敛速度也要足够快。
'''
#因此进行适当的修改
#@dataMatriX: 数据集列表
#@classLabels : 标签类别o
#@numIter :迭代次数,默认150
def stocGradAscent1(dataMatriX,classLabels,numTter = 150):
    #为了方便计算转换为numpy数组,也就是矩阵
    dataMat = array(classLabels)
    #获取数据集的行数和列数
    m,n = shape(dataMat)
    #初始化权值向量各个参数为1.0
    #而且是一个矩阵或者向量
    weights= ones(n)
    #循环150次数
    for j in range(numTter):
        #获取数据集行下标列表
        dataIndex = range(m)
        #遍历行列表
        for i in range(m):
            #每次更新参数时候设置动态的学习效率,且为保证多次迭代后对数据仍有一定的影响
            #添加了固定的效率.1
            alpha = 4/(1.0+j+i)+0.1
            #随机获得样本
            randomIndex = int(random.nuiform(0,len(dataIndex)))
            #计算当前sigmoid函数值
            h = sigmoid(dataMat[randomIndex]*weights)
            #计算权值更新
            #***********************************************
            error = classLabels - h
            weights = weights + alpha*error*dataMat[randomIndex]
            #***********************************************
            #选取该样本后,该样本下表删除,确保每次迭代时候只使用一次
            del(dataIndex[randomIndex])
'''
上述代码有两点改进:
1.一个是学习效率,每次迭代时候都会调整,这样就不会有高频波动,而且有一个固定的常数0.1.也不会为0
2.随机选取样本更新回归系数,减少了周期性的波动,用完就删除这个值
3.s手链的更快,因为遍历的次数不再是500次,而是20次
'''
#*********************************************
'''
从疝气病症预测病马死亡率
1.现有数据集有100个样本和20个特征,但是数据存在一定的问题,即数据集有30%的缺失,因此,我们在对病马进行预测死亡率前,首先要解决数据的缺失问题。
遇到数据缺失的情况,但有时候数据相当昂贵,扔掉和重新获取均不可取,这显然是会带来更多的成本负担,所以我们可以选取一些有效的方法来解决该类问题。比如:
1 使用可用特征的均值填补缺失值
2 使用特殊值来填补缺失的特征,如-1
3 忽略有缺失值的样本
4 使用相似样本的平均值填补缺失值
5 使用另外的机器学习算法预测缺失值
    这里我们根据logistic回归的函数特征,选择实数0代替所有缺失值,这恰好使用logistics回归,因此他在参数更新时候不会影响参数的值
    也就是:如果某个特征对应值为0.公式w也不会发生改变
'''
#------------------------------实例:从疝气病预测病马的死亡率----------------------------
#1.准备数据:处理数据的缺失值
#这里讲特征的缺失值补为0,从而在更新时候不影响系数的值
#2 分类决策函数
def clasifyVector(inX , weights):
    #计算logistics回归预测概率
    prob = sigmoid(inX*weights)
    #大于0.5预测为1
    if prob > 0.5:
        return 1.0
    else:
        return 0.0
#logistic回归预测算法
def colicTest():
    #打开训练数据集
    frTrain = open('horseColicTraining.txt')
    #打开测试数据集
    frTest = open('horseColicTest.txt')
    #新建两个空列表,保存训练集和测试集
    trainingSet = [];trainingLabels = []
    #读取训练集文档的每一行
    for line in frTrain.readlines():
        #对当前行进行特征分割
        #去掉'','\t',\n\
        currLine = line.strip().split()
        #新建列表存储每个样本的特征向量
        lineArr = []
        #遍历每个样本的特征
        #range(21)是0-20
        for i in range(21):
            #讲这个样本的特征存入lineArr列表
            lineArr.append(float(currLine(i)))
        #讲这个样本的标签存入标签列表
        trainingLabels.append(currLine[21])
        #将这个样本的特征向量添加到数据集列表
        trainingSet.append(lineArr)
    #调动随机梯度上升发更新logistics回归的权值参数
    trainWeights = stocGradAscent1(trainingSet, trainingLabels, 500)
    #统计测试数据集预测错误样本数量和样本总数
    errorCount = 0 ; numTestVec = 0.0
    #遍历测试数据集的每个样本
    for line in frTest.readlines():
        #样本总数加1
        numTestVec += 1.0
        #对当前行进行处理,分割出各个特征以及样本标签
        currLine = line.strip().split()
        #新建特征向量
        lineArr = []
        #将各个特征构成特征向量
        for i in range(21):
            lineArr.append(float(currLine[i]))
        #利用分类预测函数对这个样本进行预测,并且与样本标签比较
        if(clasifyVector(lineArr, trainWeights) != currLine[21]):
            errorCount +=1
    #计算测试机总的预测错误率
    errorRate = float(errorCount)/numTestVec
    #打印
    print('error is : %f'%'errorRate')
    return errorRate

#多次测试算法求平均值
def multTest():
    #设计为10次
    numTest = 10 ; errorRateSum = 0.0
    #每一次测试算法并统计错误率
    for k in range(numTest):
        errorRateSum += colicTest()
    #打印10次平均值
    print('after %d iterations the average error rate is: %f''%(numTests,errorRateSum/float(numTests)')
    
'''
这样,经过10次的迭代,平均误差为35%,显然这跟30%的数据缺失有一定的关系,当然,如果我们调整合适的步长和迭代次数,相信错误率会有所下降。

四,小结

  logistic回归的目的是寻找一个非线性函数sigmoid的最佳拟合参数,从而来相对准确的预测分类结果。为了找出最佳的函数拟合参数,最常用的优化算法为梯度上升法,当然我们为了节省计算损耗,通常选择随机梯度上升法来迭代更新拟合参数。并且,随机梯度上升法是一种在线学习算法,它可以在新数据到来时完成参数的更新,而不需要重新读取整个数据集来进行批处理运算。

  总的来说,logistic回归算法,其具有计算代价不高,易于理解和实现等优点;此外,logistic回归算法容易出现欠拟合,以及分类精度不太高的缺点。
'''

猜你喜欢

转载自blog.csdn.net/xinjieyuan/article/details/81368577