机器学习(五)——逻辑回归

机器学习(四)——PCA

我们在机器学习(二)——线性回归中介绍了线性回归算法,我们知道线性回归主要做的工作是拟合样本数据,用拟合好的模型进行预测。在本篇中我们将介绍逻辑回归,它主要用来解决分类问题。

1. 引言

1.1 分类问题

在日常生活中我们会遇到各种各样的分类:垃圾分类、邮件分类、商品分类等等。我们从最简单的二分类问题引入:肿瘤分类问题,即区分一个肿瘤是恶性的还是良性的?

假设用1表示肿瘤是恶性的,用0表示肿瘤是良性的,现在我们的样本数据如下图所示。我们的样本数据的特征是肿瘤的大小,输出标记为0或1(其中1代表恶性,0代表良性)。
注:图片来自吴恩达老师的PPT
注:图片来自吴恩达老师的PPT
我们现在希望能够训练出一个模型,我们输入肿瘤的大小,模型为我们预测该肿瘤是恶性还是良性。我们如何得到这样一个模型呢?我们首先想尝试使用线性回归来解决这个问题。

假设我们根据样本数据拟合得到这样的一条直线作为预测模型:
注:图片来自吴恩达老师的PPT
我们最终的预测结果为0或1,所以我们可以得到一个预测阈值0.5:
(1)当 h θ ( x ) > = 0.5 h_{\theta}(x)>=0.5 hθ(x)>=0.5时,预测结果为1;
(2)当 h θ ( x ) < 0.5 h_{\theta}(x)<0.5 hθ(x)<0.5时,预测结果为0.

但是倘若我们的样本数据中出现了极端数据,我们拟合得到的直线是下图中的蓝色直线,结果又会如何呢?
注:图片来自吴恩达老师的PPT
我们可以看出此时再使用0.5作为阈值便不合适了,而且线性回归模型的预测值并不是在[0,1]之间,它可以超越[0,1]的范围,这和我们希望的预测值只有0或1的意愿相违背。

那么就需要用新的方法来得到新的分类模型,使用该分类模型能够得到我们希望的分类结果。

1.2 逻辑回归

可见线性回归并不能很好地解决分类问题,因此我们引入逻辑回归方法。虽然它也叫“回归”,但它主要用来解决分类问题。

我们依然考虑二分类问题,假设我们的预测结果只有0或1。我们希望我们的模型能够:
(1)当模型根据输入的数据计算得到的值>=0.5时,预测结果为1;
(2)当模型根据输入的数据计算得到的值<0.5时,预测结果为0。
(反之亦可)

这样我们通过计算样本发生的概率,就能得到样本的分类,接下来我们就详细介绍一下逻辑回归。

2. 逻辑回归算法

2.1 逻辑回归模型

通过1.2,我们预期得到的模型的输出范围为[0,1],且输出值大于等于0.5时预测结果为1,输出值小于0.5时预测结果为0。

恰好就有这样一个符合我们要求的函数,sigmod函数:
g ( z ) = 1 1 + e − z g(z) = \frac{1}{1 + e^{-z}} g(z)=1+ez1
注:图片来自吴恩达老师的PPT
sigmod函数的特点很明显:
(1) z = 0 z=0 z=0时, g ( z ) = 0.5 g(z)=0.5 g(z)=0.5
(2) z > 0 z>0 z>0时, g ( z ) > 0.5 g(z)>0.5 g(z)>0.5;
(3) z < 0 z<0 z<0时, g ( z ) < 0.5 g(z)<0.5 g(z)<0.5

由此,我们假设逻辑回归模型为:
h θ ( x ) = g ( θ T x ) h_{\theta}(x) = g(\theta^Tx) hθ(x)=g(θTx)
显而易见,有:
(1)当 θ T x > = 0 \theta^Tx>=0 θTx>=0时, h θ ( x ) > = 0.5 h_{\theta}(x)>=0.5 hθ(x)>=0.5,预测结果为1;
(2)当 θ T x < 0 \theta^Tx<0 θTx<0时, h θ ( x ) < 0.5 h_{\theta}(x)<0.5 hθ(x)<0.5,预测结果为0。
这个模型就很好地符合了我们的预想。

2.2 决策边界

什么是决策边界?我们先来举个例子。
假设我们现在有这样的样本数据:
注:图片来自吴恩达老师的PPT
我们的预测模型为:
h θ ( x ) = g ( θ 0 + θ 1 x 1 + θ 2 x 2 ) h_{\theta}(x)=g(\theta_0+\theta_1x_1+\theta_2x_2) hθ(x)=g(θ0+θ1x1+θ2x2)
且参数 θ = ( θ 0 , θ 1 , θ 2 ) T = ( − 3 , 1 , 1 ) T \theta=(\theta_0,\theta_1,\theta_2)^T=(-3,1,1)^T θ=(θ0,θ1,θ2)T=(3,1,1)T
于是我们知道:
(1)当 θ T x = − 3 + x 1 + x 2 > = 0 \theta^Tx=-3+x_1+x_2>=0 θTx=3+x1+x2>=0 时, h θ ( x ) > = 0.5 h_{\theta}(x)>=0.5 hθ(x)>=0.5,预测结果为1;
(2)当 θ T x = − 3 + x 1 + x 2 < 0 \theta^Tx=-3+x_1+x_2<0 θTx=3+x1+x2<0 时, h θ ( x ) < 0.5 h_{\theta}(x)<0.5 hθ(x)<0.5,预测结果为0;

我们可以在二维平面上绘制一条直线 x 1 + x 2 = 3 x_1+x_2=3 x1+x2=3 ,这条直线能够将预测结果为0的区域和预测结果为1的区域分开:
注:图片来自吴恩达老师的PPT

这样的一条直线就叫做决策边界。

再假如我们的样本数据是这样的:
注:图片来自吴恩达老师的PPT
我们的预测模型为:
h θ ( x ) = g ( θ 0 + θ 1 x 1 + θ 2 x 2 + θ 3 x 1 2 + θ 4 x 2 2 ) h_{\theta}(x)=g(\theta_0+\theta_1x_1+\theta_2x_2+\theta_3x_1^2+\theta_4x_2^2) hθ(x)=g(θ0+θ1x1+θ2x2+θ3x12+θ4x22)
且参数 θ = ( θ 0 , θ 1 , θ 2 , θ 3 , θ 4 ) T = ( − 1 , 0 , 0 , 1 , 1 ) T \theta = (\theta_0,\theta_1,\theta_2, \theta_3,\theta_4)^T=(-1,0,0,1,1)^T θ=(θ0,θ1,θ2,θ3,θ4)T=(1,0,0,1,1)T
于是我们知道:
(1)当 θ T x = − 1 + x 1 2 + x 2 2 > = 0 \theta^Tx=-1+x_1^2+x_2^2>=0 θTx=1+x12+x22>=0 时, h θ ( x ) > = 0.5 h_{\theta}(x)>=0.5 hθ(x)>=0.5,预测结果为1;
(2)当 θ T x = − 1 + x 1 2 + x 2 2 < 0 \theta^Tx=-1+x_1^2+x_2^2<0 θTx=1+x12+x22<0 时, h θ ( x ) < 0.5 h_{\theta}(x)<0.5 hθ(x)<0.5,预测结果为0;

我们可以在二维平面上绘制一条曲线 x 1 2 + x 2 2 = 1 x_1^2+x_2^2=1 x12+x22=1 ,这条曲线能够将预测结果为0的区域和预测结果为1的区域分开:
在这里插入图片描述
这样的一条曲线就是决策边界。

我的理解就是,决策边界就是将不同类别的样本数据区分开来的那条边界,在这条边界的一侧的数据属于这一类,在这条边界的另一侧的数据不属于这一类

我们的逻辑回归模型似乎就是在找这条决策边界。

2.3 逻辑回归的损失函数

2.3.1 损失函数的定义

我们先来思考在线性回归中是怎么定义损失函数的?计算所有误差的平方和:
J ( θ ) = 1 2 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 J(\theta) = \frac{1}{2m}\sum_{i=1}^m (h_{\theta}(x^{(i)}) - y^{(i)})^2 J(θ)=2m1i=1m(hθ(x(i))y(i))2
倘若将我们的逻辑回归模型 h θ ( x ) = 1 1 + e − θ T x h_{\theta}(x) = \frac{1}{1+e^{-\theta^Tx}} hθ(x)=1+eθTx1带入到线性回归的损失函数定义中,我们得到的是一个非凸函数,如下图所示,函数有许多局部最小值,这将影响梯度下降算法寻找全局最小值:
注:图片来自吴恩达老师的PPT
为了不影响梯度下降算法寻找全局最小值,我们重新定义逻辑回归的损失函数:
C o s t ( h θ ( x ) , y ) = { − l o g ( h θ ( x ) ) if y=1 − l o g ( 1 − h θ ( x ) ) if y=0 Cost(h_{\theta}(x),y) = \begin{cases} -log(h_{\theta}(x)) & \text{if y=1}\\ -log(1-h_{\theta}(x))& \text{if y=0}\\ \end{cases} Cost(hθ(x),y)={log(hθ(x))log(1hθ(x))if y=1if y=0
为什么要这样定义损失函数呢?我们来画图理解一下。
在这里插入图片描述
(1)当 y = 1 y=1 y=1时,如果我们的预测值 h θ ( x ) h_{\theta}(x) hθ(x)越接近1,那么损失函数的值就越小,反之,损失函数的值越大;
(2)当 y = 0 y=0 y=0时,如果我们的预测值 h θ ( x ) h_{\theta}(x) hθ(x)越接近0,那么损失函数的值就越小,反之,损失函数的值越大。

因此这就符合了我们损失函数的定义:预测值和真实值越接近,损失越小;预测值和真实值差距越大,损失越大。所以我们就知道了为什么要这么定义逻辑回归的损失函数了。

2.3.2 使用梯度下降最小化损失函数

在进行梯度下降之前,先对损失函数进行化简:
C o s t ( h θ ( x ) , y ) = − y × l o g ( h θ ( x ) ) − ( 1 − y ) × l o g ( 1 − h θ ( x ) ) Cost(h_{\theta}(x) ,y)=-y\times log(h_{\theta}(x))-(1-y)\times log(1-h_{\theta}(x)) Cost(hθ(x),y)=y×log(hθ(x))(1y)×log(1hθ(x))
进一步有:
J ( θ ) = − 1 m ∑ i = 1 m [ y ( i ) l o g ( h θ ( x ( i ) ) ) + ( 1 − y ( i ) ) l o g ( 1 − h θ ( x ( i ) ) ) ] J(\theta)=-\frac{1}{m}\sum_{i=1}^m [y^{(i)}log(h_{\theta}(x^{(i)}))+(1-y^{(i)})log(1-h_{\theta}(x^{(i)}))] J(θ)=m1i=1m[y(i)log(hθ(x(i)))+(1y(i))log(1hθ(x(i)))]
此时我们得到的 J ( θ ) J(\theta) J(θ)是一个凸函数,而且它没有局部最小值,之后我们使用梯度下降算法来求使得 J ( θ ) J(\theta) J(θ)最小的参数 θ \theta θ

根据梯度下降算法(梯度下降可参考:机器学习(三)——梯度下降)我们知道算法会一直执行如下操作直至收敛:
θ j = θ j − α ∂ J ( θ ) ∂ θ j \theta_j = \theta_j - \alpha \frac{\partial J(\theta)}{\partial \theta_j} θj=θjαθjJ(θ)
θ j \theta_j θj求偏导之后得到:(求导过程不再赘述)
θ j = θ j − α 1 m ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) x j ( i ) \theta_j = \theta_j - \alpha \frac{1}{m}\sum_{i=1}^m(h_{\theta}(x^{(i)}) -y^{(i)})x_j^{(i)} θj=θjαm1i=1m(hθ(x(i))y(i))xj(i)
注意 θ j \theta_j θj是同时更新的。

2.3.3 一些高级优化

吴恩达老师的视频中介绍了一些高级优化算法(视频课可参考:B站吴恩达机器学习系列视频),老师讲解道:当我们执行梯度下降算法时,算法会计算出:
(1) J ( θ ) J(\theta) J(θ);
(2) j = 0 , 1 , . . . , n j=0,1,..., n j=0,1,...,n时的偏导数项。

算法会反复执行同时更新 θ j \theta_j θj,此时考虑:如果我们编写代码来计算 J ( θ ) J(\theta) J(θ)和这些偏导数,然后将其插入到梯度下降算法中,执行算法就能最小化损失函数。

老师介绍了共轭梯度法BFGS和L-BFGS,本人查阅了一些关于BFGS资料,在此做一下整理:
(1)一篇文章:深入机器学习系列17-BFGS & L-BFGS
(2)一片博文,介绍 了BFGS算法和在python的包中调用最优化算法函数:BFGS算法
(3)一篇介绍python包中的BFGS博文:【python】scipy包中的BFGS算法

如果对这些高级优化算法感兴趣,大家可以参考这些内容或查阅相关资料,本人对此没有深入研究,在此就简单整理一下我查阅过的资料,给大家提供一个参考。

2.4 解决多分类问题

对于多分类问题,吴恩达老师介绍了一种叫做one-vs-all一对多的方法,有些地方也称其为one-vs-rest。还有一种叫做one-vs-one的方法也可以用来解决多分类问题,接下来就简要介绍这两种方法。

2.4.1 OvR(one-vs-rest)

假设我们的样本数据是这样的:(三种颜色代表三种不同的类别)
在这里插入图片描述
我们假设红色点为类别1,蓝色点为类别2,绿色点为类别3。

OvR算法在进行分类时,它会把多分类问题转变为多个二分类问题,比如当对红色的点(类别为1)进行分类时,算法会把红色点的类别标记为正向类,记 y = 1 y=1 y=1,然后将其他的点(蓝色和绿色的点)标记为负向类,得到的分类模型为 h θ ( 1 ) ( x ) h_{\theta}^{(1)}(x) hθ(1)(x)。同样的,对蓝色点(类别为2)进行分类时,算法会把蓝色点的类别标记为正向类,记 y = 2 y=2 y=2,然后将其他的点(红色和绿色的点)标记为负向类,得到的分类模型为 h θ ( 2 ) ( x ) h_{\theta}^{(2)}(x) hθ(2)(x),以此类推,绿色点的分类模型就是 h θ ( 3 ) ( x ) h_{\theta}^{(3)}(x) hθ(3)(x)
在这里插入图片描述

于是我们得到了三个分类器: h θ ( 1 ) ( x ) h_{\theta}^{(1)}(x) hθ(1)(x) h θ ( 2 ) ( x ) h_{\theta}^{(2)}(x) hθ(2)(x) h θ ( 3 ) ( x ) h_{\theta}^{(3)}(x) hθ(3)(x)。那么在进行预测时,我们会运行所有的分类器,对于我们的输入变量 x p r e d i c t x_{predict} xpredict ,我们会得到三个分类得分: s c o r e ( 1 ) ( x p r e d i c t ) score^{(1)}(x_{predict}) score(1)(xpredict) s c o r e ( 12 ) ( x p r e d i c t ) score^{(12)}(x_{predict}) score(12)(xpredict) s c o r e ( 3 ) ( x p r e d i c t ) score^{(3)}(x_{predict}) score(3)(xpredict),我们最后选择得分最高的作为最终结果。

2.4.2 OvO(one-vs-one)

假设我们的样本数据依然是:
在这里插入图片描述
OvO算法在进行分类时,它每次会选择两个类别进行二分类,如果有n个类别,那就要进行 C n 2 C_n^2 Cn2 次分类,得到 C n 2 C_n^2 Cn2 个分类器。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在进行预测时,同样运行所有的分类器,最后选择得分最高的作为最终结果。

3. 逻辑回归算法的实现

接下来我们简单实现一下逻辑回归算法并进行测试。注:代码编写和测试均在notebook中进行。

3.1 解决二分类问题

3.1.1 分类器的实现

定义我的自己的逻辑回归类:

class myLogisticRegression:
    def __init__(self):
        self.coef_ = None
        self.intercept_ = None
        self._theta = None
    
    def _sigmoid(self, z):
        return 1. / (1. + np.exp(-z))
    
    def fit(self, X_train, y_train, eta=0.01, n_iter=1e4):
        
        def J(theta, X_b, y):
            y_hat = self._sigmoid(X_b.dot(theta))
            # 防止溢出
            try:
                return -np.sum(y*np.log(y_hat) + (1-y)*np.log(1-y_hat)) / len(y)
            except:
                return float('inf')
            
        def dJ(theta, X_b, y):
            return X_b.T.dot(self._sigmoid(X_b.dot(theta)) - y) / len(y)
        
        def gradient_descent(X_b, y, initial_theta, eta, n_iter=1e4, epsilon=1e-8):
            theta = initial_theta
            cur_iter = 0
            
            while cur_iter < n_iter:
                gradient = dJ(theta, X_b, y)
                last_theta = theta
                theta = theta - eta * gradient
                if (abs(J(theta, X_b, y) - J(last_theta, X_b, y)) < epsilon):
                    break
                    
                cur_iter += 1
            
            return theta
        
        X_b = np.hstack([np.ones((len(X_train),1)), X_train])
        initial_theta = np.zeros(X_b.shape[1])
        self._theta = gradient_descent(X_b, y_train, initial_theta, eta, n_iter)
        
        self.intercept_ = self._theta[0]
        self.coef_ = self._theta[1:]
        
        return self
    
    def predict_(self, X_predict):
        X_b = np.hstack([np.ones((len(X_predict),1)), X_predict])
        return self._sigmoid(X_b.dot(self._theta))
    
    def predict(self, X_predict):
        possibility = self.predict_(X_predict)
        return np.array(possibility >= 0.5, dtype='int')
    
    def score(self, X_test, y_test):
        y_predict = self.predict(X_test)
        score = np.sum(y_test == y_predict) / len(y_test)
        return score                        
        

3.1.2 生成样本数据

我们使用sklearn中的鸢尾花数据,我们先进行二分类,所以只选取鸢尾花的前两个特征:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data
y = iris.target
X = X[y<2,:2]
y = y[y<2]

样本数据可视化:

plt.scatter(X[y==0,0], X[y==0,1], color="red")
plt.scatter(X[y==1,0], X[y==1,1], color="blue")
plt.show()

在这里插入图片描述

3.1.3 测试

划分训练集和测试集:

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y)

训练我们自己的逻辑回归模型:

log_reg = myLogisticRegression()
log_reg.fit(X_train, y_train)

使用测试集数据进行预测,先看看预测得到的概率值是多少:

log_reg.predict_(X_test)

在这里插入图片描述
我们知道当预测的概率值>=0.5时,得到的预测结果是1,反之为0。
接下来看看预测的类别:

log_reg.predict(X_test)

我们可以看到分类类别和预测的概率值是相对应的:
在这里插入图片描述
最后我们来计算一下预测的得分值:

log_reg.score(X_test, y_test)

在这里插入图片描述

3.2 解决多分类问题

我们在上文中提到了解决多分类问题的两种方式:one-vs-rest和one-vs-one,接下来我们就来看看这两种算法是如何解决多分类问题的。

3.2.1 准备工作

参考python3入门机器学习。
(1)准备样本数据
我们依然使用sklearn中的鸢尾花数据:

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets

iris = datasets.load_iris()
X = iris.data[:,:2]
y = iris.target

(2)划分训练集和测试集

from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=666)

(3)使用sklearn中的逻辑回归类,训练一个分类器

from sklearn.linear_model import LogisticRegression
log_reg = LogisticRegression()
log_reg.fit(X_train, y_train)

(4)为了后面方便可视化,这里定义一个绘制决策边界的函数

def plot_decision_boundary(model, axis):
    
    x0, x1 = np.meshgrid(
        np.linspace(axis[0], axis[1], int((axis[1]-axis[0])*100)).reshape(-1, 1),
        np.linspace(axis[2], axis[3], int((axis[3]-axis[2])*100)).reshape(-1, 1),
    )
    X_new = np.c_[x0.ravel(), x1.ravel()]

    y_predict = model.predict(X_new)
    zz = y_predict.reshape(x0.shape)

    from matplotlib.colors import ListedColormap
    custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    
    plt.contourf(x0, x1, zz, linewidth=5, cmap=custom_cmap)  
3.2.2 one-vs-rest

使用sklearn中的的OvR算法:

from sklearn.multiclass import OneVsRestClassifier

ovr = OneVsRestClassifier(log_reg)
ovr.fit(X_train, y_train)
print(ovr.score(X_test, y_test))

得分为:
在这里插入图片描述
分类过程可视化:

plot_decision_boundary(ovr, axis=[4, 9, 1, 5])
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.scatter(X[y==2,0], X[y==2,1])
plt.show()

在这里插入图片描述

3.2.3 one-vs-one

使用sklearn中的OvO算法:

from sklearn.multiclass import OneVsOneClassifier

ovo = OneVsOneClassifier(log_reg)
ovo.fit(X_train, y_train)
print(ovo.score(X_test, y_test))

得分为:
在这里插入图片描述
分类过程可视化:

plot_decision_boundary(ovo, axis=[4, 9, 1, 5])
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.scatter(X[y==2,0], X[y==2,1])
plt.show()

在这里插入图片描述

4. 总结

(1)首先我们引入了分类问题,并讨论了为什么线性回归算法无法很好地解决分类问题,并由此引出了逻辑回归算法,逻辑回归算法主要用来解决分类问题;
(2)接着我们介绍了逻辑回归的假设模型,并解释了为什么要使用sigmoid函数,进一步又介绍了什么是决策边界;
(3)之后我们通过线性回归的损失函数,引出了逻辑回归的损失函数,并解释了为什么直接使用线性回归的损失函数是不可行的(联系非凸函数),从而我们知道了为什么要重新定义逻辑回归的损失函数;
(4)之后介绍了使用梯度下降算法来最小化逻辑回归的损失函数,并介绍了一些高级优化算法;
(5)接着我们引入了多分类问题,并介绍了两种解决多分类问题的方法:one-vs-rest 和 one-vs-one;
(6)然后我们通过编写代码实现了逻辑回归算法,并尝试用逻辑回归算法解决二分类和多分类问题。

至此,逻辑回归算法的相关内容就大致总结完了。有几点需要说明一下:
(1)因为本人也是初学者,文章内容可能会有错误和不足,非常欢迎大家指正和补充!
(2)本文是个人的理解和学习内容,有些地方并不深入,如果感兴趣并希望深入研究的话,大家可查阅相关资料。

附:
B站吴恩达老师视频课:链接
一位大佬总结的笔记(包含老师的PPT):链接

好啦,希望本篇能够帮助大家理解逻辑回归算法,也期待大家的补充和指正!期待与大家交流!

机器学习(六)——正则化

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值