大家好,我是Petter Guo
一位热爱探索
的全栈工程师
。在这里,我将用最接地气
的方式,带你玩转前端
、后端
到 DevOps
的硬核技术,解锁AI
,助你打通技术任督二脉,成为真正的全能玩家
!!
如果对你有帮助, 请点赞
+ 收藏
+关注
鼓励下, 学习公众号为 全栈派森
。
**卷积神经网络(Convolutional Neural Network,简称 CNN)**作为深度学习领域的核心技术,在计算机视觉、语音识别、自然语言处理等众多领域发挥着关键作用。接下来,我们将围绕 CNN 的核心结构、各层原理与功能、实际应用、性能优化以及常见大型模型,进行全面且深入的探讨,并结合示意图辅助理解。
卷积网络的结构
CNN 主要由卷积层、池化层、全连接层和激活函数层构成,各层相互协作,实现数据从输入到输出的高效处理与任务决策。
- 网络左边仍然是数据输入部分,对数据做一些初始处理, 如标准化,图片压缩,降维等工作, 最后输入数据集的形状为(样本,图像高度,图像宽度,颜色深度)。
- 中间是卷积层,这一层中, 也有激活函数的存在, 示例中用的是ReLU。
- 一般卷积层之后接一个池化层, 池化层包括区域平均池化或最大池化(对数据进行下采样,降低数据维度)。
- 通常卷积+池化的架构会重复几次,形成深度卷积网络。
- 之后是一个展平层, 用于将网络展平。
- 展平之后接一个普通的全连接层。
- 最右边的输出层也是全连接层, 用Softmax进行激活分类输出层, 这与普通神经网络的做法一致。
- 在编译网络时, 使用Adam优化器, 以分类交叉熵作为损失函数,采用了准确率作为评估指标。
卷积层的原理
卷积网络是通过卷积层(Conv2D层)中的过滤器(filter)用卷积计算对图像核心特征进行抽取, 从而提高图像处理的效率和准确率。
-
机器通过 “模式” 进行图像识别
聚焦于“小模式”, 只要两只手,两只脚,中间的躯干这些小模式还在, 就会增加相应的权重。
-
平移不变的模式识别
卷积神经网络在图像中学习到某个模式, 就可以在任何地方识别这个模式, 不管模式是出现在左上角, 中间, 还是右下角。
-
用滑动窗口抽取局部特征
通过滑动窗口一帧一帧的抽取局部特征,也就是一块一块的抠图实现。直观地说,这个分解过程,就像是人眼在对物体进行从左到右, 从上到下, 火眼金睛般地审视。
-
过滤器和响应通道
要与过滤器进行卷积计算,进行图像特征的提取(也就是模式的识别)。
卷积网络中的过滤器也叫卷积核, 是卷积层中带着一组组固定权重的神经元。 -
对特征图进行卷积运算
具体运算规则就是卷积核与抠下来的数据窗口子区域的对应元素相乘后求和, 求得两个矩阵的内积。
-
模式层级结构的形成
经过这样的卷积过程, 卷积神经网络就可以逐渐学到模式的空间层级结构。
层数越深,特征图将越来越抽象,无关的信息则越来越少, 而关于目标的信息则越来越多。这样卷积神经网络就可以有效的学习到越来越抽象,越来越复杂的视觉概念。特征组合由浅入深, 彼此叠加, 产生良好的模式识别效果。
池化层的功能
池化层通常紧跟在卷积层之后,其主要功能是对数据进行下采样,降低数据维度。常见的池化方式有最大池化和平均池化。最大池化是在每个池化窗口中选取最大值作为输出,能够保留最显著的特征;平均池化则计算池化窗口内的平均值作为输出,对数据中的噪声有一定的平滑作用。
用卷积网络给狗狗图像分类
- 图像数据的读入 (kaggle平台数据集-stanford-dogs-dataset)
import numpy as np
import pandas as pd
import os
print(os.listdir("../input/stanford-dogs-dataset/images/Images"))
- 选取其中10个种类
dir = '../input/stanford-dogs-dataset/images/Images/'
chihuahua_dir = dir+'n02085620-Chihuahua' #吉娃娃
japanese_spaniel_dir = dir+'n02085782-Japanese_spaniel' #日本狆
maltese_dir = dir+'n02085936-Maltese_dog' #马尔济斯犬
pekinese_dir = dir+'n02086079-Pekinese' #狮子狗
shitzu_dir = dir+'n02086240-Shih-Tzu' #西施犬
blenheim_spaniel_dir = dir+'n02086646-Blenheim_spaniel' #英国可卡犬
papillon_dir = dir+'n02086910-papillon' #蝴蝶犬
toy_terrier_dir = dir+'n02087046-toy_terrier' #玩具猎狐梗
afghan_hound_dir = dir+'n02088094-Afghan_hound' #阿富汗猎犬
basset_dir = dir+'n02088238-basset' #巴吉度猎犬
使用OpenCV库中的图像文件读取和resize函数, 把全部图像转换成大小为150*150标准格式
import cv2
X = []
y_label = []
imgsize = 150
def training_data(label, data_dir):
print("正在读入:", data_dir)
for img in os.listdir(data_dir):
path = os.path.join(data_dir,img)
img = cv2.imread(path, cv2.IMREAD_COLOR)
img = cv2.resize(img, (imgsize, imgsize))
X.append(np.array(img))
y_label.append(str(label))
training_data('chihuahua',chihuahua_dir)
training_data('japanese_spaniel', japanese_spaniel_dir)
training_data('maltese', maltese_dir)
training_data('pekinese', pekinese_dir)
training_data('shitzu', shitzu_dir)
training_data('blenheim_spaniel', blenheim_spaniel_dir)
training_data('papillon', papillon_dir)
training_data('toy_terrier', toy_terrier_dir)
training_data('afghan_hound', afghan_hound_dir)
training_data('basset', basset_dir)
- 特征归一化
from sklearn.preprocessing import LabelEncoder # 编码工具
from tensorflow.keras.utils import to_categorical # 导入one-hot编码工具
label_encoder = LabelEncoder()
y = label_encoder.fit_transform(y_label) # 标签编码
y = to_categorical(y, 10) # 将标签转换为 one-hot 编码
X = np.array(X) # 将X从列表转换为张量数组
X = X/255 # 将X张量归一化
- 显示向量化后的图像
import matplotlib.pyplot as plt # 导入matplotlib库
import random as rdm # 导入随机数工具
fig, ax = plt.subplots(5, 2)
fig.set_size_inches(15, 15)
for i in range(5):
for j in range(2):
r = rdm.randint(0, len(X))
X[r] = X[r][...,::-1]
ax[i, j].imshow(X[r])
ax[i, j].set_title('Dog' + y_label[r])
plt.tight_layout()
- 拆分数据集
from sklearn.model_selection import train_test_split # 导入拆分工具
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2, random_state = 0)
- 构建卷积网络
from keras import layers
from keras import models
from tensorflow.keras import optimizers # 导入优化器
cnn = models.Sequential() # 序贯模型
cnn.add(layers.Conv2D(32, (3, 3), activation = 'relu', input_shape=(150, 150, 3)))
cnn.add(layers.MaxPooling2D((2, 2))) # 最大池化层
cnn.add(layers.Conv2D(64, (3, 3), activation='relu')) # 卷积层
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3), activation='relu')) # 卷积层
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Conv2D(128,(3,3), activation='relu')) # 卷积层
cnn.add(layers.MaxPooling2D((2,2))) # 最大池化层
cnn.add(layers.Flatten()) # 展平层
cnn.add(layers.Dense(512, activation='relu')) # 全连接层
cnn.add(layers.Dense(10, activation='softmax')) # 分类输出
cnn.compile(loss = 'categorical_crossentropy', optimizer='rmsprop', metrics=['acc']) # 损失函数, 优化器, 评估指标
- 参数说明
-
Conv2D
2D卷积层, 对平面图像进行卷积。
-
MaxPooling2D
最大池化层, 一般紧随卷积层出现, 通常采用2*2的窗口, 默认步幅为2。这是将特征图进行2倍下采样, 也就是高度特征减半。
-
Flatten
展平层, 将卷积操作的特征图展平后, 才能够输入全连接层进一步处理。
-
Dense 全连接层
第一个是普通层,用于计算权重, 确定分类, 用ReLU函数激活。
第二个负责输出分类结果,因为是多分类, 所以用Softmax函数激活
卷积网络性能优化
- 更新优化器并设置学习速率
cnn.compile(loss = 'categorical_crossentropy', optimizer=optimizers.Adam(learning_rate=1e-4), metrics=['acc']) # 损失函数, 优化器, 评估指标
- 添加Dropout层
- 进行数据增强
# 定义一个数据增强器, 并设定各种增强选项
from tensorflow.keras.preprocessing.image import ImageDataGenerator
augs_gen = ImageDataGenerator(
featurewise_center = False,
samplewise_center = False,
featurewise_std_normalization = False,
samplewise_std_normalization = False,
zca_whitening = False,
rotation_range = 10,
zoom_range = 0.1,
width_shift_range = 0.2,
height_shift_range = 0.2,
horizontal_flip = True,
vertical_flip = False
)
augs_gen.fit(X_train) # 针对训练集拟合数据增强
各种大型卷积网络模型
-
经典的VGGNet
VGGNet 以其简洁的网络结构和良好的性能而闻名。它主要由多个 3×3 的小卷积核堆叠而成,通过增加网络的深度来提高网络的表达能力。
- 结构简介。由5层卷积层, 3层全连接层, 1层Softmax输出层构成, 层与层之间使用最大化池层分开, 所有隐层的激活单元都采用ReLU函数。
- 小卷积核核多卷积子层。
- 小池化核。
- 通道数多。
- 层数更深, 特征图更宽。
-
采用Inception结构的GoogleNet
AlexNet, VGGNet等结构都是通过单纯增加网络的深度(层数)来提高准确率, 由此带来的副作用, 包括过拟合,梯度消失,梯度爆炸等。 Inception通过模块串联来更高效地利用计算资源, 在相同的计算量下能提取到更多的特征, 从而提升训练的结果。
-
残差网络ResNet
VGGNet和GoogleNet都说明了一个道理: 足够的深度是神经网络模型表现得更良好的前提, 在网络达到一定深度之后,简单的堆叠网络反而使效果变差了, 这是由于梯度消失和过拟合造成的。
残差网络增加了一个恒等映射(identity mapping), 把当前输出直接传输到下一层网络, 跳过本层运算, 同时在反向传播过程中, 也是将下一层网络的梯度直接传递给上一层网络, 这样就解决了深层网络的梯度消失问题。 -
总结
这些大型卷积网络学到的知识是可迁移的。也就是说, 一个训练好的大型卷积网络可以迁移到我们自己的新模型上, 来解决我们的问题。