图像分类概述
- 图像分类实质上就是从给定的类别集合中为图像分配对应标签的任务,当我们输入一张图片,返回一个该图像类别的标签。
- 限制条件:只能输入单目标图像。
- 常用数据集:mnist、CIFAR-10、CIFAR-100、ImageNet
图像分类算法
这里介绍四种。
-
LeNet-5
-
意义:LeNet-5是最早的卷积神经网络,是其他深度学习模型的基础, 主要用来做手写数字识别。
-
结构:2个5x5卷积层 + 2个全连接层+ 输出层

-
输入:32x32
-
输出:10分类
-
tensorflow实现:
import numpy as np import matplotlib.pyplot as plt import tensorflow as tf from tensorflow.keras import layers, models, utils, optimizers, losses from tensorflow.keras.datasets import mnist# 1 数据获取 (X_train, y_train), (X_test, y_test) = mnist.load_data() print('训练集大小:', X_train.shape, '测试集大小:', X_test.shape)# 2数据处理 # 添加通道数 X_train = tf.expand_dims(X_train, 3) X_test = tf.expand_dims(X_test, 3) # 3模型搭建 net = models.Sequential([# 卷积层1layers.Conv2D(filters=6, kernel_size=5, activation='sigmoid', input_shape=(28, 28, 1)),# 池化层1layers.MaxPool2D(pool_size=2),# 卷积层2layers.Conv2D(filters=16, kernel_size=5, activation='sigmoid'),# 池化层2layers.MaxPool2D(pool_size=2),# 更改维度layers.Flatten(),# 全连接层layers.Dense(120, activation='sigmoid'),layers.Dense(84, activation='sigmoid'),layers.Dense(10, activation='softmax')], name='LeNet-5') # 4模型编译和训练 sgd = optimizers.SGD(learning_rate=0.9, momentum=0.5) net.compile(optimizer=sgd, loss='SparseCategoricalCrossentropy', metrics=['accuracy']) # history = net.fit(X_train, y_train, epochs=100, batch_size=128) # 5模型评估和预测 history = net.fit(X_train, y_train, validation_data=(X_test[:100], y_test[:100]), epochs=80, batch_size=128) # 评估 loss, accuracy = net.evaluate(X_test[101:], y_test[101:], verbose=True)
-
-
AlexNet
-
意义:开山之作,证明学习到的特征可以超越手工设计的特征,打破了cv的研究方向
-
网络特点:

-
8层网络:5个卷积层+2个全连接层+1个输出层
其中使用了1个11x11、1个5x5和3个3x3的卷积核
-
-
输入:227x227的图像
-
输出:1000分类
-
Tensor flow 实现:
import tensorflow as tf from tensorflow.keras import models, layers import numpy as npclass MyModel(models.Model):def __init__(self):super(MyModel, self).__init__()self.conv1 = layers.Conv2D(96, kernel_size=11, strides=4, activation='relu')self.maxpool1 = layers.MaxPool2D(pool_size=3, strides=2)self.conv2 = layers.Conv2D(256, kernel_size=5, padding='same', activation='relu')self.maxpool2 = layers.MaxPool2D(pool_size=3, strides=2)self.conv3 = layers.Conv2D(384, kernel_size=3, padding='same', activation='relu')self.conv4 = layers.Conv2D(384, kernel_size=3, padding='same', activation='relu')self.conv5 = layers.Conv2D(256, kernel_size=3, padding='same', activation='relu')self.maxpool3 = layers.MaxPool2D(pool_size=3, strides=2)self.flat = layers.Flatten()self.fc1 = layers.Dense(4096, activation='relu')self.dropout1 = layers.Dropout(0.5)self.fc2 = layers.Dense(4096, activation='relu')self.dropout2 = layers.Dropout(0.5)self.out = layers.Dense(1000, activation='softmax')def call(self, inputs):x = self.conv1(inputs)x = self.maxpool1(x)x = self.conv2(x)x = self.maxpool2(x)x = self.conv3(x)x = self.conv4(x)x = self.conv5(x)x = self.maxpool3(x)x = self.flat(x)x = self.fc1(x)x = self.fc2(x)x = self.out(x)return xmodel = MyModel() input = tf.random.uniform((1, 227, 227, 1)) y = model(input) print(model.summary())
-
-
VGG-16
-
意义:2014年牛津大学计算机视觉组和Google一起研发的新的神经网络,取得了ILSVR2014年比赛分类项目的第二名,主要贡献是使用很小的卷积核构建卷积神经网络结构,展现出深度是算法优良性能的关键部分。
-
网络架构:

VGG使用的基本结构是vgg块,一共有5个卷积块,前2块使用两个卷积层、而后3块使用三个卷积层,第一个输出通道是64,之后每次对输出通道数翻倍,知道变为512
-
输入:224x224
-
输出:1000分类,也是在ImageNet中训练的
-
vgg块的实现:
# 定义VGG网络中的卷积块:卷积层的个数,卷积层中卷积核的个数 def vgg_block(num_convs, num_filters):# 构建序列模型blk = tf.keras.models.Sequential()# 遍历所有的卷积层for _ in range(num_convs):# 每个卷积层:num_filter个卷积核,卷积核大小为3*3,padding是same,激活函数是relublk.add(tf.keras.layers.Conv2D(num_filters,kernel_size=3,padding='same',activation='relu'))# 卷积块最后是一个最大池化,窗口大小为2*2,步长为2blk.add(tf.keras.layers.MaxPool2D(pool_size=2, strides=2))return blk -
-
GoogLeNet
-
意义:这里应该注意不是googleNet,而是GooLeNet,是为了致敬LeNet。GoogLeNet和AlexNet/VGGNet这类依靠加深网络结构的深度的思想不完全一样。GoogLeNet在加深度的同时做了结构上的创新,引入了一个叫做Inception的结构来代替之前的卷积加激活的经典组件。GoogLeNet在ImageNet分类比赛上的Top-5错误率降低到了6.7%。
-
网络结构:
-
inception块:

这里大量使用了1x1的卷积,主要作用是实现跨通道的语义信息融合和降维减少参数量。
实现:
class Inception(tf.keras.layers.Layer):# 输入参数为各个卷积的卷积核个数def __init__(self, c1, c2, c3, c4):super().__init__()# 线路1:1 x 1卷积层,激活函数是RELU,padding是sameself.p1_1 = tf.keras.layers.Conv2D(c1, kernel_size=1, activation='relu', padding='same')# 线路2,1 x 1卷积层后接3 x 3卷积层,激活函数是RELU,padding是sameself.p2_1 = tf.keras.layers.Conv2D(c2[0], kernel_size=1, padding='same', activation='relu')self.p2_2 = tf.keras.layers.Conv2D(c2[1], kernel_size=3, padding='same',activation='relu')# 线路3,1 x 1卷积层后接5 x 5卷积层,激活函数是RELU,padding是sameself.p3_1 = tf.keras.layers.Conv2D(c3[0], kernel_size=1, padding='same', activation='relu')self.p3_2 = tf.keras.layers.Conv2D(c3[1], kernel_size=5, padding='same',activation='relu')# 线路4,3 x 3最大池化层后接1 x 1卷积层,激活函数是RELU,padding是sameself.p4_1 = tf.keras.layers.MaxPool2D(pool_size=3, padding='same', strides=1)self.p4_2 = tf.keras.layers.Conv2D(c4, kernel_size=1, padding='same', activation='relu')# 完成前向传播过程def call(self, x):# 线路1p1 = self.p1_1(x)# 线路2p2 = self.p2_2(self.p2_1(x))# 线路3p3 = self.p3_2(self.p3_1(x))# 线路4p4 = self.p4_2(self.p4_1(x))# 在通道维上concat输出outputs = tf.concat([p1, p2, p3, p4], axis=-1)return outputs-
GoogLeNet:
共分为5个模块:
-
B1模块:64个 7x7卷积层
-
B2模块:64个1x1卷积层 + 1个3x3卷积层+1个3x3池化层
-
B3模块:2个Inception模块 + 3x3最大池化
-
B4模块:5个Inception + 3x3最大池化,还有两个辅助输出类,防止网络太深过拟合
-
B5模块:2个Inception + GAP全局平均池化 + 全连接层输出分类结果
-
-
-
-
ResNet
在这之前,所有研究都倾向于通过加深网络来提高网络的精度,大家一致认为网络越深,获取的信息就越多,特征就越丰富。但在实践中,随着网络的加深,优化效果反而越差,测试数据和训练数据的准确率反而降低了。
-
意义:提出了残差连接结构,解决了网络退化的问题
-
网络结构:
-

残差块:

一共是两种残差块,右边的残差块中1x1的卷积,用来改变输入图像的通道数
残差块实现:
```python# ResNet网络中模块的构成class ResnetBlock(tf.keras.layers.Layer):# 网络层的定义:输出通道数(卷积核个数),模块中包含的残差块个数,是否为第一个模块def __init__(self,num_channels, num_residuals, first_block=False):super(ResnetBlock, self).__init__()# 模块中的网络层self.listLayers=[]# 遍历模块中所有的层for i in range(num_residuals):# 若为第一个残差块并且不是第一个模块,则使用1*1卷积,步长为2(目的是减小特征图,并增大通道数)if i == 0 and not first_block:self.listLayers.append(Residual(num_channels, use_1x1conv=True, strides=2))# 否则不使用1*1卷积,步长为1 else:self.listLayers.append(Residual(num_channels)) # 定义前向传播过程def call(self, X):# 所有层依次向前传播即可for layer in self.listLayers.layers:X = layer(X)return X```

















