使用神经网络进行机器学习
数据介绍
本次练习所用的数据集有5000个训练样本,每个样本对应于20x20大小的灰度图像。这些训练样本包括了9-0共十个数字的手写图像。这些样本中每个像素都用浮点数表示。加载得到的数据中,每幅图像都被展开为一个400维的向量,构成了数据矩阵中的一行。完整的训练数据是一个5000x400的矩阵,其每一行为一个训练样本(数字的手写图像)。数据中,对应于数字”0”的图像被标记为”10”,而数字”1”到”9”按照其自然顺序被分别标记为”1”到”9”。数据集保存在NN_data.mat
.
模型表示
我们准备训练的神经网络是一个三层的结构,一个输入层,一个隐层以及一个输出层。由于我们训练样本(图像)是20x20的,所以输入层单元数为400(不考虑额外的偏置项,如果考虑单元个数需要+1)。在我们的程序中,数据会被加载到变量 $X$ 和 $y$ 里。
本项练习提供了一组训练好的网络参数 $(\Theta^{(1)}, \Theta^{(2)})$ 。这些数据存储在数据文件 NN_weights.mat
,在程序中被加载到变量 Theta1
与 Theta2
中。参数的维度对应于第二层有25个单元、10个输出单元(对应于10个数字 的类别)的网络。
1 | import numpy as np |
1 | def display_data(data, img_width=20): |
前向传播与代价函数
现在你需要实现神经网络的代价函数及其梯度。首先需要使得函数 nn_cost_function
能够返回正确的代价值。
神经网络的代价函数(不包括正则化项)的定义为:
其中 $h_{\theta}(x^{(i)})$ 的计算如神经网络结构图所示, $K=10$ 是 所有可能的类别数。这里的 $y$ 使用了one-hot 的表达方式。
运行程序,使用预先训练好的网络参数,确认你得到的代价函数是正确的。(正确的代价约为0.287629)。
代价函数的正则化
神经网络包括正则化项的代价函数为: </br>
注意在上面式子中,正则化项的加和形式与练习中设定的网络结构一致。但是你的代码实现要保证能够用于任意大小的神经网络。
此外,还需要注意,对应于偏置项的参数不能包括在正则化项中。对于矩阵 Theta1
与 Theta2
而言,这些项对应于矩阵的第一列。
运行程序,使用预先训练好的权重数据,设置正则化系数$\lambda=1$ (lmb
) 确认你得到的代价函数是正确的。(正确的代价约为0.383770)。
此步练习需要你补充实现 nn_cost_function
。
1 | def convert_to_one_hot(y,c): |
误差反传训练算法 (Backpropagation)
现在你需要实现误差反传训练算法。误差反传算法的思想大致可以描述如下。对于一个训练样本 $(x^{(t)}, y^{(t)})$ ,我们首先使用前向传播计算网络中所有单元(神经元)的激活值(activation),包括假设输出 $h_{\Theta}(x)$ 。那么,对于第 $l$ 层的第 $j$ 个节点,我们期望计算出一个“误差项” $\delta_{j}^{(l)}$ 用于衡量该节点对于输出的误差的“贡献”。
对于输出节点,我们可以直接计算网络的激活值与真实目标值之间的误差。对于我们所训练的第3层为输出层的网络,这个误差定义了 $\delta_{j}^{(3)}$ 。对于隐层单元,需要根据第 $l+1$ 层的节点的误差的加权平均来计算 $\delta_{j}^{(l)}$ 。
下面是误差反传训练算法的细节(如图3所示)。你需要在一个循环中实现步骤1至4。循环的每一步处理一个训练样本。第5步将累积的梯度除以 $m$ 以得到神经网络代价函数的梯度。
- 设输入层的值 $a^{(1)}$ 为第 $t$ 个训练样本 $x^{(t)}$ 。执行前向传播,计算第2层与第3层各节点的激活值( $z^{(2)}, a^{(2)}, z^{(3)}, a^{(3)}$ )。注意你需要在 $a^{(1)}$ 与 $a^{(2)}$ 增加一个全部为 +1 的向量,以确保包括了偏置项。在
numpy
中可以使用函数ones
,hstack
,vstack
等完成(向量化版本)。 对第3层中的每个输出单元 $k$ ,计算
其中 $y_k \in \{0, 1\}$ 表示当前训练样本是否是第 $k$ 类。
对隐层 $l=2$ , 计算
其中$g^{\prime}$ 表示 Sigmoid 函数的梯度,
.*
在numpy
中是通 常的逐个元素相乘的乘法,矩阵乘法应当使用numpy.dot
函数。使用下式将当前样本梯度进行累加:
在
numpy
中,数组可以使用+=
运算。计算神经网络代价函数的(未正则化的)梯度,
这里,你需要(部分)完成函数 nn_grad_function
。程序将使用函数 check_nn_gradients
来检查你的实现是否正确。在使用循环的方式完成函数 nn_grad_function
后,建议尝试使用向量化的方式重新实现这个函数。
神经网络的正则化
你正确实现了误差反传训练算法之后,应当在梯度中加入正则化项。
假设你在误差反传算法中计算了 $\Delta_{ij}^{(l)}$ ,你需要增加的正则化项为
注意你不应该正则化 $\Theta^{(l)}$ 的第一列,因其对应于偏置项。
此步练习需要你补充实现函数 nn_grad_function
。
1 |
|
误差反传训练算法
Sigmoid
函数及其梯度
Sigmoid 函数定义为
Sigmoid 函数的梯度可以按照下式进行计算
为验证你的实现是正确的,以下事实可供你参考。当 $z=0$ 是,梯度的精确值为 0.25 。当 $z$ 的值很大(可正可负)时,梯度值接近于0。
这里,你需要补充完成函数 sigmoid
与 sigmoid_gradient
。 你需要保证实现的函数的输入参数可以为矢量和矩阵( numpy.ndarray
)。
网络参数的随机初始化
训练神经网络时,使用随机数初始化网络参数非常重要。一个非常有效的随机初始化策略为,在范围 $[ -\epsilon_{init}, \epsilon_{init} ]$ 内按照均匀分布随机选择参数 $\Theta^{(l)}$ 的初始值。这里你需要设置 $\epsilon_{init} = 0.12$ 。这个范围保证了参数较小且训练过程高效。
你需要补充实现函数 rand_initialize_weigths
。
对于一般的神经网络,如果第 $l$ 层的输入单元数为 $L_{in}$ ,输出单元数为 $L_{out}$ ,则 $\epsilon_{init} = {\sqrt{6}}/{\sqrt{L_{in} + L_{out}}}$ 可以做为有效的指导策略。
1 | def sigmoid(z): |
1 | def sigmoid_gradient(z): |
1 | def rand_initialize_weights(L_in, L_out): |
1 | def debug_initialize_weights(fan_out, fan_in): |
1 | def compute_numerical_gradient(cost_func, theta): |
检查梯度
在神经网络中,需要最小化代价函数 $J(\Theta)$ 。为了检查梯度计算是否正确,考虑把参数 $\Theta^{(1)}$ 和 $\Theta^{(2)}$ 展开为一个长的向量 $\theta$ 。假设函数 $f_i(\theta)$ 表示 $\frac{\partial}{\partial \theta_i} J(\theta)$ 。
令
上式中, $\theta^{(i+)}$ 除了第 $i$ 个元素增加了 $\epsilon$ 之 外,其他元素均与 $\theta$ 相同。类似的, $\theta^{(i-)}$ 中仅第 $i$ 个元素减少了 $\epsilon$ 。可以使用数值近似验证 $f_i(\theta)$ 计算是否正确:
如果设 $\epsilon=10^{-4}$ ,通常上式左右两端的差异出现于第4位有效数字之后(经常会有更高的精度)。
在练习的程序代码中,函数 compute_numerical_gradient
已经实现,建议你认真阅读该函数并理解其实现原理与方案。
之后,程序将执行 check_nn_gradients
函数。该函数将创建一个较小的神经网络用于检测你的误差反传训练算法所计算得到的梯度是否正确。如果你的实现是正确的,你得到的 梯度与数值梯度之后的绝对误差(各分量的绝对值差之和)应当小于 $10^{-9}$ 。
1 | def check_nn_gradients(lmb=0.0): |
1 | def predict(Theta1, Theta2, X): |
1 | # Parameters |
加载数据集
1 | # =========== 第一部分 =============== |
Loading and Visualizing Data...
加载神经网络模型的权重
1 | # =========== 第二部分 =============== |
Loading Saved Neural Network Parameters ...
1 | # ================ Part 3: Compute Cost (Feedforward) ================ |
Feedforward Using Neural Network ...
Cost at parameters (loaded from PRML_NN_weights): 0.287629
(this value should be about 0.287629)
1 | # =============== Part 4: Implement Regularization =============== |
Checking Cost Function (w/ Regularization) ...
Cost at parameters (loaded from PRML_NN_weights): 0.383770
(this value should be about 0.383770)
1 | # ================ Part 5: Sigmoid Gradient ================ |
Evaluating sigmoid gradient...
Sigmoid gradient evaluated at [1 -0.5 0 0.5 1]: [0.19661193 0.23500371 0.25 0.23500371 0.19661193]
神经网络参数初始化
1 | # ================ Part 6: Initializing Pameters ================ |
Initializing Neural Network Parameters ...
epsilon_init: 0.1188177051572009
epsilon_init: 0.4140393356054125
1 |
|
Checking Backpropagation...
[[ 1.27220311e-02 1.27220311e-02]
[ 1.58832809e-04 1.58832809e-04]
[ 2.17690452e-04 2.17690455e-04]
[ 7.64045027e-05 7.64045009e-05]
[ 6.46352264e-03 6.46352265e-03]
[ 2.34983744e-05 2.34983735e-05]
[-3.74199116e-05 -3.74199098e-05]
[-6.39345021e-05 -6.39345006e-05]
[-5.74199923e-03 -5.74199923e-03]
[-1.34052016e-04 -1.34052019e-04]
[-2.59146269e-04 -2.59146269e-04]
[-1.45982635e-04 -1.45982634e-04]
[-1.26792390e-02 -1.26792390e-02]
[-1.67913183e-04 -1.67913187e-04]
[-2.41809017e-04 -2.41809017e-04]
[-9.33867517e-05 -9.33867522e-05]
[-7.94573534e-03 -7.94573535e-03]
[-4.76254503e-05 -4.76254501e-05]
[-2.64923861e-06 -2.64923844e-06]
[ 4.47626736e-05 4.47626708e-05]
[ 1.09347722e-01 1.09347722e-01]
[ 5.67965185e-02 5.67965185e-02]
[ 5.25298306e-02 5.25298306e-02]
[ 5.53542907e-02 5.53542907e-02]
[ 5.59290833e-02 5.59290833e-02]
[ 5.23534682e-02 5.23534682e-02]
[ 1.08133003e-01 1.08133003e-01]
[ 5.67319602e-02 5.67319602e-02]
[ 5.14442931e-02 5.14442931e-02]
[ 5.48296085e-02 5.48296085e-02]
[ 5.56926532e-02 5.56926532e-02]
[ 5.11795651e-02 5.11795651e-02]
[ 3.06270372e-01 3.06270372e-01]
[ 1.59463135e-01 1.59463135e-01]
[ 1.45570264e-01 1.45570264e-01]
[ 1.56700533e-01 1.56700533e-01]
[ 1.56043968e-01 1.56043968e-01]
[ 1.45771544e-01 1.45771544e-01]] 9.96691174908528e-11
The above two columns you get should be very similar.
(Left-Your Numerical Gradient, Right-Analytical Gradient)
1 | # =============== Part 8: Implement Regularization =============== |
Checking Backpropagation (w/ Regularization) ...
[[ 0.01272203 0.06321029]
[ 0.05471668 0.05471668]
[ 0.00868489 0.00868489]
[-0.04533175 -0.04533175]
[ 0.00646352 -0.05107193]
[-0.01674143 -0.01674143]
[ 0.03938178 0.03938178]
[ 0.05929756 0.05929756]
[-0.005742 0.01898511]
[-0.03277532 -0.03277532]
[-0.06025856 -0.06025856]
[-0.03234036 -0.03234036]
[-0.01267924 0.01253078]
[ 0.05926853 0.05926853]
[ 0.03877546 0.03877546]
[-0.01736759 -0.01736759]
[-0.00794574 -0.06562958]
[-0.04510686 -0.04510686]
[ 0.00898998 0.00898998]
[ 0.05482148 0.05482148]
[ 0.10934772 0.15983598]
[ 0.11135436 0.11135436]
[ 0.06099703 0.06099703]
[ 0.00994614 0.00994614]
[-0.00160637 -0.00160637]
[ 0.03558854 0.03558854]
[ 0.108133 0.1475522 ]
[ 0.11609346 0.11609346]
[ 0.0761714 0.0761714 ]
[ 0.02218834 0.02218834]
[-0.00430676 -0.00430676]
[ 0.01898519 0.01898519]
[ 0.30627037 0.33148039]
[ 0.21889958 0.21889958]
[ 0.18458753 0.18458753]
[ 0.13942633 0.13942633]
[ 0.09836012 0.09836012]
[ 0.10071231 0.10071231]] 0.33076217369064975
The above two columns you get should be very similar.
(Left-Your Numerical Gradient, Right-Analytical Gradient)
训练神经网络
1 | # =================== Part 8: Training NN =================== |
Training Neural Network...
Warning: Maximum number of iterations has been exceeded.
Current function value: 0.449704
Iterations: 50
Function evaluations: 99
Gradient evaluations: 99
模型预测
1 | # ================= Part 9: Implement Predict ================= |
ok 4796
Training Set Accuracy: 0.9592