文章目录
专业英语
英文 | 中文 |
---|---|
CNN(Convolutional Neural Network) | 卷积神经网络 |
RNN(Recurrent Neural Network) | 循环神经网络 |
DNN(Deep Neural Network) | 深度神经网络 |
ANN(Artificial Neural Network) | 人工神经网络 |
GNN(Graph Neural Network) | 图神经网络 |
BRNN(Bi-directional Recurrent Neural Network) | 双向循环神经网络 |
GAN(Generative Adversarial Network) | 生成式对抗网络 |
MTL(Multi-Task-Learning) | 多任务学习 |
LSTM(Long Short-Term Memory) | 长短期记忆网络 |
SVM(Support Vector Machine) | 支持向量机 |
KNN(K-NearestNeighbor) | K最近邻分类算法 |
NBM(Naive Bayesian Model) | 朴素贝叶斯模型 |
… | … |
四维张量
四维张量在卷积神经网络中应用非常广泛,它用于保存特征图(Feature maps)数据,格式一般定义为: [b, h, w, c]。
其中b表示输入样本的数量,h/w分别表示特征图的高/宽,c表示特征图的通道数。
图片数据是特征图的一种,对于含有 RGB 3 个通道的彩色图片,每张图片包含了h行w列像素点,每个点需要 3 个数值表示 RGB 通道的颜色强度,因此一张图片可以表示为[h, w, 3]。如下图所示,最上层的图片表示原图,它包含了下面 3 个通道的强度信息。
神经网络中一般并行计算多个输入以提高计算效率,故b张图片的张量可表示为[b, h, w, 3],例如:
In [48]:
# 创建32x32的“彩色”图片输入,个数为4
x = tf.random.normal([4,32,32,3]) #输入矩阵: batch=4,32*32,3通道
# 创建卷积神经网络
layer = layers.Conv2D(16,kernel_size=3) #16个卷积核,3*3
out = layer(x) # 前向计算
out.shape # 输出大小
Out[48]: TensorShape([4, 30, 30, 16]) #输出矩阵: batch=4,30*30,16个输出矩阵
其中卷积核张量也是 4 维张量,可以通过 kernel 成员变量访问:
In [49]:layer.kernel.shape # 访问卷积核张量
Out[49]: TensorShape([3, 3, 3, 16]) #3*3,3通道,16个卷积核
卷积神经网络(CNN)初步
-
共享权值的“局部连接层”网络,就是卷积神经网络。
-
卷积神经网络通过充分利用
局部相关性
和权值共享
的思想,大大地减少了网络的参数量,从而提高训练效率,更容易实现超大规模的深层网络。 -
输入通道的通道数量决定了卷积核的通道数。一个卷积核只能得到一个输出矩阵,无论输入𝑿的通道数量。
-
卷积运算的“最终输出矩阵大小”由卷积核的大小k,输入𝑿的高宽h/w,移动步长s,是否填充等因素共同决定
-
feature map = 卷积后的特征数量 = 卷积层的输出维度 = 用了几个卷积核(就有几个输出矩阵) = 提取出图片的特征数量
-
解释“最终输出”: 使用卷积核处理图片后, 所总结出的图片“特征”。用更多的卷积核来处理图片,就会得到更多的图片特征
注: 一个卷积核可以有多个通道
感受野
如下图所示,以实心网格所在的像素为参考点,它周边欧式距离小于或等于 𝑘/√2 的像素点以矩形网格表示,网格内的像素点重要性较高,网格外的像素点较低。这个高宽为𝑘的窗口称为感受野(Receptive Field)
,它表征了每个像素对于中心像素的重要性分布情况,网格内的像素才会被考虑,网格外的像素对于中心像素会被简单地忽略
。
这种基于距离的重要性分布假设特性称为局部相关性
,它只关注和自己距离较近的部分节点,而忽略距离较远的节点。在这种重要性分布假设下,全连接层的连接模式变成了如下图所示,输出节点𝑗只与以𝑗为中心的局部区域(感受野)相连接,与其它像素无连接
:
权值共享
能否再将参数量进一步减少,比如只需要𝑘 * 𝑘个参数即可完成当前层的计算? 答案是肯定的,通过权值共享的思想,对于每个输出节点,均使用相同的权值矩阵𝑾,那么无论输出节点的数量是多少,网络层的参数量总是𝑘 * 𝑘
。如下面图片所示,在计算左上角位置的输出像素时,使用权值矩阵:
与对应感受野内部的像素相乘累加,作为左上角像素的输出值; 在计算右下方感受野区域时,共享权值参数𝑾,即使用相同的权值参数𝑾相乘累加,得到右下角像素的输出值,此时网络层的参数量只有3 * 3 = 9个,且与输入、输出节点数无关。
总结句: 共享权值矩阵的“局部连接层”网络就是卷积神经网络。
单通道输入和单卷积核
首先讨论单通道输入c=1,如灰度图片只有灰度值一个通道,单个卷积核c=1的情况。以输入𝑿为 5 * 5 的矩阵,卷积核为3 * 3的矩阵为例,如下图所示。与卷积核同大小的感受野(输入𝑿上方的绿色方框)
首先移动至输入𝑿最左上方,选中输入𝑿上3 * 3 的感受野元素,与卷积核(图片中间3 * 3方框)对应元素相乘:
⨀符号表示哈达马积(Hadamard Product),即矩阵的对应元素相乘。运算后得到3*3的矩阵,这9个数值全部-1-1+0-1+2+6+0-2+4=标量7,写入输出矩阵第一行、第一列的位置,如下图:
完成第一个感受野区域的特征提取后,感受野窗口向右移动一个步长单位(Strides,记为𝑠,默认为 1),选中下图中绿色方框中的 9 个感受野元素,按照同样的计算方法, 与卷积核对应元素相乘累加,得到输出 10,写入第一行、第二列位置:
感受野窗口再次向右移动一个步长单位,选中下图中绿色方框中的元素,并与卷积核相乘累加,得到输出 3,并写入输出的第一行、第三列位置,如下图所示:
此时感受野已经移动至输入𝑿的有效像素的最右边,无法向右边继续移动(在不填充无效元素的情况下),因此感受野窗口向下移动一个步长单位(𝑠 = 1),并回到当前行的行首位置,继续选中新的感受野元素区域,如下图所示,与卷积核运算得到输出-1。此时的感受野由于经过向下移动一个步长单位,因此输出值-1 写入第二行、第一列位置:
按照上述方法,每次感受野向右移动𝑠 = 1个步长单位,若超出输入边界,则向下移动 𝑠 = 1个步长单位,并回到行首,直到感受野移动至最右边、最下方位置,如下图所示。每次选中的感受野区域元素,和卷积核对应元素相乘累加,并写入输出的对应位置。 最终输出我们得到一个3×3的矩阵,比输入5 × 5略小,这是因为感受野不能超出元素边界的缘故。可以观察到,卷积运算的输出矩阵大小由卷积核的大小𝑘,输入𝑿的高宽h/w,移动步长s,是否填充等因素共同决定。这里为了演示计算过程,预绘制了一个与输入等大小的网格,并不表示输出高宽为 5×5 ,这里的实际输出高宽只有3×3:
多通道输入和单卷积核
多通道输入的卷积层更为常见,比如彩色的图片包含了 R/G/B 三个通道,每个通道上面的像素值表示 R/G/B 色彩的强度
。下面我们以 3 通道输入、单个卷积核为例,将单通道输入的卷积运算方法推广到多通道的情况。
如下图所示,每行的最左边5 * 5的矩阵表示输入𝑿的 1 ~ 3 通道,第 2 列的3 * 3矩阵分别表示卷积核的 1 ~ 3 通道,第 3 列的矩阵表示当前通道上运算结果的中间矩阵,最右边一个矩阵表示卷积层运算的最终输出:
随后,感受野窗口同步在𝑿的每个通道上向右移动 s = 1 个步长单位,此时感受野区域元素如下图 10.18 所示,每个通道上面的感受野与卷积核对应通道上面的矩阵相乘累加, 得到中间变量 10、20、20,全部相加得到输出 50,写入第一行、第二列元素位置:
以此方式同步移动感受野窗口,直至最右边、最下方位置,此时全部完成输入和卷积核的卷积运算,得到3 * 3的输出矩阵,如图 10.19 所示:
多通道输入、多卷积核
概念
多通道输入、多卷积核是卷积神经网络中最为常见的形式,前面我们已经介绍了单卷积核的运算过程,每个卷积核和输入𝑿做卷积运算,得到一个输出矩阵。当出现多卷积时,第𝑖 (𝑖 ∈ 𝑛 ,𝑛为卷积核个数)个卷积核与输入𝑿运算得到第𝑖个输出矩阵(也称为输出张量𝑶的通道𝑖),最后全部的输出矩阵在通道维度上进行拼接(Stack 操作,创建输出通道数的新维度),产生输出张量𝑶,𝑶包含了𝑛个通道数
。
有几个卷积核,就有几个输出矩阵O;一个卷积核只能得到一个输出矩阵。每个卷积核可以有多个通道,卷积核的通道数等于输入矩阵X的通道数。
以 3 通道输入、2 个卷积核的卷积层为例。第一个卷积核与输入𝑿运算得到输出𝑶的第一个通道,第二个卷积核与输入𝑿运算得到输出𝑶的第二个通道,如下图 10.21 所示,输出的两个通道拼接在一起形成了最终输出𝑶。每个卷积核的大小𝑘、步长𝑠、填充设定等都是统一设置,这样才能保证输出的每个通道大小一致,从而满足拼接的条件。
注1: feature map = 卷积后的特征数量 = 卷积层的输出维度 = 用了几个卷积核(就有几个输出矩阵,这些输出矩阵会拼接在一起)
注2: 解释“最终输出”: 使用卷积核处理图片后, 所总结出的图片“特征”。用更多的卷积核来处理图片,就会得到更多的图片特征。每个输出矩阵就是一个特征。
注3: 一个卷积核可以有多个通道,但是每个卷积核只能产出一个输出矩阵
实例(视频)
下面视频清晰版: 链接: https://pan.baidu.com/s/1T7Q1SRG4mMhXmjHOKrh6sA?pwd=gf79 提取码: gf79
CVNLP基础文章9多维输入多维卷积核
下面视频清晰版: 链接: https://pan.baidu.com/s/1MI1CR4PElWhtOji1w4B2gw?pwd=g8a6 提取码: g8a6
CVNLP基础文章9多维输入多维卷积核重要解读
步长(Strides)
在卷积运算中,如何控制感受野布置的密度呢? 对于信息密度较大的输入,如物体数量很多的图片,为了尽可能的少漏掉有用信息,在网络设计的时候希望能够较密集地布置感受野窗口; 对于信息密度较小的输入,比如全是海洋的图片,可以适量的减少感受野窗口的数量。 感受野密度的控制手段一般是通过移动步长(Strides)
实现的。步长是指感受野窗口每次移动的长度单位,对于 2D 输入来说,分为沿𝑥(向右)方向和 𝑦(向下)方向的移动长度。 为了简化讨论,这里只考虑𝑥/𝑦方向移动步长相同的情况,这也是神经网络中最常见的设定。如下图 10.22 所示,绿色实线代表的感受野窗口的位置是当前位置,绿色虚线代表是上一次感受野所在位置,从上一次位置移动到当前位置的移动长度即是步长的定义。图 10.22 中感受野沿𝑥方向的步长为 2,表达为步长𝑠 = 2。
循环往复移动,直至达到最下方、最右边边缘位置,如图 10.24 所示,最终卷积层输出的高宽只有2 * 2。对比前面 s = 1 的情形,输出高宽由3 * 3降低为2 * 2,感受野的数量 减少为仅 4 个。
可以看到,通过设定步长s,可以有效地控制信息密度的提取。当步长设计的较小时, 感受野以较小幅度移动窗口,有利于提取到更多的特征信息,输出张量的尺寸也更大; 当步长设计的较大时,感受野以较大幅度移动窗口,有利于减少计算代价,过滤冗余信息,输出张量的尺寸也更小。
填充(Padding)
经过卷积运算后的输出𝑶的高宽一般会小于输入𝑿的高宽,即使是步长s = 1时,输出𝑶 的高宽也会略小于输入𝑿高宽。在网络模型设计时,有时希望输出𝑶的高宽能够与输入𝑿的高宽相同,从而方便网络参数的设计、残差连接等。为了让输出𝑶的高宽能够与输入𝑿的相等,一般通过在原输入𝑿的高和宽维度上面进行填充(Padding)若干无效元素操作,得到增大的输入𝑿′。 通过精心设计填充单元的数量,在𝑿′上面进行卷积运算得到输出𝑶的高宽可以和原输入𝑿相等,甚至更大。
如下图 10.25 所示,在高/行方向的上(Top)、下(Bottom)方向,宽/列方向的左(Left)、 右(Right)均可以进行不定数量的填充操作,填充的数值一般默认为 0,也可以填充自定义的数据。图 10.25 中上、下方向各填充 1 行,左、右方向各填充 2 列,得到新的输入𝑿′。
那么添加填充后的卷积层怎么运算呢? 同样的方法,仅仅是把参与运算的输入从𝑿换成了填充后得到的新张量𝑿′。如下图 10.26 所示,感受野的初始位置在填充后的𝑿′的左上方,完成相乘累加运算,得到输出 1,写入输出张量的对应位置。
移动步长 s = 1 个单位,重复运算逻辑,得到输出0,如图10.27所示。
通过精心设计的Padding方案,即上下左右各填充一个单位,记为 p = 1,我们可以得到输出𝑶和输入𝑿的高、宽相等的结果。
CNN引入(视频)
下面视频清晰版: 链接: https://pan.baidu.com/s/1VWFE4–dksPsjgK_bnHAFg?pwd=vx9j 提取码: vx9j
CVNLP基础文章9引入CNN视频
卷积层的实现
自定义权值(tf.nn.conv2d)
重要补充
当步长s>1时,设置padding=‘same’,将使得输出高、宽成1/s倍减少。高宽先padding成可以整除s的最小整数,再除以步长:
例1:
例2:
例3:
例4:
例5:
卷积层类(layers.Conv2D)
注1: padding若等于“valid”,就是不padding
注2: 上面的例子,使用layer.kernel可以看“权重”参数;使用layer.bias可以看“偏置”参数
LeNet-5
概念
下图是 LeNet-5 的网络结构图,它接受32 × 32大小的数字、字符图片,经过第一个卷积层(6个卷积核,5 * 5)得到 [b, 28, 28, 6] 形状的张量,经过一个向下采样层,张量尺寸缩小到[b, 14, 14, 6] ,经过第二个卷积层(16个卷积核,5 * 5),得到[b, 10, 10, 16]形状的张量,同样经过下采样层,张量尺寸缩小到[b, 5, 5, 16],在进入全连接层之前,先将张量打平成[b, 400] 的张量,送入输出节点数分别为 120、84 的 2 个全连接层,得到 [b, 84] 的张量,最后通过 Gaussian connections 层。
现在看来,LeNet-5 网络层数较少(2 个卷积层和 2 个全连接层),参数量较少,计算代价较低,尤其在现代 GPU 的加持下,数分钟即可训练好 LeNet-5 网络。
在 LeNet-5 的基础上进行了少许调整,使得它更容易在现代深度学习框架上实现。首先我们将输入𝑿形状由32 × 32调整为28 × 28,然后将 2 个下采样层实现为最大池化层(降低特征图的高、宽),最后利用全连接层替换掉 Gaussian connections 层。下文统一称修改的网络也为 LeNet-5 网络。网络结构图如下图所示:
模型测试代码,运行截图:
可以看到,卷积层的参数量非常少,主要的参数量集中在全连接层。由于卷积层将输入特征维度降低很多,从而使得全连接层的参数量不至于过大,整个模型的参数量约 6万,因此通过卷积神经网络可以显著降低网络参数量,同时增加网络深度。
代码实践
我们基于 MNIST 手写数字图片数据集训练 修改过的LeNet-5 网络,并测试其最终准确度。这里是修改过的LeNet -5网络,输入X的形状为: [4, 28, 28, 1]。卷积核的size为3 * 3
导入各种包、获取mnist数据
import os
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, optimizers, datasets, Sequential
from __future__ import division
(x, y), (x_val, y_val) = datasets.mnist.load_data()
x.shape, y.shape
运行截图:
处理数据
def preprocessing(xdata, ydata):
xdata = tf.cast(xdata, dtype=tf.float32)/255
ydata = tf.cast(ydata, dtype=tf.int32)
return xdata, ydata
db_train = tf.data.Dataset.from_tensor_slices((x, y))
db_test = tf.data.Dataset.from_tensor_slices((x_val, y_val))
db_train = db_train.map(preprocessing).batch(100) #每组100张图片,共600组
db_test = db_test.map(preprocessing).batch(100) #每组100张图片,共100组
运行截图:
搭建模型
model = keras.Sequential([ #网络容器
layers.Conv2D(6, kernel_size=3, strides=1), # 第一个卷积层,6个5*5卷积核
layers.MaxPool2D(pool_size=2, strides=2), # 高宽各减半的池化层
layers.ReLU(), # 激活函数
layers.Conv2D(16, kernel_size=3, strides=1), # 第二个卷积层,16个5*5卷积核
layers.MaxPool2D(pool_size=2, strides=2), # 高宽各减半的池化层
layers.ReLU(), # 激活函数
layers.Flatten(), # 打平层,方便全连接层处理
layers.Dense(120, activation='relu'), # 全连接层,120个节点
layers.Dense(84, activation='relu'), # 全连接层,84个节点
layers.Dense(10, activation='relu') # 全连接层,10个节点
])
optimizer = optimizers.Adam(learning_rate=0.0001)
model.build(input_shape=(4,28,28,1))
model.summary()
运行截图:
使用“训练数据集”训练模型过程及每轮计算准确率
注意1,处理输入数据时为: x = tf.expand_dims(x, axis=3);
注意2,计算准确率,使用argmax和cast时,指定axis=-1;
for epoch in range(30):
print(f"当前是 第 {epoch} 次 大循环")
for step, (x, y) in enumerate(db_train):
with tf.GradientTape() as tape:
#x = tf.reshape(x, (-1, 784))
x = tf.expand_dims(x, axis=3)
y = tf.one_hot(y, depth=10)
out = model(x)
loss_ce = tf.losses.categorical_crossentropy(y, out, from_logits=True)
loss_ce = tf.reduce_mean(loss_ce)
grads = tape.gradient(loss_ce, model.trainable_variables)
optimizer.apply_gradients(zip(grads, model.trainable_variables))
if step%100==0:
print(f"epoch: {epoch}; step: {step}; loss: {loss_ce.numpy()}")
total_correct = 0
total_number = 0
for (xval, yval) in db_test:
#xval = tf.reshape(xval, (-1, 784))
xval = tf.expand_dims(xval, axis=3)
logits_test = model(xval)
probability = tf.nn.softmax(logits_test, axis=-1)
predict = tf.argmax(probability, axis=-1)
predict = tf.cast(predict, dtype=tf.int32)
bool_correct = tf.equal(predict, yval)
correct_number = tf.cast(bool_correct, dtype=tf.int32)
correct_number = tf.reduce_sum(correct_number)
total_correct += correct_number
total_number += yval.shape[0]
print(f"正确个数为: {total_correct}; 当前总图片数为: {total_number}")
print(f"本轮准确率为: {total_correct / total_number}")
print('-----------------------')
部分运行截图:
表示学习
复杂的卷积神经网络模型也是基于卷积层的堆叠
构成的。网络层数越深,模型的表达能力越强
。
2014 年,Matthew D. Zeiler 等人尝试利用可视化的方法去理解卷积神经网络到底学到了什么。通过将每层的特征图利用“反卷积”网络(Deconvolutional Network)映射回输入图片,即可查看学到的特征分布,如下图所示。可以观察到,第二层的特征对应到边、角、色彩等底层图像提取;第三层开始捕获到纹理这些中层特征;第四、五层呈现了 物体的部分特征,如小狗的脸部、鸟类的脚部等高层特征。通过这些可视化的手段,我们可以一定程度上感受卷积神经网络的特征学习过程。
图片数据的识别过程一般认为也是表示学习(Representation Learning)的过程,从接受到的原始像素特征开始,逐渐提取边缘、角点等底层特征,再到纹理等中层特征,再到头部、物体部件等高层特征,最后的网络层基于这些学习到的抽象特征表示(Representation)做分类逻辑的学习
。学习到的特征越高层、越准确,就越有利于分类器的分类
,从而获得较好的性能。从表示学习的角度来理解,卷积神经网络通过层层堆叠来逐层提取特征
,网络训练的过程可以看成特征的学习过程,基于学习到的高层抽象特征可以方便地进行分类任务。
应用表示学习的思想,训练好的卷积神经网络往往能够学习到较好的特征,这种特征的提取方法一般是通用的。比如在猫、狗任务上学习到头、脚、身躯等特征的表示,在其它动物上也能够一定程度上使用。基于这种思想,可以将在任务 A 上训练好的深层神经网络的前面数个特征提取层迁移到任务 B 上,只需要训练任务 B 的分类逻辑(表现为网络的最末数层),即可取得非常好的效果,这种方式是迁移学习的一种,从神经网络角度也称为网络微调(Fine-tuning)。