机器学习笔记——4 广义线性模型的基本思想和各个常用的回归特例(附logistic模型的python实现)

广义线性模型(Generalized Liner Model)的基本思想和各个常用的回归特例(附logistic模型的python实现)


为什么需要广义线性模型?“广义”和“线性”的含义是什么?

首先我们需要解释线性的重要性。线性之所以如此重要,其本质原因在于两方面:

  1. 线性形式相对是简单的,而且我们擅长处理线性问题,无论是在工程实现上还是在数学分析上。在线性的世界里,无论是对象本身的描述还是变换的描述,我们都有很好的工具和语言去运用和表述它们,诸如直线、超平面、向量、矩阵和行列式等等,对于线性的优化问题,也已有系统的理论和可行的求解算法。
  2. 很多非线性问题,可以通过转化为线性问题进行求解。在这里不得不提起人类智慧的伟大结晶之一——微积分。微积分可以说是利用线性形式处理非线性问题的经典范例。这里我们需要扯开谈一下微积分。
    我们经常谈微积分的基本思想“以直代曲,极限求和”,前者是微分的思想,后者是积分的思想。在微分中,实际上我们关注的是找一个线性变换,将各个独立的自变量增量进行变换,用以近似因变量的增量。这个线性变换是一个确定的变换,它由非线性函数 f f f 在某点 x 0 x_0 x0处确定,需要满足一定的条件。微分和导数是围绕这个线性变换来定义的,导数指的是这个线性变换对应的矩阵,而微分指的是变换后的因变量近似量。而所谓积分,即是将因变量的各个微分求和,这里体现的是极限的思想。

在广义线性模型中,线性体现在,我们的参数 θ \theta θ和各个属性 x i x_i xi之间的结合是线性的形式,即 θ T x \theta^T x θTx,直观地看,我们是在找一个方向 θ \theta θ x x x进行投影(严格上应该是做内积)。在广义线性模型的各个特例中,这一线性的结合形式是始终不变的。

现在我们讨论“广义”的含义。在简单线性模型中,我们假设标签值 y y y服从 N ( u , σ 2 ) N(u,\sigma^2) N(u,σ2)。从而自然假设输出的形式为 h θ ( x ) = θ T x h_\theta(x) = \theta^T x hθ(x)=θTx。这一输出形式带来的问题是,输出的范围不受限制,例如上篇我们讲分类问题的时候,对于取1概率p的输出估计值显然需要落在 [ 0 , 1 ] [0,1] [0,1]之间。因此广义的含义在于,输出的形式限制可以打开,为
h θ ( x ) = g ( θ T x ) h_\theta(x) = g(\theta^T x) hθ(x)=g(θTx)这样通过适当选取映射 g ( ) g() g()就能达到范围限制。这里我们称映射 g ( ) g() g()为响应函数,称映射 g − 1 ( ) g^{-1}() g1()为联系函数。可以看到,简单线性模型选用的响应函数为恒等函数。

典则联系函数和典则响应函数(canonical link function)

响应函数的选择可以有多种,比如在上一篇的二分类问题中,对范围在 [ 0 , 1 ] [0,1] [0,1]的概率p,我们可以选择logistic函数和标准正态的分布函数。本小节讨论一种特殊的响应函数,称为典则响应函数。其来源的基本思想是,我们希望响应函数是从标签值的分布中自然导出来的,因此我们需要对预先假设的分布的密度函数进行变形,使得我们可以 θ T x \theta^Tx θTx来估计某个无范围限制的参数。
显然,密度函数的值必须大于0,因此自然的变换为

  • 先进行log变换,在进行exp变换。

这样我们就可以提炼出 e x p ( f ( η ) ) exp(f(\eta)) exp(f(η))中的参数 η \eta η,参数 η \eta η必须是充分的,即我们给出它的估计后,这个分布就可以被完全确定下来了。显然 η \eta η的范围在 R R R中均是有意义的,因为密度函数值始终大于0。此时就可以直接假设 η = θ T x \eta = \theta^Tx η=θTx,假设未进行变换前的参数是 ϕ \phi ϕ,那么根据 η \eta η的选取,总有 η = g − 1 ( ϕ ) \eta = g^{-1}(\phi) η=g1(ϕ),从而可以反解出 ϕ = g ( η ) \phi = g(\eta) ϕ=g(η)。从而得到
ϕ = g ( η ) = g ( θ T x ) \phi = g(\eta) = g(\theta^Tx) ϕ=g(η)=g(θTx)因此这实际上就等价与我们选用了上式的 g ( ) g() g()作为响应函数,这样选取得到的 g ( ) g() g()称为典则响应函数,其逆函数 g − 1 ( ) g^{-1}() g1()称为典则联系函数。

广义线性模型的系统化定义

  1. 指数型分布簇
    密度函数可以改写为如下形式的分布,属于指数型分布簇:
    p ( y ; η ) = b ( y ) e x p ( η ′ T ( y ) − a ( η ) ) p(y;\eta) = b(y)exp(\eta^{'}T(y)-a(\eta)) p(y;η)=b(y)exp(ηT(y)a(η))其中, η \eta η是一个充分统计量,也就是我们要利用 θ T x \theta^Tx θTx去估计的, η \eta η给定后,分布就可以确定下来。 a ′ ( η ) a^{'}(\eta) a(η)是y的期望,而 T ( y ) T(y) T(y)大部分情况下是 y y y,而 η = g − 1 ( u ) \eta = g^{-1}(u) η=g1(u),其中 u u u为原来的参数值。

  2. E ( T ( y ) ∣ x ) = E ( y ∣ x ) = u = g ( η ) = g ( θ T x ) E(T(y)|x) = E(y|x) = u = g(\eta) = g(\theta^Tx) E(T(y)x)=E(yx)=u=g(η)=g(θTx)

广义线性模型的参数估计(具有统一形式的梯度更新公式)

我们记 h θ ( x ) = g ( θ T x ) h_\theta(x) = g(\theta^Tx) hθ(x)=g(θTx),则模型的训练必须确定参数 θ \theta θ的值。显然我们可以用最小二乘的思想,但是这里我们将使用极大似然估计的方法。我们将看到,利用梯度上升的数值算法求解其偏导函数的零点时,广义线性模型具有统一的梯度更新公式。

现在我们证明这个事情。我们将原来的密度函数 p ( y ; α ) p(y;\alpha) p(y;α)变化形式为 p ( y ; η ) p(y;\eta) p(y;η),那么似然函数可以表示为:
L ( θ ; x ) = ∏ i = 1 m p ( y i ; η ) L(\theta;x) = \prod_{i = 1}^{m}p(y_i;\eta) L(θ;x)=i=1mp(yi;η)目标为 θ = m a x θ ∈ Θ L ( θ ; x ) \theta = max_{\theta \in \Theta}L(\theta;x) θ=maxθΘL(θ;x)对数之,求导,得到
ℓ ′ ( θ ) = ∂ l o g L ( θ , x ) ∂ θ = ∑ i = 1 m 1 p ( y i ; η ) ∂ p ( y i ; η ) ∂ y i ∂ η ∂ θ = ∑ i = 1 m 1 p ( y i ; η ) [ p ( y i ; η ) ( y i − a ′ ( η ) ) ] x i = ∑ i = 1 m ( y i − u ) x i = ∑ i = 1 m ( y i − h θ ( x ) ) x i \begin{aligned} \ell^{'}(\theta) = & \frac{\partial{logL(\theta,x)}}{\partial{\theta}} \\ = & \sum_{i = 1}^{m}\frac{1}{p(y_i;\eta)}\frac{\partial p(y_i;\eta)}{\partial y_i}\frac{\partial \eta}{\partial \theta} \\ = & \sum_{i = 1}^{m}\frac{1}{p(y_i;\eta)}[p(y_i;\eta)(y_i-a^{'}(\eta))]x_i \\ = & \sum_{i = 1}^{m}(y_i - u)x_i \\ = & \sum_{i = 1}^{m}(y_i - h_\theta(x))x_i \end{aligned} (θ)=====θlogL(θ,x)i=1mp(yi;η)1yip(yi;η)θηi=1mp(yi;η)1[p(yi;η)(yia(η))]xii=1m(yiu)xii=1m(yihθ(x))xi这里就解释了上一篇中,为什么简单线性回归和logistic回归具有相同的梯度更新公式,其原因在于logistic函数 η = l o g ( ϕ 1 − ϕ ) \eta = log(\frac{\phi}{1-\phi}) η=log(1ϕϕ)是二项分布的典则联系函数,而恒等函数 y = x y = x y=x是正态分布的典则联系函数,因此在MLE的意义下,二者必然有相同的梯度更新公式。

广义线性模型下各个特例

有很多的分布是属于指数型分布簇,比如正态分布、二项分布、多项分布、泊松分布、几何分布、伽马分布等等。现在我们选择一些,看看它们在广义线性模型下的典则响应函数的形式。

概率分布 参数 均值(向量) 典则联系函数 ( η = θ T x ) \eta = \theta^Tx) η=θTx) 典则响应函数 h θ ( x ) h_\theta(x) hθ(x)
正态分布 μ \mu μ μ \mu μ = μ \mu μ η = μ \eta = \mu η=μ μ = η \mu = \eta μ=η
二项分布 ϕ \phi ϕ μ \mu μ = ϕ \phi ϕ η = l o g ( μ 1 − μ ) \eta = log(\frac{\mu}{1-\mu}) η=log(1μμ) μ = e η 1 + e η \mu = \frac{e^{\eta}}{1+e^{\eta}} μ=1+eηeη
泊松分布 λ \lambda λ μ \mu μ = λ \lambda λ η = l o g ( μ ) \eta = log(\mu) η=log(μ) μ = e η \mu = e^{\eta} μ=eη
几何分布 ϕ \phi ϕ μ \mu μ = 1 ϕ \frac{1}{\phi} ϕ1 η = l o g ( μ − 1 μ ) \eta = log(\frac{\mu-1}{\mu}) η=log(μμ1) μ = 1 1 − e η \mu = \frac{1}{1-e^{\eta}} μ=1eη1
多项分布 ϕ \phi ϕ μ = ϕ \mu = \phi μ=ϕ η i = l o g ( ϕ i ϕ k ) \eta_{i} = log(\frac{\phi_i}{\phi_k}) ηi=log(ϕkϕi) ϕ i = e η i 1 + ∑ j = 1 k − 1 e η j \phi_i = \frac{e^{\eta_i}}{1+\sum_{j = 1}^{k-1}e^{\eta_j}} ϕi=1+j=1k1eηjeηi

上述公式的推导是简单的,我们只需要将概率密度函数变换为指数型分布簇的形式,就可以得到 η \eta η与原参数 ϕ \phi ϕ的联系函数,从而进一步可推得典则联系函数和典则响应函数。
注意,以上各种情况的参数都可采用MLE进行估计。其解可以利用牛顿迭代法或者梯度上升算法进行迭代,值得注意的是,通过上述的证明,我们知道在梯度上升中更新公式具有统一的形式,在随机梯度上升中,其为:
θ : = θ + α ( y i − h θ ( x i ) ) x i \theta: = \theta + \alpha(y_i - h_\theta(x_i))x_i θ:=θ+α(yihθ(xi))xi

实战项目:利用二项分布假设下的logistic回归进行数据分类(python)

注:在本实验中,我们可以通过上述梯度上升公式使得预测准确率达到72%左右,对外部的测试数据也有72%左右的预测准确率。通过实践,可以认识到随机梯度上升可以在较短的迭代次数下达到收敛。本实验中,对迭代步长进行一些动态的修改,具体来讲,就是求解函数我们允许用户传入最大最小两个步长值,在迭代过程中进行步长修改。修改可以是线性也可以是非线性的,但方向应该是从大步长逐渐减弱到小步长,个中原因在于在迭代伊始时,大步长有利于加速收敛,而越接近局部优解时,小步长有利于收敛的稳定。本例采用线性的步长下降。
myLogistic.py

import numpy as np
from numpy import dot
import random

def sigmoid(X):
    return  1/(1+np.exp(-1*X))

def logistic(X,Y,alphaMax = 0.01,alphaMin = 0.001,epsilon = 0.00000001,numIter = 2000,method = 'SGD'):
    '''
    本函数利用Logstic模型,利用梯度下降法求出似然函数意义下的最优参数
    '''
    # X^T(y - h(X*theta))       \patial
    if(method == 'BGD'):
        theta_0 = np.ones(X.shape[1])
        # alpha = 0.01
        # epsilon = 0.000001
        logValue = sigmoid(dot(X,theta_0))
        errorV = Y - logValue
        error_0  = dot(errorV,errorV)
        error = np.inf
        #n = 0
        while((error > epsilon) & (n < numIter)):
            alpha = alphaMax - (n/numIter)*(alphaMax-alphaMin)
            #alpha = alphaMax
            patial = dot(X.T,errorV)
            theta_0 = theta_0 + alpha*patial
            logValue = sigmoid(dot(X,theta_0))
            errorV = Y - logValue
            error_1 = dot(errorV,errorV)
            error = abs(error_1 - error_0)
            error_0 = error_1
            #n = n + 1
            #print('---------------第',n,'轮迭代--------------')
            # print('theta:',theta_0)
            # print('error:',error)
            #print('errorSUM:',error_1)
            #print('alpha:',alpha)
            #print('patial:',patial) 
        return theta_0
    if(method == 'SGD'):    
        theta_0 = np.ones(X.shape[1])
        error_0  = 0
        error = np.inf
        k = list(range(X.shape[0]))       
        #n = 0
        while((error > epsilon)&(n < numIter)):
            random.shuffle(k)
            alpha = alphaMin + (1-n/numIter)*(alphaMax-alphaMin)
            for i in k:
                patial = (Y[i] - sigmoid(dot(X[i,:],theta_0)))*X[i,:]
                theta_0 = theta_0 + alpha*patial
            logValue = sigmoid(dot(X,theta_0))
            errorV = Y - logValue
            error_1 = dot(errorV,errorV)
            error = abs(error_1 - error_0)
            error_0 = error_1
            #n = n + 1
            logValue = sigmoid(dot(X,theta_0))
            errorV = Y - logValue
            patial1 = dot(X.T,errorV)
            # print('---------------第',n,'轮迭代--------------')
            # print('theta:',theta_0)
            # print('error:',error)
            # print('patial:',patial1)
            # print('errorSUM:',error_1)
            # print('alpha:',alpha)
            # print(n)
        return theta_0

测试代码logisticReg.py

import myLogistic
import numpy as np
import pandas as pd

if __name__ == '__main__':
    #simpleRegre.txt
    samData = pd.read_table('C:/Users/Administrator/Desktop/MLCourseOfWSQ/pythonProject/mlData/Logistic/TestSet.txt',
                            header = None)  
    # 构造样本矩阵
    sample = np.array(samData)
    sampleY = sample[:,sample.shape[1]-1] 
    oneCol = np.ones([sample.shape[0],1])
    sampleX = np.hstack((oneCol,sample[:,0:sample.shape[1]-1]))    
    
    # 调用函数估计参数并预测
    theta = myLogistic.logistic(sampleX,sampleY,alphaMax = 0.0001,alphaMin = 0.0000001,epsilon = 0.000001,numIter = 4000,method = 'SGD')
    predictConfidence = np.dot(sampleX,theta)
    predictY = myLogistic.sigmoid(predictConfidence)
    
    #输出预测结果与真实结果的比较
    result = np.zeros(sample.shape[0])
    result[predictY>0.5] = 1
    compareResult = (sampleY == result)
    for i in range(sample.shape[0]):
        print('Y:',sampleY[i],'    Predict Y:',result[i],'    boolCompare:',compareResult[i],'    predictP:',predictY[i])
    print('right discrimination number:',sum(compareResult))
    print('right discrimination ratio :',sum(compareResult)/sample.shape[0])    
        
    # 测试代码部分        
    # samData = pd.read_table('C:/Users/Administrator/Desktop/MLCourseOfWSQ/pythonProject/mlData/Logistic/HorseColicTest.txt',
                        # header = None) 
    # testSample = np.array(samData)
    # testY = testSample[:,testSample.shape[1]-1] 
    # oneCol = np.ones([testSample.shape[0],1])
    # testX = np.hstack((oneCol,testSample[:,0:testSample.shape[1]-1]))  
    # predictConfidence = np.dot(testX,theta)
    # predictY = myLogistic.sigmoid(predictConfidence)
    # result = np.zeros(testSample.shape[0])
    # result[predictY>0.5] = 1
    # compareResult = (testY == result)
    # for i in range(testSample.shape[0]):
        # print('Y:',testY[i],'    Predict Y:',result[i],'    boolCompare:',compareResult[i],'    predictP:',predictY[i])
    # print('right discrimination number:',sum(compareResult))
    # print('right discrimination ratio :',sum(compareResult)/testSample.shape[0])

你可能感兴趣的:(机器学习笔记,机器学习,人工智能,广义线性模型,统计学习,数学)