图神经网络学习(一)

深刨浅析图神经网络(一)

前言

本文记录博主近期回顾图神经网络各组件的基础机理,包括从卷积层、池化层、激活函数、全连接层、循环层和注意力层等,到CNN、RNN、LSTM、GRU、Attention、Self-Attention和MultiHead-Attention。将撰写多篇博客进行总结反思,如有描述不妥之处,欢迎大家对博文进行批评指正、共同进步。

(一)神经网络层级结构浅析

1、卷积层

卷积层(Convolutional Layer)是深度学习中常用的一种神经网络层。卷积层可以自动学习输入数据的特征,并通过卷积操作将输入数据转化为输出特征图。

(1)结构

卷积层由多个卷积核组成,每个卷积核是一个小的矩阵,它在输入数据上滑动,对输入数据的局部区域进行处理,提取出相应的特征。
卷积层的输入是一个三维张量,即(batch_size, height, width, channels),其中batch_size表示输入数据的样本数量,height和width表示输入数据的高度和宽度,channels表示输入数据的通道数。
卷积层的输出也是一个三维张量,即(batch_size, height, width, filters),其中filters表示输出特征图的数量,也称为卷积核的个数。

(2)计算原理

卷积操作是卷积层的核心,其计算原理如下:

(1)取出输入数据中的一个小的局部区域,与卷积核进行逐元素相乘,得到一个标量值。

(2)将相乘得到的所有标量值相加,得到输出特征图中对应位置的元素值。

(3)将卷积核向右滑动一个像素,重复上述操作,得到输出特征图中的下一个元素值,以此类推,直到输出特征图中的所有元素都计算完毕。

卷积操作可以用下面的数学公式来表示:

其中,X表示输入数据,W表示卷积核,b表示偏置项,Y表示输出特征图,i和j分别表示输出特征图中的行和列,k表示输入数据的通道数,kh和kw分别表示卷积核的高度和宽度。

(1)什么是卷积核(Kernel)?:在卷积神经网络中,卷积核通常是一个大小为k×k的矩阵,其中k通常是一个奇数。卷积核的参数是可以学习的,也就是说,卷积神经网络在训练过程中可以通过优化卷积核的参数来提高网络的性能。

卷积核通过卷积运算来提取输入数据中的特定特征。具体来说,在卷积运算中,卷积核会在输入数据的每个位置进行滑动,并与对应位置的值相乘并相加,从而得到卷积层的输出。在这个过程中,卷积核的权重参数被共享使用,因此卷积层可以对输入数据的不同位置进行相同的处理,所以卷积层能够捕捉输入数据中的局部关系。

卷积核的选择和设计是卷积神经网络中的一个重要问题,不同的卷积核可以用于提取输入数据中的不同特征,从而实现不同的任务。例如,在图像分类任务中,卷积核可以被用于提取输入图像中的边缘、纹理等特征,从而实现对不同物体的分类。在语音识别任务中,卷积核可以被用于提取语音信号中的声谱图等特征,从而实现对不同语音的识别。在推荐任务中,卷积核可以被用于从用户历史行为数据中提取有用的特征,这些特征可以被用于构建用户和物品的特征表示,以及预测用户对未知商品的喜好程度。

(2)卷积核如何优化?:卷积核的优化是卷积神经网络中非常重要的一部分,因为卷积核的性能和效果很大程度上取决于其参数的优化。以下是卷积核优化的一些方法:

  • 随机初始化:卷积核的初始参数通常是随机初始化的,这可以避免所有卷积核具有相同的初始参数,并为卷积神经网络的训练提供一个较好的起点。
  • 反向传播算法:卷积神经网络通常使用反向传播算法来计算损失函数关于卷积核参数的梯度,从而优化卷积核的参数。
  • 常见优化算法:卷积神经网络中常用的优化算法包括梯度下降、随机梯度下降、Adam等算法。这些算法可以根据卷积核参数的梯度来更新卷积核的参数,从而实现卷积核的优化。
  • 正则化:为了防止过拟合,可以使用L1、L2正则化等方法来对卷积核的参数进行惩罚,从而使卷积核更加平滑和稀疏。
  • 预训练:预训练是指在有标注数据集上对卷积神经网络进行训练,并使用训练好的卷积核作为初始化参数来训练新的卷积神经网络。这可以帮助新的卷积神经网络更快地收敛和更好地适应数据集。

(3)什么是偏置项(Bias)?:偏置项是一个常数项,它会在卷积操作后被加到输出特征图中的每个元素上,用来引入非线性变换,增加网络的表达能力。

在TensorFlow 2中,卷积层函数是tf.keras.layers.Conv2D。参数如下:

filters:卷积核的数量,即输出的通道数。
kernel_size:卷积核的大小,可以是一个整数,表示正方形卷积核的边长,或者是一个元组,表示卷积核的高度和宽度。
strides:卷积核的步幅,可以是一个整数,表示在高度和宽度方向上的相同步幅,或者是一个元组,表示在高度和宽度方向上的不同步幅。
padding:填充方式,可以是 'valid'(不填充)或 'same'(填充),默认为 'valid'。
activation:激活函数,用于增加模型的非线性能力。
input_shape:输入数据的形状,可以忽略该参数,让模型根据第一次输入数据自动推断。
data_format:输入数据的格式,可以是 'channels_first'(通道数在前)或 'channels_last'(通道数在后),默认为 'channels_last'。
use_bias:是否使用偏置向量,即增加一些可学习的常数偏置来调整模型的输出。
kernel_initializer:卷积核权重的初始化方法。
bias_initializer:偏置向量的初始化方法。
kernel_regularizer:卷积核权重的正则化方法。
bias_regularizer:偏置向量的正则化方法。
activity_regularizer:输出数据的正则化方法。
kernel_constraint:卷积核权重的约束方法。
bias_constraint:偏置向量的约束方法。

该函数支持2D卷积和深度可分离卷积等不同类型的卷积操作,可以根据不同的参数设置来实现不同的卷积操作。
例如:

import tensorflow as tf

# 创建一个卷积层
conv_layer = tf.keras.layers.Conv2D(filters=32, kernel_size=(3, 3), strides=(1, 1), padding='same', activation='relu')

# 输入数据
inputs = tf.keras.Input(shape=(28, 28, 1))

# 将输入数据输入到卷积层中
x = conv_layer(inputs)

# 创建一个模型
model = tf.keras.Model(inputs=inputs, outputs=x)

在上面的代码中,我们创建了一个卷积层,将其应用于输入数据中,并将输出结果作为模型的输出。在实际使用中,我们可以根据需要来调整卷积核的数量、大小、步幅、填充方式等参数,以获得更好的卷积特征提取效果。

2、池化层和全连接层

池化层的作用是对卷积层的输出进行下采样,以减少计算量和参数数量。常用的池化方式包括最大池化和平均池化。最大池化会选择卷积区域中的最大值作为输出,平均池化则选择卷积区域中的平均值作为输出。池化层的输出通常会传递到下一个卷积层或全连接层中。

全连接层是最简单的层级结构,它将前一层中所有的神经元与当前层中所有的神经元都相连。全连接层通常用于输出层,用于将前一层的特征转换为最终输出。全连接层通过将上一层的输出与权重矩阵相乘,然后添加偏置项,并通过激活函数进行非线性变换,产生输出结果。全连接层的参数量较大,可以自由地调节和拟合数据,但也容易导致过拟合问题。因此,通常使用正则化方法来缓解过拟合问题,例如Dropout和L2正则化等。

在 TensorFlow 2 中,全连接层的函数是 tf.keras.layers.Dense,它有以下参数:

units:整数,输出空间的维度(即该层的神经元数)。
activation:激活函数。默认为无激活函数(即线性激活函数)。
use_bias:布尔值,是否使用偏置向量。默认为 True。
kernel_initializer:权重矩阵的初始化器。默认为 "glorot_uniform" 初始化器。
bias_initializer:偏置向量的初始化器。默认为 "zeros" 初始化器。
kernel_regularizer:可选的正则化函数应用于权重矩阵。默认为无正则化。
bias_regularizer:可选的正则化函数应用于偏置向量。默认为无正则化。
activity_regularizer:可选的正则化函数应用于层的输出(即激活值)。默认为无正则化。
kernel_constraint:可选的约束函数应用于权重矩阵。默认为无约束。
bias_constraint:可选的约束函数应用于偏置向量。默认为无约束。

在 TensorFlow 2 中,池化层可以通过 tf.keras.layers 中的 MaxPool2D 和 AveragePooling2D 函数来实现。其中,MaxPool2D 函数实现最大池化,AveragePooling2D 函数实现平均池化。全连接层可以通过tf.keras.layers 中的Dense函数实现。
例如:

import tensorflow as tf

model = tf.keras.Sequential([
    # 输入大小为 (batch_size, height, width, channels)
    tf.keras.layers.Conv2D(filters=64, kernel_size=(3, 3), activation='relu', padding='same'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=(2, 2)),
    tf.keras.layers.Conv2D(filters=128, kernel_size=(3, 3), activation='relu', padding='same'),
    tf.keras.layers.MaxPool2D(pool_size=(2, 2), strides=(2, 2)),
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(units=10, activation='softmax')
])

在这个例子中,我们先定义了一个卷积层,然后接上一个最大池化层(MaxPool2D),再接上另一个卷积层和最大池化层。最后通过 Flatten 层将特征图展平为一维向量,再接上一个全连接层用于分类。池化层的参数可以通过 pool_size 和 strides 来设置池化窗口的大小和步长。

3、全连接层和卷积层的区别和联系

全连接层和卷积层是卷积神经网络中的两种基本的神经网络层。以下是它们的区别和联系:

  • 数据输入方式:全连接层接收一个一维向量作为输入,而卷积层接收一个二维或三维张量作为输入。
  • 神经元连接方式:全连接层的每一个神经元都和输入层的所有神经元相连接,而卷积层的神经元只和输入数据中的局部区域相连接。
  • 参数共享:卷积层的参数共享可以减少网络参数数量,从而避免过拟合,而全连接层的参数不共享,每个神经元都有自己的参数。
  • 特征提取:卷积层的主要作用是提取输入数据中的局部特征,而全连接层主要用于将特征向量映射到输出空间中,从而实现分类或回归等任务。
  • 连接方式:卷积层中通常使用卷积操作来实现神经元之间的连接,而全连接层使用矩阵乘法来实现神经元之间的连接。

虽然全连接层和卷积层在实现上有很多不同之处,但它们通常被结合在一起来构建卷积神经网络,以实现图像分类、目标检测等任务。例如,通常会在卷积层之后添加池化层,以减少特征图的大小和数量,然后再将特征图传递到全连接层进行分类或回归等任务。因此,在卷积神经网络中,全连接层和卷积层通常相互作用,共同完成特征提取和分类等任务。

4、激活函数的作用

激活函数是神经网络中的重要组成部分,通常在每个神经元的输出处应用激活函数。在神经网络的前向传播过程中,激活函数将线性变换的输出映射到非线性空间中,增强了网络的表达能力,使其能够处理更加复杂的数据分布和任务。

激活函数的主要作用有以下几点:

  • 引入非线性。激活函数能够引入非线性,从而使神经网络能够处理更加复杂的数据分布和任务。如果不使用激活函数,多层神经网络就会退化为单层线性模型,无法处理非线性数据分布和任务。
  • 激活输出。激活函数将神经元的线性输出转化为激活输出,通常用于分类、回归等任务中,用于计算输出的概率分布或实际值。
  • 解决梯度消失问题。激活函数能够通过限制输出值的范围,缓解梯度消失问题,从而更好地训练深度神经网络。

常见的激活函数包括 Sigmoid 函数、ReLU 函数、LeakyReLU 函数、tanh 函数等。不同的激活函数在神经网络中的表现和适用场景不同,选择合适的激活函数能够提高网络的性能和泛化能力。

5、循环层

循环层(Recurrent Layer)是神经网络中常用的一种层级结构,它被设计用来处理和建模序列数据。循环层中的每个神经元在每个时刻都接收来自前一个时刻的输入和自身的上一个状态作为输入,然后计算输出。这种输入和输出的反馈机制使得循环层能够捕捉序列数据中的时间依赖性。

循环层主要有三种类型:简单循环层(Simple Recurrent Layer,SRN)、长短期记忆层(Long Short-Term Memory,LSTM)和门控循环单元层(Gated Recurrent Unit,GRU)。这些不同类型的循环层具有不同的结构和特点,但它们都共享一些基本的属性。

在循环层中,每个时间步骤的输入首先被乘以一个权重矩阵,然后与之前时间步骤的隐藏状态相加。这个隐藏状态是网络在之前时间步骤输出的结果。通过这种方式,循环层可以在处理序列数据时考虑到之前的信息。这也使得循环层可以捕获到序列数据中的时序信息,例如文本中的单词顺序或音频信号中的音调变化。

  • 在简单循环层中,每个神经元接收一个输入以及前一个时间步骤的隐藏状态,并输出一个隐藏状态。这个隐藏状态可以被送到下一个时间步骤使用。然而,简单循环层有一个问题,即梯度消失问题,即当序列很长时,网络的梯度会消失,导致网络无法学习到长期依赖关系。为了解决这个问题,LSTM和GRU被提出。
  • 在LSTM中,每个神经元有三个门:输入门、输出门和遗忘门。这些门可以控制信息的流动和保留。通过这种方式,LSTM可以更好地处理长序列,并避免梯度消失问题。
  • 在GRU中,每个神经元只有两个门:重置门和更新门。重置门控制之前的隐藏状态在当前时间步骤被忽略的程度,更新门控制之前隐藏状态对当前时间步骤的贡献。

TensorFlow 2中提供了多种循环层,包括SimpleRNN、LSTM和GRU等。这些循环层都是继承自Keras的Recurrent类的,因此它们的API接口和使用方法都非常相似。

在 TensorFlow 2 中,Embedding函数参数如下:

input_dim:整数,表示词汇表大小,即最大整数 index + 1。
output_dim:整数,表示嵌入向量的维度。
input_length:可选的整数列表,表示输入序列的长度,当它是固定的时使用。

LSTM和GRU函数参数如下:

units:整数,表示LSTM或GRU中隐藏单元的数量。
activation:激活函数,默认为tanh。
return_sequences:布尔值,表示是否返回所有时间步的输出序列。
return_state:布尔值,表示是否返回最后一个时间步的输出以及LSTM或GRU的状态。
recurrent_dropout:介于0和1之间的浮点数,表示循环层的丢失率。
dropout:介于0和1之间的浮点数,表示循环层中的丢失率。

下面是一个使用SimpleRNN层进行文本分类的例子:

import tensorflow as tf
from tensorflow.keras.layers import Dense, SimpleRNN, Embedding
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(input_dim=10000, output_dim=32))
model.add(SimpleRNN(32))
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])

model.fit(x_train, y_train, epochs=10, batch_size=128, validation_split=0.2)

在上面的例子中,我们首先使用Embedding层将每个单词映射成一个32维的向量,然后使用SimpleRNN层进行处理,最后使用Dense层输出分类结果。在模型编译时,我们指定了优化器、损失函数和评价指标,并在训练过程中使用了验证集来评估模型的性能。

除了SimpleRNN层外,我们还可以使用LSTM层和GRU层。这些层的使用方法和SimpleRNN层类似,只需要将SimpleRNN层替换成LSTM或GRU层即可。例如,使用LSTM层的文本分类代码如下所示:

import tensorflow as tf
from tensorflow.keras.layers import Dense, LSTM, Embedding
from tensorflow.keras.models import Sequential

model = Sequential()
model.add(Embedding(input_dim=10000, output_dim=32))
model.add(LSTM(32))
model.add(Dense(1, activation='sigmoid'))

model.compile(optimizer='rmsprop', loss='binary_crossentropy', metrics=['accuracy'])

model.fit(x_train, y_train, epochs=10, batch_size=128, validation_split=0.2)

6、注意力层

在深度学习中,注意力机制(Attention Mechanism)是一种常用的技术,它允许神经网络在处理序列数据时更加关注重要的部分,从而提高模型的性能。

具体来说,注意力机制通常用于处理序列数据,如自然语言处理中的文本序列、语音序列等。假设我们有一个序列模型,如循环神经网络(Recurrent Neural Network, RNN)或者转换器(Transformer),该模型将输入序列依次处理,并在每个时间步输出一个向量,表示当前输入与之前输入的关系。然而,当输入序列较长时,模型很难将所有信息都考虑进去,而注意力机制可以帮助模型关注与当前输出最相关的部分。

具体来说,注意力机制可以分为以下几个步骤:

  • 计算注意力权重(Attention Weights):在每个时间步,模型会计算当前输出与输入序列中每个元素之间的相似度,然后将这些相似度转化为权重。通常采用点积(DotProduct)、缩放点积(Scaled DotProduct)、累加性(Additive)等方式计算相似度,然后使用softmax函数将其转化为权重,使得所有权重之和为1。
  • 计算加权输入(Weighted Input):将输入序列中每个元素乘以其对应的权重,然后将它们加权求和,得到一个加权输入向量。
  • 计算上下文向量(Context Vector):将加权输入向量与当前输出向量进行拼接,然后经过一个线性变换得到上下文向量。
  • 计算输出向量(Output Vector):将上下文向量经过一个线性变换和非线性激活函数,得到最终的输出向量。

在 TensorFlow 2中,可以使用tf.keras.layers.Attention来实现注意力机制,它的主要参数如下:

query_shape: 查询张量的形状。它可以是一个整数或一个元组。如果是整数,则表示查询张量的最后一个维度的大小;如果是元组,则表示查询张量的形状。例如,如果查询张量的形状为(batch_size,seq_length,hidden_size),则可以将query_shape设置为(seq_length,hidden_size)。
key_shape: 键张量的形状。与query_shape类似,它可以是一个整数或一个元组。如果是整数,则表示键张量的最后一个维度的大小;如果是元组,则表示键张量的形状。
value_shape: 值张量的形状。与query_shape和key_shape类似,它可以是一个整数或一个元组。
use_scale: 是否使用缩放点积注意力。如果为True,则在计算注意力得分时将对注意力进行缩放,以防止得分过大。
dropout: 在注意力计算后应用的dropout率。
return_attention_scores: 是否返回注意力得分。如果为True,则返回一个元组,其中包含注意力张量和注意力得分张量。

注意力层接收三个输入:query,key和value,输出为加权和的结果,其中权重是由query和key之间的相似度计算得出的。
例如:

import tensorflow as tf

# 定义query, key和value
query = tf.keras.Input(shape=(None, 512))
key = tf.keras.Input(shape=(None, 512))
value = tf.keras.Input(shape=(None, 512))

# 定义注意力层
attention = tf.keras.layers.Attention()([query, key, value])

# 定义模型
model = tf.keras.Model(inputs=[query, key, value], outputs=attention)

# 打印模型概述
model.summary()

当然,你也可以自己搭建注意力网络层。例如:

import tensorflow as tf

class Attention(tf.keras.layers.Layer):
    def __init__(self, units):
        super(Attention, self).__init__()
        self.W1 = tf.keras.layers.Dense(units)
        self.W2 = tf.keras.layers.Dense(units)
        self.V = tf.keras.layers.Dense(1)

    def call(self, features, hidden):
        hidden_with_time_axis = tf.expand_dims(hidden, 1)
        score = tf.nn.tanh(self.W1(features) + self.W2(hidden_with_time_axis))
        attention_weights = tf.nn.softmax(self.V(score), axis=1)
        context_vector = attention_weights * features
        context_vector = tf.reduce_sum(context_vector, axis=1)
        return context_vector, attention_weights

在上述代码中,我们定义了一个名为Attention的自定义层,其初始化函数定义了三个Dense层,每个层使用相同的单元数units。在调用函数中,我们使用输入张量features和隐藏状态hidden来计算注意力得分score,注意力权重attention_weights,以及上下文向量context_vector。最后,我们返回上下文向量和注意力权重。

注意,上述代码只是一个简单的例子,实际中根据需要可能需要对其进行修改和扩展。

下一篇将深刨浅析由上述层级结构组成的各类神经网络,记得三连哦~

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值