前言
仅记录学习过程,有问题欢迎讨论
cifar 数据集
可以使用图片增强增加训练的样本数据
图像识别:
图像识别技术的过程分以下几步:
- 信息的获取:是指通过传感器,将光或声音等信息转化为电信息。也就是获取研究对象的基本信息并通
过某种方法将其转变为机器能够认识的信息。 - 预处理:主要是指图像处理中的去噪、平滑、变换等的操作,从而加强图像的重要特征。图像增强。
- 特征抽取和选择:是指在模式识别中,需要进行特征的抽取和选择。特征抽取和选择在图像识别过程中
是非常关键的技术之一。 - 分类器设计:是指通过训练而得到一种识别规则,通过此识别规则可以得到一种特征分类,使图像识别
技术能够得到高识别率。分类决策是指在特征空间中对被识别对象进行分类,从而更好地识别所研究的
对象具体属于哪一类。
AlexNet:
网络结构:AlexNet 共包含 8 层权重层,其中包括 5 个卷积层和 3 个全连接层,具体如下:
- 输入层:输入为 RGB 三通道的 224×224×3(或 227×227×3)大小的图像。
- 卷积层:第一层卷积层使用 96 个 11×11 的卷积核,步长为 4;第二层卷积层使用 256 个 5×5 的卷积核,步长为 1;第三、四、五层卷积层分别使用 384 个和 256 个 3×3 的卷积核,步长为 1。
每个卷积层后面都跟着一个 ReLU 激活函数层,用于增加网络的非线性表示能力。 - 池化层:在第二、四、五个卷积层后面都跟着一个最大池化层,使用 3×3 的池化窗口和 2 的步长。
- 全连接层:三个全连接层分别有 4096 个神经元,其中前两个全连接层采用了 Dropout 正则化技术,以减少过拟合。最后一个全连接层有 1000 个神经元,对应于 ImageNet 数据集的 1000 个类别,
使用 softmax 激活函数输出每个类别的概率。
关键技术:
- ReLU 激活函数:AlexNet 首次在 CNN 中成功应用了 ReLU 激活函数,相比传统的 sigmoid 和 tanh 函数,ReLU 能够加速训练过程并有效解决梯度消失问题。
- Dropout 正则化:在训练过程中随机忽略一部分神经元,以减少过拟合。Dropout 技术在 AlexNet 中得到了有效应用,提高了模型的泛化能力。
重叠池化:池化窗口的步长小于窗口的大小,使得池化层能够更好地提取空间信息,提升特征的丰富性。 - 局部响应归一化(LRN):对局部神经元的活动创建竞争机制,增强模型的对比度敏感性。虽然 LRN 在现代 CNN 中已较少使用,但在 AlexNet 中起到了重要作用。
数据增强:通过随机裁剪、水平翻转和颜色抖动等技术增加训练数据的多样性,进一步提高模型的泛化能力。 - GPU 加速训练:AlexNet 利用 GPU 强大的并行计算能力进行分布式训练,显著加快了训练速度。
VGG
- 使用比较深层次的神经层学习
- 可以使用卷积模拟全连接层,省略最后一个平铺的操作,卷积核需要设置为输入的大小
- 可以使用1*1的卷积的数量实现通道的升维和降维
ResNet(这个最重要)
- ResNet50有两个基本的块,分别名为Conv Block和Identity Block,其中Conv Block输入和输出的维度是不一样的,所以不能连续串联,它的作用是改变网络的维度;Identity Block输入维度和输出维度相同,可以串联,用于加深网络。
- 两个块都带有残差机制,在增加深度的同时,保护信息的完整性,只要学习输入输出的差别,简化学习难度。
BatchNormalization:
- 所有输出保证在0~1之间。
- 所有输出数据的均值接近0,标准差接近1的正太分布。使其落入激活函数的敏感区,避免梯度
消失,加快收敛。 - 加快模型收敛速度,并且具有一定的泛化能力。
- 可以减少dropout的使用
实现AlexNet
"""
实现AlexNet
"""
import torch
import torch.nn as nn
class AlexNet(nn.Module):
def __init__(self, num_classes=1000, init_weights=False):
super(AlexNet, self).__init__()
self.features = nn.Sequential(
# 输入(3, 224, 224),输出(48, 55, 55) (224-11+2*2)/4+1=55
nn.Conv2d(3, 48, kernel_size=11, stride=4, padding=2),
nn.ReLU(inplace=True),
# 输出(48, 27, 27)
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(48, 128, kernel_size=5, padding=2), # 输出(128, 27, 27)
nn.ReLU(inplace=True),
# 输出(128, 13, 13)
nn.MaxPool2d(kernel_size=3, stride=2),
nn.Conv2d(128, 192, kernel_size=3, padding=1), # 输出(192, 13, 13)
nn.ReLU(inplace=True),
nn.Conv2d(192, 192, kernel_size=3, padding=1), # 输出(192, 13, 13)
nn.ReLU(inplace=True),
nn.Conv2d(192, 128, kernel_size=3, padding=1), # 输出(128, 13, 13)
nn.ReLU(inplace=True),
# 输出(128, 6, 6)
nn.MaxPool2d(kernel_size=3, stride=2),
)
# 全连接层
self.classifier = nn.Sequential(
nn.Dropout(p=0.5),
nn.Linear(128 * 6 * 6, 2048),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(2048, 2048),
nn.ReLU(inplace=True),
nn.Linear(2048, num_classes),
)
# 初始化权重
if init_weights:
self._initialize_weights()
def forward(self, x):
x = self.features(x)
# flatten展平处理,start_dim=1从索引1开始
x = torch.flatten(x, start_dim=1)
x = self.classifier(x)
return x
# 权重初始化
def _initialize_weights(self):
# 每个层都需要初始化权重
for m in self.modules():
# 判断是否为卷积层
if isinstance(m, nn.Conv2d):
# 用凯明初始化对权重w进行初始化
nn.init.kaiming_normal_(m.weight, mode='fan_out', nonlinearity='relu')
if m.bias is not None:
nn.init.constant_(m.bias, 0)
# 全连接层
elif isinstance(m, nn.Linear):
# 通过正态分布给权重赋值,0表示正态分布的均值,0.01表示方差
nn.init.normal_(m.weight, 0, 0.01)
nn.init.constant_(m.bias, 0)
if __name__ == '__main__':
# 模型实例化
model = AlexNet(num_classes=1000, init_weights=True)
print(model)
# 输出模型参数
input = torch.randn(1, 3, 224, 224)
output = model(input)
print(output.shape)
实现VGG16
"""
实现VGG16网络
1、一张原始图片被resize到(224,224,3)。
2、conv1两次[3,3]卷积网络,输出的特征层为64,输出为(224,224,64),再2X2最大池化,输出net为
(112,112,64)。
3、conv2两次[3,3]卷积网络,输出的特征层为128,输出net为(112,112,128),再2X2最大池化,输出
net为(56,56,128)。
4、conv3三次[3,3]卷积网络,输出的特征层为256,输出net为(56,56,256),再2X2最大池化,输出net
为(28,28,256)。
5、conv3三次[3,3]卷积网络,输出的特征层为256,输出net为(28,28,512),再2X2最大池化,输出net
为(14,14,512)。
6、conv3三次[3,3]卷积网络,输出的特征层为256,输出net为(14,14,512),再2X2最大池化,输出net
为(7,7,512)。
7、利用卷积的方式模拟全连接层,效果等同,输出net为(1,1,4096)。共进行两次。
8、利用卷积的方式模拟全连接层,效果等同,输出net为(1,1,1000)
"""
from torch import nn
class Vgg16(nn.Module):
def __init__(self):
super(Vgg16, self).__init__()
self.conv1 = nn.Sequential(
# 输入为224*224*3,输出为224*224*64
nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(64, 64, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
# 输出为112*112*64
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(128, 128, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
# 输出为56*56*128
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(128, 256, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(256, 256, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
# 输出为28*28*256
nn.MaxPool2d(kernel_size=2, stride=2),
nn.Conv2d(256, 512, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, stride=1, padding=1),
nn.ReLU(inplace=True),
# 输出为14*14*512
nn.MaxPool2d(kernel_size=2, stride=2),
# 第五个卷积块
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(512, 512, kernel_size=3, padding=1),
nn.ReLU(inplace=True),
# 输出为7*7*512
nn.MaxPool2d(kernel_size=2, stride=2),
)
self.fc = nn.Sequential(
# 映射为4096维度
nn.Linear(512 * 7 * 7, 4096), # 输入为512*7*7
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(4096, 4096),
nn.ReLU(inplace=True),
nn.Dropout(p=0.5),
nn.Linear(4096, 1000),
)
def forward(self, x):
x = self.conv1(x)
x = x.view(x.size(0), -1)
x = self.fc(x)
return x
if __name__ == '__main__':
vgg16 = Vgg16()
print(vgg16)
实现ResNet50
"""
实现Resnet50!!!(important)
"""
import torchvision
import torch
import torch.nn as nn
import torchvision.transforms as transforms
from PIL import Image
class ResNet50(nn.Module):
def __init__(self, num_classes=1000):
super(ResNet50, self).__init__()
self.conv1 = nn.Conv2d(3, 64, kernel_size=(7, 7), stride=(2, 2), padding=(3, 3), bias=False)
self.bn1 = nn.BatchNorm2d(64)
self.relu = nn.ReLU(inplace=True)
self.maxpool = nn.MaxPool2d(kernel_size=(3, 3), stride=(2, 2), padding=(1, 1))
self.conv_block1 = ConvBlock(64, [64, 64, 256], kernel_size=(3, 3), strides=(1, 1))
self.identity_block1 = IdentityBlock(256, [64, 64, 256], kernel_size=(3, 3))
self.identity_block2 = IdentityBlock(256, [64, 64, 256], kernel_size=(3, 3))
self.conv_block2 = ConvBlock(256, [128, 128, 512], kernel_size=(3, 3))
self.identity_block3 = IdentityBlock(512, [128, 128, 512], kernel_size=(3, 3))
self.identity_block4 = IdentityBlock(512, [128, 128, 512], kernel_size=(3, 3))
self.identity_block5 = IdentityBlock(512, [128, 128, 512], kernel_size=(3, 3))
self.conv_block3 = ConvBlock(512, [256, 256, 1024], kernel_size=(3, 3))
self.identity_block6 = IdentityBlock(1024, [256, 256, 1024], kernel_size=(3, 3))
self.identity_block7 = IdentityBlock(1024, [256, 256, 1024], kernel_size=(3, 3))
self.identity_block8 = IdentityBlock(1024, [256, 256, 1024], kernel_size=(3, 3))
self.identity_block9 = IdentityBlock(1024, [256, 256, 1024], kernel_size=(3, 3))
self.identity_block10 = IdentityBlock(1024, [256, 256, 1024], kernel_size=(3, 3))
self.identity_block11 = IdentityBlock(1024, [256, 256, 1024], kernel_size=(3, 3))
self.conv_block4 = ConvBlock(1024, [512, 512, 2048], kernel_size=(3, 3))
self.identity_block12 = IdentityBlock(2048, [512, 512, 2048], kernel_size=(3, 3))
self.identity_block13 = IdentityBlock(2048, [512, 512, 2048], kernel_size=(3, 3))
self.avgpool = nn.AdaptiveAvgPool2d((1, 1))
self.fc = nn.Linear(2048, num_classes)
def forward(self, x):
x = self.conv1(x)
x = self.bn1(x)
x = self.relu(x)
x = self.maxpool(x)
x = self.conv_block1(x)
x = self.identity_block1(x)
x = self.identity_block2(x)
x = self.conv_block2(x)
x = self.identity_block3(x)
x = self.identity_block4(x)
x = self.identity_block5(x)
x = self.conv_block3(x)
x = self.identity_block6(x)
x = self.identity_block7(x)
x = self.identity_block8(x)
x = self.identity_block9(x)
x = self.identity_block10(x)
x = self.identity_block11(x)
x = self.conv_block4(x)
x = self.identity_block12(x)
x = self.identity_block13(x)
x = self.avgpool(x)
x = torch.flatten(x, 1)
x = self.fc(x)
return x
class IdentityBlock(nn.Module):
def __init__(self, in_channels, filters, kernel_size):
super(IdentityBlock, self).__init__()
filters1, filters2, filters3 = filters
self.conv1 = nn.Conv2d(in_channels, filters1, kernel_size=(1, 1), bias=False)
self.bn1 = nn.BatchNorm2d(filters1)
self.relu1 = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(filters1, filters2, kernel_size=kernel_size, padding='same', bias=False)
self.bn2 = nn.BatchNorm2d(filters2)
self.relu2 = nn.ReLU(inplace=True)
self.conv3 = nn.Conv2d(filters2, filters3, kernel_size=(1, 1), bias=False)
self.bn3 = nn.BatchNorm2d(filters3)
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu1(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu2(out)
out = self.conv3(out)
out = self.bn3(out)
# 残差!
out += identity
out = nn.ReLU(inplace=True)(out)
return out
class ConvBlock(nn.Module):
def __init__(self, in_channels, filters, kernel_size, strides=(2, 2)):
super(ConvBlock, self).__init__()
filters1, filters2, filters3 = filters
self.conv1 = nn.Conv2d(in_channels, filters1, kernel_size=(1, 1), bias=False)
# 输入数据进行归一化处理,使得数据在进入下一层之前具有稳定的分布
self.bn1 = nn.BatchNorm2d(filters1)
self.relu1 = nn.ReLU(inplace=True)
self.conv2 = nn.Conv2d(filters1, filters2, kernel_size=kernel_size, padding='same', bias=False)
self.bn2 = nn.BatchNorm2d(filters2)
self.relu2 = nn.ReLU(inplace=True)
self.conv3 = nn.Conv2d(filters2, filters3, kernel_size=(1, 1), bias=False)
self.bn3 = nn.BatchNorm2d(filters3)
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, filters3, kernel_size=(1, 1), bias=False),
nn.BatchNorm2d(filters3)
)
def forward(self, x):
identity = x
out = self.conv1(x)
out = self.bn1(out)
out = self.relu1(out)
out = self.conv2(out)
out = self.bn2(out)
out = self.relu2(out)
out = self.conv3(out)
out = self.bn3(out)
# 多处理了一层
identity = self.shortcut(identity)
out += identity
out = nn.ReLU(inplace=True)(out)
return out
# if __name__ == '__main__':
# # 这里使用官方预训练权重的加载方式
# model = torchvision.models.resnet50(pretrained=True)
# # 如果要使用自己的权重文件,需要按照PyTorch的权重格式和加载方式进行调整
#
# model.eval()
# img_path = 'elephant.jpg'
# img = Image.open(img_path).convert('RGB')
# transform = transforms.Compose([
# transforms.Resize((224, 224)),
# transforms.ToTensor(),
# transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
# ])
# img = transform(img).unsqueeze(0)
#
# with torch.no_grad():
# output = model(img)
# probabilities = torch.nn.functional.softmax(output, dim = 1)
# top_p, top_class = probabilities.topk(5, dim = 1)
# print('Predicted top 5 classes:')
# for i in range(5):
# print(f'Class {top_class[0][i].item()}: probability {top_p[0][i].item()}')