斯坦福机器学习(4-5)
前情回顾
上一篇文章里,我们介绍了机器学习的2大方法。回归和分类。一个基于LOG和sigmod的分类的代价函数,一个是在回归领域的误差开方的代价函数。我们用标准化加正则化来优化梯度下降算法。最后求出可以做预测的模型theta。
神经网络
在介绍回归问题时候,他的函数计算的代价会比较大。如果我们遇到的是非线性才能拟合的情况,也就是说拟合的函数是个曲线。那么我们可能需要引入高次项。那么当特征非常大的时候,这个数量会是平方级别增长,如果要引入3次项,那么就是3次方级别增长。
这个时候为了解决这类问题,我们可以引入神经网络来处理。
image.png
神经网络其实就是模拟人类大脑的神经元间如何传递信息。它会介绍很多输入,经过自己的做一些处理再输出出去,许多神经元功能传递,就能整体实现大脑的许多功能。
视频里介绍了神经元是如何组织起来模拟与门或门的。
image.png
而这里神经元自己做的事情就是一个sigmoid。我们观察上图可以发现,让整个神经网络产生神奇的是-10,20,20 这3个权重。如果配成这样的权重,就可以实现或门。那么人们想预测某个事物的时候,该如何设置权重呢?这就是训练神经网络的目的。最后你能获得一组网络间的权重。我们用他和输入一起经过整个网络就会得到预测结果。
其中网络分为3种层,输入层代表初始输入,中间的都叫隐藏层,这里层数越多就可以模拟越复杂的功能。比如一层的隐藏层我们可以很好的处理线性问题,如果发生了欠拟合。我们能做的是增加隐藏层的复杂度。多加几层。比如2层神经网络就可以计算异或了。最后结果会到输出层,告诉你分到了哪一类。这时我们要对每类的概率求最大值。
其实人类在做认知的时候,也不是确定的,我们的神经元也会给出概率,然后大脑会选出概率最高的反应给我们。我们看到模糊的东西的时候这种情况尤为明显,我们要比看到清楚东西的时候要迟钝,因为概率比较接近。而且会判断错。
但是要说明的是,大脑远比现有的神经网络复杂的多,人脑有很多稍微研究清楚的领域。我们只是在尽我们的理解模仿。比如最近就有研究发现人脑的单个神经元可以解决异或问题,要比其他动物的神经元要高级。
说了这么多,其实想表达的就是,我们只要拿到整个神经网络的每层的theta,我们就可以做之前分类算法没法很好scale的复杂非线性多分类问题。
这也是第三周作业要我们实现的。
首先是怎么用传统的分类算法,依次求解每一类的概率,然后综合K类来找出输入最有可能的输出。
下面是手写数字的问题,我们必须对每一个数字做分类的训练,把每一种数字的模型theta给训练出来。
for c = 1:num_labels
[theta] = ...
fmincg (@(t)(lrCostFunction(t, X, (y == c), lambda)), ...
initial_theta, options);
all_theta(c,:) = theta;
end
在使用的时候, 就取概率最高的
[_, p] = max(X * all_theta', [], 2); % 5000 * 10
如果用神经网络的话,就是把输入,一次经过每一层神经元。最后在输出层取概率最高的。
a2 = sigmoid(X * Theta1'); % 5000 * 25
a2 = [ones(m, 1) a2];
a3 = sigmoid(a2 * Theta2');
[_, p] = max(a3, [], 2);
训练神经网络
这里就介绍了反向传播算法。很多初学者看到这个反向传播就被劝退了。我一开始也看的云里雾里,之后花了些时间还是弄清楚了。这里我争取可以把大家讲明白。前提是我前面写的你都理解了。
在之前逻辑回归的算法里,我们的目标是训练处一组theta,然后是基于代价函数的梯度下降算法。有了这组theta之后,我们只要让他和输入相乘,放进sigmoid函数里的z,我们就能得到他是不是这类的概率。
而神经网络其实大同小异。我们的目标是训练网络里每条的权重。然后反向传播算法,其实本质也是基于代价函数的梯度下降算法。
对于这个代价,因为网络里每一层。他有多个神经元,我们必须计算每个神经元都算的对不对。假设输出层,有3个神经元。我们必须3个都要考虑。不然有一个错的很离谱,也会造成误判。如何3个都考虑呢,就是求和。所以这里的代价函数相比较于逻辑回归的代价函数
image.png
前半部分多了个求和。
对于正则化,我是这么理解的,原来是对一个列向量theta的求和。而神经网络是多层的,所以多了个维度。那么这里其实就是对矩阵做了平方求和。也没有什么特殊的。
解释了代价函数的区别。下面就是如何使用神经网络的梯度下降。
一开始我们先任选一组theta,用前向传播算出最终输出层的结果。
这里我们来回想一下梯度下降,其实也是随意给了个theta,然后找到和代价函数的构成的山坡图的位置,沿着斜率走向谷底。
而这里我们任选出一组theta,我们就是在山坡图里找到一个位置,然后我们知道这不是谷底(代价函数全局最小,相对于theta 偏导斜率为0),我们就要找一个修改每个theta,这里theta修改了,其实等价于我们朝山坡图的谷底挪了一小步。(因为改theta=山坡图换了个位置)
那么前面的算法,是直接算斜率来微调的。神经网络就是需要先计算出在这一层的误差。所以我们的算法一开始要先前向传播,然后拿到最后一层当前的结果。基于这个结果和期望值,得到一个误差。
我们小时候玩过拷贝不走样的游戏,就是前一个人在后一个人的背上笔划,最后那个人画出的东西,和第一个人看到的东西其实已经相差甚远。这里每一个人都扮演了神经元的角色,每个人对画的东西理解都和上一层的输入产生了关系。所以为了训练处更好的上家。我们必须就取上家的输入,和上家的误差率(基于上家的输出),来修正(训练)上家。
这里神经网络更加复杂一点,上家的输出是基于3个上家的输入。
所以一个下家,要负责反向训练3个上家
image.png
训练的本质还是求偏导,每一个数怎么算出来 就怎么导回去。
然后再以一个学习率作为系数去更新上家的权重,这样下次有输入的时候,上家的对画理解的误差就会变小。
image.png
然后就这样每一个下家更新他的上家们。注意下家A 和 下家B 更新上家C,其实更新的2个不同的技能。下家A 只负责更新C在A上输出的技能。不会影响到C在B上输出的技能。
这样一直更新到输入层的权重。然后基于新的theta, 再看下一组数据。然后训练多轮。最后走到山坡图谷底的故事。
我们结合代码以前来体验下
下面代码是3层神经网络
image.png
如果你还有所困惑,建议你再和这篇文章一起阅读,你一定也可以理解。
整个第四次作业最核心的内容就是上面了。
当然还有debug时,我们可以使用梯度检验的技巧。
image.pngimage.png
function numgrad = computeNumericalGradient(J, theta)
numgrad = zeros(size(theta));
perturb = zeros(size(theta));
e = 1e-4;
for p = 1:numel(theta)
% Set perturbation vector
perturb(p) = e;
loss1 = J(theta - perturb);
loss2 = J(theta + perturb);
% Compute Numerical Gradient
numgrad(p) = (loss2 - loss1) / (2*e);
perturb(p) = 0;
end
end
[cost, grad] = costFunc(nn_params);
numgrad = computeNumericalGradient(costFunc, nn_params);
% Visually examine the two gradient computations. The two columns
% you get should be very similar.
disp([numgrad grad]);
还有另外一个技巧是展开参数
这是为了我们要适配参数到高级优化的梯度下降算法(fminunc)里,需要一个向量而不是矩阵。那么我们就把2维数组 转成1维,需要的2维的时候,再转回来即可。
总结
1.选择一个网络模型
- 输入层单元数:特征 x(i) 的维数
- 输出层单元数:分类的类别数。 多类别分类问题输出层有多个单元,输出的 y 不是一个数了,而是由一些 0 和一个 1 组成的向量。
- 隐藏层数目:默认使用1个隐藏层,如果隐藏层数目多于1个,则每个隐藏层应该有相同的单元个数。 隐藏层单元数越多,效果越好,通常取稍大于输入特征的数目。
2.训练神经网络
3.预测新样本