0%

第三周

[TOC]

六、逻辑回归(Logistic Regression)

6.1 分类问题

在分类问题中要预测的变量 $y$ 是离散的值,用逻辑回归 (Logistic Regression) 的算法来引入

分类问题的例子有:判断一封电子邮件是否是垃圾邮件;判断一次金融交易是否是欺诈;之前我们也谈到了肿瘤分类问题的例子,区别一个肿瘤是恶性的还是良性的……

从二元的分类问题开始讨论:将因变量(dependent variable)可能属于的两个类分别称为负向类(negative class)和正向类(positive class),则因变量$y\in \{ 0,1 \}$ ,其中 0 表示负向类,1 表示正向类。

如果要用线性回归算法来解决一个分类问题,对于分类, $y$ 取值为 0 或1,但如果使用的是线性回归,那么假设函数的输出值可能远大于 1,或者远小于0,即使所有训练样本的标签 $y$ 都等于 0 或 1。

逻辑回归算法的输出值永远在0到 1 之间,虽然叫回归,但是其实际上是一种分类算法。

6.2 假设函数

此前我们说过希望我们的分类器的输出值在0和1之间,因此我们希望构造一个满足某些性质的假设函数(它的预测值要在0和1之间)。

回顾在一开始提到的乳腺癌分类问题,我们可以用线性回归的方法求出适合数据的一条直线:

根据线性回归模型,我们只能预测连续的值;而对于分类问题,我们需要输出0或1。我们可以这样预测:

  • 当${h_\theta}\left( x \right)\ge0.5$时,预测 $y=1$。
  • 当${h_\theta}\left( x \right)<0.5$时,预测 $y=0$ 。

对于上图所示的数据,这样的一个线性模型似乎能很好地完成分类任务。假使我们又观测到一个非常大尺寸的恶性肿瘤,将其作为实例加入到我们的训练集中来,这将使得我们获得一条新的直线。

这时再使用0.5作为阀值来预测肿瘤是良性还是恶性便不合适了:从中可以看出,线性回归模型因为其预测的值可以超越[0,1]的范围,并不适合解决这样的问题。

我们引入一个新的模型,逻辑回归,该模型的输出变量范围始终在0和1之间。

逻辑回归模型的假设是: $h_\theta \left( x \right)=g\left(\theta^{T}X \right)$

其中:$X$ 代表特征向量;$g$ 代表逻辑函数(logistic function)是一个常用的逻辑函数为Sigmoid函数(Sigmoid function),公式为: $g\left( z \right)=\frac{1}{1+{ {e}^{-z} } }$。(相当于在线性回归的基础上用函数把原有的模型“框住”)

python实现:

1
2
3
import numpy as np 
def sigmoid(z):
return 1 / (1 + np.exp(-z))

该函数的图像为:

合起来,我们得到逻辑回归模型的假设:

$h_\theta \left( x \right)$的作用是,对于给定的输入变量,根据选择的参数计算输出变量为1的可能性(estimated probablity)即 $h_\theta \left( x \right)=P\left( y=1|x;\theta \right)$

例如,如果对于给定的$x$,通过已经确定的参数计算得出$h_\theta \left( x \right)=0.7$,则表示有70%的几率 $y$ 为正类,相应地 $y$ 为负类的几率为1-0.7=0.3。

6.3 决策边界

现在讲下决策边界(decision boundary)的概念。这个概念能更好地帮助我们理解逻辑回归的假设函数在计算什么。

在逻辑回归中,我们预测:

  • 当${h_\theta}\left( x \right)>=0.5$时,预测 $y=1$
  • 当${h_\theta}\left( x \right)<0.5$时,预测 $y=0$

根据上面绘制出的 Sigmoid函数图像,我们知道当

  • $z=0$ 时 $g(z)=0.5$
  • $z>0$ 时 $g(z)>0.5$
  • $z<0$ 时 $g(z)<0.5$

又 $z={\theta^{T} }x$ ,即:

  • ${\theta^{T} }x\ge0$ 时,预测 $y=1$
  • ${\theta^{T} }x<0$ 时,预测 $y=0$

现在假设我们有一个模型:

参数$\theta$ 是向量[-3 1 1]

则当$-3+{x_1}+{x_2} \geq 0$,即${x_1}+{x_2} \geq 3$时,模型将预测 $y=1$。

我们可以绘制直线${x_1}+{x_2} = 3$,这条线便是我们模型的分界线,将预测为1的区域和预测为 0的区域分隔开。

但如果我们的数据呈现非线性的,该怎样选择模型

因为需要用曲线才能分隔 $y=0$ 的区域和 $y=1$ 的区域,我们需要二次方特征:${h_\theta}\left( x \right)=g\left( {\theta_0}+{\theta_1}{x_1}+{\theta_{2} }{x_{2} }+{\theta_{3} }x_{1}^{2}+{\theta_{4} }x_{2}^{2} \right)$,其中的参数$\theta$是[-1 0 0 1 1],则我们得到的判定边界恰好是圆点在原点且半径为1的圆形(即$x_1^2+x_2^2=1$,高中所学的圆的方程)。

可以用复杂的模型来适应复杂形状的判定边界。

6.4 代价函数

定义用来拟合参数的优化目标叫代价函数,是监督学习问题中的逻辑回归模型的拟合问题

对于线性回归模型,我们定义的代价函数是所有模型误差的平方和。

理论上来说,我们也可以对逻辑回归模型沿用这个定义,但是当我们将${h_\theta}\left( x \right)=\frac{1}{1+{e^{-\theta^{T}x} }}$带入到这样定义的代价函数中时,我们得到的代价函数将是一个非凸函数(non-convexfunction)。

这意味着代价函数有许多局部最小值,会影响梯度下降算法寻找全局最小值

线性回归的代价函数为:$J\left( \theta \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{\frac{1}{2}{ {\left( {h_\theta}\left({x}^{\left( i \right)} \right)-{y}^{\left( i \right)} \right)}^{2} } }$ 。

我们重新定义逻辑回归的代价函数为:$J\left( \theta \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{ {Cost}\left( {h_\theta}\left( {x}^{\left( i \right)} \right),{y}^{\left( i \right)} \right)}$,其中

${h_\theta}\left( x \right)$与 $Cost\left( {h_\theta}\left( x \right),y \right)$之间的关系如下图所示:

这样构建的$Cost\left( {h_\theta}\left( x \right),y \right)$函数的特点是:

  • 当实际的$y=1$ 且${h_\theta}\left( x \right)$也为 1 时误差为 0,当 $y=1$ 但${h_\theta}\left( x \right)$不为1时误差随着${h_\theta}\left( x \right)$变小而变大;当实际的 $y=0$ 且${h_\theta}\left( x \right)$也为 0 时代价为 0,当$y=0$ 但${h_\theta}\left( x \right)$不为 0时误差随着 ${h_\theta}\left( x \right)$的变大而变大。

将构建的 $Cost\left( {h_\theta}\left( x \right),y \right)$简化如下:

  • $Cost\left( {h_\theta}\left( x \right),y \right)=-y\times log\left( {h_\theta}\left( x \right) \right)-(1-y)\times log\left( 1-{h_\theta}\left( x \right) \right)$

带入代价函数得到:

$J\left( \theta \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{[-{ {y}^{(i)} }\log \left( {h_\theta}\left( { {x}^{(i)} } \right) \right)-\left( 1-{ {y}^{(i)} } \right)\log \left( 1-{h_\theta}\left( { {x}^{(i)} } \right) \right)]}$

即:$J\left( \theta \right)=-\frac{1}{m}\sum\limits_{i=1}^{m}{ [ { {y}^{(i)} }\log \left( {h_\theta}\left( { {x}^{(i)} } \right) \right)+\left( 1-{ {y}^{(i)} } \right)\log \left( 1-{h_\theta}\left( { {x}^{(i)} } \right) \right)] }$

Python代码实现:

1
2
3
4
5
6
7
8
import numpy as np
def cost(theta, X, y):
theta = np.matrix(theta)
X = np.matrix(X)
y = np.matrix(y)
first = np.multiply(-y, np.log(sigmoid(X* theta.T)))
second = np.multiply((1 - y), np.log(1 - sigmoid(X* theta.T)))
return np.sum(first - second) / (len(X))

在得到这样一个代价函数以后,以用梯度下降算法来求得能使代价函数最小的参数了。算法为:

Repeat {
$\theta_j := \theta_j - \alpha \frac{\partial}{\partial\theta_j} J(\theta)$
(simultaneously update all )
}

求导后得到:

Repeat {
$\theta_j := \theta_j - \alpha \frac{1}{m}\sum\limits_{i=1}^{m}{ {\left( {h_\theta}\left( \mathop{x}^{\left( i \right)} \right)-\mathop{y}^{\left( i \right)} \right)} }\mathop{x}_{j}^{(i)}$
(simultaneously update all )
}

代价函数$J(\theta)$会是一个凸函数,且没有局部最优值,推导过程如下:

$J\left( \theta \right)=-\frac{1}{m}\sum\limits_{i=1}^{m}{ [ { {y}^{(i)} }\log \left( {h_\theta}\left( { {x}^{(i)} } \right) \right)+\left( 1-{ {y}^{(i)} } \right)\log \left( 1-{h_\theta}\left( { {x}^{(i)} } \right) \right)]}$

考虑:

${h_\theta}\left( { {x}^{(i)} } \right)=\frac{1}{1+{ {e}^{-{\theta^T}{ {x}^{(i)} } } } }$

则:

  • 其中最后一行是将log()函数内的$1+{ {e}^{-{\theta^T}{ {x}^{(i)} }}}$上下同乘${e}^{-{\theta^T}{ {x}^{(i)}}}$的结果

所以:

注:虽然得到的梯度下降算法看上去与线性回归的梯度下降算法形式相同,但是这里的${h_\theta}\left( x \right)=g\left( {\theta^T}X \right)$与线性回归中不同。

进行特征缩放依旧依旧有必要

梯度下降算法之外的选择:还有一些常被用来令代价函数最小的算法,这些算法更加复杂和优越,而且通常不需要人工选择学习率,通常比梯度下降算法要更加快速。这些算法有:共轭梯度Conjugate Gradient),局部优化法(Broyden fletcher goldfarb shann,BFGS)和有限内存局部优化法(LBFGS)

6.5 简化的成本函数和梯度下降

找出一种简单一点的方法重写代价函数,以此来替换现在用的方法;同时弄清楚如何运用梯度下降法来拟合出逻辑回归的参数:通过此节应该了解到如何实现一个完整的逻辑回归算法。

这就是逻辑回归的代价函数:

这个式子可以合并成:

$Cost\left( {h_\theta}\left( x \right),y \right)=-y\times log\left( {h_\theta}\left( x \right) \right)-(1-y)\times log\left( 1- {h_\theta} \left( x \right) \right)$

即,逻辑回归的代价函数:

$Cost\left( {h_\theta}\left( x \right),y \right)=-y\times log\left( {h_\theta}\left( x \right) \right)-(1-y)\times log\left( 1-{h_\theta}\left( x \right) \right)$
$=-\frac{1} {m}\sum\limits_{i=1}^{m} {[{ {y}^{(i)} }\log \left( {h_\theta}\left( { {x}^{(i)} } \right) \right)+\left( 1-{ {y}^{(i)} } \right)\log \left( 1-{h_\theta}\left( { {x}^{(i)} } \right) \right)] }$

我们要试图找尽量让$J\left( \theta \right)$ 取得最小值的参数$\theta $:$\underset{\theta}{\min }J\left( \theta \right)$

所以我们想要尽量减小这一项,这将我们将得到某个参数$\theta $。

另外,我们假设的输出实际上是概率值:$p(y=1|x;\theta)$,就是关于 $x$以$\theta $为参数,$y=1$ 的概率,即假设就是估计 $y=1$ 的概率,所以,接下来就是弄清楚如何最大限度地最小化代价函数$J\left( \theta \right)$,作为一个关于$\theta $的函数,这样我们才能为训练集拟合出参数$\theta $。

最小化代价函数的方法,是使用梯度下降法(gradient descent)。

如果我们要最小化这个关于$\theta$的函数值,这就是我们通常用的梯度下降法的模板。

我们要反复更新每个参数,用这个式子来更新,就是用它自己减去学习率 $\alpha$
乘以后面的微分项。求导后得到:

$\theta_j := \theta_j - \alpha \frac{\partial}{\partial\theta_j} J(\theta)$

${\theta_j}:={\theta_j}-\alpha \frac{1}{m}\sum\limits_{i=1}^{m}{({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} }){x_{j} } ^{(i)} }$

将后面这个式子,在 $i=1$ 到 $m$ 上求和,其实就是预测误差乘以$x_j^{(i)}$ ,导数项$\frac{\partial }{\partial {\theta_j} }J\left( \theta \right)$即其中的更新项

所以,如果你有 $n$ 个特征,也就是说:那么需要用这个式子:${\theta_j}:={\theta_j}-\alpha \frac{1}{m}\sum\limits_{i=1}^{m} {({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} }){ {x}_{j} }^{(i)} }$来同时更新所有$\theta $的值。

把这个更新规则和我们之前用在线性回归上的进行比较会发现,这个式子正是我们用来做线性回归梯度下降的。

那么,线性回归和逻辑回归是同一个算法吗?实际上,假设的定义发生了变化。

对于线性回归假设函数:

${h_\theta}\left( x \right)={\theta^T}X={\theta_{0} }{x_{0} }+{\theta_{1} }{x_{1} } +{\theta_{2} }{x_{2} }+…+{\theta_{n} } {x_{n} }$

而现在逻辑函数假设函数:

${h_\theta}\left( x \right)=\frac{1}{1+{ {e}^{-{\theta^T}X} } }$

因此,即使更新参数的规则看起来基本相同,但由于假设的定义发生了变化,所以逻辑函数的梯度下降,跟线性回归的梯度下降实际上是两个完全不同的东西。

我们在谈论线性回归的梯度下降法时谈到了如何监控梯度下降法以确保其收敛,把同样的方法用在逻辑回归中,来监测梯度下降也同样可以确保它正常收敛。

当使用梯度下降法来实现逻辑回归时,我们有不同的参数$\theta $,即${\theta_{0} }$ ${\theta_{1} }$ ${\theta_{2} }$ 一直到${\theta_{n} }$,我们需要用这个表达式来更新这些参数。可以使用 for循环来更新这些参数值,用 for i=1 to n,或者 for i=1 to n+1;但理想情况下,我们更提倡使用向量化的实现,可以把所有这些 $n$个参数同时更新。

6.6 高级优化

现在我们换个角度来看什么是梯度下降:有代价函数$J\left( \theta \right)$,而我们想要使其最小化,那么我们需要做的是编写代码,当输入参数 $\theta$ 时,它们会计算出两样东西:$J\left( \theta \right)$ 以及$J$ 等于 0、1直到 $n$ 时的偏导数项。

假设我们已经完成了可以实现这两件事的代码,那么梯度下降所做的就是反复执行这些更新。

另一种考虑梯度下降的思路是:我们需要写出代码来计算$J\left( \theta \right)$ 和这些偏导数,然后把这些插入到梯度下降中,然后它就可以为我们最小化这个函数。

对于梯度下降来说,我认为从技术上讲,你实际并不需要编写代码来计算代价函数$J\left( \theta \right)$。你只需要编写代码来计算导数项,但是,如果你希望代码还要能够监控这些$J\left( \theta \right)$ 的收敛性,那么我们就需要自己编写代码来计算代价函数$J(\theta)$和偏导数项$\frac{\partial }{\partial {\theta_j} }J\left( \theta \right)$。所以,在写完能够计算这两者的代码之后,我们就可以使用梯度下降。

梯度下降并不是我们可以使用的唯一算法,还有其他一些算法更高级、更复杂。如果我们能用这些方法来计算代价函数$J\left( \theta \right)$和偏导数项$\frac{\partial }{\partial {\theta_j} }J\left( \theta \right)$两个项的话,那么这些算法就是为我们优化代价函数的不同方法,共轭梯度法 BFGS (变尺度法) 和L-BFGS (限制变尺度法) 就是其中一些更高级的优化算法,它们需要有一种方法来计算 $J\left( \theta \right)$,以及需要一种方法计算导数项,然后使用比梯度下降更复杂的算法来最小化代价函数。

这三种算法有许多优点:一个是使用这其中任何一个算法,你通常不需要手动选择学习率 $\alpha$,对于这些算法的一种思路是给出计算导数项和代价函数的方法,可以认为算法有一个智能的内部循环,事实上,他们确实有一个智能的内部循环,称为线性搜索(line search)算法,它可以自动尝试不同的学习速率 $\alpha$,并自动选择一个好的学习速率 $a$,因此它甚至可以为每次迭代选择不同的学习速率,那么你就不需要自己选择。这些算法实际上在做更复杂的事情,不仅仅是选择一个好的学习速率,所以它们往往最终比梯度下降收敛得快

主要缺点是它们比梯度下降法复杂多了,最好不要使用 L-BGFSBFGS这些算法,除非是数值计算方面的专家。

如何直接使用这些算法:

比方说,你有一个含两个参数的问题,这两个参数是${\theta_{0} }$和${\theta_{1} }$,因此,通过这个代价函数,你可以得到${\theta_{1} }$和 ${\theta_{2} }$的值,如果你将$J\left( \theta \right)$ 最小化的话,那么它的最小值将是${\theta_{1} }=5$ ,${\theta_{2} }=5$。代价函数$J\left( \theta \right)$的导数推出来就是这两个表达式:

$\frac{\partial }{\partial { {\theta }_{1} } }J(\theta)=2({ {\theta }_{1} }-5)$

$\frac{\partial } {\partial { {\theta }_{2} } }J(\theta)=2({ {\theta }_{2} }-5)$

如果我们不知道最小值,但你想要代价函数找到这个最小值,是用比如梯度下降这些算法,但最好是用比它更高级的算法,你要做的就是运行一个像这样的python函数:

1
2
3
4
5
6
7
8
9
10
11
import numpy as np

def cost_function(theta):
# 计算代价函数的值
function = (theta[0] - 5) ** 2 + (theta[1] - 5) ** 2
# 初始化梯度数组
gradient = np.zeros((2, 1))
# 计算梯度
gradient[0] = 2 * (theta[0] - 5)
gradient[1] = 2 * (theta[1] - 5)
return function, gradient

这样就计算出这个代价函数,函数返回的第二个值是梯度值,梯度值应该是一个2×1的向量,梯度向量的两个元素对应这里的两个偏导数项,运行这个costFunction 函数后,可以调用高级的优化函数进行下一步的操作,在python中可以使用minimize

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from scipy.optimize import minimize
import numpy as np
# 定义代价函数
# 上面提到的那个
# 定义代价函数的梯度
def cost_function_grad(theta):
return cost_function(theta)[1]
# 设置优化选项
options = {'maxiter': 100}
# 初始参数
initial_theta = np.zeros((2, 1))
# 使用minimize函数进行优化
result = minimize(fun=cost_function_grad, x0=initial_theta.flatten(), jac=True, options=options)
# 提取结果
opt_theta = result.x.reshape(-1, 1)
function_val = cost_function(opt_theta)[0]
exit_flag = result.success
opt_theta, function_val, exit_flag

在MATLAB中,fminunc函数用于无约束的最小化问题。它需要一个目标函数、一个初始点和一个选项结构体。在Python中,我们可以使用SciPy库中的minimize函数来实现类似的功能。

下面是将MATLAB代码转换成Python代码的版本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
from scipy.optimize import minimize
import numpy as np
# 定义代价函数
## 这个和之前相同
# 定义代价函数的梯度
def cost_function_grad(theta):
return cost_function(theta)[1]
## 返回之前函数的第二个值
# 设置优化选项
options = {'maxiter': 100}
# 初始参数
initial_theta = np.zeros((2, 1))
# 使用minimize函数进行优化
result = minimize(fun=cost_function_grad, x0=initial_theta.flatten(), jac=True, options=options)
## fun参数是目标函数,x0是初始点,jac参数设置为True表示我们提供了梯度函数,options是优化选项
# 提取结果
opt_theta = result.x.reshape(-1, 1)
## 结果中提取优化后的参数,并将其重新塑形成2行1列的数组
function_val = cost_function(opt_theta)[0]
## 计算优化后参数的代价函数值
exit_flag = result.success
## 从结果中提取退出标志,表示优化是否成功。
opt_theta, function_val, exit_flag

6.7 多类别分类:一对多

使用逻辑回归 (logistic regression)来解决多类别分类问题:”一对多” (one-vs-all) 的分类算法。

先看这样一些例子。

  • 第一个例子:假如说你现在需要一个学习算法能自动地将邮件归类到不同的文件夹里,或者说可以自动地加上标签,那么,你也许需要一些不同的文件夹,或者不同的标签来完成这件事,来区分开来自工作的邮件、来自朋友的邮件、来自家人的邮件或者是有关兴趣爱好的邮件,那么,我们就有了这样一个分类问题:其类别有四个,分别用$y=1$、$y=2$、$y=3$、$y=4$ 来代表。
  • 第二个例子是有关药物诊断的,如果一个病人因为鼻塞来到你的诊所,他可能并没有生病,用 $y=1$ 这个类别来代表;或者患了感冒,用 $y=2$ 来代表;或者得了流感用$y=3$来代表。
  • 第三个例子:如果你正在做有关天气的机器学习分类问题,那么你可能想要区分哪些天是晴天、多云、雨天、或者下雪天,对上述所有的例子,$y$ 可以取一个很小的数值,一个相对”谨慎”的数值,比如1 到3、1到4或者其它数值,以上说的都是多类分类问题,顺便一提的是,对于下标是0 1 2 3,还是 1 2 3 4 都不重要,我更喜欢将分类从 1 开始标而不是0,其实怎样标注都不会影响最后的结果。

然而对于之前的一个,二元分类问题,我们的数据看起来可能是像这样:

对于一个多类分类问题,数据集或许看起来像这样:

用3种不同的符号来代表3个类别,问题是给出3个类型的数据集,我们如何得到一个学习算法来进行分类

我们现在已经知道如何进行二元分类,可以使用逻辑回归,对于直线或许你也知道,可以将数据集一分为二为正类和负类。用一对多的分类思想,我们可以将其用在多类分类问题上

现在我们有一个训练集,类似上图表示的有3个类别,我们用三角形表示 $y=1$,方框表示$y=2$,叉叉表示 $y=3$。

下面要做的就是使用一个训练集,将其分成3个二元分类问题

先从用三角形代表的类别1开始,实际上我们可以创建一个,新的”伪”训练集,类型2和类型3定为负类,类型1设定为正类,我们创建一个新的训练集,如下图所示的那样,我们要拟合出一个合适的分类器。

为了能实现这样的转变,我们将多个类中的一个类标记为正向类($y=1$),然后将其他所有类都标记为负向类,这个模型记作$h_\theta^{\left( 1 \right)}\left( x \right)$

接着,类似地第我们选择另一个类标记为正向类($y=2$),再将其它类都标记为负向类,将这个模型记作 $h_\theta^{\left( 2 \right)}\left( x \right)$

依此类推,最后我们得到一系列的模型简记为: $h_\theta^{\left( i \right)}\left( x \right)=p\left( y=i|x;\theta \right)$其中:$i=\left( 1,2,3….k \right)$

最后,在我们需要做预测时,我们将所有的分类机都运行一遍,然后对每一个输入变量,都选择最高可能性的输出变量。

总之,我们已经把要做的做完了,现在要做的就是训练这个逻辑回归分类器:$h_\theta^{\left( i \right)}\left( x \right)$, 其中 $i$ 对应每一个可能的 $y=i$,最后,为了做出预测,我们给出输入一个新的 $x$ 值,用这个做预测。我们要做的就是在我们三个分类器里面输入 $x$,然后我们选择一个让 $h_\theta^{\left( i \right)}\left( x \right)$ 最大的$ i$,即$\mathop{\max}\limits_i\,h_\theta^{\left( i \right)}\left( x \right)$。

你现在知道了基本的挑选分类器的方法,选择出哪一个分类器是可信度最高效果最好的,就可认为得到一个正确的分类,无论$i$值是多少,我们都有最高的概率值,我们预测$y$就是对应最高概率的那个值,这就是多类别分类问题

七、正则化(Regularization)

7.1 过拟合的问题

线性回归和逻辑回归等算法将它们应用到某些特定的机器学习应用时,会遇到过拟合(over-fitting)的问题,可能会导致它们效果很差。

如果我们有非常多的特征,我们通过学习得到的假设可能能够非常好地适应训练集(代价函数可能几乎为0),但是可能会不能推广到新的数据。

下图是一个回归问题的例子:

第一个模型是一个线性模型,欠拟合,不能很好地适应我们的训练集;

第三个模型是一个四次方的模型,过于强调拟合原始数据,而丢失了算法的本质:预测新数据。我们可以看出,若给出一个新的值使之预测,它将表现的很差,是过拟合,虽然能非常好地适应我们的训练集但在新输入变量进行预测时可能会效果不好;

而中间的模型最合适

分类问题中也存在这样的问题:

以多项式理解,$x$ 的次数越高,拟合的越好,但相应的预测的能力就可能变差。

问题是,如果我们发现了过拟合问题,应该如何处理?

  1. 丢弃一些不能帮助我们正确预测的特征。可以是手工选择保留哪些特征,或者使用一些模型选择的算法来帮忙(例如PCA

  2. 正则化。 保留所有的特征,但是减少参数的大小(magnitude)。

7.2 代价函数

上面的回归问题中如果我们的模型是:
${h_\theta}\left( x \right)={\theta_{0} }+{\theta_{1} } {x_{1} }+{\theta_{2} } {x_{2}^2}+{\theta_{3} }{x_{3}^3}+{\theta_{4} } {x_{4}^4}$
可以从之前的事例中看出,是高次项导致了过拟合的产生,所以如果我们能让这些高次项的系数接近于0的话,我们就能很好的拟合

所以我们要做的就是在一定程度上减小这些参数$\theta $ 的值,这就是正则化,要做的便是修改代价函数,在其中 ${\theta_{3} }$和${\theta_{4} }$设置一点惩罚。这样做的话,我们在尝试最小化代价时也需要将这个惩罚纳入考虑中,并最终导致选择较小一些的${\theta_{3} }$和${\theta_{4} }$。

例如,修改后的代价函数如下:$\underset{\theta }{\mathop{\min } }\,\frac{1}{2m}[\sum\limits_{i=1}^{m}{ { {\left( { {h}_{\theta } }\left( { {x}^{(i)} } \right)-{ {y}^{(i)} } \right)}^{2} }+1000\theta _{3}^{2}+10000\theta _{4}^{2} ] }$

通过这样的代价函数选择出的${\theta_{3} }$和${\theta_{4} }$ 对预测结果的影响就比之前要小许多。

假如我们有非常多的特征,我们并不知道其中哪些特征我们要惩罚,我们就对所有的特征进行惩罚,并且让代价函数最优化的软件来选择这些惩罚的程度。这样的结果是得到了一个较为简单的能防止过拟合问题的假设:$J\left( \theta \right)=\frac{1}{2m}[\sum\limits_{i=1}^{m}{ { {({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} })}^{2} }+\lambda \sum\limits_{j=1}^{n}{\theta_{j}^{2} } ] }$

其中$\lambda $又称为正则化参数(Regularization Parameter)。

注:根据惯例,我们不对${\theta_{0} }$ 进行惩罚

经过正则化处理的模型与原模型的可能对比如下图所示:

如果选择的正则化参数$\lambda$ 过大,则会把所有的参数都最小化了,导致模型变成 ${h_\theta}\left( x \right)={\theta_{0} }$,也就是上图中红色直线所示的情况,造成欠拟合
为什么增加的一项$\lambda =\sum\limits_{j=1}^{n}{\theta_j^{2} }$ 可以使$\theta $的值减小:因为如果我们令 $\lambda$ 的值很大的话,为了使Cost Function 尽可能的小,所有的 $\theta $ 的值(不包括${\theta_{0} }$)都会在一定程度上减小;但如果$\lambda$ 的值太大了,那么$\theta $(不包括${\theta_{0} }$)都会趋近于0,这样我们所得到的只能是一条平行于$x$轴的直线。

所以对于正则化,我们要取一个合理的 $\lambda$ 的值,这样才能更好的应用正则化

7.3 正则化线性回归

正则化线性回归的代价函数为:

$J\left( \theta \right)=\frac{1}{2m}\sum\limits_{i=1}^{m}{[({ {({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} } ) }^{2} }+\lambda \sum\limits_{j=1}^{n}{\theta _{j}^{2} })]}=\frac{1}{2m}\sum\limits_{i=1}^{m}{ {({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} } ) }^{2} }+\frac{\lambda}{2} \sum\limits_{j=1}^{n}{\theta _{j}^{2} }$

如果我们要使用梯度下降法令这个代价函数最小化,因为我们未对$\theta_0$进行正则化,所以梯度下降算法将分两种情形:

$Repeat$ $until$ $convergence${

​ ${\theta_0}:={\theta_0}-a\frac{1}{m}\sum\limits_{i=1}^{m}{(({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} })x_{0}^{(i)} })$

​ ${\theta_j}:={\theta_j}-a[\frac{1}{m}\sum\limits_{i=1}^{m}{(({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} })x_{j}^{\left( i \right)} }+\frac{\lambda }{m}{\theta_j}]$

​ $for$ $j=1,2,…n$

​ }

对上面的算法中$ j=1,2,…,n$ 时的更新式子进行调整可得:

${\theta_j}:={\theta_j}(1-a\frac{\lambda }{m})-a\frac{1}{m}\sum\limits_{i=1}^{m}{({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} })x_{j}^{\left( i \right) } }$
可以看出,正则化线性回归的梯度下降算法的变化在于,每次都在原有算法更新规则的基础上令$\theta $值减少了一个额外的值。

同样也可以利用正规方程来求解正则化线性回归模型,方法如下所示:

  • 图中的矩阵尺寸为 $(n+1)*(n+1)$。

7.4 正则化的逻辑回归模型

针对逻辑回归问题,之前已经学习过两种优化算法:我们首先学习了使用梯度下降法来优化代价函数$J\left( \theta \right)$,接下来学习了更高级的优化算法,这些高级优化算法需要你自己设计代价函数$J\left( \theta \right)$。

对于逻辑回归,我们也给代价函数增加一个正则化的表达式,得到代价函数:

$J\left( \theta \right)=\frac{1}{m}\sum\limits_{i=1}^{m}{[-{ {y}^{(i)} }\log \left( {h_\theta}\left( { {x}^{(i)} } \right) \right)-\left( 1-{ {y}^{(i)} } \right)\log \left( 1-{h_\theta}\left( { {x}^{(i)} } \right) \right)]}+\frac{\lambda } {2m}\sum\limits_{j=1}^{n}{\theta _{j}^{2} }$

  • $\frac{1}{2m}$是一个缩放因子。它确保正则化项与原始的代价函数(对数似然项)在同一数量级上,使得两者可以平衡

Python代码:

1
2
3
4
5
6
7
8
9
10
import numpy as np

def costReg(theta, X, y, learningRate):
theta = np.matrix(theta)
X = np.matrix(X)
y = np.matrix(y)
first = np.multiply(-y, np.log(sigmoid(X*theta.T)))
second = np.multiply((1 - y), np.log(1 - sigmoid(X*theta.T)))
reg = (learningRate / (2 * len(X))* np.sum(np.power(theta[:,1:theta.shape[1]],2))
return np.sum(first - second) / (len(X)) + reg

要最小化该代价函数,通过求导,得出梯度下降算法为:

$Repeat$ $until$ $convergence${

​ ${\theta_0}:={\theta_0}-a\frac{1}{m}\sum\limits_{i=1}^{m}{(({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} })x_{0}^{(i)} })$

​ ${\theta_j}:={\theta_j}-a[\frac{1}{m}\sum\limits_{i=1}^{m}{({h_\theta}({ {x}^{(i)} })-{ {y}^{(i)} })x_{j}^{\left( i \right)} }+\frac{\lambda }{m}{\theta_j}]$

​ $for$ $j=1,2,…n$

​ }

注:看上去同线性回归一样,但是知道 ${h_\theta}\left( x \right)=g\left( {\theta^T}X \right)$,所以与线性回归不同。