0%

过拟合与欠拟合

举例:

upload successful

例如对与如上曲线的拟合,如果用两个参数表示,则会成为一条直线,不能正确的表示出size与price间的通常关系。如下图

upload successful

加入一个二次项后,会发现拟合情况变好了,曲线基本能够反映出数据集中各点的位置,并能判断size与price的大概关系。如下图

upload successful

另一个极端情况是,当继续加入多项式时,会发现得到了一条扭曲的曲线,在不断地上下波动。看上去它拟合了所有数据集中的点,但是很明显这不是一个能够真实反应Size与Price的曲线。

upload successful

第一种情况由于参数过少,项数过少不能很好拟合数据的情况就是欠拟合;第三种情况由于项数过多,变量太多而没有更多的数据去约束这个过多变量的模型,就会导致过度拟合,或者叫过拟合(overfittin)

逻辑回归中一样有这样的情况,如图

upload successful

欠拟合的改进比较简单,增加参数增加多次项即可。

过拟合则需要“正则化”来对其进行约束。

正则化

过拟合的一个特征就是高方差。如何避免过拟合的情况呢?可以通过减少变量的形式来避免过拟合。而具体减少哪些变量并不能在所有的模型中很快得出,因此只能说是尽量减少所有的参数值。

\[ \lambda \sum_{j=1}^{n}\theta^{2}_{j}\]

上式可以表述所有的参数大小的总体。lambda被称为“正则化参数”。为了使上式尽量小,可以将其加入代价函数$ J()$中,

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

当参数过多过大时,后面的正则化项的值就会很大,因而导致\(J(\theta )\)也变大,从而使这个预测方程得到更多的惩罚,使其像参数变量尽量小的地步靠拢。对正则化过程有影响的是lambda,当lambda数值大的时候,函数收到的惩罚更多,会更快地减少参数数值;当lambda为0的时候,函数则不会对参数进行正则化处理。

因此lambda的值与学习率一样,必须适中才能使正则化的过程正常运转。


线性回归中的正则化

当知道代价函数的正则化后,可以来看梯度下降中的正则化

这是正常没有做正则化的梯度下降,没有对参数过多做出任何惩罚。

在式中加上正则化项,使$$尽量往数值小的方面靠拢

\[\theta_j :=\theta_j - \alpha[\frac{1}{m}\sum^m_{i=1}(h_\theta(x^{(i)})-y^{(i)})x^{(i)}_j)+\frac{\lambda}{m}\theta_j]\]

(也可直接由\(J(\theta )\)求偏导得出。) > 由于已经规定了$x_{0}=1 \(,因此\){0}\(不需要加入到正则化进行惩罚,因此上式其实是从\){1}\(开始的。 > > (在matlab中,下标号是从0开始,因此上式在matlab中表现是从\)_{2}$开始)

可经化简得到这么一个式子

\[ \theta_{j} :=\theta_{j}(1-\alpha\frac{\lambda}{m})-\alpha\frac{1}{m}\sum^{m}_{i=1}(h_{\theta}(x^{(i)})-y^{(i)})x_{j}^{(i)}\](从第二项开始)

\[ 1-\alpha \frac{\lambda}{m} \]是小于1的

因此在迭代中,$$会因为参数过大过多被惩罚加速下降,最后以此避免过拟合的出现。

可以推广到逻辑回归的正则化,与线性回归是一样的,只是它们的h(z)和g(x)不一样。

更高级的优化函数中都一样,只是将CostFucion里面加上\(\frac{\lambda }{2m}\sum_{j=1}^{n}\theta^{2}_{j}\)正则化值。


暂不深入正则化的向量写法。

Headroom.js 是一个轻量级、高性能的JS小工具,通过在指定的div class中加上"headroom","headroom headroom--unpinned","headroom headroom--pinned"来使用户能够方便地基于网页上下滚动控制菜单样式。

使用时,只需在网页中引用js库(jquery版需要同时引用 jQuery.headroom.min.js 与 headroom.min.js),将事件绑定在元素上

1
$("#header").headroom();

或者

1
2
3
4
5
var myElement = document.querySelector("header");

var headroom = new Headroom(myElement);

headroom.init();

即可方便使用。

成功使用时,可以在调试台看到页面上下滚动时设置的元素的class也同时在变化。由class的变化可以在css中添加样式与动画,达到想要的效果。例如

1
2
3
4
5
6
7
8
9
10
.headroom {
will-change: transform;
transition: transform 200ms linear;
}
.headroom--pinned {
transform: translateY(0%);
}
.headroom--unpinned {
transform: translateY(-100%);
}

以上代码简单实现了菜单栏随着页面上下滚动的显示与隐藏效果。

cdn 地址:http://www.bootcdn.cn/headroom/

直线分类问题

upload successful

对于此数据图可以发现正类与负类的决策边界大约是一条直线。

与线性回归类似,先计算代价方程J

\[J(\theta_0,\theta_1)=\frac{1}{2m} \sum^m_{i=1}(h_\theta(x^{(i)}-y^{(i)}))^2\]

但是其中的h(x)发生了变化,实际上为

\[h_\theta(x)=g(\theta^Tx)\] \[g(z)=\frac{1}{1+e^{-z}}\]

带入得

\[J(\theta)=\frac{1}{m}\sum^m_{i=1}Cost(h_\theta(x^{(i)},y^{(i)})) \\=-\frac{1}{m}[\sum^m_{i=1}y^{(i)}log{h_\theta(x^{(i)})}+(1-y^{(i)})log{(1-h_\theta(x^{(i)})})]\]

同理带入梯度下降函数

\[\theta:=\theta-\alpha\frac{1}{m}\sum^m_{i=1}[(h_\theta(x^{(i)})-y^{(i)})\cdot x^{(i)}]\]

最后将它们放入fminunc(matlab求最小值函数)中运算即可得到训练结果。

1
2
3
4
5
6
7
8
9
for i=(1:m);
h = sigmoid(X(i,:)*theta);
J += (-y(i).*(log(h))-(1-y(i)).*(log(1-h)))/m;
end
//----
for i=(1:m);
h = sigmoid(X(i,:)*theta);
grad += ((h - y(i))*X(i,:)')/m;
end
1
2
3
4
options = optimset('GradObj', 'on', 'MaxIter', 400);

[theta, cost] = ...
fminunc(@(t)(costFunction(t, X, y)), initial_theta, options);

即可得到训练后的\(\theta\)值,在图上表现出来呈

upload successful

可以看到正集与负集基本分开。

非直线分类

upload successful

如图所示,可以看出正负集的边界应该是曲线。

曲线的方程是多项式,因此首先对曲线的多项式的项进行罗列:

upload successful
1
2
3
4
5
6
7
degree = 6;
out = ones(size(X1(:,1)));
for i = 1:degree
for j = 0:i
out(:, end+1) = (X1.^(i-j)).*(X2.^j);
end
end

以上生成了一个28项的向量作为这个曲线的项。

\[J(\theta)=\frac{1}{m}[\sum^m_{i=1}-y^{(i)}log{h_\theta(x^{(i)})}-(1-y^{(i)})log{(1-h_\theta(x^{(i)})})]+\frac{\lambda}{2m}\sum^n_{j=1}\theta^2_j\]

根据此公式进行代价函数的运算。

由于上式中最后一项\(\theta\)不需要惩罚\(\theta_0\),因此从\(\theta_1\)开始相乘。由于matlab的下标号是从1开始的,因此最后一项的θ要从2开始相乘。后面的梯度函数同理

\[\frac{\partial J(\theta)}{\partial \theta_0}=\frac{1}{m}\sum^m_{i=1}(h_\theta(x^{(i)})-y^{(i)})x^{(i)}_j, \text{for j=0}\\ \frac{\partial J(\theta)}{\partial \theta_j}=(\frac{1}{m}\sum^m_{i=1}(h_\theta(x^{(i)})-y^{(i)})x^{(i)}_j)+\frac{\lambda}{m}\theta_j ,\text{for j}\geq 1\]

简单用循环写代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for i=(1:m);
h = sigmoid(X(i,:)*theta);
J += (-y(i).*(log(h))-(1-y(i)).*(log(1-h)))/m;
end
for j=(2:size(theta))
J += theta(j)^2.*lambda/(2*m);
end
//-------
for i=(1:m);
h = sigmoid(X(i,:)*theta);
grad += ((h - y(i))*X(i,:)')./m;
end

for j=(2:size(theta))
grad(j) += theta(j).*lambda./m;
end

可以得到多项式的各个\(\theta\)值。

根据多项式画出图形可以看到

upload successful

基本将正负极分开。

如果对\(\lambda=1\)(正则化参数)进行修改,例如修改成别的值可以得到:

\(\lambda=0\)

upload successful

\(\lambda=100\)

upload successful

可以看到\(\lambda=0\)的时候拟合曲线绕的弯非常多,试图去匹配所有的点,因而失去了这个集整体的特征,变得过度复杂,这种现象称为过拟合;

\(\lambda=100\)的时候明显拟合曲线并没有良好地工作,称为欠拟合(\(\theta\)的乘积占的权重过大导致除了θ0之外的项全部变为0)。

举例:电子邮件的垃圾邮件判断;在线翻译的正确与否;肿瘤的良性或恶性等。

共性:都是要判断一个集y,

\[y \in \{0,1\}\]

y=0被称为负类,y=1被称为正类。通常上来说负类表示缺少某样东西(例如缺少而行肿瘤)

实际情况多为多类分类(y为3或更多)


实际分类举例:

upload successful

尝试用线性回归拟合这个训练集,发现通常会得到不好的预测结果。

在二元分类中,线性回归预测方法并不适用。因为分类集只有0或1,而线性回归的预测往往会出现小于0与大于1出现。


学习新算法:逻辑回归算法(Logistic Regression):

\[0 \leq h_\theta(x)\leq 1\]

输出值不会小于0,也不会大于1.这个算法是一种分类算法,适用于y标签离散的情况。

\[h_\theta=g(\theta^T x)\]

\[g(z)=\frac{1}{1+e^{-z}}\]

这个函数被称为S型函数。函数图形如下

upload successful

g(z)在z趋近负无穷的时候趋近于0,z趋近正无穷的时候趋近与1.

使用这个假设函数来拟合离散的事件。

由于二元分类问题只有0或1,因此可以与概率问题等同考虑。例如上面病人得肿瘤的例子,当知道他的肿瘤大小后,通过逻辑回归算法得到他得恶性肿瘤的概率为70%那么他得良性肿瘤的概率就是30%。


概念:决策边界(decision boundary)

对于S行函数来说,当输出y=1的概率大于等于0.5时,可以预测输出为1;概率小于0.5时,预测输出为0。

根据图像可以发现z>0是g(z)<0.5。

因此\(\theta^T x\)大于等于0时就能预测输出为1,反之亦然。

决策边界将样品集分为了两个部分,一个部分预测为1一个部分预测为0.

决策边界是假设函数本身的属性,而不是数据集的属性。它包括假设函数的几个参数。

多项式逻辑回归。与单项式相同,只是在式中多加上了多次项。举例:

upload successful

当$ -1+x2_1+x2_2 $时可以预测y=1

它的决策边界是以原点为圆心,半径为1的圆。因此圆外的一切样本都将被预测为0.

此代码是未进行向量化的

代价函数

1
2
3
4
5
6
7
8
function J = computeCost(X, y, theta)
m = length(y); % number of training examples
J = 0;
for i=1:m
J += (theta(1)+ theta(2)*X(i,2)-y(i))^2;
end
J = J/(2*m);
end

梯度下降

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function [theta, J_history] = gradientDescent(X, y, theta, alpha, num_iters)
m = length(y);
J_history = zeros(num_iters, 1);
for iter = 1:num_iters
k = 0;
o = 0;
for i=1:m
k +=(theta(1)+ theta(2).*X(i,2)-y(i));
o +=(theta(1)+ theta(2).*X(i,2)-y(i)).*X(i,2);
end
theta(1) -= (alpha/m).* k;
theta(2) -= (alpha/m).* o;
J_history(iter) = computeCost(X, y, theta);
end
end

运行结果

upload successful
upload successful
upload successful

常为javascript中的indexOf之类的函数返回值"-1"纠结,现用上取反符号

对于javascript来说,取反符号的运算过程如下:首先将对取反的数值进行二进制转换,用0在数前补满32位;

然后在32位二进制中按位取反,即1变0,0变1。如果取反的数是正数,则取反后32位二进制数第一位为负;反之亦然。总之,要对32位二进制数进行转换十进制负数的过程(先反码,加上1,在数前加上负号)

实际例子:

1
2
3
4
5
6
7
8
console.log(~2)
//2的二进制为10
//补满32位&nbsp;00000000000000000000000000000010
//反码,变成11111111111111111111111111111101
//第一位是1,因此转换成10进制时要反码
//00000000000000000000000000000010
//加1,换为10进制,在数前加上负号,得-3
//输出-3

indexOf之类的函数返回值为-1,可以通过取反符号将-1化为0,有利于编写判断式。

例如

1
if(~orders.indexOf("item")){alert("Not found")}

正规方程

在一些线性回归问题中,可以用正规方程方法更快地解决问题,直接解出参数值来。

设特征矩阵为X(第一列是\(x_0=1\)),训练集结果为向量y,能够带入公式

\[\theta = (X^T X)^{-1}X^T y\]

直接解出\(\theta\)的向量。

举例:

upload successful

带入公式进行矩阵运算即可得出结果。 以此题为例,我使用 Octave 进行了运算

先输入输入值X的矩阵

upload successful

输入输出值y的向量

upload successful

直接带入公式

\[\theta = (X^T X)^{-1}X^T y\]

upload successful

梯度下降法与正规方程的对比

梯度下降 正规方程
需要选择学习率\(\alpha\) 不需要
需要多次迭代 一次运算得出
当特征数量 n 大时也能较好适用 需要计算\((X^TX)^{-1}\) 如果特征数量 n 较大则运算代价大,因为矩阵逆的计算时间复杂度为 \(O(n^3)\),通常来说当 n 小于 10000 时还可以接受
适用于各种类型的模型 只适用于线性模型,不适合逻辑回归模型等其它模型

特征缩放(Feature Scaling)

在线性回归的实际例子中中多数会遇到这样的情况,某两个特征值的数值相差很远,例如房价可能是100(万元)的数量级,房屋面积可能是100的数量级。表现在轮廓图上的形式就是如下图一样,

upload successful

等高线(代价函数J)的形状呈现出非常细长的椭圆。

upload successful

可以看到在这种情况下的梯度下降函数需要迭代很多次,在最小值附近震荡直到最后收敛。为了使梯度下降能够尽量朝着一个方向有效率地进行,我们需要对输入值中的某些特征值进行统一的缩放。

upload successful

这样的参数值接近的代价方程,梯度下降函数的迭代就会方便与均匀很多,从而达到减少迭代次数的目的。一般来说,将两个参数都缩放到-1,1之间较为合适。特征缩放可直接参照公式:

\[x_n=\frac{x_n-\mu _n}{S_n}\]

其中 \(\mu_n\) 是平均值,\(S_n\) 是标准差。

为了简单方便,可以直接用最大值-最小值来代替标准差。

学习率

学习率α在梯度下降方程中起着重要的作用,梯度下降算法的每次迭代受到学习率的影响,如果学习率 α过小,则达到收敛所需的迭代次数会非常高;如果学习率α 过大,每次迭代可能不会减小代价函数,可能会越过局部最小值导致无法收敛。

在一个线性回归情况中尝试多个学习率可以找到计算效率最高的学习率。通常可以考虑尝试些学习率:α=0.01,0.03,0.1,0.3,1,3,10 (它们都是3倍3倍地增加)

多项式回归

在现实情况中,往往线性回归是不足以很好的契合一个数据集的。

例如

upload successful

用的三次方程,

其实也可以化为线性方程看:

upload successful

但是经过平方立方之后,每个参数的差值会变得更大,因此特征缩放在这样的情况中更加的重要。

比如上面如果size为1到1000的话,x2就是1到1000000,x3就是1到1000000000。不作处理会使算法很难正常进行下去。可以使x1=size/1000,x2=size2/1000000,x3=size3/1000000000这样就能使几个参数的值在近似的范围内。

再例如

\[h_\theta(x)=\theta_0+\theta_1(size)+\theta_2\sqrt{(size)}\]

要看成

\[h_\theta(x)=\theta_0+\theta_1x_1+\theta_2x_2\]

线性方程,size在1-1000间,那么\(x_1=\frac{size}{1000},x_2=\frac{\sqrt{size}}{\sqrt{1000}}\)