前言
参考代码与作业指引请自行下载:github地址,以下为答案与解析。
实验目的:
- 理解BP神经网络的基本原理
- 掌握BP神经网络的模型选择和参数估计方法
- 利用手写数字数据,掌握BP神经网络,并实现对手写数字数据的识别
实验要求:
- 根据所给文档和代码注释的提示独立完成代码中的缺失部分。
- 根据实验报告模板的步骤完成实验报告(需要有必要的图或表)
方法、步骤:
1. 代价函数
在nnCostFunction.py中,编写代价函数
根据数学公式
实现代码如下所示:
# 2.计算代价函数
J = 0
for i in range(m):
first_term = np.multiply(-y_onehot[i, :], np.log(h[i, :]))
second_term = np.multiply((1 - y_onehot[i, :]), np.log(1 - h[i, :]))
J += np.sum(first_term - second_term)
J = J / m
需要注意的是,需要传入的参数是经过onehot编码后的y值。原始的标签(在变量 y 中)是 1,2,…,10,为了训练神经网络,需要将标签重新编码为只包含值 0 或 1 的向量,因此需要将不同值转化为相应的变量
第二步是完成带正则化的代价函数编写
根据数学公式:只需要在原代价函数的基础上加上正则化项。
实现代码如下所示:
J += (float(_lambda) / (2 * m)) * \
(np.sum(np.power(Theta1[:, 1:], 2)) +
np.sum(np.power(Theta2[:, 1:], 2)))
主函数验证:因此代码编写正确。
2. Sigmoid梯度
在sigmoidGradient.py中编写Sigmoid梯度代码
Sigmoid 函数的梯度可以计算为:
其中:
实现代码如下所示:
def sigmoidGradient(z):
g = np.zeros(z.shape)
g = np.multiply(sigmoid(z), 1 - sigmoid(z))
return g
主函数验证:因此代码编写正确。
3. 反向传播算法
在nnCostFunction.py中编写反向传播算法代码
对于本项目而言,反向传播算法按照一下步骤进行计算:
首先进行一次前向传播,获得a1, z2, a2, z3, h
a1, z2, a2, z3, h = forward_propagate(X, Theta1, Theta2)
然后在循环中,每次处理一条数据。每一条数据执行以下4步操作:
① 获取第t条数据
a1_t = np.mat(a1[t, :])
z2_t = np.mat(z2[t, :])
a2_t = np.mat(a2[t, :])
h_t = np.mat(h[t, :])
y_t = np.mat(y_onehot[t, :])
② 添加偏置单元
z2_t = np.insert(z2_t, 0, values=np.ones(1))
③ 对于第 3 层(输出层)中的每个输出单元 k,设置
d3_t = h_t - y_t
④ 对于隐藏层l = 2,设置
d2_t = np.multiply((Theta2.T * d3_t.T).T, sigmoidGradient(z2_t))
⑤ 使用下面的公式计算累计梯度。
Theta1_grad += (d2_t[:, 1:]).T * a1_t
Theta2_grad += d3_t.T * a2_t
最后,将累积梯度除以 m,得到神经网络代价函数的(非正则化)梯度:
Theta1_grad /= m
Theta2_grad /= m
代码汇总:
for t in range(m):
a1_t = np.mat(a1[t, :])
z2_t = np.mat(z2[t, :])
a2_t = np.mat(a2[t, :])
h_t = np.mat(h[t, :])
y_t = np.mat(y_onehot[t, :])
z2_t = np.insert(z2_t, 0, values=np.ones(1))
d3_t = h_t - y_t
d2_t = np.multiply((Theta2.T * d3_t.T).T, sigmoidGradient(z2_t))
Theta1_grad += (d2_t[:, 1:]).T * a1_t
Theta2_grad += d3_t.T * a2_t
Theta1_grad /= m
Theta2_grad /= m
然后尝试添加正则化的梯度:
计算公式如下所示:
具体实现如下所示:
Theta1_grad[:, 1:] += (Theta1[:, 1:] * _lambda) / m
Theta2_grad[:, 1:] += (Theta2[:, 1:] * _lambda) / m
完成反向传播算法代码的编写后,使用源码中的梯度检查进行验证:
不含正则化: 结果正确
包含正则化:结果正确
将lambda设置为3,重新验证,计算结果正确
由此可见,反向传播算法编写正确。
4. 使用共轭梯度法学习参数
使用fmin_cg学习一个合适的参数。
结果如下所示:
最终模型的准确率为96.04%。尝试可视化隐藏层的图像,如下所示: