前言

参考代码与作业指引请自行下载:github地址,以下为答案与解析。

实验目的:

  1. 理解BP神经网络的基本原理
  2. 掌握BP神经网络的模型选择和参数估计方法
  3. 利用手写数字数据,掌握BP神经网络,并实现对手写数字数据的识别

实验要求:

  1. 根据所给文档和代码注释的提示独立完成代码中的缺失部分。
  2. 根据实验报告模板的步骤完成实验报告(需要有必要的图或表)

方法、步骤:

1. 代价函数

在nnCostFunction.py中,编写代价函数

根据数学公式

神经网络与深度学习 吴恩达 吴恩达bp神经网络_神经网络与深度学习 吴恩达

实现代码如下所示:

# 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 的向量,因此需要将不同值转化为相应的变量

第二步是完成带正则化的代价函数编写

根据数学公式:只需要在原代价函数的基础上加上正则化项。

神经网络与深度学习 吴恩达 吴恩达bp神经网络_机器学习_02

实现代码如下所示:

J += (float(_lambda) / (2 * m)) * \
        (np.sum(np.power(Theta1[:, 1:], 2)) +
         np.sum(np.power(Theta2[:, 1:], 2)))

主函数验证:因此代码编写正确。

神经网络与深度学习 吴恩达 吴恩达bp神经网络_神经网络与深度学习 吴恩达_03

2. Sigmoid梯度

在sigmoidGradient.py中编写Sigmoid梯度代码

Sigmoid 函数的梯度可以计算为:

神经网络与深度学习 吴恩达 吴恩达bp神经网络_神经网络与深度学习 吴恩达_04


其中:

神经网络与深度学习 吴恩达 吴恩达bp神经网络_神经网络与深度学习 吴恩达_05

实现代码如下所示:

def sigmoidGradient(z):
    g = np.zeros(z.shape)
    
    g = np.multiply(sigmoid(z), 1 - sigmoid(z))

    return g

主函数验证:因此代码编写正确。

神经网络与深度学习 吴恩达 吴恩达bp神经网络_python_06

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,设置

神经网络与深度学习 吴恩达 吴恩达bp神经网络_python_07

d3_t = h_t - y_t

④ 对于隐藏层l = 2,设置

神经网络与深度学习 吴恩达 吴恩达bp神经网络_d3_08

d2_t = np.multiply((Theta2.T * d3_t.T).T, sigmoidGradient(z2_t))

⑤ 使用下面的公式计算累计梯度。

神经网络与深度学习 吴恩达 吴恩达bp神经网络_神经网络与深度学习 吴恩达_09

Theta1_grad += (d2_t[:, 1:]).T * a1_t
        Theta2_grad += d3_t.T * a2_t

最后,将累积梯度除以 m,得到神经网络代价函数的(非正则化)梯度:

神经网络与深度学习 吴恩达 吴恩达bp神经网络_d3_10

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

然后尝试添加正则化的梯度:

计算公式如下所示:

神经网络与深度学习 吴恩达 吴恩达bp神经网络_python_11

具体实现如下所示:

Theta1_grad[:, 1:] += (Theta1[:, 1:] * _lambda) / m
    Theta2_grad[:, 1:] += (Theta2[:, 1:] * _lambda) / m

完成反向传播算法代码的编写后,使用源码中的梯度检查进行验证:

不含正则化: 结果正确

神经网络与深度学习 吴恩达 吴恩达bp神经网络_d3_12

包含正则化:结果正确

神经网络与深度学习 吴恩达 吴恩达bp神经网络_d3_13

将lambda设置为3,重新验证,计算结果正确

神经网络与深度学习 吴恩达 吴恩达bp神经网络_神经网络与深度学习 吴恩达_14

由此可见,反向传播算法编写正确。

4. 使用共轭梯度法学习参数

使用fmin_cg学习一个合适的参数。

结果如下所示:

神经网络与深度学习 吴恩达 吴恩达bp神经网络_正则化_15


最终模型的准确率为96.04%。尝试可视化隐藏层的图像,如下所示:

神经网络与深度学习 吴恩达 吴恩达bp神经网络_机器学习_16