zoukankan      html  css  js  c++  java
  • Python实现逻辑回归(Logistic Regression)

    1.逻辑回归概念

    逻辑分类(Logistic Classification)是一种线性模型,可以表示为y=f(wx+b)y=f(w*x+b),w是训练得到的权重参数(Weight);
    x是样本特征数据(逻辑回归一般要求需要对x进行归一化处理,常见的做法有最大最小值归一化:(x-min(x))/(max(x)-min(x)),0均值标准化:(x-μ)/δ);
    y是对应的分类变量(注意这里的0、1、2、3只是表示对应的标称分类,并不表示具体的数值,不能进行运算处理),二分类一般用{0,1}或{-1,1}表示,前者更常用一些,方便推导,后者也可以推导出损失函数只是表达上要更复杂一些;
    b是偏置(Bias);
    f称为激活函数。就是给定一批样本数据集,和样本对象所属的分类,进行建立模型。使用模型对新来的对象分类预测。

    当f函数为sigmoid函数时,就是逻辑回归。即:y=sigmoid(wx+b)y=sigmoid(w*x+b)
    在这了我们做一些定义,方便下面求导:
    hθ(x)=g(θTx)=11+exh_{ heta}(x)=g( heta^{T}x)=frac{1}{1+e^{-x}}

    sigmoid函数求导:
    g(x)=(11+ex)=ex(1+ex)2=1+ex1(1+ex)2=1(1+ex)1(1+ex)2=1(1+ex)(11(1+ex))=g(x)(1g(x))g^{'}(x)=(frac{1}{1+e^{-x}})^{'}=frac{e^{-x}}{(1+e^{-x})^{2}}=frac{1+e^{-x}-1}{(1+e^{-x})^{2}}\=frac{1}{(1+e^{-x})}-frac{1}{(1+e^{-x})^{2}}=frac{1}{(1+e^{-x})}(1-frac{1}{(1+e^{-x})})\=g(x)(1-g(x))
    请牢记以上sigmoid函数求导结论:g(x)(1-g(x))

    对于二分类,分类标号为{0,1},可以理解为正样本的输出概率。而由于类标号只能取{0,1},取值结果是一个二项分布,概率函数服从伯努利分布。
    假设:
    P(y=1x;θ)=hθ(x)P(y=0x;θ)=1hθ(x)P(y=1|x; heta )=h_{ heta }(x)\P(y=0|x; heta )=1-h_{ heta }(x)
    将上面两个公式合成一个公式为:
    P(yx;θ)=(hθ(x))y((1hθ(x)))1yP(y|x; heta )=(h_{ heta }(x))^{y}((1-h_{ heta }(x)))^{1-y}
    此公式没有特别复杂的地方,只是很巧妙的将y=0和y=1结合成了一个公式,当y=0时,前式为1,当y=1时后式变为了1。

    预测样本x的结果为真实结果y的概率为上面的公式。那么预测一批样本,预测结果全部都和真实结果一样的概率为下面的公式。这也是最大似然的思想,用样本取估计w权重的过程,逻辑回归似然函数:

    L(θ)=i=1mP(yixi;θ)=i=1m(hθ(xi))yi((1hθ(xi)))1yiL( heta )=prod_{i=1}^{m}P(y_{i}|x_{i}; heta )=prod_{i=1}^{m}(h_{ heta }(x_{i}))^{y_{i}}((1-h_{ heta }(x_{i})))^{1-y_{i}}

    其中m表示样本数量。关于为什么最大似然函数就要取乘积,可以这样理解。最大似然就是让已经发生的事情(也就是样本)在你的模型中出现的概率尽可能大。也就是如果有一个和已知结果的样本特征一样的待测对象,那个你的预测结果也要和样本的真实结果尽可能一样。而每个样本可以理解为一个独立的事件。所有事件发生的概率尽可能大,就可以用联合概率表示所有样本都发生的概率。联合概率使用乘积的形式表示。这也就有了上式乘积的形式。
    上式取对数,连乘法变加法:
    l(θ)=logL(θ)=i=1m(yiloghθ(xi)+(1yi)log(1hθ(xi)))l( heta )=logL( heta )=sum_{i=1}^{m}(y_{i}logh_{ heta }(x_{i})+(1-y_{i})log(1-h_{ heta }(x_{i})))

    求导:
    θjl(θ)=(y1g(θ)Tx(1y)11g(θ)Tx)θjg(θTx)=(y1g(θ)Tx(1y)11g(θ)Tx)g(θTx)(1g(θTx))θjθTx=(y(1g(θTx))(1y)g(θTx))xj=(yhθ(x))xjfrac{∂}{∂ heta_{j}}l( heta )=(yfrac{1}{g( heta)^{T}x}-(1-y)frac{1}{1-g( heta)^{T}x})frac{∂}{∂ heta_{j}}g( heta^{T}x)\ =(yfrac{1}{g( heta)^{T}x}-(1-y)frac{1}{1-g( heta)^{T}x})g( heta^{T}x)(1-g( heta^{T}x))frac{∂}{∂ heta_{j}} heta^{T}x\=(y(1-g( heta^{T}x))-(1-y)g( heta^{T}x))x_{j}\=(y-h_{ heta}(x))x_{j}

    上式求导中第二行直接使用了sigmoid函数求导结论:g(x)(1-g(x)),对第二行中的θ^Tx求导实际上得到的就是第j个x,记做xj。

    然后我们另上式求导结果等于0,你会发现它无法解析求解,在这里我们只能借用迭代法来求解,下面我们采用梯度下降算法求解。

    2.梯度下降算法

    在上一章节最后求导结果中,我们稍作修改,和线性回归梯度下降算法类似,很容易即可实现基于梯度下降算法的逻辑回归。
    θj=θj+α(yhθ(x))xj heta_{j}= heta_{j} + α(y-h_{ heta}(x))x_{j}

    基于梯度上式算法求解theta,Python源代码如下:

    def sigmoid(x):
        return 1/(1+np.exp(-x))
    
    def LogRegGD(x,y):
        # 梯度上升算法
        alpha=0.05  # 步长
        max_loop=500 # 循环次数
        #x = StandardScaler().fit_transform(x) # 数据进行标准化
        x=np.mat(x)
        y=np.mat(y).transpose()
        m,n=np.shape(x)
        weights=np.ones((n,1))
        
        for k in range(max_loop):
            # k=2
            h=sigmoid(x*weights)
            error=y-h
            weights=weights+alpha*x.transpose()*error # 梯度下降算法公式
        print('k:',k,'weights:',weights.T)
        return weights.getA()
    

    3.随机梯度下降算法

    随机梯度下降就是每一个样本更新一次权值,超过样本范围就循环从第一个样本再循环,同等数据量情况下(数据量不是特别巨大时)训练速度与批量梯度下降要慢一些,但是适合在线学习,来一条数据迭代训练一次。
    在上节代码中增加按一条数据循环一次,即可实现:

    def LogRegSGD(x,y):
        # 随机梯度上升算法
        alpha=0.05
        max_loop=500
        #x = StandardScaler().fit_transform(x) # 数据进行标准化
        x=np.mat(x)
        y=np.mat(y).T
        m,n=np.shape(x)
        weights=np.ones((n,1))
        
        for k in range(max_loop):
            for i  in range(0,len(x)):
                # k=0;i=0
                h=sigmoid(x[i]*weights)
                error=y[i]-h[0]
                weights=weights+alpha*x[i].T*error[0]  # 随机梯度下降算法公式
                print('k:',k,'i:',i,'weights:',weights.T)
        return weights.getA()
    

    以上代码汇总如下:

    # -*- coding: utf-8 -*-
    """
     @Time    : 2018/10/26 13:13
     @Author  : hanzi5
     @Email   : **@163.com
     @File    : linear_regression_bgd.py
     @Software: PyCharm
    """
    import numpy as np
    import pandas as pd
    import matplotlib.pyplot as plt
    from sklearn import preprocessing
    from sklearn.preprocessing import StandardScaler
    from sklearn.datasets import load_iris
    from sklearn.pipeline import Pipeline
    from sklearn.linear_model import LogisticRegression
    
    def sigmoid(x):
        return 1/(1+np.exp(-x))
    
    def LogRegGD(x,y):
        # 梯度上升算法
        alpha=0.05  # 步长
        max_loop=500 # 循环次数
        #x = StandardScaler().fit_transform(x) # 数据进行标准化
        x=np.mat(x)
        y=np.mat(y).transpose()
        m,n=np.shape(x)
        weights=np.ones((n,1))
        
        for k in range(max_loop):
            # k=2
            h=sigmoid(x*weights)
            error=y-h
            weights=weights+alpha*x.transpose()*error # 梯度下降算法公式
        #print('k:',k,'weights:',weights.T)
        return weights.getA()
    
    def LogRegSGD(x,y):
        # 随机梯度上升算法
        alpha=0.05
        max_loop=500
        #x = StandardScaler().fit_transform(x) # 数据进行标准化
        x=np.mat(x)
        y=np.mat(y).T
        m,n=np.shape(x)
        weights=np.ones((n,1))
        
        for k in range(max_loop):
            for i  in range(0,len(x)):
                # k=0;i=0
                h=sigmoid(x[i]*weights)
                error=y[i]-h[0]
                weights=weights+alpha*x[i].T*error[0]  # 随机梯度下降算法公式
                #print('k:',k,'i:',i,'weights:',weights.T)
        return weights.getA()
    
    # 对新对象进行预测
    def predict1(weights,testdata):
        testdata.insert(0, 1.0)       #现在首部添加1代表b偏量
        testMat = np.mat([testdata])
        z = testMat * np.mat(weights)
        y=sigmoid(z)
        if y>0.5:
        # if z>0:    #h>0.5的判断等价于  z>0
            return 1,y
        else:
            return 0,y
        
    
    if __name__ == "__main__":
        # 读入100条数据,数据文件原名“testSet.txt”
        data=[
    [-0.017612,14.053064,0],[-1.395634,4.662541,1],[-0.752157,6.538620,0],[-1.322371,7.152853,0],[0.423363,11.054677,0],
    [0.406704,7.067335,1],[0.667394,12.741452,0],[-2.460150,6.866805,1],[0.569411,9.548755,0],[-0.026632,10.427743,0],
    [0.850433,6.920334,1],[1.347183,13.175500,0],[1.176813,3.167020,1],[-1.781871,9.097953,0],[-0.566606,5.749003,1],
    [0.931635,1.589505,1],[-0.024205,6.151823,1],[-0.036453,2.690988,1],[-0.196949,0.444165,1],[1.014459,5.754399,1],
    [1.985298,3.230619,1],[-1.693453,-0.557540,1],[-0.576525,11.778922,0],[-0.346811,-1.678730,1],[-2.124484,2.672471,1],
    [1.217916,9.597015,0],[-0.733928,9.098687,0],[-3.642001,-1.618087,1],[0.315985,3.523953,1],[1.416614,9.619232,0],
    [-0.386323,3.989286,1],[0.556921,8.294984,1],[1.224863,11.587360,0],[-1.347803,-2.406051,1],[1.196604,4.951851,1],
    [0.275221,9.543647,0],[0.470575,9.332488,0],[-1.889567,9.542662,0],[-1.527893,12.150579,0],[-1.185247,11.309318,0],
    [-0.445678,3.297303,1],[1.042222,6.105155,1],[-0.618787,10.320986,0],[1.152083,0.548467,1],[0.828534,2.676045,1],
    [-1.237728,10.549033,0],[-0.683565,-2.166125,1],[0.229456,5.921938,1],[-0.959885,11.555336,0],[0.492911,10.993324,0],
    [0.184992,8.721488,0],[-0.355715,10.325976,0],[-0.397822,8.058397,0],[0.824839,13.730343,0],[1.507278,5.027866,1],
    [0.099671,6.835839,1],[-0.344008,10.717485,0],[1.785928,7.718645,1],[-0.918801,11.560217,0],[-0.364009,4.747300,1],
    [-0.841722,4.119083,1],[0.490426,1.960539,1],[-0.007194,9.075792,0],[0.356107,12.447863,0],[0.342578,12.281162,0],
    [-0.810823,-1.466018,1],[2.530777,6.476801,1],[1.296683,11.607559,0],[0.475487,12.040035,0],[-0.783277,11.009725,0],
    [0.074798,11.023650,0],[-1.337472,0.468339,1],[-0.102781,13.763651,0],[-0.147324,2.874846,1],[0.518389,9.887035,0],
    [1.015399,7.571882,0],[-1.658086,-0.027255,1],[1.319944,2.171228,1],[2.056216,5.019981,1],[-0.851633,4.375691,1],
    [-1.510047,6.061992,0],[-1.076637,-3.181888,1],[1.821096,10.283990,0],[3.010150,8.401766,1],[-1.099458,1.688274,1],
    [-0.834872,-1.733869,1],[-0.846637,3.849075,1],[1.400102,12.628781,0],[1.752842,5.468166,1],[0.078557,0.059736,1],
    [0.089392,-0.715300,1],[1.825662,12.693808,0],[0.197445,9.744638,0],[0.126117,0.922311,1],[-0.679797,1.220530,1],
    [0.677983,2.556666,1],[0.761349,10.693862,0],[-2.168791,0.143632,1],[1.388610,9.341997,0],[0.317029,14.739025,0]
    ]
        data_array=np.array(data) # 转换为numpy array类型
        x = data_array[:, :-1]    # 除最后一列,其他列作为x
        a= np.ones(len(x))        # 新增一列,默认值设为1,作为截距b的初始化值
        x=np.insert(x, 0, values=a, axis=1)
        
        y = data_array[:,-1] # 分类变量y
        
        # 使用sklearn包实现逻辑回归
        # x = StandardScaler().fit_transform(x)
        # lr = LogisticRegression()   # Logistic回归模型
        # lr.fit(x, y.ravel())        # 根据数据[x,y],计算回归参数
        # 等价形式
        lr = Pipeline([('sc', StandardScaler()),('clf', LogisticRegression()) ])
        lr.fit(x, y.ravel())
        
        # 1、梯度下降算法实现逻辑回归
        weights1=LogRegGD(x,y) 
        type,y_hat1 = predict1(weights1,[0.317029,14.739025])
        print('梯度下降算法实现逻辑回归',type,y_hat1)
        
        # 2、随机梯度下降算法实现逻辑回归
        weights2=LogRegSGD(x,y)
        type,y_hat2 = predict1(weights2,[0.317029,14.739025])
        print('随机梯度下降算法实现逻辑回归',type,y_hat2)
    

    参考资料:
    1、Machine-Learning-With-Python
    2、《机器学习实战》Peter Harrington著
    3、《机器学习》西瓜书,周志华著
    4、 斯坦福大学公开课 :机器学习课程
    5、机器学习视频,邹博
    6、python机器学习案例系列教程——逻辑分类/逻辑回归LR/一般线性回归(softmax回归)
    7、逻辑回归----Python实现
    8、逻辑回归python实现
    9、机器学习算法与Python实践之(七)逻辑回归(Logistic Regression)

  • 相关阅读:
    gzip是一种数据格式,deflate是一种压缩算法
    js 实现图片上传 续
    iframe 元素会创建包含另外一个文档的内联框架(即行内框架)
    HTTPS简介----
    回归测试
    HTTP 返回码 400
    js 实现 一张图片的上传
    121. Best Time to Buy and Sell Stock
    119. Pascal's Triangle II
    118. Pascal's Triangle
  • 原文地址:https://www.cnblogs.com/hanzi5/p/10105620.html
Copyright © 2011-2022 走看看