HBU-NNDL 作业4:第四章课后题

目录

1、试设计一个前馈神经网络来解决XOR问题,要求该前馈神经网络具有两个隐藏神经元和一个输出神经元,并使用ReLU作为激活函数。

1.1 XOR问题描述

1.2 XOR运算的网络结构设计 

1.3 模型训练

1.4 训练结果

2、试举例说明“死亡ReLU问题”,并提出解决方法。

3、为什么在神经网络模型的结构化风险函数中不对偏置进行正则化?

4、为什么在用反向传播算法进行参数学习时要采用随机参数初始化的方式而不是直接令 w = 0, = 0?

 5、梯度消失问题是否可以通过增加学习率来缓解

小结

参考


1、试设计一个前馈神经网络来解决XOR问题,要求该前馈神经网络具有两个隐藏神经元和一个输出神经元,并使用ReLU作为激活函数。

1.1 XOR问题描述

异或是对两个运算元的一种逻辑分析类型,当两两数值相同时为否,而数值不同时为真。异或的真值表见表1-1所示:

HBU-NNDL 作业4:第四章课后题_第1张图片

表1-1 异或的真值表 

 代码实现:

input_x = torch.tensor([[0, 0], [0, 1], [1, 0], [1, 1]])
input_x = input_x.float()
real_y = torch.tensor([[0], [1], [1], [0]])
real_y = real_y.float()

 

1.2 XOR运算的网络结构设计 

为了采用前馈神经网络解决XOR问题,首先我们通过引入其他逻辑运算与XOR问题进行对比分析,从而设计适合解决XOR运算的网络结构模型。如图1-1和图1-2所示,图中的直线是决策边界。在逻辑运算与、或运算中我们总可以找到一条直线对它们进行准确的分类,它们属于线性可分。然而在图1-3所示的XOR问题中,我们无法找到一条直线将其进行准确的分类,XOR属于一种线性不可分。由于单层神经网络只能解决线性问题,无法解决非线性问题,异或问题属于非线性问题,所以感知机无法解决异或问题。要解决XOR运算问题,需要生成非线性的决策边界。因此,我们使用多层感知机进行求解,在单层前馈神经网络的基础上,加入一层隐含层,即二层的前馈神经网络进行XOR的运算网络结构设计如图1-4所示。从图中可以看出我们设计了一个二层的前馈神经网络对XOR运算进行求解。网络结构由一个输入层、一个隐含层和一个输出层构成,其中输入层有两个神经元,隐含层有两个神经元,输出层有一个神经元构成。
HBU-NNDL 作业4:第四章课后题_第2张图片

                      图1-1 AND                                图1-2 OR                                图1-3 XOR

 

隐含层的表达式见公式1和公式2所示。

                                                 h_1 = w_{11}*x_1+w_{21}*x_2+b_{11}                                                  (1)

                                                 h_1 = w_{12}*x_1+w_{22}*x_2+b_{12}                                                  (2)

输出层表达式如公式3所示。

                                                  y=w'_{11}*h_1+w'_{21}*h_2+b_{21}                                                   (3)
HBU-NNDL 作业4:第四章课后题_第3张图片

图1-4 XOR运算网络结构图 

 代码实现:

import torch
from torch.nn.init import constant_, normal_
import numpy as np

class XOR_module(torch.nn.Module):
    def __init__(self, input_size=2, output_size=1,mean_init=0.,std_init=1.,b_init=0.0):
        super(XOR_module, self).__init__()
        self.fc1 = torch.nn.Linear(input_size, 2)
        normal_(tensor=self.fc1.weight, mean=mean_init, std=std_init)
        constant_(tensor=self.fc1.bias, val=b_init)
        self.fc2 = torch.nn.Linear(2, output_size)
        normal_(tensor=self.fc2.weight, mean=mean_init, std=std_init)
        constant_(tensor=self.fc2.bias, val=b_init)
        # 使用'torch.nn.ReLU'定义 relu 激活函数
        self.act = torch.nn.ReLU()

    # 前向计算
    def forward(self, inputs):
        outputs = self.fc1(inputs)
        outputs = self.act(outputs)
        outputs = self.fc2(outputs)
        outputs = self.act(outputs)
        return outputs

1.3 模型训练

net = XOR_module()
learing_rate = 0.1
epochs = 10000
loss_function = torch.nn.MSELoss()  # 用交叉熵损失函数会出现维度错误
optimizer = torch.optim.SGD(net.parameters(), lr=learing_rate)

# 进行训练
for epoch in range(epochs):

    out_y = net(input_x)
    loss = loss_function(out_y, real_y)  # 计算损失函数
    loss.backward()  # 反向传播
    optimizer.step()  # 参数更新
    optimizer.zero_grad()  # 对梯度清零,避免造成累加

 

1.4 训练结果

# 打印计算的权值和偏置
print('w1 = ', net.fc1.weight.detach().numpy())
print('b1 = ', net.fc1.bias.detach().numpy())
print('w2 = ', net.fc2.weight.detach().numpy())
print('b2 = ', net.fc2.bias.detach().numpy())

#进行测试
input_test = input_x
out_test = net(input_test)
print('input_x', input_test.detach().numpy())
print('out_y', np.around(out_test.detach().numpy()))

结果: 

w1 =  [[-0.82448673  0.8244869 ]
 [-1.3191193   1.3191193 ]]
b1 =  [ 8.244868e-01 -9.024528e-08]
w2 =  [[-1.2128732  1.5161606]]
b2 =  [0.99999887]
input_x [[0. 0.]
 [0. 1.]
 [1. 0.]
 [1. 1.]]
out_y [[0.]
 [1.]
 [1.]
 [0.]]

 从测试结果看,模型可以很好预测出xor的结果。

2、试举例说明“死亡ReLU问题”,并提出解决方法。

        在反向传播过程中,如果学习率比较大,一个很大的梯度经过ReLU神经元,可能会导致ReLU神经元更新后的偏置和权重是负数,进而导致下一轮正向传播过程中ReLU神经元的输入是负数,输出是0。由于ReLU神经元的输出为0,在后续迭代的反向过程中,该处的梯度一直为0,相关参数不再变化,从而导致ReLU神经元的输入始终是负数,输出始终为0。即为“死亡ReLU问题”
解决方法:
使用Leaky ReLU/PReLU、ELU函数或者Softplus函数替换。

3、为什么在神经网络模型的结构化风险函数中不对偏置进行正则化?

        首先正则化主要是为了防止过拟合,而过拟合一般表现为模型对于输入的微小改变产生了输出的较大差异,这主要是由于有些参数w过大的关系,通过对w进行惩罚,可以缓解这种问题。

        而如果对b进行惩罚,其实是没有作用的,因为在对输出结果的贡献中,参数b对于输入的改变是不敏感的,不管输入改变是大还是小,参数b的贡献就只是加个偏置而已。举个例子,如果你在训练集中,w和b都表现得很好,但是在测试集上发生了过拟合,b是不背这个锅的,因为它对于所有的数据都是一视同仁的(都只是给它们加个偏置),要背锅的是w,因为它会对不同的数据产生不一样的加权。

        或者说,模型对于输入的微小改变产生了输出的较大差异,这是因为模型的“曲率”太大,而模型的曲率是由w决定的,b不贡献曲率(对输入进行求导,b是直接约掉的)。同时,对b进行正则化容易引起欠拟合。

4、为什么在用反向传播算法进行参数学习时要采用随机参数初始化的方式而不是直接令 w = 0, = 0?

        反向传播就是要将神经网络的输出误差,一级一级地传播到输入。在计算过程中,计算每一个w对总的损失函数的影响,即损失函数对每个w的偏导。根据w的误差的影响,再乘以步长,就可以更新整个神经网络的权重。当一次反向传播完成之后,网络的参数模型就可以得到更新。更新一轮之后,接着输入下一个样本,算出误差后又可以更新一轮,再输入一个样本,又来更新一轮,通过不断地输入新的样本迭代地更新模型参数,就可以缩小计算值与真实值之间的误差,最终完成神经网络的训练。当直接令w=0,b=0时,会让下一层神经网络中所有神经元进行着相同的计算,具有同样的梯度,同样权重更新。

在斋藤老师的鱼书中也提到过此问题:

HBU-NNDL 作业4:第四章课后题_第4张图片

 HBU-NNDL 作业4:第四章课后题_第5张图片

 

 5、梯度消失问题是否可以通过增加学习率来缓解

        梯度消失是传统神经网络训练中非常致命的一个问题,其本质是由于链式法则的乘法特性导致的。比如之前神经网络中最流行的激活函数之一Sigmoid,其表达式如下:

                                                                f(x)=\frac{1}{1+e^{-x}}

把这个函数的导数画出来,如图:

                                                           f'(x)=f(x)(1-f(x))                        

        对于Sigmoid,导数的最大值在输入为0处,值为0.25.考虑一个激活函数都是Sigmoid的多层神经网络,则梯度向后传导时,每经过一个Sigmoid就需要乘以一个小于0.25的梯度。而每乘以一个小于0.25的梯度,则梯度的值又变得更小一些。况且在优化的过程中,每个激活层输入都在0附近的概率非常的低。也就是说随着层数的加深,梯度的衰减会非常的大,迅速接近0,这就是梯度消失的现象。
        离输出层越近的参数,梯度越大,成了主要在学习的参数;而远离输出层的参数则只能在接近0的梯度则以一个非常小的速率进行学习。这种情况相当于一个恶性循环,因为靠近输出层的节点的值都是由前面那些学习速率慢的层执行前向运算得来。而前向层因为学习速率慢,所以参数未必学到了什么特征,所以这些后面层的输入随机性会比较强,这样相当于在一个有随机性的数据上进行学习,即时学习速率再快,最后也未必能真的学到有用的特征,这一过程又会让前面层的参数更难学到有效的值。

        增加学习率在一定程度上可以缓解。适当增大学习率可以使学习率与导数相乘结果变大,缓解梯度消失;过大学习率可能梯度巨大,导致梯度爆炸。

 

小结

        这次作业利用本次实验的神经网络解决了之前的异或问题,本次作业花了很多时间研究的是为什么不对b正则化,同时对正则化,过拟合及欠拟合的理解也更加深入。

参考

machine learning - No regularisation term for bias unit in neural network - Cross Validated

你可能感兴趣的:(HBU-NNDL 作业4:第四章课后题)