【educoder】多层感知机的实现

神经网络是由若干个网络层堆叠而成的模型。多层感知机作为最简单的神经网络,其核心组成的网络层是全连接层和激活函数层。全连接层是对输入神经元的线性变换。激活函数层能够在神经网络中引入非线性成分,是神经网络强大表示能力的基础。下图是一个简单的神经网络示例,其中最左边是输入层(input layer),中间两层是隐含层(hidden layer),最右边是输出层(output layer)。

第1关:实现全连接层的前向传播

本关任务
实现全连接层的前向传播。
相关知识
为了完成本关任务,你需要掌握:

神经网络的结构; 全连接层的定义。 本实训内容可参考《深度学习入门——基于 Python 的理论与实现》一书中第 3.1-3.5章节的内容。

神经网络的结构
在上一个实训中,我们对感知机和多层感知机进行了学习,也了解了利用感知机的堆叠构建多层感知机,从而得到表达能力更强的模型的方法。在本实训中,我们更进一步,来学习神经网络的结构。神经网络由若干神经网络层堆叠而成,不同的层能够有效的提取数据的特征,实现对输入数据从低阶特征到高阶特征的逐渐提取,并完成分类、回归等机器学习任务。多层感知机其实已经是一种非常简单的神经网络。常见的神经网络包括多层感知机、卷积神经网络、循环神经网络等。下图展示了一个简单的神经网络模型。
在这里插入图片描述
神经网络在实现上包括前向传播和反向传播两个部分。前向传播在训练和预测时都会用到,用于计算网络模型的预测结果和损失函数。而反向传播则在训练过程中,用来计算参数的梯度,使用梯度下降法来对网络模型进行训练。在下面的几个实训中,我们将关注神经网络模型的前向传播,而将反向传播放在后面的实训介绍。
从本实训开始,你将会使用numpy来实现一个简单的深度学习框架,包括常见的神经网络层的前向和反向传播,以及神经网络训练的基本方法。

全连接层的定义
回顾之前使用感知机实现逻辑门的例子,每个输出都是所有输入信号的线性组合。把每个输入和输出信号都看作是一个神经元,那么输出神经元和输入神经元之间是两两互相连接的,这种网络层叫做全连接层。
形式化地,一个包含N个输入神经元,M个输出神经元的全连接层包含两组参数:权重W∈R N×M 和偏置b∈R M ,其输入可以看作是一个N维的(列)向量x∈R N ,此时全连接层的计算可以表示为:y=x T W+b
通过全连接层,可以将输入特征进行线性变换,得到一组新的特征。

全连接层的实现
实训已经预先定义了一个FullyConnected类,在该类的构造函数中,其接受对应的权重W和偏置b。权重W是一个N×M的numpy.ndarray,偏置b是一个长度为M的numpy.ndarray,其中N是全连接层的输入通道数,M是全连接层的输出通道数。

在本实训中,你需要实现前向传播函数forward()。forward()函数的输入x是一个维度大于等于2的numpy.ndarray,形状为(B,dim1 ​,dim 2 ​,…,dim k ​),其中B是 batch size,即数据的个数。首先,你需要对x的形状进行调整,将其转化为形状为(B,N)的2维numpy.ndarray。同时还需要记录x的原始形状,并记录在original_x_shape中。最后返回线性变换后的结果。

编程要求
根据提示,在右侧编辑器中 Begin 和 End 之间补充代码,实现上述全连接层的前向传播。

import numpy as np


class FullyConnected:
    def __init__(self, W, b):
        r'''
        全连接层的初始化。

        Parameter:
        - W: numpy.array, (D_in, D_out)
        - b: numpy.array, (D_out)
        '''
        self.W = W
        self.b = b

        self.x = None
        self.original_x_shape = None

    def forward(self, x):
        r'''
        全连接层的前向传播。

        Parameter:
        - x: numpy.array, (B, d1, d2, ..., dk)

        Return:
        - y: numpy.array, (B, M)
        '''
        ########## Begin ##########
        y = np.dot(x,self.W) + self.b
        return y
        ########## End ##########


第2关:实现常见激活函数的前向传播

本关任务
实现常见激活函数的前向传播。
相关知识
为了完成本关任务,你需要掌握:常见激活函数的定义。

本实训内容可参考《深度学习入门——基于 Python 的理论与实现》一书中第 3.1-3.5 章节的内容。

激活函数的作用
我们首先来考虑这样一种神经网络,它由两个全连接层组成。假设这两个全连接层的权重和偏置分别为W1 、b1 ​和W 2 ​,b 2 ​,输入为x,那么由上一个关中全连接层的计算公式,我们可以得到一些结论:
y ​=(x×W 1 ​ +b 1 ​)×W 2 ​+b 2 ​=x×W 1 ​×W 2 +b 1 ×W 2 ​+b 2 ​ ​
因为矩阵乘法满足结合律,令W0=W1×W 2 ​,b 0 ​=b1 ​×W 2 ​+b2 ​,那么上面的两层全连接层就可以写为: y=x×W 0 ​+b 0 ​
这就说明这样堆叠两个全连接层与一个全连接层是等效的,那么这种堆叠就是无效的,并不能增强模型的拟合能力。为了解决这一问题,通常在每一层的后面接一个非线性激活函数。通过引入非线性激活函数,可以极大的增强模型的拟合能力。
回顾之前我们使用感知机实现逻辑门的时候,我们在线性变换之后使用了符号函数,其实就是一种激活函数。也正是因为使用了符号函数,使得我们能够通过将感知机堆叠成多层感知机的方式实现异或门。

常见激活函数的定义
1. sigmoid激活函数

sigmoid 激活函数是一个 S型函数,可以将任意范围的输入转化到[0,1]的范围内,而这一范围与概率的范围是相同的,因此可以借此实现“概率”。另一方面,sigmoid激活函数在x的绝对值较大时,梯度非常接近0,会导致“梯度消失”的问题,使得网络难以收敛。其函数表达式如下:
sigmoid(x)=1/(1+e −x )
目前,sigmoid 激活函数的应用场景主要包含两种:第一是用于计算概率,这与 sigmoid 函数的本身性质吻合;第二是用于计算注意力,注意力是在计算机视觉和自然语言处理领域常用的技术,这留给感兴趣的学员自行学习。
2. ReLU激活函数
ReLU 激活函数是目前深度学习中最常用的激活函数,其只保留输入张量大于0的部分,而将小于0的部分置为0。与之前的 sigmoid不同,ReLU 激活函数不会受到梯度消失的影响。其函数表达式如下:
ReLU(x)=max(0,x)
ReLU 是目前卷积神经网络中应用最多的激活函数,其特点是计算简单,同时不会受到梯度消失问题的影响。但是 ReLU 也存在着一定的问题,例如ReLU 的输出都是非负数,因此 ReLU 输出的平均值一定是个正数,而不是0。这可能会对网络的训练产生一定的影响。针对这一问题,研究者们也提出了 LReLU、PReLU等方法进行改进,有兴趣的学员可以查找相关资料进行学习。
常见激活函数的实现
本实训希望你实现 sigmoid 和 ReLU激活函数的前向传播。实训已经预先定义了一个Sigmoid类和一个ReLU类。在本实训中,你需要实现前向传播函数forward()。forward()函数的输入x是一个维度大于等于2的numpy.ndarray,形状为(B,d1 ​,d 2 ,…,d k ​),其中B是batch size。返回经过激活函数处理的输出值。
编程要求
根据提示,在右侧编辑器中 Begin 和 End 之间补充代码,实现上述激活函数。
为了以后实现反向传播的方便,这里希望你:
在实现 sigmoid 时,将输出结果记录在self.out中; 在实现 ReLU 时,将小于 0 的元素按照 mask的形式记录在self.mask中。

import numpy as np


class Sigmoid:
    def __init__(self):
        self.out = None

    def forward(self, x):
        r'''
        Sigmoid激活函数的前向传播。

        Parameter:
        - x: numpy.array, (B, d1, d2, ..., dk)

        Return:
        - y: numpy.array, (B, d1, d2, ..., dk)
        '''
        ########## Begin ##########
        y = 1. / (1. + np.exp(-x))
        self.out = y
        return y
        ########## End ##########


class ReLU:
    def __init__(self):
        self.mask = None

    def forward(self, x):
        r'''
        ReLU激活函数的前向传播。

        Parameter:
        - x: numpy.array, (B, d1, d2, ..., dk)

        Return:
        - y: numpy.array, (B, d1, d2, ..., dk)
        '''
        ########## Begin ##########
        self.mask = (x <= 0)
        y = x.copy()
        y[self.mask] = 0
        return y
        ########## End ##########

猜你喜欢

转载自blog.csdn.net/weixin_44787324/article/details/129819323