《机器学习实战》 (3) logistic regression(逻辑斯蒂回归)小结(下)

在上一讲中,通过数学推导,将logistics回归的数学原理推导了一遍,并用推导得到的偏导具体表达式求了出来,同时,通过Python实现了在数据集上上的logistics回归算法的基本应用,但是,基本的logistics回归是对所有数据求偏导的之和,所以需要通过传统方法求得的曲线虽然准确,但是每次实现运算量过大,所以需要对算法进行优化。

这里提出的第一种算法是SGD,随机梯度上升算法,该算法每次运算时,需要通过在数据集中任意选出一个数据,求出该数据的偏导,来对数据进行更新。通过这一种方法可以大大加快运算速度,同时,该方法得到的数据由于新样本来临时,可以进行批量更新,故又是一个批量更新算法。该算法的函数如下所示

def stocGranfAscent0(dataMatrix,classLabels):#SGD函数
    m,n=np.shape(dataMatrix)#获取数据集的长宽
    alpha=0.01#每次的步率
    weights=np.ones(n)#权重矩阵,赋初值全为1
    for i in range(m):#做m次SGD
        h=sigmoid(sum(dataMatrix[i]*weights))#每一行的数据和做激活函数运算,即输出的类
        error=classLabels[i]-h#实际值与运算值做差值
        weights=weights+alpha*error*dataMatrix[i]#代入weights的迭代公式进行迭代
    return weights
将上面的函数载入,获得200次运行的结果
from numpy import *
reload (logRegres)
dataArr,labelMat=logRegres.loadDataSet()
weights=logRegres.stocGranfAscent0(array(dataArr),labelMat)
logRegres.plotBestFit(weights)

生成的分割曲线是

观察图像不难发现图像相对于原先全梯度上升法,准确率下降了不少,同时,运算速度也快了一些。

其实,在尝试其他不同的迭代次数后可以发现,在经过500次迭代后的分割曲线会达到稳定值,这是因为在如果遇到没有被正确分类的样本点时,会在迭代时造成巨大的波动,这主要是由于每次所取的点过少导致的,所以单次只取一个点的做法实际上并不实际。

第二种方法相对于之前的SGD进行了进一步,首先每次迭代的步长发生了变化,把梯度上升比作下山的话,一般在斜率大的地方一般也会较陡峭,实际距离较大,而在接近底部的时候,往往会较平缓,此时每走一步的步长相对就会较小,基于这一想法,把步长从静态的恒定值变为前期较大,后期较小,这样既可以保证在一定的迭代次数以内就能实现找到最小值的目的,同时,还可以通过在后期减小步长实现收敛,防止在最小值附近震荡。第二,通过随机选取参数实现参数更新迭代,其函数如下所示

def stocGranfAscent1(dataMatrix,classLabels,numIter=1500):#DSGD函数
    m,n=np.shape(dataMatrix)
    weights=np.ones(n)
    for j in range(numIter):
        dataIndex=list(range(m))#将所有m值列表化表示出来
        for i in range(m):            
            alpha=4/(1.0+j+i)+0.01#alpha为变值,随运算进行不断减少
            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])#每计算一次权重更新后将刚刚用的m内的值删除,即不会再用 
    return weights

将其载入到jupyter notebook里

reload (logRegres)
dataArr,labelMat=logRegres.loadDataSet()
weights=logRegres.stocGranfAscent1(array(dataArr),labelMat)
logRegres.plotBestFit(weights)

得到的图像是

从图中不难发现此时的回归曲线分类正确率已经大大上升了。

下面,我们将随机梯度上升方法应用到病马生死预测问题上。

这个问题是真正意义上第一次我们学习着如何通过现有的一些特征来学习如何处理残缺值。

问题的情境是如果一头马患有疝病,则需要通过一些影响患病的因素来判断患病马的死亡率。

一个实际数据分析问题的流程分为如下几步:

一.收集数据,首先需要提取需要的特征,根据不同的特征需要来提取数据,这一步非常重要,因为特征的选择与收集决定了一个模型的上限,而无论采用何种算法本质上是对这些数据上限的逼近,本案例数据的训练集和验证集已经整理好了,所以这次不用操心。

二.整理数据,将收集到的数据进行整理,实际生活中收集到的数据往往是有残缺的,如何对残缺数据进行处理就是整理数据的主要工作。数据的缺失值需要根据不同的情况来进行分析,如果一个特征的数据出现缺失,常见的处理方法是:1.使用数据均值填补缺失值,2.使用特殊值来填补缺失值,3.讲有缺失的样本忽略。4.使用相似样本来填补缺失值5.使用其他机器学习算法实现缺失值的预测。

在本实验中,对于x与y要分别考虑,如果x出现缺失,需要通过将该缺失值以0来代替,这样做的好处是在特征值运算中,该特征对应值相乘后为0,对实际效果无影响,同时,0带入sigmoid函数输出值为0.5,代表的种类恰好在0与1之间,无任何偏向性,所以不会产生影响,而对于y而言,一旦该特征的某一个数据标签为0,则需要将该数据抛弃,这是因为标签很难在无提示条件下进行判断,所以将其舍弃。

这样将之前的算法进行处理,获得必要的信息进行分类,具体函数如下

def classifyVector(inX,weights):
                   prob=sigmoid(sum(inX*weights))
                   if prob>0.5: return 1.0
                   else: return 0
                   
def colicTest():
    frTrain=open('machinelearning/Ch05/horseColicTraining.txt')
    frTest=open('machinelearning/Ch05/horseColicTest.txt')
    trainingSet=[]
    trainingLabels=[]
    for line in frTrain.readlines():
        currLine = line.strip().split('\t')
        lineArr = []
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        trainingSet.append(lineArr)
        trainingLabels.append(float(currLine[-1]))
    trainWeights = stocGranfAscent1(np.array(trainingSet), trainingLabels, 50000)        #使用改进的随即上升梯度训练
    errorCount = 0; numTestVec = 0.0
    for line in frTest.readlines():
        numTestVec += 1.0
        currLine = line.strip().split('\t')
        lineArr =[]
        for i in range(len(currLine)-1):
            lineArr.append(float(currLine[i]))
        if int(classifyVector(np.array(lineArr), trainWeights))!= int(currLine[-1]):
            errorCount += 1
    errorRate = (float(errorCount)/numTestVec) * 100                         #错误率计算
    print("测试集错误率为: %.2f%%" % errorRate)
    return errorRate
                   
def multiTest():
    numTests=10
    errorSum=0.0
    for k in range(numTests):
        errorSum+=colicTest()
    print("after %d iteration the zverage error rate is:                  %f"%(numTests,errorSum/float(numTests)))
上述函数分为三部分,第一个函数是一旦某个特征sigmoid函数输出大于0.5判定其为1类,反之为0类。第二个函数是载入测试集与训练集,迭代500次获得模型,再将模型在测试集上进行测试,以获得错误率。第三个函数是将第二个函数重复进行三次以获得结果的平均值。载入jupyter notebook,
reload(logRegres)
logRegres.multiTest()
得到结果为36.1%
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值