《机器学习》线性模型公式推导与算法实现 线性回归 广义线性模型 逻辑斯蒂回归 对数逻辑回归代码实现

参考西瓜书《机器学习》线性回归

给定训练集(D={(oldsymbol x_1, y_1), (oldsymbol x_2, y_2), ..., (oldsymbol x_i, y_i), ( oldsymbol x_n, y_n)}),其中(oldsymbol {x_i} = (x_{i1};x_{i2}; ...; x_{im}))(y_iin mathbb{R}).线性回归(linear regression)试图学得一个线性模型以尽可能准确地预测实值输出标记

(b)的最小二乘法估计。

线性回归试图学得:$$f(x_i)=wx_i+b,使得f(x_i)simeq y_i$$

  • 最小二乘法是基于预测值和真实值的均方差最小化的方法来估计参数(w)(b),即:

    [eqalign{ (w^*,b^*) & = mathop {arg min }limits_{(w,b)} sumlimits_{i = 1}^n {{{(f({x_i}) - {y_i})}^2}} cr & = mathop {arg min }limits_{(w,b)} sumlimits_{i = 1}^n {{{({y_i} - w{x_i} - b)}^2}} cr}]
  • 求解(w)(b)使({E_{(w,b)}} = sumlimits_{i = 1}^n {{{({y_i} - w{x_i} - b)}^2}})最小化的过程,称为线性回归模型的最小二乘“参数估计”(parameter estimation)。

  • 我们可将(E_{(w,b)})分别对(w)(b)求导,得到

    [eqalign{ & {{partial E(w,b)} over {partial w}} = 2left( {wsumlimits_{i = 1}^n {x_i^2 - sumlimits_{i = 1}^n {({y_i} - b){x_i}} } } ight) cr & {{partial E(w,b)} over {partial b}} = 2left( {nb - sumlimits_{i = 1}^n {({y_i} - w{x_i})} } ight) cr}]
  • 令上两式为零可得到(w)(b)最优解的闭式(closed-form)解:

    [eqalign{ w & = {{sumlimits_{i = 1}^n {{y_i}({x_i} - ar x)} } over {sumlimits_{i = 1}^n {x_i^2 - { extstyle{1 over n}}{{left( {sumlimits_{i = 1}^n {{x_i}} } ight)}^2}} }} cr b & = { extstyle{1 over n}}sumlimits_{i = 1}^n {({y_i} - w{x_i})} cr}]

    其中,$$ar x = { extstyle{1 over n}}sumlimits_{i = 1}^n {{x_i}} $$为(x)的均值。

更一般的情况

给定数据集D,样本由d个属性描述,此时我们试图学得

(f(oldsymbol {x_i}) = oldsymbol {w^T}oldsymbol {x_i} + b), 使得(f({x_i}) simeq {y_i})

这称为“多元线性回归”(multivariate linear regression).

类似的,可利用最小二乘法来对(w)(b)进行估计。为便于讨论,我们把(w)(b)吸收入向量形式(widehat w = (w;b))

相应的,把数据集D表示为一个mx(d+1)大小的矩阵({f{X}}),其中每行对应于一个示例,该行前d个元素对应于前d个属性值,最后一个元素恒置为1,即

[{f{X}} = left( {matrix{ {{x_{11}}} & cdots & {{x_{1d}}} & 1 cr {{x_{21}}} & cdots & {{x_{2d}}} & 1 cr vdots & ddots & vdots & 1 cr {{x_{m1}}} & cdots & {{x_{md}}} & 1 cr } } ight) = left( {matrix{ {x_1^T} cr {x_2^T} cr vdots cr {x_m^T} cr } matrix{ 1 cr 1 cr vdots cr 1 cr } } ight)]

再把标记也写成向量形式(oldsymbol{y}=(y_1;y_2;...;y_m)),有

[hat {oldsymbol {w}^*} = mathop {arg min }limits_{hat w} oldsymbol{(y - Xhat w)^T}oldsymbol {(y - Xhat w)} ]

({E_{hat w}} = {(oldsymbol y - {f{X}}oldsymbol {hat w})^T}(oldsymbol y - {f{X}}oldsymbol {hat w})),对(hat w)求导得到:

[{{partial {E_{hat w}}} over {partial hat {oldsymbol w}}} = 2{{f{X}}^T}({f{X}}hat w - oldsymbol y) ]

令上式为零可得(hat {w})最优解的闭式解。

({{f{X}}^T}{f{X}})为满秩矩阵或正定矩阵时,令上式为零可得:

[oldsymbol {hat w^*} = {({{f{X}}^T}{f{X}})^{ - 1}}{{f{X}}^T}oldsymbol {y} ]

(oldsymbol {hat x_i} = (oldsymbol{x_i};1)),则最终学得的多元线性回归模型为

[f({oldsymbol{hat x_i}}) = oldsymbol {hat x_i}{({{f{X}}^T}{f{X}})^{ - 1}}{{f{X}}^T}oldsymbol y ]

广义线性模型

线性回归假定输入空间到输出空间的函数映射成线性关系,但现实应用中,很多问题都是非线性的。为了拓展其应用场景,我们可以将线性回归的预测值做一个非线性的函数变化去逼近真实值,这样得到的模型统称为广义线性模型(generalized linear model):

[y = {g^{ - 1}}(oldsymbol{w^T}oldsymbol x + b) ]

其中函数(g(cdot))称为“联系函数”(link function),它连续且充分光滑

当联系函数为(g(cdot)=ln(cdot))时,称为对数线性回归。即

[ln y= oldsymbol{w^T}oldsymbol x + b \ y = e^{oldsymbol{w^T}oldsymbol x + b}]

逻辑斯蒂回归

前面介绍的都是完成回归任务,如果要做的是分类任务该怎么办呢?

按照上面介绍的广义线性模型,要完成分类任务,也就是去寻找一个合适的(g(cdot))函数。为了简化问题,我们先考虑二分类任务,其输出标记(y in { 0,1})。线性模型产生的预测值(z=oldsymbol{w^T}oldsymbol {x} + b)是实值,因此,我们需要将实值z转换为0/1值。最理想的是“单位阶跃函数”。

[y=egin{cases} 0,&z<0 \ 0.5, &z=0 \ 1,&z>0 end{cases} ]

即若预测值z大于零就判为正例,小于零则判为反例,预测值为临界值零则可任意判定,如图所示
《机器学习》线性模型公式推导与算法实现
线性回归
广义线性模型
逻辑斯蒂回归
对数逻辑回归代码实现

从图中可以发现,单位阶跃函数不连续,因此不能直接用作联系函数(g(cdot))。于是我们希望找到能在一定程度上近似单位阶跃函数的替代函数,并希望它在临界点连续且单调可微。

对数几率函数(logistic function)正是这样一个常用的替代函数。

[y = frac{1}{{1 + {e^{ - (oldsymbol{w^T}oldsymbol x + b)}}}} ]

从图中可以看出,它的图像形式S,对这类图像形式S的函数称为"Sigmoid函数",对数几率函数是它最重要的代表。

这种利用对数几率函数去逼近真实标记的对数几率的模型称为“对数几率回归”,又常叫做“逻辑斯蒂回归”,虽然它的名字是“回归”,但实际上是一种分类学习方法。

对数逻辑回归有很多优点:

  • 1.可以直接对分类可能性进行预测,将y视为样本x作为正例的概率;
  • 2.无需事先假设数据分布,这样避免了假设分布不准确带来的问题;
  • 3.它是任意阶可导的凸函数,可直接应用现有的数值优化算法求取最优解。

(b)的确定

将y视为样本(oldsymbol x)作为正例的可能性。显然有:

[p(y=1|oldsymbol x) = frac{{{e^{oldsymbol {w^T}oldsymbol x + b}}}}{{1 + {e^{oldsymbol {w^T}oldsymbol x + b}}}} ]

[p(y=0|oldsymbol x) = frac{1}{{1 + {e^{oldsymbol {w^T}oldsymbol x + b}}}} ]

于是,可通过“极大似然法”来估计参数(oldsymbol w)(b)。给定数据集({ (oldsymbol {x_i},{y_i})} _{i = 1}^m),其“对数似然”为:

[l(oldsymbol w,b) = sumlimits_{i = 1}^n {ln p({y_i}|oldsymbol {x_i};oldsymbol w,b)} ]

[eqalign{ & oldsymbol eta = (oldsymbol w;b),hat {oldsymbol x} = (oldsymbol x;1) cr & {oldsymbol eta ^T}hat {oldsymbol x} = oldsymbol{w^T}oldsymbol x + b cr & {p_1}(oldsymbol {hat x};oldsymbol eta ) = p(y = 1|oldsymbol {hat x};oldsymbol eta ) cr & {p_0}(oldsymbol {hat x};oldsymbol eta ) = p(y = 0|oldsymbol {hat x};oldsymbol eta ) cr} ]

则似然项可重写为:

[p({y_i}|oldsymbol {x_i};oldsymbol w,b) = {y_i}{p_1}({oldsymbol {hat x_i}};oldsymbol eta ) + (1 - {y_i}){p_0}(oldsymbol {hat x};oldsymbol eta ) ]

将上式带入似然函数:

[eqalign{ l(eta ) & = sumlimits_{i = 1}^n {ln ({y_i}{p_1}(hat x;eta ) + (1 - {y_i}){p_0}(hat x;eta )} cr & = sumlimits_{i = 1}^n {ln left( {{y_i}frac{{{e^{{eta ^T}hat x}}}}{{1 + {e^{{eta ^T}hat x}}}} + (1 - {y_i})frac{1}{{1 + {e^{{eta ^T}hat x}}}}} ight)} cr & = sumlimits_{i = 1}^n {ln left( {frac{{{y_i}{e^{{eta ^T}hat x}} + (1 - {y_i})}}{{1 + {e^{{eta ^T}hat x}}}}} ight)} cr & = sumlimits_{i = 1}^n {ln left( {frac{{{y_i}({e^{{eta ^T}hat x}} - 1) + 1}}{{1 + {e^{{eta ^T}hat x}}}}} ight)} cr & = sumlimits_{i = 1}^n {left( {ln ({y_i}({e^{{eta ^T}hat x}} - 1) + 1) - ln (1 + {e^{{eta ^T}hat x}})} ight)} cr & =egin{cases} {sumlimits_{i = 1}^n { - ln (1 + {e^{{eta ^T}hat x}})} },&y_i=0 \ {sumlimits_{i = 1}^n {({eta ^T}hat x - ln (1 + {e^{{eta ^T}hat x}}))} },&y_i=1 cr end{cases}} ]

考虑到(y_i in {0, 1}),即最大化(l(oldsymbol w,b))等价于最小化

[l(oldsymbol eta ) = sumlimits_{i = 1}^n {(-y_ioldsymbol{eta ^T}oldsymbol {hat x_i} + ln (1 + {e^{{eta ^T}hat x_i}}))} ]

接下来可以利用数值优化算法对其求解,即

[eta ^* { ext{ = }}mathop {arg min }limits_eta l(eta ) ]

对数逻辑回归代码实现

回归模型

[y = frac{1}{{1 + {e^{ - z}}}} ]

损失函数(最小化):

[l(oldsymbol eta ) = sumlimits_{i = 1}^n {(-y_ioldsymbol{eta ^T}oldsymbol {hat x_i} + ln (1 + {e^{{eta ^T}hat x_i}}))} ]

[eta ^* { ext{ = }}mathop {arg min }limits_eta l(eta ) ]

以牛顿法求解为例:其第(t+1)轮迭代解的更新公式为

[oldsymbol eta ^{t+1}=oldsymbol eta ^t-left ( frac{partial ^2l(oldsymbol eta))}{partial {oldsymbol eta}partial {oldsymbol eta} ^T} ight )^{-1}frac{partial l(oldsymbol eta)}{partial oldsymbol eta} ]

其中关于(oldsymbol eta)的一阶、二阶导数分别为

[frac{partial l(eta)}{partial eta} = -sum ^m_{i=1}hat x_i(y_i-p_1(hat x_i;eta)) ]

[frac{partial ^2l(eta)}{partial etapartial eta ^T}=sum ^m_{i=1}hat x_ihat x_i^Tp_1(hat x_i;eta)(1-p_1(hat x_i; eta)) ]
import os
import numpy as np
import pandas as pd
data = pd.read_csv("../data/irisdata.txt")
# 只保留两种标签,进行二分类任务
data = data[data['name'] != 'Iris-setosa']
data['name'].value_counts()
Iris-versicolor    50
Iris-virginica     50
Name: name, dtype: int64
# 分离标签,并将标签映射到数值
y = data['name']
y[y == 'Iris-versicolor'] = 1
y[y == 'Iris-virginica'] = 0
X = data.drop('name', axis=1)
# 划分训练集和验证集
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.5)
class LogisticReressionClassifier:
    def __init__(self, max_iter):
        self.max_iter = max_iter
        
    def sigmod(self, z):
        return 1 / (1 + np.exp(-z))
    
    def fit(self, X, y):
        self.beta = np.random.normal(size=(X.shape[0], X.shape[1] + 1))  # 初始化参数
        self.X_hat = np.c_[X, np.ones(X.shape[0])]  # 为数据集加入一列1
        self.loss_function(X, y)  # 打印训练前loss
        for j in range(self.max_iter): # 迭代优化
            pd1 = 0  # 一阶偏导
            for i in  range(len(y)):
                pd1 -= self.X_hat[i]*(y[i] - self.sigmod(np.dot(self.beta[i].T, self.X_hat[i])))
            pd2 = 0  # 二阶偏导
            for i in range(len(y)):
                pd2 += self.X_hat[i].dot(self.X_hat[i].T.dot(self.sigmod(self.beta[i].T.dot(self.X_hat[i]))*(1 - self.sigmod(self.beta[i].T.dot(self.X_hat[i])))))
            self.beta = self.beta - (1 / pd2)*pd1  # 更新参数beta
        self.loss_function(X, y)  # 打印训练后的loss
        
    def loss_function(self, X, y):
        loss = 0
        # 根据损失函数公式计算当前loss
        for i in range(len(y)):
            loss += -y[i]*np.dot(self.beta[i].T, self.X_hat[i]) + np.log(1 + np.exp(np.dot(self.beta[i].T, self.X_hat[i])))
        print(loss)
        
    def predict(self, X):
        y = [] # 存储预测结果
        X = np.c_[X, np.ones(X.shape[0])]  # 为训练集加入一列1
        for i in range(X.shape[0]):
            # 计算样本作为正例的相对可能性(几率)
            odds = self.sigmod(np.mean(self.beta, axis=0).T.dot(X[i]))
            if (odds >= 0.5):
                y.append(1)
            else:
                y.append(0)
        return y

clf = LogisticReressionClassifier(10000)
clf.fit(X_train.values, y_train.values)
187.27577618364464
38.2785420108109
y_pred = clf.predict(X_test.values)
# 正确率
sum(y_pred == y_test)/len(y_test)
0.96

更多

《机器学习》线性模型公式推导与算法实现
线性回归
广义线性模型
逻辑斯蒂回归
对数逻辑回归代码实现