京东-优惠雷达
新人页面
精选商品
首月0月租体验,领12个月京东PLUS
自营热卖

分类算法----Logistic Regression算法

飞逝的流水 1年前   阅读数 88 0

某厂面试归来,发现自己落伍了!>>>

分类算法----Logistic Regression算法

1. LR概述

1.1 概述

回归,就是对于一些无规律的点,利用线或者面将这些点进行拟合,这个过程就称之为回归。简单如下图所示:

其中红色的线就是将这些点进行拟合的直线。

LR回归,从名字上来看是回归算法,但实际上却是分类算法。其模型是在线性回归模型的基础上,使用sigmoid函数,将线性模型

w^Tx

的结果压缩到[0,1] 之间,使其拥有概率意义。LR思想是根据现有数据对分类边界线建立回归公式,以此进行分类 。其本质仍然是一个线性模型,实现相对简单。

从上面讲述来看,如果想了解LR回归模型就必须先了解sigmoid函数,公式如下:

h_\theta(x)=g(\theta^Tx)=\frac{1}{1+e^-\theta^Tx}

其中

\theta=[\theta_0,\theta_1,\theta_2,...,\theta_n]^T; x= [x_0,x_1,x_2,...,x_n]^T

其中θ就是要求的解,x就是样本给定的列项量。如果有合适的参数列向量θ,以及样本列向量x,样本x的分类就可以通过上述公式计算出一个概率,如果这个概率大于0.5,那么样本是正样本,否则就是负样本。

sigmoid函数图像如下图所示:

1.2 使用领域

在医学界,广泛应用于流行病学中,比如探索某个疾病的危险因素,根据危险因素预测疾病是否发生,与发生的概率。 比如探讨胃癌,可以选择两组人群,一组是胃癌患者,一组是非胃癌患者。因变量是“是否胃癌”,这里“是”与“否”就是要研究的两个分类类别。自变量是两组人群的年龄,性别,饮食习惯,等等许多(可以根据经验假设),自变量可以是连续的,也可以是分类的。

在金融界,较为常见的是使用逻辑回归去预测贷款是否会违约,或放贷之前去估计贷款者未来是否会违约或违约的概率。

在消费行业中,也可以被用于预测某个消费者是否会购买某个商品,是否会购买会员卡,从而针对性得对购买概率大的用户发放广告,或代金券等等,进行精准营销。

2 逻辑斯蒂分布( logistic distribution)

设X是连续随机变量,X服从逻辑分布式指X具有下列分布函数与密度函数:

F(X)=P(X)=1/(1+e^x)

f(x)=F^`(X)=-e^x/(1+e^x)^2

根据sigmoid函数特性,假设一批样本数据可分为两类:1和0,其条件概率分别为:

P(y=1|x;\theta)=h_\theta(x)

P(y=0|x;\theta)=1-h_\theta(x)

理想状态下,根据上述公式,求出各个点的概率均为1,也就是完全分类都正确。但是考虑到实际情况,样本点的概率越接近于1,其分类效果越好。比如一个样本属于正样本的概率为0.51,那么我们就可以说明这个样本属于正样本。另一个样本属于正样本的概率为0.99,那么我们也可以说明这个样本属于正样本。但是显然,第二个样本概率更高,更具说服力。我们可以把上述两个概率公式合二为一:

P(y|x;\theta)=h_\theta(x)^y(1-h_\theta(x))^{(1-y)}

合并出来的P()函数称之为代价函数。对于跟定的样本,就可以根据上式求出其所属类别的概率,而这个概率则是越大越好,此处就涉及求最大似然的问题。

L(θ)=p(\vec{y}|x;θ)=(\prod_{i=1}^np(y^(i)|x^(i);θ)=(\prod_{i=1}^np(y^i|x^i;θ) =h_θ(x^(i))^{y^(i)}(1-h_θ(x^(i)))^{1-y^(i)}

然后利用拉格朗日函数计算

\begin{eqnarray}J(θ) &=&logL(θ) &=&log(h_θ(x^(i))^{y^(i)}(1-h_θ(x^(i)))^{1-y^(i)}) \end{eqnarray}

=\sum_{i=1}^n(y^{(i)} log(h_θ(x^{(i)})+(1-y^(i))log(1-h_θ(x^{(i)})))

其中,m为样本的总数,y(i)表示第i个样本的类别,x(i)表示第i个样本,需要注意的是θ是多维向量,x(i)也是多维向量

综上所属,满足J(θ)的最大值θ的值就是我们需要的权值。

那么如何求解使J(θ)最大的θ值呢?因为是求最大值,所以我们需要使用梯度上升算法。同理,如果面对的问题是求解使J(θ)最小的θ值,那么我们就需要使用梯度下降算法。它们的思想都是相同的,学会其一,就也会了另一个。本文使用梯度上升算法进行求解

梯度上升法

梯度上升法也就是利用迭代的方法,逐渐"逼近"函数最大值的过程。由于J(θ)过于复杂,那么我们就举个简单的例子。

f(x)=-x^2+4x

这道题目是高中的,所以拿到之后会想到先求导,求出极值点,带入求得最大值。但是在真实环境中不会有这么简单,所以就可以利用刚刚的梯度上升法来求解。至于迭代的过程:

x_{i+1}=x_i+\alpha\cdot\frac{\partial f(x_i)} {\partial x_i}

其中a为步长,也就是学习率,控制着更新的幅度。

代码如下:

def Gradient_Ascent_test():
    def f_prime(x_old):                                    #f(x)的导数
        return -2 * x_old + 4
    x_old = -1                                            #初始值,给一个小于x_new的值
    x_new = 0                                            #梯度上升算法初始值,即从(0,0)开始
    alpha = 0.01                                        #步长,也就是学习速率,控制更新的幅度
    presision = 0.00000001                                #精度,也就是更新阈值
    while abs(x_new - x_old) > presision:
        x_old = x_new
        x_new = x_old + alpha * f_prime(x_old)            #上面提到的公式
    print(x_new)                                        #打印最终求解的极值近似值

if __name__ == '__main__':
    Gradient_Ascent_test()

同理,J(θ)这个函数的极值也可以这么求解,公式如下:

\theta_j :=\theta_i+\alpha\cdot\frac{\partial J(\theta)} {\partial \theta_j}

有上面的讨论,得到了J(θ)的函数式,然后对上公式两边同时求导

\frac{\partial J(θ)}{\partial θ_j}=\sum_{i=1}^m\left(\frac{y^(i)}{h_θ(x^{(i)})}-\frac{1-y^(i)}{1-h_θ(x^{(i)})}\right)\cdot \frac{\partial h_θ(x)^{(i)}}{\partial θ_{(j)}}

=\sum_{i=1}^m\left(\frac{y^(i)}{g(θ^Tx^{(i)})}-\frac{1-y^(i)}{1-g(θ^Tx^{(i)})}\right)\cdot \frac{\partial g(θ^Tx^{(i)})}{\partial θ_{(j)}}

=\sum_{i=1}^m\left(\frac{y^(i)}{g(θ^Tx^{(i)})}-\frac{1-y^(i)}{1-g(θ^Tx^{(i)})}\right)\cdot\left(g(θ^Tx^{(i)})(1- g(θ^Tx^{(i)})\right)\cdot \frac{\partial (θ^Tx^{(i)})}{\partial θ_{(j)}}

=\sum_{i=1}^m\left(y^{(i)}(1-{g(θ^Tx^{(i)})})-(1-y^{(i)}){g(θ^Tx^{(i)})}\right)\cdot x_j^{(i)}

=\sum_{i=1}^m\left(y^{(i)}-{g(θ^Tx^{(i)}})\right)\cdot x_j^{(i)}

其中第二步到第三步的过程主要是因为:

\frac{\partial g(θ^Tx^{(i)})}{\partial θ_{(j)}}=\frac{\partial g(θ^Tx^{(i)})}{\partial (\theta^Tx^{i})}\cdot \frac{\partial (\theta^Tx^i) }{\partial \theta_{j}}

=\frac{e^{-\theta^Tx^i}}{(1+e^{-\theta^Tx^i})^2}\cdot \frac{\partial (\theta^Tx^i) }{\partial \theta_{j}}

=\frac{1}{(1+e^{-\theta^Tx^i})}\left(\frac{1+e^{-\theta^Tx^i}}{(1+e^{-\theta^Tx^i})}-\frac{1}{(1+e^{-\theta^Tx^i})}\right) \cdot \frac{\partial (\theta^Tx^i) }{\partial \theta_{j}}

=\left(g(θ^Tx^{(i)})(1- g(θ^Tx^{(i)})\right)\cdot \frac{\partial (\theta^Tx^i) }{\partial \theta_{j}}

所以,梯度上升迭代公式为:

\theta_j :=\theta_i+\alpha\cdot\sum_{i=1}^m\left(y^{(i)}-{g(θ^Tx^{(i)}})\right)\cdot x_j^{(i)}

实战

参考《机器学习实战》第五章节。

  1. 改进

梯度上升算法在每次更新回归系数(最优参数)时,都需要遍历整个数据集。可以看一下我们之前写的梯度上升算法:

def gradAscent(dataMatIn, classLabels):
    dataMatrix = np.mat(dataMatIn)                                       
    labelMat = np.mat(classLabels).transpose()                            
    m, n = np.shape(dataMatrix)                                           
    alpha = 0.01                                                       
    maxCycles = 500                                                       
    weights = np.ones((n,1))
    for k in range(maxCycles):
        h = sigmoid(dataMatrix * weights)                               
        error = labelMat - h
        weights = weights + alpha * dataMatrix.transpose() * error
    return weights.getA(),weights_array    

假设使用的数据集共有100个样本。那么,dataMatrix就是一个1003的矩阵。每次计算h的时候,都要对dataMatrixweights这个矩阵做乘法运算,要进行1003次乘法运算和1002次加法运算。同理,更新回归系数(最优参数)weights时,也需要用到整个数据集,要进行矩阵乘法运算。总而言之,该方法处理100个左右的数据集时尚可,但如果有数十亿样本和成千上万的特征,那么该方法的计算复杂度就太高了。因此,需要对算法进行改进。在每次更新回归系数(最优参数)的时候,能不能不用所有样本呢?一次只用一个样本点去更新回归系数(最优参数)?这样就可以有效减少计算量了,这种方法就叫做随机梯度上升算法。

2.1 随机梯度上升算法

直接看代码:

def stocGradAscent1(dataMatrix, classLabels, numIter=150):
    m,n = np.shape(dataMatrix)                                               
    weights = np.ones(n)                                                      
    for j in range(numIter):                                           
        dataIndex = list(range(m))
        for i in range(m):           
            alpha = 4/(1.0+j+i)+0.01                                          
            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])                                        
    return weights      

该算法第一个改进之处在于,alpha在每次迭代的时候都会调整,并且,虽然alpha会随着迭代次数不断减小,但永远不会减小到0,因为这里还存在一个常数项。必须这样做的原因是为了保证在多次迭代之后新数据仍然具有一定的影响。如果需要处理的问题是动态变化的,那么可以适当加大上述常数项,来确保新的值获得更大的回归系数。另一点值得注意的是,在降低alpha的函数中,alpha每次减少1/(j+i),其中j是迭代次数,i是样本点的下标。第二个改进的地方在于跟新回归系数(最优参数)时,只使用一个样本点,并且选择的样本点是随机的,每次迭代不使用已经用过的样本点。这样的方法,就有效地减少了计算量,并保证了回归效果。

2.2 回归系数与迭代次数的关系

可以看到分类效果也是不错的。不过,从这个分类结果中,我们不好看出迭代次数和回归系数的关系,也就不能直观的看到每个回归方法的收敛情况。因此,我们编写程序,绘制出回归系数和迭代次数的关系曲线:

由于改进的随机梯度上升算法,随机选取样本点,所以每次的运行结果是不同的。但是大体趋势是一样的。我们改进的随机梯度上升算法收敛效果更好。为什么这么说呢?让我们分析一下。我们一共有100个样本点,改进的随机梯度上升算法迭代次数为150。而上图显示15000次迭代次数的原因是,使用一次样本就更新一下回归系数。因此,迭代150次,相当于更新回归系数150*100=15000次。简而言之,迭代150次,更新1.5万次回归参数。从上图左侧的改进随机梯度上升算法回归效果中可以看出,其实在更新2000次回归系数的时候,已经收敛了。相当于遍历整个数据集20次的时候,回归系数已收敛。训练已完成。

再让我们看看上图右侧的梯度上升算法回归效果,梯度上升算法每次更新回归系数都要遍历整个数据集。从图中可以看出,当迭代次数为300多次的时候,回归系数才收敛。凑个整,就当它在遍历整个数据集300次的时候已经收敛好了。

2.3 从疝气病症状预测病马的死亡率

参见《机器学习实战》第五章

3.使用Sklearn构建Logistic回归分类器

开始新一轮的征程,让我们看下Sklearn的Logistic回归分类器!

官网文档链接


注意:本文归作者所有,未经作者允许,不得转载

全部评论: 0

    我有话说: