逻辑回归 & 分类问题

1. 分类问题

在分类问题中,如果想要预测的变量y是离散的值,那么可以只用一种叫做逻辑回归(logistic regression)的算法,这是在目前最流行的一种学习算法之一。

在分类问题中,我们尝试预测的是结果是否属于某一个类(例如正确或错误)。分类问题的例子有:判断一封电子邮件是否是垃圾邮件;判断一次金融交易是否是欺诈;之前我们也谈到了肿瘤分类问题的例子,区别一个肿瘤是恶性的还是良性的。

1540997286168

我们将因变量(dependent variable)可能属于的两个类分别称为负向类(negative class)和正向类(positive class),则因变量y∈0,1 ,其中 0 表示负向类,1 表示正向类。

1540997531192

线型回归不适合分类问题

如果我们要用线性回归算法来解决一个分类问题,对于分类, y 取值为 0 或者1,但如果你使用的是线性回归,那么假设函数的输出值可能远大于 1,或者远小于0,即使所有训练样本的标签 y 都等于 0 或 1。尽管我们知道标签应该取值0 或者1,但是如果算法得到的值远大于1或者远小于0的话,就会感觉很奇怪。所以我们在接下来的要研究的算法就叫做逻辑回归算法,这个算法的性质是:它的输出值永远在0到 1 之间。

逻辑回归算法

逻辑回归算法是分类算法,我们将它作为分类算法使用。有时候可能因为这个算法的名字中出现了“回归”使你感到困惑,但逻辑回归算法实际上是一种分类算法,它适用于标签 y 取值离散的情况,如:1 0 0 1。

2. 逻辑回归表达式

在分类问题中,要用什么样的函数来表示我们的假设。在解决分类问题的时候,我们希望分类器的输出值在0和1之间,因此,我们希望想出一个满足某个性质的假设函数,这个性质是它的预测值要在0和1之间。

为什么线型回归不适合分类问题?

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

1541078937881

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

当 $h_θ(x) >= 0.5$ 时, 预测y= 1; 当$h_θ(x) < 0.5$ 时,预测y = 0

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

1541079100064

这时,再使用0.5作为阀值来预测肿瘤是良性还是恶性便不合适了, 所以使用线型回归模型,很难确定断定一个问题分类的边界。可以看出,线性回归模型,因为其预测的值可以超越[0,1]的范围,并不适合解决分类这样的问题。

逻辑回归模型

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

$h_θ(x) = g(θ^TX)$ 函数g的表达式为: $g(z) = {1\over1+e^{-z}}$

其中: X 代表特征向量, g代表逻辑函数(logistic function)是一个常用的逻辑函数为S形函数(Sigmoid function)

g(z) 的函数图像为:

1541080097808

对逻辑回归模型理解

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

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

g(z) (Sigmoid function)函数的作用就是将$θ^TX$的结果映射到0-1之间,

3. 决策边界

对决策边界的理解

决策边界(decision boundary),能够更好地帮助我们理解逻辑回归的假设函数中计算什么

1541080767309

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

当$h_θ(x) >= 0.5$ 时, 预测 y= 1; 当 $h_θ(x) >= 0.5$ 时,预测 y = 0;

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

z = 0 时, g(z) = 0.5

z > 时, g(z) > 0.5

z < 0 时, g(z) < 0.5

又 $z = θ^Tx$, 即: $θ^TX >= 0$ 时, 预测 y = 1 , $θ^TX < 0$ 时, 预测 y = 0

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

1541081223807

并且参数θ是向量[-3 1 1]. 则当 -3 + x1 + x2 >= 0, 即x1 + x2 >= 3时, 模型将预测 y = 1. 我们可以绘制直线x1 + x2 = 3, 这条线便是我们模型的分界线,将预测为1的区域和预测为 0的区域分隔开。

1541081375902

复杂形状的决策边界

假使我们的数据呈现这样的分布情况,怎样的模型才能适合呢?

1541081567959

因为需要用曲线才能分隔 y = 0 的区域和 y = 1 的区域,我们需要二次方特征:

所以按照假设函数应该趋近, $h_θ(x) = g(θ_0 + θ_1x_1 + θ_2x_2 + θ_1x_1^2 + θ_2x_2^2 )$ θ最终结果趋近 [-1 0 0 1 1], 我们最终得到的判定边界恰好是圆点在原点且半径为1的圆形 , 综上我们可以用复杂的模型来适应飞象复杂形状的判定边界

4. 逻辑回归代价函数和梯度下降

逻辑回归代价函数及简化

对于线性回归模型,我们定义的代价函数是所有模型误差的平方和和。理论上来说,我们也可以对逻辑回归模型沿用这个定义,但是问题在于,当我们将 $h_θ(x) = g(θ^TX)$ 带入到这样定义了的代价函数中时,我们得到的代价函数将是一个非凸函数(non-convexfunction)如下图,这意味着我们的代价函数有许多局部最小值,这将影响梯度下降算法寻找全局最小值。

1541082558471

线型回归代价函数为: $J_{(θ)} = {1\over m} \sum_{i=1}^m{1\over2}(h_θ(x^{(i)})-y^{(i)})^2​$

重新定义逻辑回归代价函数,使代价函数在逻辑回归算法中为凸函数, 过程如下:

1541083131135

根据推倒可以证明, 我们推倒的的代价值函数会给我们一个凸优化问题。代价函数 J(θ) 会是一个凸函数,并且没有局部最优值。

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 { $θ_j : =θ_j − α{∂\over∂θ_j }J(θ)$ (simultaneously update all ) }

求导后得到:

Repeat { $θj : =θ_j − α{1\over m}\sum{i=1}^m(h_θ(x^{(i)}) - y^{(i)})x_j^{(i)}$ (simultaneously update all ) }

1541084166476

注:虽然得到的梯度下降算法表面上看上去与线性回归的梯度下降算法一样,但是这里的 $h_θ(x) = g(θ^TX)$ 与线性回归中不同,所以实际上是不一样的。另外,在运行梯度下降算法之前,进行特征缩放依旧是非常必要的。

5. 高级优化算法

一些高级算法的介绍

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

1541166796206

假设我们已经完成了可以实现这两件事的代码,那么梯度下降所做的就是反复执行这些更新。 另一种考虑梯度下降的思路是:我们需要写出代码来计算 J(θ) 和这些偏导数,然后把这些插入到梯度下降中,然后它就可以为我们最小化这个函数。 对于梯度下降来说,我认为从技术上讲,你实际并不需要编写代码来计算代价函数J(θ)。你只需要编写代码来计算导数项,但是,如果你希望代码还要能够监控这些 J(θ)的收敛性,那么我们就需要自己编写代码来计算代价函数J(θ)和偏导数项$α{∂\over∂θ_j }J(θ)$。所以,在写完能够计算这两者的代码之后,我们就可以使用梯度下降。

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

这三种算法的优点:

一个是使用这其中任何一个算法,你通常不需要手动选择学习率 α ,所以对于这些算法的一种思路是,给出计算导数项和代价函数的方法,你可以认为算法有一个智能的内部循环,而且,事实上,他们确实有一个智能的内部循环,称为线性搜索(line search)算法,它可以自动尝试不同的学习速率 α ,并自动选择一个好的学习速率 α,因此它甚至可以为每次迭代选择不同的学习速率,那么你就不需要自己选择。

Octave 有一个非常理想的库用于实现这些先进的优化算法,所以,如果你直接调用它自带的库,你就能得到不错的结果。我必须指出这些算法实现得好或不好是有区别的,因此,如果你正在你的机器学习程序中使用一种不同的语言,比如如果你正在使用C、C++、Java等等,你可能会想尝试一些不同的库,以确保你找到一个能很好实现这些算法的库。

如何使用这些算法

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

$α{∂\over∂θ_1}J(θ) = 2(θ_1 - 5)$

$α{∂\over∂θ_2}J(θ) = 2(θ_2 - 5)$

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

1
2
3
4
5
6
function [jVal, gradient]=costFunction(theta)
jVal=(theta(1)‐5)^2+(theta(2)‐5)^2;
gradient=zeros(2,1);
gradient(1)=2*(theta(1)‐5);
gradient(2)=2*(theta(2)‐5);
end

这样就计算出这个代价函数,函数返回的第二个值是梯度值,梯度值应该是一个2×1的向量,梯度向量的两个元素对应这里的两个偏导数项,运行这个costFunction 函数后,你就可以调用高级的优化函数,这个函数叫fminunc,它表示Octave 里无约束最小化函数。调用它的方式如下:

1
2
3
options=optimset('GradObj','on','MaxIter',100);
initialTheta=zeros(2,1);
[optTheta, functionVal, exitFlag]=fminunc(@costFunction, initialTheta, options);

你要设置几个options,这个 options 变量作为一个数据结构可以存储你想要的options,所以 GradObj 和On,这里设置梯度目标参数为打开(on),这意味着你现在确实要给这个算法提供一个梯度,然后设置最大迭代次数,比方说100,我们给出一个 的猜测初始值,它是一个2×1的向量,那么这个命令就调用fminunc,这个@符号表示指向我们刚刚定义的costFunction 函数的指针。如果你调用它,它就会使用众多高级优化算法中的一个,当然你也可以把它当成梯度下降,只不过它能自动选择学习速率,你不需要自己来做。然后它会尝试使用这些高级的优化算法,就像加强版的梯度下降法,为你找到最佳的值。

实际运行过程示例

1541168153304

6. 多分类问题

多分类的介绍

一些多分类的例子:

例一: 假如说你现在需要一个学习算法能自动地将邮件归类到不同的文件夹里,或者说可以自动地加上标签,那么,你也许需要一些不同的文件夹,或者不同的标签来完成这件事,来区分开来自工作的邮件、来自朋友的邮件、来自家人的邮件或者是有关兴趣爱好的邮件,那么,我们就有了这样一个分类问题:其类别有四个,分别用 y=1, y=2, y=3, y=4 来表示

例二: 如果一个病人因为鼻塞来到你的诊所,他可能并没有生病,用 y=1 这个类别来代表;或者患了感冒,用 y=2 来代表;或者得了流感用 y=3 来代表.

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

1541168782303

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

1541168809943

一对于多分类思路

我用3种不同的符号来代表3个类别,问题就是给出3个类型的数据集,我们如何得到一个学习算法来进行分类呢?我们现在已经知道如何进行二元分类,可以使用逻辑回归,对于直线或许你也知道,可以将数据集一分为二为正类和负类。用一对多的分类思想,我们可以将其用在多类分类问题上。下面将介绍如何进行一对多的分类工作,有时这个方法也被称为“一对余”方法。

1541169223823

现在我们有一个训练集,好比上图表示的有3个类别,我们用三角形表示 y = 1 ,方框表示 y = 2,叉叉表示 y = 3。下面要做的就是使用一个训练集,将其分成3个二元分类问题。我们先从用三角形代表的类别1开始,实际上我们可以创建一个,新的”伪”训练集,类型2和类型3定为负类,类型1设定为正类,我们创建一个新的训练集,如下图所示的那样,我们要拟合出一个合适的分类器。

这里的三角形是正样本,而圆形代表负样本。可以这样想,设置三角形的值为1,圆形的值为0,下面我们来训练一个标准的逻辑回归分类器,这样我们就得到一个正边界。

1541169503523

为了能实现这样的转变,我们将多个类中的一个类标记为正向类(y=1),然后将其他所有类都标记为负向类,这个模型记作$h_θ^{(1)}(x)$。接着,类似地第我们选择另一个类标记为正向类(y=2),再将其它类都标记为负向类,将这个模型记作$h_θ^{(2)}(x)$ ,依此类推。 最后我们得到一系列的模型简记为:$h_θ^{(i)}(x) = P(y=i|x; θ)$ 其中:i = (1, 2, 3, 4, 5, …, k) k为总分类数量
最后,在我们需要做预测时,我们将所有的分类机都运行一遍,然后对每一个输入变量,都选择最高可能性的输出变量。
总之,我们已经把要做的做完了,现在要做的就是训练这个逻辑回归分类器:$h_θ^{(i)}(x)$ , 其中 i 对应每一个可能的y=i,最后,为了做出预测,我们给出输入一个新的 x 值,用这个做预测。我们要做的就是在我们三个分类器里面输入 x ,然后我们选择一个让 $h_θ^{(i)}(x)$ 最大的 i 作为最终的分类结果。
现在知道了基本的挑选分类器的方法,选择出哪一个分类器是可信度最高效果最好的,那么就可认为得到一个正确的分类,无论值是多少,我们都有最高的概率值,我们预测就是那个值。这就是多类别分类问题,以及一对多的方法,通过这个小方法,你现在也可以将逻辑回归分类器用在多类分类的问题上。

刘小恺(Kyle) wechat
如有疑问可联系博主