分类问题
在分类问题中,我们需要预测的变量y
是离散的值,我们将学习一种叫做逻辑回归 (Logistic Regression) 的算法
在分类问题中,我们尝试预测的是结果是否属于某一个类(例如正确或错误)。
将因变量(dependent variable)可能属于的两个类分别称为负向类(negative class)和正向类(positive class),则因变量y ∈ 0,1
,其中 0
表示负向类,1
表示正向类。
如果我们要用线性回归算法来解决一个分类问题,对于分类,y
取值为 0 或者1,但如果你使用的是线性回归,那么假设函数的输出值可能远大于 1,或者远小于0,即使所有训练样本的标签y
都等于 0 或 1。尽管我们知道标签应该取值0 或者1,但是如果算法得到的值远大于1或者远小于0的话,就会感觉很奇怪。
所以我们在接下来的要研究的算法就叫做逻辑回归算法,这个算法的性质是:它的输出值永远在0到 1 之间
逻辑回归算法是分类算法,我们将它作为分类算法使用。有时候可能因为这个算法的名字中出现了“回归”使你感到困惑,但逻辑回归算法实际上真的是一种分类算法
假说表示
我们引入一个新的模型,逻辑回归,该模型的输出变量范围始终在0和1之间。
逻辑回归模型的假设是:h_theta(x) = g(theta'X)
其中:X
代表特征向量,g
代表逻辑函数(logistic function)是一个常用的S形函数(Sigmoid function),公式为:g(z) = 1 / (1 + exp(-z)
Python代码实现
import numpy as np
def sigmoid(z):
return 1 / (1 + np.exp(-z))
该函数图像为
合起来,我们得到逻辑回归模型的假设:
对模型的理解:g(z) = 1 / (1 + exp(-z)
h_theta(x)
的作用是,对于给定的输入变量,根据选择的参数计算输出变量=1的可能性(estimated probablity)即h_theta(x) = P(y=1 | x;theta)
例如,如果对于给定的x
,通过已经确定的参数计算得出h_theta(x) = 0.7
,则表示有70%的几率为正向类,相应地为负向类的几率为1-0.7=0.3。
判定边界
决策边界(decision boundary)这个概念能更好地帮助我们理解逻辑回归的假设函数在计算什么。
逻辑回归中,我们预测:
- 当
h(x) >= 0.5
时,预测y = 1
- 当
h(x) < 0.5
时,预测y = 0
根据上面绘制出的S形函数图像,我们知道当
z = 0
时g(z) = 0.5
z > 0
时g(z) > 0.5
z < 0
时g(z) < 0.5
又 z = theta'x
,即:theta'x >= 0
时,预测y = 1
;theta'x < 0
时,预测y =0
现在假设我们有一个模型:
并且参数theta
是向量[-3 1 1]
,则当-3+x1+x2>-0
,即x1+x2>=3
时,模型将预测y = 1
。我们可以绘制直线x1 + x2 = 3
,这条线时我们模型的分界线,将预测为1
的区域和预测为0
的区域分隔开
假使我们的数据呈现这样的分布情况,怎样的模型才能适合呢?
因为需要用曲线才能分隔y = 0
的区域和y = 1
的区域,我们需要二次方特征:
h_theta(x) = g(theta0 + theta1x1 + theta2x2 + theta3x1^2 + theta4x2^2)
则我们得到的判定边界恰好是圆点在原点且半径为1的圆形。
我们可以用非常复杂的模型来适应非常复杂形状的判定边界。
代价函数
我们要介绍如何拟合逻辑回归模型的参数theta
。具体来说,要定义用来拟合参数的优化目标或者叫代价函数,这便是监督学习问题中的逻辑回归模型的拟合问题。
对于线性回归模型,我们定义的代价函数是所有模型误差的平方和。理论上来说,我们也可以对逻辑回归模型沿用这个定义,但是问题在于,当我们将h_theta(x)
带入到这样定义了的代价函数中时,我们得到的代价函数将是一个非凸函数(non-convexfunction)(左图)。
这意味着我们的代价函数有许多局部最小值,这将影响梯度下降算法寻找全局最小值。
在线性回归中,代价函数为
重新定义逻辑回归的代价函数为
h(x)
与Cost(h(x), y)
之间的关系如图所示
这样构建的函数的特点是:
- 当实际的
y=1
且h(x)
也为 1 时误差为 0 - 当
y=1
但h(x)
不为1时误差随着h(x)
变小而变大; - 当实际的
y=0
且h(x)
也为 0 时代价为 0 - 当
y=0
但h(x)
不为 0 时误差随着h(x)
的变大而变大。
将构建的Cost(h(x), y)
简化如下
Cost(h(x),y) = -y*log(h(x)) - (1-y)*log(1-h(x))
带入代价函数得
Python实现
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))
在得到这样一个代价函数以后,我们便可以用梯度下降算法来求得能使代价函数最小的参数了。算法为:
我们定义了单训练样本的代价函数,凸性分析的内容是超出这门课的范围的,但是可以证明我们所选的代价值函数会给我们一个凸优化问题。代价函数J(theta)
会是一个凸函数,并且没有局部最优值。
推导过程如下:
注:虽然得到的梯度下降算法表面上看上去与线性回归的梯度下降算法一样,但是这里的
h_theta(x) = g(theta'X)
与线性回归中不同,所以实际上是不一样的。另外,在运行梯度下降算法之前,进行特征缩放依旧是非常必要的。
一些梯度下降算法之外的选择: 除了梯度下降算法以外,还有一些常被用来令代价函数最小的算法,这些算法更加复杂和优越,而且通常不需要人工选择学习率,通常比梯度下降算法要更加快速。这些算法有:共轭梯度(Conjugate Gradient),局部优化法(Broyden fletcher goldfarb shann,BFGS)和有限内存局部优化法(LBFGS),fminunc是 matlab中的一个最小值优化函数,使用时我们需要提供代价函数和每个参数的求导,下面是 matlab 中使用 fminunc 函数的代码示例:
function [jVal, gradient] = costFunction(theta)
jVal = [...code to compute J(theta)...];
gradient = [...code to compute derivative of J(theta)...];
end
options = optimset('GradObj', 'on', 'MaxIter', '100');
initialTheta = zeros(2,1);
[optTheta, functionVal, exitFlag] = fminunc(@costFunction, initialTheta, options);
简化的成本函数和梯度下降
我们将会找出一种稍微简单一点的方法来写代价函数,来替换我们现在用的方法。同时我们还要弄清楚如何运用梯度下降法,来拟合出逻辑回归的参数。
根据这个代价函数,为了拟合出参数,我们需要找到能使J(theta)
尽量小的参数theta
,
如果我们给出一个新的样本,假如某个特征x
,我们可以用拟合训练样本的参数theta
,来输出对假设的预测。们假设的输出,实际上就是这个概率值:p(y = 1|x;theta)
,即关于x
以theta
为参数,y=1
的概率,可以认为我们的假设就是估计y=1
的概率
最小化代价函数的方法,是使用梯度下降法(gradient descent)。这是我们的代价函数:
如果我们要最小化这个关于theta
的函数值,这就是我们通常用的梯度下降法的模板。
我们要反复更新每个参数,用这个式子来更新,就是用它自己减去学习率alpha
乘以后面的微分项。求导后得到:
如果我们有n个特征,也就是theta = [theta0, theta1, theta2 ... thetan]'
,参数向量theta
包括theta0
一直到theta n
,那么我们旧需要用这个实在来同时更新所有theta
值
我们之前在谈线性回归时讲到的特征缩放,我们看到了特征缩放是如何提高梯度下降的收敛速度的,这个特征缩放的方法,也适用于逻辑回归。如果你的特征范围差距很大的话,那么应用特征缩放的方法,同样也可以让逻辑回归中,梯度下降收敛更快。
高级优化
这一节,我们学习一些高级优化算法和一些高级的优化概念,利用这些方法,我们就能够使通过梯度下降,进行逻辑回归的速度大大提高,而这也将使算法更加适合解决大型的机器学习问题
比如共轭梯度法,BFGS (变尺度法) 和 L-BFGS (限制变尺度法)
如何使用这些算法:
假如我们有一个含两个参数得问题,这两个参数是theta0
和theta1
,通过这个代价函数,我们可以得到theta0
和theta1
的值,如果我们不知道J(theta)
的最小值,可以使用这样一个MATLAB函数
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,它表示 MATLAB 里无约束最小化函数。调用它的方式如下:
options=optimset('GradObj','on','MaxIter',100);
initialTheta=zeros(2,1);
[optTheta, functionVal, exitFlag]=fminunc(@costFunction, initialTheta, options);
要设置几个options,这个 options 变量作为一个数据结构可以存储你想要的options,所以 GradObj 和On,这里设置梯度目标参数为打开(on),这意味着你现在确实要给这个算法提供一个梯度,然后设置最大迭代次数,比方说100,我们给出一个theta
的猜测初始值,它是一个2×1的向量,那么这个命令就调用fminunc,这个@符号表示指向我们刚刚定义的costFunction 函数的指针。如果你调用它,它就会使用众多高级优化算法中的一个,当然你也可以把它当成梯度下降,只不过它能自动选择学习速率alpha
,你不需要自己来做。然后它会尝试使用这些高级的优化算法,就像加强版的梯度下降法,为你找到最佳的theta
值。
这个关于theta
的 costFunction
函数,它计算出代价函数 jval
以及梯度 gradient
,gradient
有两个元素,是代价函数对于 theta(1)
和 theta(2)
这两个参数的偏导数。
多类别分类:一对多
对于一个二元分类问题,我们的数据看起来可能是像这样
对于一个多类分类问题,我们的数据集或许看起来像这样:
我们现在已经知道如何进行二元分类,可以使用逻辑回归,对于直线或许你也知道,可以将数据集一分为二为正类和负类。用一对多的分类思想,我们可以将其用在多类分类问题上。
下面将介绍如何进行一对多的分类工作,有时这个方法也被称为"一对余"方法。
现在我们有一个训练集,好比上图表示的有3个类别,我们用三角形表示 y=1
,方框表示 y=2
,叉叉表示 y=3
。我们下面要做的就是使用一个训练集,将其分成3个二元分类问题。
我们创建一个新的训练集,如下图所示的那样,我们要拟合出一个合适的分类器。
这里的三角形是正样本,而圆形代表负样本。可以这样想,设置三角形的值为1,圆形的值为0,下面我们来训练一个标准的逻辑回归分类器,这样我们就得到一个正边界。
为了能实现这样的转变,我们将多个类中的一个类标记为正向类(y=1)
,然后将其他所有类都标记为负向类,这个模型记作h1(x)
。接着,类似地第我们选择另一个类标记为正向类(y=2)
,再将其它类都标记为负向类,将这个模型记作h2(x)
,依此类推。 最后我们得到一系列的模型简记为:hi(x) = p(y=i|x;theta)
其中:i = (1, 2, 3... k)
最后,在我们需要做预测时,我们将所有的分类机都运行一遍,然后对每一个输入变量,都选择最高可能性的输出变量。
我们要做的就是在我们三个分类器里面输入x
,然后我们选择一个让hi(x)
最大的i
选择出哪一个分类器是可信度最高效果最好的,那么就可认为得到一个正确的分类,无论i
值是多少,我们都有最高的概率值,我们预测y
就是那个值。这就是多类别分类问题,以及一对多的方法,通过这个小方法,你现在也可以将逻辑回归分类器用在多类分类的问题上。