吴恩达机器学习作业 基于BP神经网络的手写数字识别

Alex_Shen
2022-04-18 / 0 评论 / 0 点赞 / 195 阅读 / 1,991 字 / 正在检测是否收录...
温馨提示:
本文最后更新于 2022-04-18,若内容或图片失效,请留言反馈。部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

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

实验目的:

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

实验要求:

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

方法、步骤:

1. 代价函数

在nnCostFunction.py中,编写代价函数
根据数学公式
image-1650220643500

实现代码如下所示:

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

第二步是完成带正则化的代价函数编写
根据数学公式:只需要在原代价函数的基础上加上正则化项。
image-1650220697800

实现代码如下所示:

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

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

2. Sigmoid梯度

在sigmoidGradient.py中编写Sigmoid梯度代码
Sigmoid 函数的梯度可以计算为:
image-1650220773056
其中:
image-1650220781041

实现代码如下所示:

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

    return g

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

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

        d3_t = h_t - y_t

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

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

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

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

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

    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

然后尝试添加正则化的梯度:
计算公式如下所示:
image-1650221012555

具体实现如下所示:

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

完成反向传播算法代码的编写后,使用源码中的梯度检查进行验证:
不含正则化: 结果正确
image-1650221041153

包含正则化:结果正确
image-1650221053317

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

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

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

使用fmin_cg学习一个合适的参数。
结果如下所示:
image-1650221078786
最终模型的准确率为96.04%。

尝试可视化隐藏层的图像,如下所示:
image-1650221098436

0

评论区