神经网络架构
神经网络的架构决定了这些网络如何运行,这是执行各种任务和扩展神经网络应用的关键因素,主要有两种方法:前馈神经网络和反馈神经网络。在本文中,在彻底分析每种方法之后,我们将对这两种架构进行深入比较,并研究用例以比较每种神经网络结构的优缺点。对于实现深度网络,我们使用两种方法:顺序和功能。
深度学习中的顺序和函数
在深度学习中,设计神经网络模型有两种不同的方法:顺序方法和功能方法。这两种方法之间的选择取决于问题的类型、模型的复杂性以及我们的具体要求。顺序方法和功能方法都可以在 Keras 和 PyTorch 等框架中执行。
此外,我们可以结合使用这两种方法。这意味着,只要模型的结构定义正确,并且每个部分的输入和输出相互兼容,我们就可以使用顺序方法构建模型的一部分,使用函数方法构建另一部分。例如,在人脸识别系统中,你可以使用顺序方法设计模型的一部分,使用卷积神经网络 (CNN) 处理图像,使用函数方法设计另一部分,使用循环神经网络 (RNN) 分析面部特征序列。
顺序模型
我们逐层构建模型。顺序模型是线性且按顺序构建的,这意味着一层的输出作为下一层的输入,非相邻层之间没有任何连接。但是,不允许共享层或分支层(尽管我们在某些同时执行多个任务的网络模型中拥有分支层)。此外,您不能有多个输入或输出。
在 Keras 中,要实现 Sequential 模型,我们首先导入必要的库,然后创建一个 Sequential 模型,并使用 add() 方法逐层向模型中添加层。以下代码片段是 Sequential 模型的简单实现:
从keras.models导入Sequential 从keras.layers导入Dense # 创建 Sequential 模型 model = Sequential() # 向模型添加层 model.add(Dense(units= 64 ,activation= 'relu' ,input_shape=( 100 ,))) model.add(Dense(units= 64 ,activation= 'relu' )) model.add(Dense(units= 10 ,activation= 'softmax' )) # 摘要模型 model.summary()
此代码创建了一个具有三层的顺序模型:两个具有 ReLU 激活函数的密集层和一个具有 softmax 激活函数的密集输出层。
功能模型
在 Keras 中,Functional API 提供了一种定义具有多个输入、多个输出或共享层的更复杂的深度学习模型的方法。与仅支持线性层堆栈的 Sequential 模型相比,Functional API 是一种更灵活、更强大的定义模型的方法。在函数模型中,您仍然可以通过按顺序连接层来创建线性层序列,但可以通过合并层来创建更多的架构复杂性。这意味着,虽然各层彼此线性连接,但模型的整体架构可能不是线性的,并且可能包含不同类型的层。这两个模型的主要区别在于层的连接方式及其灵活性。
Functional API 可以管理具有非线性架构、共享层甚至多个输入或输出的模型。Functional API 背后的主要思想是将每个层定义为单独的对象,然后通过明确定义每个层的输入和输出将各层连接在一起。下面的伪代码首先使用“Input”定义一个输入层。然后,使用“Dense”将全连接层定义为输出,并使用“Model”创建具有指定输入和输出的模型。最后,通过调用“summary()”,打印模型架构的摘要。以下是 Functional API 模型的一个简单实现:
#导入库 from keras.layers import Input, Dense from keras.models import Model # 输入层和输出层的定义 input_layer = Input(shape=( 784 ,)) output_layer = Dense( 10 ,activation= 'softmax' )(input_layer) # 创建模型 model = Model(inputs=input_layer,outputs=output_layer) # 摘要模型 model.summary()
我们将继续探索神经网络的迷人架构,并研究其应用、优点和缺点。让我们一起深入研究这些前沿领域,踏上探索它们的旅程。你准备好了吗?我们走吧。
前馈网络
这是一个非循环网络,实际上与循环神经网络相反。前馈神经网络主要用于监督学习任务,其中要学习的数据既不是连续的也不是时间相关的。
前馈神经网络由三个主要部分组成:输入层、一个或多个隐藏层(如果存在)和输出层。每层处理信息并将其传递到下一层。换句话说,信号只能沿一个方向流动。这样的网络中没有循环,输出层的运行方式与其他层不同,这意味着其中没有循环。
前馈神经网络的类型
前馈神经网络有多种类型,每种类型都有自己的特性和架构,适用于不同的应用。一些最常见的类型包括:
单层前馈网络(感知器)
来源
最简单的神经网络类型是感知器。因此,感知器只有一个输入层和一个输出层。输出单元直接由其权重 (W) 与相应输入单元的总和加上偏差计算得出。这些网络只能学习数据之间的线性关系,并且对错误数据很敏感,因此更适合简单的分类任务。
多层前馈网络
来源
这种类型的网络在输入层和输出层之间有一个或多个隐藏层。其概念是前馈神经网络有多个加权层(隐藏层)。隐藏层允许网络学习更复杂的路径来表示其接收到的数据,从而使其能够解决更复杂的问题。
反馈网络
来源
顾名思义,反馈神经网络与前馈神经网络不同,它具有反馈路径,允许信号通过循环双向移动。神经连接可以通过多种方式创建,由于信号不断地来回循环,这些网络可能会变得更加复杂。
反馈神经网络的目标是达到一种平衡状态,这种状态一直持续到输入不再发生变化为止。与前馈网络不同,反馈网络不需要“误差测量”。前馈神经网络中最重要的部分是输入数据,而反馈神经网络中,输出才是最关键的部分。这是因为输出值作为输入反馈到网络中,确定新的权重和偏差,直到达到期望的结果,并继续这个循环,直到达到期望的结果。
反馈神经网络可以包括各种类型的拓扑,每种拓扑都利用循环来有效地解决不同的问题。
1.循环神经网络(RNN)
来源
循环神经网络 (RNN) 是人工神经网络中一个重要且基本的类型,其中数据流基于反馈。本质上,RNN 是一个双向网络。换句话说,数据被输入到网络中,在处理数据并应用权重后,生成的输出被反馈到网络中以调整权重。我们可以使用处理时间序列数据的 RNN 来处理视频和音频,例如在自动驾驶汽车中。RNN 可能面临的最重要问题是梯度消失问题,它可能发生在训练期间。这个问题通常存在于使用时间反向传播 ( BPTT ) 训练的简单 RNN 中。
事实上,随着网络变得越来越深,在反向传播过程中,从较高层传播到较低层的梯度(偏导数)趋向于零。结果,较低层的权重没有更新,导致这些层从输入中接收的信息较少,并导致难以在长序列中学习和保留信息。
循环神经网络有多种类型,每种类型用于特定的任务:
简单循环神经网络(简单 RNN):
这些网络通常由单个循环层构成,可以解决小规模任务的即时问题。例如,它们可以预测文本序列中的下一个字符或数字序列中的下一个数字。然而,随着时间的推移,由于仅使用单个记忆单元,它们可能会遇到梯度消失问题。
门控循环单元(GRU):
和 LSTM 一样,GRU 具有复杂的结构,能够学习和保留长期信息。GRU 的计算量比 LSTM 更小,因为它们使用两个门进行学习。这使得 GRU 的训练和执行速度更快。
GRU 可以利用长期记忆来学习语言中的复杂模式。这是因为 GRU 使用两个门:更新门决定应将多少过去信息与新信息相结合,而重置门决定应保留或遗忘多少过去信息。事实上,LSTM 和 GRU 网络都可以使用长期记忆来保留模式和长期信息,但它们采用不同的方法和结构。
LSTM 也具有长期记忆。但是,由于它们具有遗忘门,该门决定了应该遗忘多少过去信息,因此它们可能具有更长的长期记忆。实际上,它们的方法和结构是不同的。
在某些情况下,由于 LSTM 具有出色的保留和控制信息的能力,因此仍然是语言建模或机器翻译等任务的常见选择。
例如,Google Translate 使用了 GRU 和 LSTM 的组合,从而能够兼具两者的优势。
双向 RNN:
它利用两层 RNN,一层为前向,一层为后向,使网络能够同时利用任何给定时刻的前后信息。这种类型的网络可以有效地解释文本和序列。其中的学习方法可以类似于单向网络中的学习方法。
双向 RNN 有三种学习方法。
第一类是前向-后向学习,即前向和后向两个 RNN 层依次从输入序列中学习。
第二种是同步预测,即同时预测网络两个方向(前向和后向)的输出。
第三类是预测和决策,其中决策或预测所需的输出首先使用双向网络计算。在所有学习类型中,最终输出都是通过组合两层的输出获得的,这使得双向 RNN 能够同时考虑过去和未来的信息,并在机器翻译、情感分析和文本生成等任务中提供更好的结果。双向 RNN 被认为是这些领域有效且强大的方法。
长短期记忆——LSTM
此gif来源于Medium上的一篇文章。
长短期记忆 (LSTM) 是一种循环神经网络架构,旨在解决传统循环神经网络 (RNN) 中出现的梯度消失问题。LSTM 与传统 RNN 的主要区别在于它们能够捕获和保留序列数据中的长期依赖关系。LSTM 通过引入带有控制信息流的门(输入门、遗忘门和输出门)的记忆单元来解决此问题。这些门是神经网络层,决定哪些信息应该存储、遗忘或从记忆单元输出。这种架构允许 LSTM 维护长期记忆并随着时间的推移有选择地更新它。
LSTM 使用时间反向传播 (BPTT)进行训练,其中模型被“深入”到过去,而不仅仅是简单的循环过程。这涉及两个阶段:前向传递,其中输入数据被输入到模型中并计算输出,以及反向传播,类似于常规的后向传递。使用链式法则,计算相对于权重和其他网络参数的误差梯度。使用这些计算出的梯度更新参数以最小化误差。
LSTM对于长期依赖关系至关重要,这使得它们更适合机器翻译、语音识别和情感分析等任务。LSTM 与人类的短期记忆有一些共同的特征和运行原理。正如人类的短期记忆可以长时间(例如几分钟到几小时)保留信息一样,LSTM 也可以利用其机制将信息长时间保持在细胞记忆中。两者都有忘记和删除不必要信息的机制。例如,在 LSTM 中,忘记门负责从细胞记忆中删除不相关的信息。两者都可以学习和保留输入数据中的复杂模式和依赖关系。由于这些原因,在某些情况下,LSTM 可以作为描述短期记忆的概念模型。
2.完全循环网络
顾名思义,在这种类型的神经网络架构中,层中的所有神经元都与其他神经元完全连接。这意味着每个神经元既是输入又是输出。全循环网络适用于处理不同长度的序列,例如文本、语音、机器翻译或时间序列预测,如预测金融市场价格或交通。然而,主要问题之一是梯度不稳定的问题,在神经网络(包括全循环网络)中尤为突出。这可能导致梯度消失或爆炸。
在梯度消失的情况下,梯度会趋向于零,从而导致网络学习能力下降。在梯度爆炸的情况下,梯度值会趋向于无穷大。这可能会导致偏离最佳路径甚至网络故障。为了解决这个问题,可以采用梯度改进技术。其中一种技术是使用时间反向传播 (BPTT) 进行训练。
FRNN 的另一个潜在问题是其计算复杂性。由于神经元之间的连接数量众多,训练这些网络可能非常耗时且具有挑战性。此外,FRNN 可能很容易过度拟合,尤其是在处理有限数据时。此问题可能导致在新数据和未见过的数据上表现不佳。
然而,通过改进训练方法和优化,以及使用近似方法和数据分析等复杂性降低技术,可以解决 FRNN 中的这些挑战并提高其性能。
3. Elman 和 Jordan 网络
乔丹网络或 Jordanian 网络是最简单的循环神经网络 (RNN) 类型。在这些网络中,输入信息被输入到每个神经元,然后作为输入传输到同一个神经元。乔丹网络仅由一个隐藏层组成,激活发生在隐藏层和输出层中。它们利用反向传播算法来优化参数。
Jordan 网络能够学习序列数据中的长期依赖关系。与其他 RNN 一样,Jordan 网络也会受到梯度消失问题的影响,因此很难学习长期依赖关系。这种架构允许网络将信息从当前时刻传递到下一个时刻。
它们可用于价格预测、金融时间序列分析或自然语言处理 (NLP) 等任务。Elman 网络与 Jordan 网络类似,主要区别在于激活仅发生在隐藏层。输出层中的激活使 Jordan 网络能够比 Elman 网络更好地学习长期依赖关系。然而,这一特性可能会导致计算效率下降。
与现代架构相比,Jordan 网络过于简单,解决复杂问题的能力有限,因此目前尚未得到广泛应用。不过,根据具体任务的不同,Jordan 网络仍有一些应用。
4.卷积神经网络(CNN)
卷积神经网络(CNN)这个术语我们已经听过无数次了。可以说,我们目前正在见证的革命以 CNN 为主要支柱之一。但问题来了:CNN 是如何工作的?它与其他网络有何不同?我们需要回到过去。和我一起踏上一段有趣的旅程,好吗?试试看吧!
CNN 最初由 Yann LeCun 于 20 世纪 80 年代提出,但直到 20 世纪 90 年代末,它才因计算限制而面临挑战。1998 年,论文《基于梯度的学习应用于文档识别》的发表带来了突破,大大促进了 CNN 在模式识别和图像处理中的应用。快速计算的进步和 GPU 的出现进一步推动了这一发展,使 CNN 广泛应用于物体检测、人脸识别、指纹识别、自动驾驶汽车、自然语言处理等各个领域。
那么,CNN 如何工作?
来源
当我们看一张图片时,在几分之一秒内,我们的大脑会利用我们已有的知识快速处理它,使我们能够快速识别图像。然而,对于计算机来说,情况就不一样了。机器需要逐个像素地查看图像。
CNN 的架构灵感来自动物视觉皮层神经元的连接模式。最显著的相似之处在于它们对信息的分层处理,其中特征在较高层变得更复杂。CNN 使用卷积滤波器从图像中提取特征,灵感来自视觉皮层神经元的运作方式。
CNN 与其他架构的不同之处在于其原理。在每一层中,CNN 都会增加复杂性并识别图像的更多部分。第一层是卷积层,其中应用过滤器来检测特征。然后是池化层,用于隐藏层以提取最重要的特征,然后转发到最后一层,即全连接层,机器的视觉任务在此执行。
与我一起深入探究 CNN 的各个层次。
卷积层
CNN 的主层是进行大多数计算的地方。输入到此层的图像的复杂程度可能有所不同。它们可以是具有宽度和高度尺寸的 2D 图像,可以是单通道(灰度)或 3 通道 RGB(彩色)。还有具有深度的 3D 图像,例如医学图像(CT 扫描和 MRI),这会增加模型的复杂性。
以下代码片段使用 `layers` 模块成员之一的 `Conv2D` 方法在 Keras 中创建卷积层:
# 添加一个具有 3 个大小为 3x3 的过滤器和 ReLU 激活函数的卷积层 model.add(layers.Conv2D( 3 , ( 3 , 3 ),activation= 'relu' , input_shape=( 28 , 28 , 1 )))
参数“input_shape”指定输入数据的形状,其尺寸为(28,28,1),表示尺寸为 28 x 28 像素的灰度图像。
卷积层由卷积滤波器(也称为核)组成。卷积层中的每个神经元都连接到特定的加权滤波器(或权重矩阵)。为了可视化有关滤波器的信息,我们可以使用该层的属性。这些属性通常在训练模型后可访问。例如,在训练模型后,您可以访问每个层的权重并打印它们。
Keras 中的以下代码片段演示了如何访问卷积层的权重。
# 访问卷积层的权重 weights= model.layers[ 0 ].get_weights()[ 0 ] # 打印过滤器权重的维度 print ( "Filter Dimensions:" , weights.shape)
这些滤波器中的每一个都从图像中提取不同的特征。提取的特征包括边缘、亮点和暗度,这些特征被称为特征图。Sobel和Scharr滤波器是图像处理中常用的滤波器,用于检测垂直和水平边缘并标记图像中的梯度。当将这些滤波器应用于图像时,它们通过计算图像在水平和垂直方向上的偏导数来测量图像中各个点的强度或亮度变化。
但是,这两个滤波器之间的主要区别在于它们的核(或矩阵)特征。Sobel 滤波器的核定义更注重图像中的水平和垂直变化。Scharr 滤波器的边缘检测与它们的方向无关;换句话说,如果图像中有边缘,无论它是水平的还是垂直的,Scharr 滤波器都能很好地检测到它。例如,在对象或图像组件检测中,边缘的方向可能不太重要,重要的是正确识别边缘。
使用 OpenCV 在 Python 中应用 Sobel 和 Scharr 滤波器的示例代码:
# 调用 OpenCV 库 import cv2 # 应用 Sobel 滤波器 sobel_x = cv2.Sobel(image, cv2.CV_64F, 1 , 0 , ksize= 3 ) sobel_y = cv2.Sobel(image, cv2.CV_64F, 0 , 1 , ksize= 3 ) # 应用 Scharr 滤波器 scharr_x = cv2.Scharr(image, cv2.CV_64F, 1 , 0 ) scharr_y = cv2.Scharr(image, cv2.CV_64F, 0 , 1 )
填充
要构建卷积神经网络 (CNN) 或循环神经网络 (RNN) 等复杂模型,需要标准输入形状。当过滤器移动到图像开头时,图像边缘的一些信息会丢失,从而导致卷积层输出图像的尺寸减小。
此gif来源于Medium上的一篇文章。
缺少填充可能会导致卷积层的输出维度缩小到无法正确执行卷积运算的程度。填充的主要优点之一是它能够标准化输入数据。CNN 中的填充涉及向输入图像或特征图的边缘添加零值。它通常以两种方式实现。
零填充
在零填充方法中,添加到图像边缘的信息会用零值填充,这会导致图像清晰度降低。使用零填充会增加神经模型中的内存消耗,因为在图像边缘添加零会增加数据量,这对于处理大图像来说可能是有问题的。零填充允许创建更深的网络,同时保留输出维度和边缘信息,这可能会加快计算速度,但可能会产生更高的计算成本。它可用于增加图像大小并保持边缘清晰度,以提高物体检测的准确性。
要在 TensorFlow 库中执行零填充,您可以使用` tf.pad() `函数。此函数允许您通过在图像边缘添加零或其他指定值来执行填充。
# 通过在图像边缘添加 2 行 2 列零来应用零填充 padded_image = tf.pad(image, [[ 0 , 0 ], [ 2 , 2 ], [ 2 , 2 ], [ 0 , 0 ]], "CONSTANT" )
有效填充
与零填充相比,有效填充方法不会在图像边缘添加任何数据。实际上,没有应用任何填充。换句话说,过滤器(内核)仅在与内核大小完全匹配的输入部分上移动。因此,卷积层的输出图像的尺寸将小于输入图像,从而减少数据量和计算复杂度,并减少执行时间。
当我们有兴趣从图像中提取特征和模式,并且输出图像的大小对于我们后续任务并不重要时,可以使用有效填充。事实上,我们不会遇到零填充的许多缺点。在现实世界中,有效填充方法通常用于诸如对象检测、图像分类和模式识别等任务,在这些任务中,增加输出图像的大小是不必要的,我们的目标是提高计算速度和效率。在下面的简单伪代码中,我们定义了一个带有有效填充的 Conv2D 层,这是通过将填充参数设置为“有效”来完成的:
# 定义一个具有有效填充的 Conv2D 层 conv_layer = Conv2D(filters= 16 , kernel_size=( 3 , 3 ), padding= 'valid' ,activation= 'relu' , input_shape=( 28 , 28 , 1 ))
没错!填充在图像预处理中起着至关重要的作用,尤其是在神经网络模型中的卷积运算之前。不同的填充方法之间的选择取决于问题的性质和具体要求。它可以作为神经网络模型中的单独层应用,也可以直接合并到卷积函数中。
然后将这些数据传递到 CNN 网络的池化层,以提取更复杂的特征,用于图像识别或图像分类等任务。
与旧方法相比,CNN 用于图像处理的一个根本区别是,以前必须使用局部过滤器手动应用这些过滤器。
池化层
执行池化操作是为了在保留重要信息的同时减少特征图的空间维度(长度和宽度)。池化通常以滑动窗口的形式在特征图上移动,每次使用指定的标准(例如最大值或平均值)从该窗口的开始到结束选择一个值。重复此操作以在保留重要信息的同时减少特征图的空间维度。此操作有助于降低模型复杂性,是提高网络速度和效率的因素之一。它还可以减少过度拟合等问题。有两种常见的池化类型,每种都适用于特定任务。
最大池化
此gif来源于Medium上的一篇文章。
当卷积滤波器(或内核)在输入图像上移动时,滤波器的一部分通常与前一图像的一部分重叠,而滤波器的另一部分与新图像的一部分重叠。此模型从特征图中的每个重叠区域获取最大值,并选择最大值作为该区域的输出值。但是,最大池化可能会丢失详细信息,因为它只选择每个区域的最大值并忽略较小的值。
重叠区域被视为超参数。它可以根据问题的需求和特点进行调整。增加重叠区域可能需要模型提取更多特征,从而导致更长的训练时间。这会增加模型的复杂性并导致过度拟合。
下面是使用 `MaxPooling2D` 层的 Keras 中 Max Pooling 的简单伪代码,其中 `pool_size` 指定 Max Pooling 过滤器的大小:
# 从 Keras 导入必要的层 from keras.layers import MaxPooling2D # 向模型添加 Max Pooling 层 model.add(MaxPooling2D(pool_size=( 2 , 2 ), strides= None , padding= 'valid' ))
平均池化
此gif来源于Medium上的一篇文章。
在该方法中,与最大池化不同,我们在每个重叠区域中取平均值并将其选为输出值。平均池化通常用于减少特征图的空间维度并创建更清晰的图像。在这种方法中,输入图像小区域中的精确信息被平均,这可能会减少对这些区域中局部特征的关注并导致详细信息丢失。这种详细信息的丢失会阻止模型训练并导致欠拟合,尤其是在图像的小细节对于各种任务(例如精确模式识别或详细成像)很重要的情况下。因此,应谨慎使用此方法,并考虑问题的具体特征。
下面是一个简单的伪代码,以便更好地理解 Keras 中的平均池化:
从keras.layers导入AveragePooling2D # 添加具有指定池大小的平均池化层 model.add(AveragePooling2D(pool_size=( 2 , 2 )))
全连接层
在全连接层中,每个神经元或节点都与前一层的每个神经元相连。这意味着前一层的所有输入信号都被视为每个神经元的输出。
全连接层通常用于神经网络的最后阶段,用于分类或回归等任务。这些层的主要问题是过度拟合,因为它们具有大量能够保留大量信息的参数。为了防止这个问题,有两种解决方案可以有效,即dropout和正则化。
在 dropout 方法中,在每个训练步骤中,一组随机的神经元及其之间的连接被丢弃或移除。Dropout 通常使神经网络更加灵活。此外,正则化旨在通过向目标函数添加一定系数的权重来限制权重的大小并防止它们变得过大,从而降低模型的复杂性。通常使用 L1 和 L2 正则化,分别称为“Lasso 正则化”和“Ridge 正则化”。
在 L1 正则化中,目标函数中添加了一个项作为权重绝对值的总和。这会导致一些权重变为零,从而有效地从模型中删除不必要的权重。在 L2 正则化中,目标函数中添加了一个项作为权重平方的总和。这会导致权重变小但连续,而不会达到零。这允许所有特征以不同的权重产生影响,但与无正则化相比,影响程度较小。
我们简单看一下代码片段中关于 FC 的所有解释。
import tensorflow as tf # 定义模型 model = tf.keras.Sequential([ tf.keras.layers.Flatten(input_shape=( 28 , 28 )), # 输入层:将图像转换为向量tf.keras.layers.Dense( 128 , activity= 'relu' ), # 具有 128 个神经元和 ReLU 激活的全连接层tf.keras.layers.Dropout( 0.2 ), # 以 0.2 的速率进行 Dropouttf.keras.layers.Dense( 64 , activity= 'relu' ), # 具有 64 个神经元和 ReLU 激活的全连接层tf.keras.layers.Dropout( 0.2 ), # 以 0.2 的速率进行 Dropouttf.keras.layers.Dense( 10 , activity= 'softmax' ) # 具有 10用于对 10 个不同类别进行分类的神经元和 softmax 激活 ])
此代码定义了完全连接(FC)层并使用 dropout 正则化,而无需编译模型。
第三部分结论
在本文中试图解释从开始到现在与神经网络和深度学习相关的最重要和最基本的主题,并深入研究深度学习层的复杂性。但这仅仅是个开始。深度学习的边界非常狭窄,不同的深度学习网络正在不断发展。事实上,今天,深度学习网络的用途已经结合起来执行各种任务。正如我们所知,我们有同时进行自然语言处理的产品,例如,还进行图像生成,反之亦然。