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

任务描述

本关任务:实现全连接层的前向传播。

相关知识

为了完成本关任务,你需要掌握:

  1. 神经网络的结构;
  2. 全连接层的定义。

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

神经网络的结构

在上一个实训中,我们对感知机和多层感知机进行了学习,也了解了利用感知机的堆叠构建多层感知机,从而得到表达能力更强的模型的方法。在本实训中,我们更进一步,来学习神经网络的结构。神经网络由若干神经网络层堆叠而成,不同的层能够有效的提取数据的特征,实现对输入数据从低阶特征到高阶特征的逐渐提取,并完成分类、回归等机器学习任务。多层感知机其实已经是一种非常简单的神经网络。常见的神经网络包括多层感知机、卷积神经网络、循环神经网络等。下图展示了一个简单的神经网络模型。

python感知机实现二分类例题 python 多层感知机_python

图1 简单神经网络

神经网络在实现上包括前向传播和反向传播两个部分。前向传播在训练和预测时都会用到,用于计算网络模型的预测结果和损失函数。而反向传播则在训练过程中,用来计算参数的梯度,使用梯度下降法来对网络模型进行训练。在下面的几个实训中,我们将关注神经网络模型的前向传播,而将反向传播放在后面的实训介绍。

从本实训开始,你将会使用numpy来实现一个简单的深度学习框架,包括常见的神经网络层的前向和反向传播,以及神经网络训练的基本方法。

全连接层的定义

回顾之前使用感知机实现逻辑门的例子,每个输出都是所有输入信号的线性组合。把每个输入和输出信号都看作是一个神经元,那么输出神经元和输入神经元之间是两两互相连接的,这种网络层叫做全连接层。

形式化地,一个包含N个输入神经元,M个输出神经元的全连接层包含两组参数:权重W∈RN×M和偏置b∈RM,其输入可以看作是一个N维的(列)向量x∈RN,此时全连接层的计算可以表示为:

y=xTW+b

通过全连接层,可以将输入特征进行线性变换,得到一组新的特征。

全连接层的实现

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

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

编程要求

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

测试说明

平台会对你编写的代码进行测试,测试方法为:平台会随机产生输入x、权重W和偏置b,然后根据你的实现代码,创建一个FullyConnected类的实例,然后利用该实例进行前向传播计算。你的答案将并与标准答案进行比较。因为浮点数的计算可能会有误差,因此只要你的答案与标准答案之间的误差不超过 1e-5 即可。

样例输入:



1. W: 
2. [[0.1, 0.2, 0.3], 
3. [0.4, 0.5, 0.6]]
4. 
5. b: 
6. [0.1, 0.2, 0.3]
7. 
8. x: 
9. [[1, 2], 
10. [3, 4]] 

则对应的输出神经元为:




1. [[1.0, 1.4, 1.8], 
2. [2.0, 2.8, 3.6]] 

开始你的任务吧,祝你成功!

实现代码:

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 ##########
self.original_x_shape=x.shape
        x=x.reshape(x.shape[0],-1)
        self.x=x
        out=np.dot(self.x,self.W)+self.b
        return out
        ########## End ##########

 代码截图:

python感知机实现二分类例题 python 多层感知机_python感知机实现二分类例题_02

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

任务描述

本关任务:实现常见激活函数的前向传播。

相关知识

为了完成本关任务,你需要掌握:常见激活函数的定义。

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

激活函数的作用

我们首先来考虑这样一种神经网络,它由两个全连接层组成。假设这两个全连接层的权重和偏置分别为W1、b1和W2、b2,输入为x,那么由上一个关中全连接层的计算公式,我们可以得到一些结论:

y=(x×W1+b1)×W2+b2=x×W1×W2+b1×W2+b2

因为矩阵乘法满足结合律,令W0=W1×W2,b0=b1×W2+b2,那么上面的两层全连接层就可以写为:

y=x×W0+b0

这就说明这样堆叠两个全连接层与一个全连接层是等效的,那么这种堆叠就是无效的,并不能增强模型的拟合能力。为了解决这一问题,通常在每一层的后面接一个非线性激活函数。通过引入非线性激活函数,可以极大的增强模型的拟合能力。

回顾之前我们使用感知机实现逻辑门的时候,我们在线性变换之后使用了符号函数,其实就是一种激活函数。也正是因为使用了符号函数,使得我们能够通过将感知机堆叠成多层感知机的方式实现异或门。

常见激活函数的定义

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,d2,...,dk),其中B是batch size。返回经过激活函数处理的输出值。

编程要求

根据提示,在右侧编辑器中 Begin 和 End 之间补充代码,实现上述激活函数。

为了以后实现反向传播的方便,这里希望你:

  1. 在实现 sigmoid 时,将输出结果记录在self.out中;
  2. 在实现 ReLU 时,将小于 0 的元素按照 mask 的形式记录在self.mask中。

测试说明

平台会对你编写的代码进行测试,测试方法为:平台会随机产生输入x和输出梯度dout,然后根据你的实现代码,创建一个Sigmoid/ReLU类的实例,然后利用该实例进行前向传播计算。你的答案将并与标准答案进行比较。因为浮点数的计算可能会有误差,因此只要你的答案与标准答案之间的误差不超过1e-5即可。

样例输入:


1. # 对于sigmoid激活函数: 
2. x: 
3. [[-1, 0, 1]]
4. 
5. #对于ReLU激活函数: 
6. x: 
7. [[-1, 0, 1]] 

则对应的输出为:

1. # 对于sigmoid激活函数: 
2. [[0.27, 0.50, 0.73]]
3. 
4. #对于ReLU激活函数: 
5. [[0, 0, 1]] 

上述结果有四舍五入的误差,你可以忽略。


开始你的任务吧,祝你成功!

实现代码:

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 ##########
 out = 1. / (1. + np.exp(-x))
        self.out = out
        return out
        ########## 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)
        out = x.copy()
        out[self.mask] = 0
        return out

        ########## End ##########

代码截图:

python感知机实现二分类例题 python 多层感知机_全连接_03

python感知机实现二分类例题 python 多层感知机_python_04