• 回归模型(二) 逻辑回归


    从线性回归到逻辑回归

       最简单的回归是线性回归,在Andrew NG的讲义,有如图1.a所示,X为数据点——肿瘤的大小,Y为观测值——是否是恶性肿瘤。通过构建线性回归模型,如hθ(x)所示,构建线性回归模型后,即可以根据肿瘤大小,预测是否为恶性肿瘤hθ(x)≥.05为恶性,hθ(x)<0.5为良性。

     然而线性回归的鲁棒性很差,例如在图1.b的数据集上建立回归,因最右边噪点的存在,使回归模型在训练集上表现都很差。这主要是由于线性回归在整个实数域内敏感度一致,而分类范围,需要在[0,1]。逻辑回归就是一种减小预测范围,将预测值限定为[0,1]间的一种回归模型,其回归方程与回归曲线如图2所示。逻辑曲线在z=0时,十分敏感,在z>>0或z<<0处,都不敏感,将预测值限定为(0,1)。

    分类问题

    二分类问题就是给定的输入 [公式],判断它的标签是A类还是类。二分类问题是最简单的分类问题。

    1. 预测一个用户是否点击特定的商品

    2. 判断用户的性别

    3. 预测用户是否会购买给定的品类

    4. 判断一条评论是正面的还是负面的

    要解决这些问题,通常会用到一些已有的分类算法,比如逻辑回归,或者支持向量机。它们都属于有监督的学习,因此在使用这些算法之前,必须要先收集一批标注好的数据作为训练集。有些标注可以从log中拿到(用户的点击,购买),有些可以从用户填写的信息中获得(性别),也有一些可能需要人工标注(评论情感极性)。另一方面,知道了一个用户或者一条评论的标签后,我们还需要知道用什么样的特征去描述我们的数据,对用户来说,可以从用户的浏览记录和购买记录中获取相应的统计特征,而对于评论来说,最直接的则是文本特征。

    但是我们拿得到的数据往往是连续的变量,输出的结果也是连续的变量。所以就考虑到下面的问题。

    用连续的数值去预测离散的标签值

    线性回归的输出是一个数值,而不是一个标签,显然不能直接解决二分类问题。那我如何改进我们的回归模型来预测标签呢?

    感知机(Perceptron)模型:

    一个最直观的办法就是设定一个阈值,比如0,如果我们预测的数值 y > 0 ,那么属于标签A,反之属于标签B。

    基于概率的辑回归模型 (Logistics Regression):

    我们不去直接预测标签,而是去预测标签为A概率,我们知道概率是一个[0,1]区间的连续数值,那我们的输出的数值就是标签为A的概率。一般的如果标签为A的概率大于0.5,我们就认为它是A类,否则就是B类。

    二分类模型评价

    拿到数据的特征和标签后,就得到一组训练数据:

    其中 xi 是一个 m 维的向量,Misplaced & ,y 在 {0, 1} 中取值。(本文用{1,0}表示正例和负例,后文沿用此定义。)

    问题可以简化为,如何找到这样一个决策函数y∗=f(x),它在未知数据集上能有足够好的表现。

    逻辑回归推导

    从线性回归到逻辑回归

    线性回归的表达式:[公式],线性回归对于给定的输入 [公式] ,输出的是一个数值 y ,因此它是一个解决回归问题的模型。

    为了消除掉后面的常数项b,我们可以令 [公式] ,同时 [公式] ,也就是说给x多加一项而且值恒为1,这样b就到了w里面去了,直线方程可以化简成为:

    [公式],后面使用的 [公式] 代指 [公式] 。

    概率是属于[0,1]区间。但是线性模型 [公式] 值域是 [公式] 。所以不能直接基于线性模型建模。需要找到一个模型的值域刚好在[0,1]区间,同时要足够好用,sigmoid函数是不二之选。

    它的表达式为: [公式] 。

    结合sigmoid函数,线性回归函数,把线性回归模型的输出作为sigmoid函数的输入。于是最后就变成了逻辑回归模型:

    [公式]

    假设我们已经训练好了一组权值 [公式] 。只要把我们需要预测的 [公式] 代入到上面的方程,输出的y值就是这个标签为A的概率,我们就能够判断输入数据是属于哪个类别。

    逻辑回归的损失函数(Loss Function)

    损失函数就是用来衡量模型的输出与真实输出的差别。

    假设只有两个标签1和0, [公式] 。我们把采集到的任何一组样本看做一个事件的话,那么这个事件发生的概率假设为p。我们的模型y的值等于标签为1的概率也就是p。

    [公式]。因为标签不是1就是0,因此标签为0的概率就是: [公式] 。

    我们把单个样本看做一个事件,那么这个事件发生的概率就是:[公式]这个函数不方便计算,它等价于:[公式] 。

    解释下这个函数的含义,我们采集到了一个样本 [公式] 。对这个样本,它的标签是 [公式] 的概率是 [公式] 。 (当y=1,结果是p;当y=0,结果是1-p)。

    如果我们采集到了一组数据一共N个, [公式] ,这个合成在一起的合事件发生的总概率怎么求呢?其实就是将每一个样本发生的概率相乘就可以了,即采集到这组样本的概率:

    [公式]。注意[公式] 是一个函数,并且未知的量只有 [公式] (在p里面)。

    通过两边取对数来把连乘变成连加的形式,即:

    [公式]  其中, [公式],这个函数 [公式] 又叫做它的损失函数。

    损失函数可以理解成衡量我们当前的模型的输出结果,跟实际的输出结果之间的差距的一种函数。这里的损失函数的值等于事件发生的总概率,我们希望它越大越好。但是跟损失的含义有点儿违背,因此也可以在前面取个负号。

    使用最大似然估计MLE(Maximum Likelihood Estimation)估计参数 [公式] 跟b的值呢,损失函数 [公式] 是正比于总概率 [公式] 的,而 [公式] 又只有一个变量 [公式] 。也就是说,通过改变 [公式] 的值,就能得到不同的总概率值 [公式] 。那么当我们选取的某个 [公式] 刚好使得总概率 [公式] 取得最大值的时候。我们就认为这个 [公式] 就是我们要求得的 [公式] 的值,这就是最大似然估计的思想。

    现在问题变成了找到一个 [公式] ,使得我们的总事件发生的概率,即损失函数 [公式] 取得最大值,数学表达就是:

    [公式]

    求取[公式] 的梯度 [公式]

    p是一个关于变量 [公式] 的函数,我们对p求导,通过链式求导法则,可得:

    [公式]

     [公式] 的值:[公式],我们也对1-p进行如上的求导。

    最终求得: [公式] 和 [公式] 。

    正式开始对 [公式] 求导,求导的时候请始终记住,我们的变量只有 [公式] ,其他的什么 [公式] 都是已知的,看做常数。

    [公式]

    所以,梯度 [公式] 的表达式最终为:

    [公式]

    把p再展开,即:

    [公式]

    梯度下降法(GD)与随机梯度下降法(SGD)

    为什么可以用梯度下降法?

    因为逻辑回归的损失函数L是一个连续的凸函数(conveniently convex)。这样的函数的特征是,它只会有一个全局最优的点,不存在局部最优。

    在逻辑回归里面就不存在局部最优,因为它的损失函数的良好特性,导致它并不会有好几个局部最优。

    当我们的GD跟SGD收敛以后,我们得到的极值点一定就是全局最优的点,因此我们可以放心地用GD跟SGD来求解。

    已经解出了损失函数 [公式]在任意 [公式] 处的梯度 [公式],我们现在要求损失函数取最大值时候的[公式]的值:[公式]

    梯度下降法(Gradient Descent),可以用来解决这个问题。

    核心思想就是先随便初始化一个 [公式],然后给定一个步长 [公式] ,通过不断地修改 [公式] <- [公式] ,从而最后靠近到达取得最大值的点,即不断进行下面的迭代过程,直到达到指定次数,或者梯度等于0为止。

    [公式]

    随机梯度下降法(Stochastic Gradient Descent),如果我们能够在每次更新过程中,加入一点点噪声扰动,可能会更加快速地逼近最优值。

    在SGD中,我们不直接使用 [公式] ,而是采用另一个输出为随机变量的替代函数 [公式] :[公式]

    当然,这个替代函数 [公式]需要满足它的期望值等于[公式],相当于这个函数围绕着 [公式] 的输出值随机波动。

    在GD求导每次都用到了所有的样本点,从1一直到N都参与梯度计算。

    [公式]

    在SGD中,我们每次只要均匀地、随机选取其中一个样本 [公式] ,用它代表整体样本,即把它的值乘以N,就相当于获得了梯度的无偏估计值,即 [公式] ,因此SGD的更新公式为:

    [公式]

    这样我们前面的求和就没有了,同时 [公式] 都是常数, [公式] 的值刚好可以并入 [公式] 当中,因此SGD的迭代更新公式为:

    [公式],其中 [公式] 是对所有样本随机抽样的一个结果。

    加入正则项的损失函数

    正则化是用来防止模型过拟合的过程,常用的有L1正则化和L2正则化两种选项,分别通过在损失函数后加上参数向
    量 的L1范式和L2范式的倍数来实现。这个增加的范式,被称为“正则项”,也被称为"惩罚项"。损失函数改变,基
    于损失函数的最优化来求解的参数取值必然改变,我们以此来调节模型拟合的程度。其中L1范式表现为参数向量中
    的每个参数的绝对值之和,L2范数表现为参数向量中的每个参数的平方和的开方值。

    加入正则的损失函数,C是用来控制正则化程度的超参数,n是方程中特征的总数,也是方程中参
    数的总数,j代表每个参数。j要大于等于1,是因为我们的参数向量 中,第一个参数是 ,是我们的截
    距,它通常是不参与正则化的。

    penalty

    可以输入"l1"或"l2"来指定使用哪一种正则化方式,不填写默认"l2"。
    注意,若选择"l1"正则化,参数solver仅能够使用求解方式”liblinear"和"saga“,若使用“l2”正则
    化,参数solver中所有的求解方式都可以使用。

    C

    C正则化强度的倒数,必须是一个大于0的浮点数,不填写默认1.0,即默认正则项与损失函数的
    比值是1:1。C越小,损失函数会越小,模型对损失函数的惩罚越重,正则化的效力越强,参数
    Θ会逐渐被压缩得越来越小

    sklearn实战

    LogisticRegression详解

    class sklearn.linear_model.LogisticRegression(penalty='l2', dual=False, tol=0.0001, C=1.0, fit_intercept=True, intercept_scaling=1, class_weight=None, 
                                random_state=None, solver='lbfgs', max_iter=100, multi_class='auto', verbose=0, warm_start=False, n_jobs=None, l1_ratio=None)

     参数介绍

    求解器

    梯度下降法,只是求解逻辑回归参数 的一种方法,sklearn提供了更多选择,让我们可以使用不同的求解器来计算逻辑回归。

    求解器的选择,由参数"solver"控制,共有五种选择。其中“liblinear”是二分类专用,也是现在的默认求解器。

     数据证明:

    莺尾花数据集多分类问题

    from sklearn.datasets import load_iris
    from sklearn.linear_model import LogisticRegression as LR
    iris = load_iris()
    for multi_class in ('multinomial', 'ovr'):
        for solver in ('lbfgs','newton-cg','sag','saga'):
            clf = LR(solver=solver, max_iter=100, random_state=42,multi_class=multi_class).fit(iris.data, iris.target)
    
            print("training score : %.3f (%s+%s)" % (clf.score(iris.data, iris.target),multi_class,solver))

    乳腺癌数据实战

    乳腺癌数据集

    import numpy as np
    import matplotlib.pyplot as plt
    from sklearn.linear_model import LogisticRegression as LR
    from sklearn.datasets import load_breast_cancer
    from sklearn.model_selection import train_test_split
    from sklearn.metrics import accuracy_score#精确性分数
     
    data = load_breast_cancer()#乳腺癌数据集
    X = data.data
    y = data.target
     
    dir(data)
    data.feature_names

     逻辑回归的重要属性coef_

    X.data.shape#(569, 30)
     
    lrl1 = LR(penalty="l1",solver="liblinear",C=0.5,max_iter=1000)
     
    #逻辑回归的重要属性coef_,查看每个特征所对应的参数
    lrl1 = lrl1.fit(X,y)
    lrl1.coef_
    # (lrl1.coef_ != 0).sum(axis=1)#array([10])    30个特征中有10个特征的系数不为0

     对参数C对的调优

    l1 = []
    l2 = []
    l1test = []
    l2test = []
     
    Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
     
    for i in np.linspace(0.05,1.5,19):
        lrl1 = LR(penalty="l1",solver="liblinear",C=i,max_iter=1000)
        lrl2 = LR(penalty="l2",solver="liblinear",C=i,max_iter=1000)
        
        lrl1 = lrl1.fit(Xtrain,Ytrain)
        l1.append(accuracy_score(lrl1.predict(Xtrain),Ytrain))
        l1test.append(accuracy_score(lrl1.predict(Xtest),Ytest))
        lrl2 = lrl2.fit(Xtrain,Ytrain)
        l2.append(accuracy_score(lrl2.predict(Xtrain),Ytrain))
        l2test.append(accuracy_score(lrl2.predict(Xtest),Ytest))
     
    graph = [l1,l2,l1test,l2test]
    color = ["green","black","lightgreen","gray"]
    label = ["L1","L2","L1test","L2test"]    
     
    plt.figure(figsize=(6,6))
    for i in range(len(graph)):
        plt.plot(np.linspace(0.05,1.5,19),graph[i],color[i],label=label[i])
    plt.legend(loc=4) #图例的位置在哪里?4表示,右下角
    plt.show()

     对参数max_iter的调

    from sklearn.model_selection import cross_val_score
    
    l1 = []
    l2 = []
    l1test = []
    l2test = []
     
    Xtrain, Xtest, Ytrain, Ytest = train_test_split(X,y,test_size=0.3,random_state=420)
     
    for i in np.linspace(1,200,50):
        lrl1 = LR(penalty="l1",solver="liblinear",C=0.75,max_iter= i )
        lrl2 = LR(penalty="l2",solver="liblinear",C=0.75,max_iter= i )
        
        lrl1 = lrl1.fit(Xtrain,Ytrain)
        l1.append(cross_val_score(lrl1,data.data,data.target,cv=10).mean())
        l1test.append(accuracy_score(lrl1.predict(Xtest),Ytest))
        lrl2 = lrl2.fit(Xtrain,Ytrain)
        l2.append(accuracy_score(lrl2.predict(Xtrain),Ytrain))
        l2test.append(accuracy_score(lrl2.predict(Xtest),Ytest))
     
    graph = [l1,l2,l1test,l2test]
    color = ["green","black","lightgreen","gray"]
    label = ["L1","L2","L1test","L2test"]    
     
    plt.figure(figsize=(6,6))
    for i in range(len(graph)):
        plt.plot(np.linspace(100,2000,50),graph[i],color[i],label=label[i])
    plt.legend(loc=4) #图例的位置在哪里?4表示,右下角
    plt.show()

     特征筛选

    fullx = []
    fsx = []
     
    threshold = np.linspace(0,abs((LR_.fit(data.data,data.target).coef_)).max(),20)
     
    #全特征
    cross_val_score_value=cross_val_score(LR_,data.data,data.target,cv=5).mean()
    #特征筛选
    for i in threshold:
        X_embedded = SelectFromModel(LR_,threshold=i).fit_transform(data.data,data.target)
        fullx.append(cross_val_score_value)
        fsx.append(cross_val_score(LR_,X_embedded,data.target,cv=5).mean())
    
        
    plt.figure(figsize=(20,5))
    plt.plot(threshold,fullx,label="full")
    plt.plot(threshold,fsx,label="feature selection")
    plt.xticks(threshold)
    plt.legend()
    plt.show()

  • 相关阅读:
    大网管 www.bigit.com
    WinAPI: midiOutUnprepareHeader 清除由 midiOutPrepareHeader 完成的准备
    WinAPI: midiInGetNumDevs 获取 MIDI 输入设备的数目
    WinAPI: midiInUnprepareHeader 清除由 midiInPrepareHeader 完成的准备
    WinAPI: midiInPrepareHeader 为 MIDI 输入准备一个缓冲区
    转贴一组 Delphi 官方网站介绍 IDE 功能的动画
    WinAPI: midiOutPrepareHeader 为 MIDI输出准备一个缓冲区
    WinAPI: midiOutGetNumDevs 获取 MIDI 输出设备的数目
    WinAPI: midiInGetID 获取输入设备 ID
    动画演示 Delphi 2007 IDE 功能[4] 自定义界面
  • 原文地址:https://www.cnblogs.com/wqbin/p/11637901.html
Copyright © 2020-2023  润新知