• 机器学习实战—逻辑回归


    逻辑回归进行分类

    有这么几个关键词:最佳拟合曲线,利用逻辑回归模型进行分类,最佳拟合参数(w和b),sigmoid函数(S函数),最优化方法,梯度上升(下降),随机梯度下降法,参数迭代公式(梯度,步长)
    梯度上升找到最佳参数:
    伪代码
    每个回归系数初值定为1
    for i in steps:
    计算整个数据集的梯度
    w=w+alpha*梯度
    更新回归系数

    #梯度上升算法的实现
    import numpy as np
    def loadDataSet():
        dataMat=[]#创建数据空列表
        labelMat=[]#创建类别空列表
        fr=open('logRegres_testSet.txt')#打开训练集文本
        for line in fr.readlines():#读取每行
            lineArr=line.strip().split()#分割数据
            #合并每行数据(不包括类别列),并在每一行的前面添加一列1
            dataMat.append([1.0,float(lineArr[0]),float(lineArr[1])])
            labelMat.append(int(lineArr[2]))#合并类别
        return dataMat,labelMat
    
    #定义S函数
    def sigmoid(inX):
        return 1.0/(1+np.exp(-inX))
    
    #定义梯度上升算法
    def gradAscent(dataMatIn,classLabels):
        dataMatrix=np.mat(dataMatIn)#list型转换为mat型(矩阵型)
        labelMat=np.mat(classLabels).transpose()#list型转换为mat型并转置
        m,n=np.shape(dataMatrix)#得到矩阵的行列数
        alpha=0.001#定义步长
        maxCycles=500#定义迭代步数
        weights=np.ones((n,1))#定义参数矩阵初值(全1)
        for k in range(maxCycles):#步数循环
            h=sigmoid(dataMatrix*weights)#求S函数的值
            error=(labelMat-h)#求误差
            weights=weights+alpha*dataMatrix.transpose()*error#更新参数数值
        return weights
    
    #画出决策边界
    def plotBestFit(wei):
        import matplotlib.pyplot as plt
        weights=wei.getA()#getA():返回自己,但是作为ndarray返回,即一个矩阵
        dataMat,labelMat=loadDataSet()#得到数据和类别
        dataArr=np.array(dataMat)#得到矩阵
        n=np.shape(dataArr)[0]#得到行数
        xcord1=[];ycord1=[]
        xcord2=[];ycord2=[]
        for i in range(n):#循环数据行数
            if int(labelMat[i])==1:
                #如果类别为1,将dataArr[i,1]的数据添加给xcord1,...
                xcord1.append(dataArr[i,1])
                ycord1.append(dataArr[i,2])
            else:#否则,将dataArr[i,1]的数据添加给xcord2,...
                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=np.arange(-3.0,3.0,0.1)#创建-3到3,间隔为0.1的点
        y=(-weights[0]-weights[1]*x)/weights[2]#由x求出Y
        ax.plot(x,y)#绘制分界线
        plt.xlabel('X1');plt.ylabel('Y1')
        plt.show()
    

    对上述梯度上升(下降)算法的解释,在这里有算法在每次更新回归系数时,都要遍历整个数据集。
    命令窗口输入:

    data,label=loadDataSet()
    
    weights=gradAscent(data,label)
    
    weights
    Out[4]: 
    matrix([[ 4.12414349],
            [ 0.48007329],
            [-0.6168482 ]])
    
    plotBestFit(weights)
    

    图形显示:
    逻辑回归决策边界

    训练算法-随机梯度上升(SGD)

    对之前的梯度算法就行改进,一次仅用一个样本点来更新回归系数,就是随机梯度上升算法。
    伪代码
    所有回归系数初始化为1
    对数据集中每个样本
    计算该样本的梯度
    w=w+alpha*梯度
    更新回归系数

    #s随机梯度上升算法
    def stocGradAscent0(dataMatrix,classLabels): 
        m,n=np.shape(dataMatrix)
        alpha=0.001
        weights=np.ones(n)
        for i in range(m):
            h=sigmoid(sum(dataMatrix[i]*weights))
            error=classLabels[i]-h
            weights=weights+alpha * error * dataMatrix[i]
        #提前修改weights的类型
        temp=np.mat(weights)
        weights=temp.transpose()
        return weights
    #随机梯度算法中,出现的数据都是数值格式,而之前的梯度算法中基本都是numpy数组格式
    #在这里输出的weights时1*3的列表,需要在使用画图操作前进行np.mat()操作和转置操作
    #而GD输出的weights是3*1的数组(array)
    

    命令程序:

    data,labels=loadDataSet()
    
    weights=stocGradAscent0(np.array(data),labels)
    
    weights
    Out[82]: 
    matrix([[ 0.963951  ],
            [ 0.9826866 ],
            [ 0.49153886]])
    
    plotBestFit(weights)
    

    SGD结果
    发现结果并没有之前好的,原因在于之前的结果是迭代了500次之后,而现在的SGD仅仅遍历了数据行数。当然我们可以发现SGD中的步长因子是0.01,而不是0.001(GD),SGD在用0.001时效果更差。
    下一步对SGD进行改进。

    #改进的SGD算法
    def stocGradAscent1(dataMatrix,classLabels,numIter=150):
        m,n=np.shape(dataMatrix)
        weights=np.ones(n)
        for j in range(numIter):#迭代次数
        #对于range(m)不存在的操作,需要用list(range(m))
            dataIndex=list(range(m))
            for i in range(m):
                alpha=4/(1.0+j+i)+0.01
                #随机选取一个样本点(第几行)
                randIndex=int(np.random.uniform(0,len(dataIndex)))
                h=sigmoid(sum(dataMatrix[randIndex]*weights))
                error=classLabels[randIndex]-h
                weights=weights+alpha*error*dataMatrix[randIndex]
                del(dataIndex[randIndex])#下次迭代前删除该值
        temp=np.mat(weights)
        weights=temp.transpose()
        return weights
     #改进后的算法在于这两处:1,每次迭代的时候alpha都会改变,不断减小但不会为0
     #通过随机选取样本更新回归系数,每次随机选一个,并在进行下一迭代前删掉该值
    
    

    得到的结果是:
    修改后的SGD150次迭代

    上面是150迭代结果,下面是500次迭代结果
    修改后的SGD500次迭代

    实例:从疝气病病症预测病马的死亡率

    数据的前期处理,如果数据(特征数据)存在缺失,应该采取的处理措施:
    使用可用特征的均值填补缺失的特征数据,使用特殊值来填补缺失值,忽略有缺失值的样本,使用相似样本的均值填补缺失值,使用另外的机器学习算法预测缺失值。
    如果是类别值缺失,那么该数据行应该丢弃,因为类别值很难确定用哪个类别来填补。
    现在数据已经没有问题,可以利用来预测。前面的数据已经可以通过训练集得到回归系数,所以只要我们再利用测试集的数据乘上回归系数,再取S函数,就可以判断死亡与否,继而得到死亡率。

    #利用以上算法从疝气病症预测病马的死亡率
    #因为上面的算法已经得出了回归系数,所以只要将测试集的数据乘上回归系数代入S函数即可
    def classifyVector(inX,weights):
        prob=sigmoid(sum(inX*weights))#S函数
        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('	')
            lineArr=[]
            #注:每一行数据有22个数据,其中最后一个表示类别
            for i in range(21):#因为有21个特征(0~20),特征数据赋给lineArr
                lineArr.append(float(currLine[i]))
            trainingSet.append(lineArr)#今儿赋给trainingSet
            trainingLabels.append(float(currLine[21]))#类别数据转赋
        #通过训练集求回归系数,也即是求出映射关系
        trainWeights=stocGradAscent1(np.array(trainingSet),trainingLabels,500)
        errorCount=0#分类错误数初始化
        numTestVec=0.0#已测试数据数目初始化
        for line in frTest.readlines():#读取测试集每一行数据
            numTestVec+=1.0#测试数据数目递增
            currLine=line.strip().split('	')#每行数据的切割
            lineArr=[]
            for i in range(21):#只取每行数据前21个特征数据
                lineArr.append(float(currLine[i]))
            if int(classifyVector(np.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)))
    
    

    要知道,利用python打开文件后,一般都要对数据进行格式化处理,基本上都是切割,定义两个空列表,分别将数据中的特征列和类别列添加到其中,即分成两个部分。
    结果:

    multiTest()
    __main__:24: RuntimeWarning: overflow encountered in exp
    the error rate of this test is;0.373134
    the error rate of this test is;0.283582
    the error rate of this test is;0.313433
    the error rate of this test is;0.238806
    the error rate of this test is;0.417910
    the error rate of this test is;0.402985
    the error rate of this test is;0.358209
    the error rate of this test is;0.432836
    the error rate of this test is;0.343284
    the error rate of this test is;0.313433
    after 10 iterations the average error rate is: 0.347761
    

    总结:逻辑回归是一种分类算法,利用S函数,将参数和特征代入,实现分类。而任务主要在于寻找回归系数,用的方法是最优化。其中最常用的是梯度上升(下降),GD和SGD。SGD是一种在线学习算法,与之对应的一次处理所有数据被称为批处理。

    invictus maneo!
  • 相关阅读:
    深入浅出 Java Concurrency (4): 原子操作 part 3 指令重排序与happens-before法则
    深入浅出 Java Concurrency (3): 原子操作 part 2
    深入浅出 Java Concurrency (2): 原子操作 part 1
    深入浅出 Java Concurrency (1) : J.U.C的整体认识
    java中关键字volatile的作用
    教你如何迅速秒杀掉:99%的海量数据处理面试题(转)
    STL容器介绍(转)
    IoC框架
    LeetCode 548. Split Array with Equal Sum (分割数组使得子数组的和都相同)$
    LeetCode 533. Lonely Pixel II (孤独的像素之二) $
  • 原文地址:https://www.cnblogs.com/rayshaw/p/8531493.html
Copyright © 2020-2023  润新知