机器学习实战之Logistic回归

逻辑回归实际上是分类算法,称之为回归是因为要选择最佳参数拟合出分类边界。

1.sigmoid函数

\large \sigma\left ( z \right )=\frac{1}{1+e^{-z}}

当输入z=0时,输出为1;随着x值增大,函数值逼近1;随着z减小,函数值逼近零。当从较长的尺度来看,sigmoid函数很像一个节约函数。

利用sigmoid函数作为分类器,我们可以将每个特征乘上一个回归系数,然后求和作为输入z,输出大于0.5的归为1类,输出不大于0.5的归为0类。

即令\large z=\Theta ^{T}\cdot x=\Theta _{0}x_{0}+\Theta_{1}x_{1}+\Theta_{2}x_{2}... ,

x为特征向量,\large \Theta为待求回归参数。

2.梯度下降法

梯度下降法是一种求解函数最小值的优化算法。即每次迭代都沿着梯度下降的方向。

其算法为:

                                            \large \Theta :=\Theta-\alpha \frac{\partial f(\Theta)}{\partial \Theta}

其中\large \alpha为学习率,需要选择恰当。

带入求梯度后,Logistic回归梯度下降法求参数的算法为:

\large \Theta:=\Theta-\alpha\frac{1}{m}\sum_{i=1}^{m}(\sigma (\Theta^{T} x_{i})-y(i))x_{i}

记特征矩阵为m*n的矩阵X,则上式变为

\large \Theta:=\Theta-\alpha\frac{1}{m}X^{^{T}}(\sigma (\Theta ^{^{T}}x)-y) 其中\large \sigma和y为向量。

3.代码

(1)数据导入

from numpy import *
#数据导入,输出为两个list,分别为特征矩阵和标签
def dataLoad(filename):
    dataMat=[]
    labelMat=[]
    fr=open(filename)
    for line in fr.readlines():
        lineArr=line.strip().split()
        dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
        labelMat.append(float(lineArr[2]))
    return dataMat,labelMat

(2)梯度下降实现

#定义sigmoid函数
def sigmoid(inX):
    return (1.0/(1.0+exp(-inX)))

def gradAscent(datamatIn,labelclss):
    dataMatrix=mat(datamatIn)
    labelMatrix=mat(labelclss).transpose()
    m,n=shape(dataMatrix)
    alpha=0.001#学习率
    cycles=500#循环次数
    weights=ones((n,1))#参数初始化
    for i in range(cycles):
        error=labelMatrix-sigmoid(dataMatrix*weights)
        weights=weights+alpha*dataMatrix.transpose()*error #梯度下降算法
    return weights.tolist()

(3)可视化


def dataPlot():
    import matplotlib.pyplot as plt
    dataMat,labelMat=dataLoad('D:\python\code\machinelearninginaction\Ch05\\testSet.txt')
    weights=gradAscent(dataMat,labelMat)
    m,n=shape(dataMat)
    x1=[];y1=[]
    x2=[];y2=[]
    for i in range(m):
        if labelMat[i]==1:
            x1.append(dataMat[i][1])
            y1.append(dataMat[i][2])
        if labelMat[i]==0:
            x2.append(dataMat[i][1])
            y2.append(dataMat[i][2])
    fig=plt.figure()
    ax=fig.add_subplot(111)
    ax.scatter(x1,y1,s=30,c='red',marker='s')
    ax.scatter(x2,y2,s=30,c='green')
    x = arange(-3.0, 3.0, 0.1)
    y = (-weights[0][0] - weights[1][0] * x) / weights[2][0]
    ax.plot(x, y)
    plt.xlabel('X1')
    plt.ylabel('X2')
    plt.show()

运行结果如图:

可以看到,分类效果还不错。但是以上算法中,每一次参数的更新都对整个数据集进行了遍历,这种算法称为批处理,当数据集较大时计算量太大,可以采用一种随机梯度法。

(4)随机梯度下降

随机梯度下降每次只采用一个样本点来更新参数

def stoGradAscent0(datamatIn,labelclass):
    m,n=shape(datamatIn)
    alpha=0.01
    weights=ones(n)
    for i in range(m):
        h=sigmoid(sum(datamatIn[i]*weights))
        error=labelclass[i]-h
        weights=weights+[error*alpha*a for a in datamatIn[i]] #随机梯度下降
    return weights

随机梯度下降中每次添加一个样本特征,与之前不同的是,这里的error和alpha都是数值。

运行结果:

效果不如第一次的好,这是因为随机梯度下降运算次数太少,我们可以做些改进

(5)改进随机梯度下降法

def stoGradAscent1(datamatIn,labelclass,numberIter=100):
    m,n=shape(datamatIn)
    weights=ones(n)
    for i in range(numberIter):
        dataIndex=list(range(m))
        for j in range(m):
            alpha=4/(1+i+j)+0.0001 #设置步长随着迭代次数增加而减小
            randIndex=int(random.uniform(0,len(dataIndex)))#随机挑选一个样本
            h=sigmoid(sum(datamatIn[randIndex]*weights))
            error=labelclass[randIndex]-h
            weights=weights+[error*alpha*a for a in datamatIn[randIndex]]
            del(dataIndex[randIndex])#删除已经使用过的样本
    return weights

该程序在原有基础上做了三点优化:

  • 1.alpha每次迭代的时候都会调整
  • 2.随机选取样本来更新回归系数,这种做法会减少周期性波动
  • 3.增加了迭代参数的设置。默认为100次,如果给定,将会按照新的参数值进行迭代

运行结果:

(6)实例 预测疝气病马死亡率

def classifyVector(inX,weights):
    prob=sigmoid(sum(inX*weights))
    if prob>0.5:
        return 1
    else:
        return 0

def colicTest():
    frTrain=open('D:\python\code\machinelearninginaction\Ch05\horseColicTraining.txt')
    frTest=open('D:\python\code\machinelearninginaction\Ch05\horseColicTest.txt')
    trainSet=[]
    trainLabel=[]
    for line in frTrain.readlines():
        currLine=line.strip().split('\t')
        lineArr=[float(each) for each in currLine]
        trainSet.append(lineArr[:21])
        trainLabel.append(lineArr[21])
    trainWeights=stoGradAscent1(trainSet,trainLabel,numberIter=500)
    errorCount=0
    numTest=0
    for line in frTest.readlines():
        numTest+=1
        currLine=line.strip().split('\t')
        lineArr = [float(each) for each in currLine]
        testFeat=lineArr[:21]
        predicLabel=lineArr[21]
        if classifyVector(testFeat,trainWeights)!=predicLabel:
            errorCount+=1
    errorRate=float(errorCount/numTest)
    print("the error rate of this test is: %f" % errorRate)
    return errorRate


def multiTest():
    numTests = 20; errorSum=0.0
    for k in range(numTests):
        errorSum += colicTest()
    print ("after %d iterations the average error rate is: %f" % (numTests, errorSum/float(numTests)))

运行结果:

20次平均预测错误率为35%,因为原始数据有很多缺失,这已经是一个不错的结果了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值