神经网络要点理解
第一次接触神经网络总是被其诸多的符号弄的眼花缭乱。几个重要的符号包括:
- 样本的个数m;
- 单个样本的feature个数n;
- 神经网络的层数L;
- 神经网络输出的类别数量K;
假设我们对5000幅20×20的手写数字灰度图像进行识别,则m=5000,n=400,K=10。对于本文用到的神经网络,L=3。
1 损失函数及其正则化
一个未经正则化的神经网络损失函数为:
J(Θ)=1mm∑i=1K∑k=1[−y(i)klog((hθ(x(i)))k)−(1−y(i)k)log(1−(hθ(x(i)))k)]其中hθ(x)的计算如下:
此处L=3.
针对上面的公式,我们有x(i)表示第i个样本的矢量,这个矢量的大小是400×1。hθ(x(i))k表示第i个输入在第K个类上的输出。每一个输入样本x(i)都会在神经网络的输出层产生K个输出,表示x(i)属于这K个类中每个类的可能性。
当我们给定Θ1,Θ2时,我们可以根据上图来计算每一个hθ(xi),进而根据J(Θ)的公式来计算损失函数。
注意在计算的过程中,我们遇到的一些矩阵(从上图到J(Θ)的计算过程中遇到的矩阵)的维度为:
矩阵 | 维度 |
---|---|
a(1) | 5000×401 |
Θ(1) | 25×401 |
z(2) | 25×5000 |
a(2) | 26×5000 |
Θ(2) | 10×26 |
z(3) | 10×5000 |
a(3) | 10×5000 |
Y | 10×5000 |
其中a(1) 为添加了a(1)0后的矩阵;a(2)为添加了a(2)0后的矩阵;Y为把1,2,…,10映射为矢量后的矩阵。
J(Θ)计算的是5000个用户在10个类上的cost之和。所以式~(1)的方括号中如果是矩阵的话应该是一个10×5000的矩阵。
计算J(Θ)的部分代码为:
a1 = [ones(m,1) X]; z2 = Theta1*a1'; a2 = 1./(1 + exp(-z2)); a2 = [ones(1,size(a2,2));a2];%add a_0^(2) z3 = Theta2 * a2; a3 = 1./(1 + exp(-z3));%10X5000 temp = eye(num_labels); Y = temp(:,y); J = (Y .* log(a3) + (1-Y).* log(1-a3))./m; J = -1*sum(sum(J));
注意为了支持任何大于K>3的分类,代码中不允许出现任何的magic number。比如:
temp = eye(num_labels);
就不能写成:
temp = eye(10);
虽然在这个例子中 num_labels=10
magic number 也是不被允许的。
接下来是正则项的计算:
λ2m[25∑j=1400∑k=1(Θ(1)j,k)2+10∑j=125∑k=1(Θ2j,k)2]同样magic number是不被允许的。
2 后向传递算法
后向传递算法的步骤为:
- 给定一个训练样本x(t),y(t) 首先计算前向过程,直到输出hθ(x)
- 对每个层l的每个节点j,计算误差项δ(l)j,这个误差项用来度量这个节点对输出负多大的“责任”;
- 对于输出节点,我们直接计算网络的activation输出和真实的目标值之间的差即可。用这个差值作为δ(3)j,对于隐藏的层,计算δ(l)j时需要加权考虑层l+1上的错误。
根据上图,我们需要循环处理所有样本,一次处理一个,所以一定会有一个 for t=1:m
。在第t次迭代的时候处理第t个样本。循环内的步骤为:
- 设定输入层的值为xt,执行前向过程,计算z(2),a(2),z(3),a(3)。注意在计算过程中需要为a添加一个bias项。
- 对于层3中的每一个输出单元,设定δ(3)k=(a(3)k−yk) 其中yk是二进制数表示当前的训练样本是不是第k类,如果是,则yk=1;如果当前样本属于其他类则yk=0。
- 对隐藏层l=2,设定:δ(2)=(Θ(2))Tδ(3).∗g′(z(2))
- 从这个样本中累计梯度值。Δ(l)=Δ(l)+δ(l+1)(a(l))T注意要去掉δ(2)0
- 获得梯度值:∂∂Θ(l)ijJ(Θ)=D(l)ij=1mΔ(l)ij
在matlab实现的过程中,也需要仔细核对相关变量的维度。由于我们在计算a(2),a(3),z(2),z(3)的过程中使用的是矢量计算,在计算∂∂Θ(l)ijJ(Θ)的过程中我们也可以使用全矢量计算。
%% calculate the theta gradient delta3 = a3 - Y; temp = Theta2; temp(:,1) = 0; Theta2_grad = delta3 * a2'./m + lambda./m*temp ; delta2 = Theta2(:,2:end)'*delta3 .* sigmoidGradient(z2); temp = Theta1; temp(:,1) = 0; Theta1_grad = delta2 * a1./m + lambda./m*temp;
需要注意的是在正则化过程中需要把Θ中对应bias项的那些值去掉,在代码中我才用了置零处理。另外在计算δ(2)的过程中也需要把Θ2中与bias相关的项去掉。