深度学习 Day19——P8YOLOv5-C3模块实现

  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍖 原作者:K同学啊 | 接辅导、项目定制

文章目录

  • 前言
  • 1 我的环境
  • 2 代码实现与执行结果
    • 2.1 前期准备
      • 2.1.1 引入库
      • 2.1.2 设置GPU(如果设备上支持GPU就使用GPU,否则使用CPU)
      • 2.1.3 导入数据
      • 2.1.4 可视化数据
      • 2.1.4 图像数据变换
      • 2.1.4 划分数据集
      • 2.1.4 加载数据
      • 2.1.4 查看数据
    • 2.2 构建yolov5-C3网络模型
    • 2.3 训练模型
      • 2.3.1 设置超参数
      • 2.3.2 编写训练函数
      • 2.3.3 编写测试函数
      • 2.3.4 正式训练
    • 2.4 结果可视化
    • 2.4 指定图片进行预测
    • 2.6 模型评估
  • 3 知识点详解
    • 3.1 YOLOv5详解
      • 3.1.1 YOLOv5算法概述
      • 3.1.2 YOLOv5算法基本原理
      • 3.1.3 特点
      • 3.1.4 YOLOv5模型结构
      • 3.1.5 Backbone骨干网络
      • 3.1.5.1 Focus模块
      • 3.1.5.1 Conv模块
      • 3.1.5.2 C3模块
      • 3.1.5.2 SPPF模块
    • 3.1.6 Neck特征金字塔
    • 3.1.7 Head目标检测头
    • 3.1.8 YOLOv5总结
  • 总结


前言

本文将采用pytorch框架创建YOLOv5-C3模块实现天气识别。讲述实现代码与执行结果,并浅谈涉及知识点。
关键字: YOLOV5 C3

1 我的环境

  • 电脑系统:Windows 11
  • 语言环境:python 3.8.6
  • 编译器:pycharm2020.2.3
  • 深度学习环境:
    torch == 1.9.1+cu111
    torchvision == 0.10.1+cu111
  • 显卡:NVIDIA GeForce RTX 4070

2 代码实现与执行结果

2.1 前期准备

2.1.1 引入库


import torch
import torch.nn as nn
from torchvision import transforms, datasets
import time
from pathlib import Path
from PIL import Image
import torchsummary as summary
import torch.nn.functional as Fimport copy
import matplotlib.pyplot as pltplt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号
plt.rcParams['figure.dpi'] = 100  # 分辨率
import warningswarnings.filterwarnings('ignore')  # 忽略一些warning内容,无需打印

2.1.2 设置GPU(如果设备上支持GPU就使用GPU,否则使用CPU)

"""前期准备-设置GPU"""
# 如果设备上支持GPU就使用GPU,否则使用CPUdevice = torch.device("cuda" if torch.cuda.is_available() else "cpu")print("Using {} device".format(device))

输出

Using cuda device

2.1.3 导入数据

'''前期工作-导入数据'''
data_dir = r"D:\DeepLearning\data\CoffeeBean"
data_dir = Path(data_dir)data_paths = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[-1] for path in data_paths]
print(classeNames)

输出

['cloudy', 'rain', 'shine', 'sunrise']

2.1.4 可视化数据

'''前期工作-可视化数据'''subfolder = Path(data_dir)/"Angelina Jolie"image_files = list(p.resolve() for p in subfolder.glob('*') if p.suffix in [".jpg", ".png", ".jpeg"])plt.figure(figsize=(10, 6))for i in range(len(image_files[:12])):image_file = image_files[i]ax = plt.subplot(3, 4, i + 1)img = Image.open(str(image_file))plt.imshow(img)plt.axis("off")# 显示图片plt.tight_layout()plt.show()

在这里插入图片描述

2.1.4 图像数据变换

'''前期工作-图像数据变换'''
total_datadir = data_dir# 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863
train_transforms = transforms.Compose([transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸transforms.ToTensor(),  # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间transforms.Normalize(  # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])
total_data = datasets.ImageFolder(total_datadir, transform=train_transforms)
print(total_data)
print(total_data.class_to_idx)

输出

Dataset ImageFolderNumber of datapoints: 1125Root location: D:\DeepLearning\data\weather_photosStandardTransform
Transform: Compose(Resize(size=[224, 224], interpolation=bilinear, max_size=None, antialias=None)ToTensor()Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]))
{'cloudy': 0, 'rain': 1, 'shine': 2, 'sunrise': 3}

2.1.4 划分数据集

'''前期工作-划分数据集'''
train_size = int(0.8 * len(total_data))  # train_size表示训练集大小,通过将总体数据长度的80%转换为整数得到;
test_size = len(total_data) - train_size  # test_size表示测试集大小,是总体数据长度减去训练集大小。
# 使用torch.utils.data.random_split()方法进行数据集划分。该方法将总体数据total_data按照指定的大小比例([train_size, test_size])随机划分为训练集和测试集,
# 并将划分结果分别赋值给train_dataset和test_dataset两个变量。
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])
print("train_dataset={}\ntest_dataset={}".format(train_dataset, test_dataset))
print("train_size={}\ntest_size={}".format(train_size, test_size))

输出

train_dataset=<torch.utils.data.dataset.Subset object at 0x0000020A3A209730>
test_dataset=<torch.utils.data.dataset.Subset object at 0x0000020A3A209BB0>
train_size=900
test_size=225

2.1.4 加载数据

'''前期工作-加载数据'''
batch_size = 32train_dl = torch.utils.data.DataLoader(train_dataset,batch_size=batch_size,shuffle=True,num_workers=1)
test_dl = torch.utils.data.DataLoader(test_dataset,batch_size=batch_size,shuffle=True,num_workers=1)

2.1.4 查看数据

'''前期工作-查看数据'''
for X, y in test_dl:print("Shape of X [N, C, H, W]: ", X.shape)print("Shape of y: ", y.shape, y.dtype)break

输出

Shape of X [N, C, H, W]:  torch.Size([32, 3, 224, 224])
Shape of y:  torch.Size([32]) torch.int64

2.2 构建yolov5-C3网络模型

在这里插入图片描述

import torch.nn.functional as Fdef autopad(k, p=None):  # kernel, padding# Pad to 'same'if p is None:p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-padreturn pclass Conv(nn.Module):# Standard convolutiondef __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groupssuper().__init__()self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)self.bn = nn.BatchNorm2d(c2)self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())def forward(self, x):return self.act(self.bn(self.conv(x)))class Bottleneck(nn.Module):# Standard bottleneckdef __init__(self, c1, c2, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, shortcut, groups, expansionsuper().__init__()c_ = int(c2 * e)  # hidden channelsself.cv1 = Conv(c1, c_, 1, 1)self.cv2 = Conv(c_, c2, 3, 1, g=g)self.add = shortcut and c1 == c2def forward(self, x):return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))class C3(nn.Module):# CSP Bottleneck with 3 convolutionsdef __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansionsuper().__init__()c_ = int(c2 * e)  # hidden channelsself.cv1 = Conv(c1, c_, 1, 1)self.cv2 = Conv(c1, c_, 1, 1)self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))def forward(self, x):return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))class model_K(nn.Module):def __init__(self):super(model_K, self).__init__()# 卷积模块self.Conv = Conv(3, 32, 3, 2) # C3模块1self.C3_1 = C3(32, 64, 3, 2)# 全连接网络层,用于分类self.classifier = nn.Sequential(nn.Linear(in_features=802816, out_features=100),nn.ReLU(),nn.Linear(in_features=100, out_features=4))def forward(self, x):x = self.Conv(x)x = self.C3_1(x)x = torch.flatten(x, start_dim=1)x = self.classifier(x)return xdevice = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))model = model_K().to(device)
print(summary.summary(model, (3, 224, 224)))#查看模型的参数量以及相关指标

输出

----------------------------------------------------------------Layer (type)               Output Shape         Param #
================================================================Conv2d-1         [-1, 32, 112, 112]             864BatchNorm2d-2         [-1, 32, 112, 112]              64SiLU-3         [-1, 32, 112, 112]               0Conv-4         [-1, 32, 112, 112]               0Conv2d-5         [-1, 32, 112, 112]           1,024BatchNorm2d-6         [-1, 32, 112, 112]              64SiLU-7         [-1, 32, 112, 112]               0Conv-8         [-1, 32, 112, 112]               0Conv2d-9         [-1, 32, 112, 112]           1,024BatchNorm2d-10         [-1, 32, 112, 112]              64SiLU-11         [-1, 32, 112, 112]               0Conv-12         [-1, 32, 112, 112]               0Conv2d-13         [-1, 32, 112, 112]           9,216BatchNorm2d-14         [-1, 32, 112, 112]              64SiLU-15         [-1, 32, 112, 112]               0Conv-16         [-1, 32, 112, 112]               0Bottleneck-17         [-1, 32, 112, 112]               0Conv2d-18         [-1, 32, 112, 112]           1,024BatchNorm2d-19         [-1, 32, 112, 112]              64SiLU-20         [-1, 32, 112, 112]               0Conv-21         [-1, 32, 112, 112]               0Conv2d-22         [-1, 32, 112, 112]           9,216BatchNorm2d-23         [-1, 32, 112, 112]              64SiLU-24         [-1, 32, 112, 112]               0Conv-25         [-1, 32, 112, 112]               0Bottleneck-26         [-1, 32, 112, 112]               0Conv2d-27         [-1, 32, 112, 112]           1,024BatchNorm2d-28         [-1, 32, 112, 112]              64SiLU-29         [-1, 32, 112, 112]               0Conv-30         [-1, 32, 112, 112]               0Conv2d-31         [-1, 32, 112, 112]           9,216BatchNorm2d-32         [-1, 32, 112, 112]              64SiLU-33         [-1, 32, 112, 112]               0Conv-34         [-1, 32, 112, 112]               0Bottleneck-35         [-1, 32, 112, 112]               0Conv2d-36         [-1, 32, 112, 112]           1,024BatchNorm2d-37         [-1, 32, 112, 112]              64SiLU-38         [-1, 32, 112, 112]               0Conv-39         [-1, 32, 112, 112]               0Conv2d-40         [-1, 64, 112, 112]           4,096BatchNorm2d-41         [-1, 64, 112, 112]             128SiLU-42         [-1, 64, 112, 112]               0Conv-43         [-1, 64, 112, 112]               0C3-44         [-1, 64, 112, 112]               0Linear-45                  [-1, 100]      80,281,700ReLU-46                  [-1, 100]               0Linear-47                    [-1, 4]             404
================================================================
Total params: 80,320,536
Trainable params: 80,320,536
Non-trainable params: 0
----------------------------------------------------------------
Input size (MB): 0.57
Forward/backward pass size (MB): 150.06
Params size (MB): 306.40
Estimated Total Size (MB): 457.04
----------------------------------------------------------------

2.3 训练模型

2.3.1 设置超参数

"""训练模型--设置超参数"""
loss_fn = nn.CrossEntropyLoss()  # 创建损失函数,计算实际输出和真实相差多少,交叉熵损失函数,事实上,它就是做图片分类任务时常用的损失函数
learn_rate = 1e-4  # 学习率
optimizer1 = torch.optim.SGD(model.parameters(), lr=learn_rate)# 作用是定义优化器,用来训练时候优化模型参数;其中,SGD表示随机梯度下降,用于控制实际输出y与真实y之间的相差有多大
optimizer2 = torch.optim.Adam(model.parameters(), lr=learn_rate)  
lr_opt = optimizer2
model_opt = optimizer2
# 调用官方动态学习率接口时使用2
lambda1 = lambda epoch : 0.92 ** (epoch // 4)
# optimizer = torch.optim.SGD(model.parameters(), lr=learn_rate)
scheduler = torch.optim.lr_scheduler.LambdaLR(lr_opt, lr_lambda=lambda1) #选定调整方法

2.3.2 编写训练函数

"""训练模型--编写训练函数"""
# 训练循环
def train(dataloader, model, loss_fn, optimizer):size = len(dataloader.dataset)  # 训练集的大小,一共60000张图片num_batches = len(dataloader)  # 批次数目,1875(60000/32)train_loss, train_acc = 0, 0  # 初始化训练损失和正确率for X, y in dataloader:  # 加载数据加载器,得到里面的 X(图片数据)和 y(真实标签)X, y = X.to(device), y.to(device) # 用于将数据存到显卡# 计算预测误差pred = model(X)  # 网络输出loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失# 反向传播optimizer.zero_grad()  # 清空过往梯度loss.backward()  # 反向传播,计算当前梯度optimizer.step()  # 根据梯度更新网络参数# 记录acc与losstrain_acc += (pred.argmax(1) == y).type(torch.float).sum().item()train_loss += loss.item()train_acc /= sizetrain_loss /= num_batchesreturn train_acc, train_loss

2.3.3 编写测试函数

"""训练模型--编写测试函数"""
# 测试函数和训练函数大致相同,但是由于不进行梯度下降对网络权重进行更新,所以不需要传入优化器
def test(dataloader, model, loss_fn):size = len(dataloader.dataset)  # 测试集的大小,一共10000张图片num_batches = len(dataloader)  # 批次数目,313(10000/32=312.5,向上取整)test_loss, test_acc = 0, 0# 当不进行训练时,停止梯度更新,节省计算内存消耗with torch.no_grad(): # 测试时模型参数不用更新,所以 no_grad,整个模型参数正向推就ok,不反向更新参数for imgs, target in dataloader:imgs, target = imgs.to(device), target.to(device)# 计算losstarget_pred = model(imgs)loss = loss_fn(target_pred, target)test_loss += loss.item()test_acc += (target_pred.argmax(1) == target).type(torch.float).sum().item()#统计预测正确的个数test_acc /= sizetest_loss /= num_batchesreturn test_acc, test_loss

2.3.4 正式训练

"""训练模型--正式训练"""
epochs = 40
train_loss = []
train_acc = []
test_loss = []
test_acc = []
best_test_acc=0for epoch in range(epochs):milliseconds_t1 = int(time.time() * 1000)# 更新学习率(使用自定义学习率时使用)# adjust_learning_rate(lr_opt, epoch, learn_rate)model.train()epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, model_opt)scheduler.step() # 更新学习率(调用官方动态学习率接口时使用)model.eval()epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)# 获取当前的学习率lr = lr_opt.state_dict()['param_groups'][0]['lr']milliseconds_t2 = int(time.time() * 1000)template = ('Epoch:{:2d}, duration:{}ms, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}, Lr:{:.2E}')if best_test_acc < epoch_test_acc:best_test_acc = epoch_test_acc#备份最好的模型best_model = copy.deepcopy(model)template = ('Epoch:{:2d}, duration:{}ms, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}, Lr:{:.2E},Update the best model')print(template.format(epoch + 1, milliseconds_t2-milliseconds_t1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss, lr))
# 保存最佳模型到文件中
PATH = './best_model.pth'  # 保存的参数文件名
torch.save(model.state_dict(), PATH)
print('Done')

输出最高精度为Test_acc:42.5%

Epoch: 1, duration:8711ms, Train_acc:77.8%, Train_loss:1.207, Test_acc:55.6%,Test_loss:1.484, Lr:1.00E-04,Update the best model
Epoch: 2, duration:7423ms, Train_acc:87.8%, Train_loss:0.400, Test_acc:74.7%,Test_loss:0.999, Lr:1.00E-04,Update the best model
Epoch: 3, duration:7113ms, Train_acc:92.4%, Train_loss:0.241, Test_acc:88.4%,Test_loss:0.343, Lr:1.00E-04,Update the best model
Epoch: 4, duration:7393ms, Train_acc:98.1%, Train_loss:0.059, Test_acc:89.3%,Test_loss:0.316, Lr:9.20E-05,Update the best model
Epoch: 5, duration:7734ms, Train_acc:99.0%, Train_loss:0.048, Test_acc:88.4%,Test_loss:0.393, Lr:9.20E-05
Epoch: 6, duration:7802ms, Train_acc:98.4%, Train_loss:0.080, Test_acc:85.8%,Test_loss:0.395, Lr:9.20E-05
Epoch: 7, duration:8622ms, Train_acc:94.9%, Train_loss:0.159, Test_acc:85.8%,Test_loss:0.466, Lr:9.20E-05
Epoch: 8, duration:7842ms, Train_acc:97.8%, Train_loss:0.067, Test_acc:87.1%,Test_loss:0.385, Lr:8.46E-05
Epoch: 9, duration:7179ms, Train_acc:99.2%, Train_loss:0.029, Test_acc:88.0%,Test_loss:0.359, Lr:8.46E-05
Epoch:10, duration:7072ms, Train_acc:99.9%, Train_loss:0.011, Test_acc:90.2%,Test_loss:0.294, Lr:8.46E-05,Update the best model
Epoch:11, duration:7350ms, Train_acc:99.9%, Train_loss:0.006, Test_acc:89.8%,Test_loss:0.300, Lr:8.46E-05
Epoch:12, duration:7111ms, Train_acc:99.8%, Train_loss:0.024, Test_acc:91.1%,Test_loss:0.677, Lr:7.79E-05,Update the best model
Epoch:13, duration:7521ms, Train_acc:99.7%, Train_loss:0.015, Test_acc:89.8%,Test_loss:0.356, Lr:7.79E-05
Epoch:14, duration:7089ms, Train_acc:99.8%, Train_loss:0.018, Test_acc:90.7%,Test_loss:0.344, Lr:7.79E-05
Epoch:15, duration:7102ms, Train_acc:99.1%, Train_loss:0.049, Test_acc:88.4%,Test_loss:0.479, Lr:7.79E-05
Epoch:16, duration:6926ms, Train_acc:99.7%, Train_loss:0.014, Test_acc:91.6%,Test_loss:2.172, Lr:7.16E-05,Update the best model
Epoch:17, duration:6975ms, Train_acc:99.7%, Train_loss:0.014, Test_acc:92.0%,Test_loss:0.353, Lr:7.16E-05,Update the best model
Epoch:18, duration:7112ms, Train_acc:99.6%, Train_loss:0.196, Test_acc:89.3%,Test_loss:0.447, Lr:7.16E-05
Epoch:19, duration:7393ms, Train_acc:98.3%, Train_loss:0.059, Test_acc:86.7%,Test_loss:0.827, Lr:7.16E-05
Epoch:20, duration:7197ms, Train_acc:99.2%, Train_loss:0.013, Test_acc:90.2%,Test_loss:0.558, Lr:6.59E-05

2.4 结果可视化

"""训练模型--结果可视化"""
epochs_range = range(epochs)plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

在这里插入图片描述

2.4 指定图片进行预测

def predict_one_image(image_path, model, transform, classes):test_img = Image.open(image_path).convert('RGB')plt.imshow(test_img)  # 展示预测的图片plt.show()test_img = transform(test_img)img = test_img.to(device).unsqueeze(0)model.eval()output = model(img)_, pred = torch.max(output, 1)pred_class = classes[pred]print(f'预测结果是:{pred_class}')# 将参数加载到model当中
model.load_state_dict(torch.load(PATH, map_location=device))"""指定图片进行预测"""
classes = list(total_data.class_to_idx)
# 预测训练集中的某张照片
predict_one_image(image_path=str(Path(data_dir)/"Dark/dark (1).png"),model=model,transform=train_transforms,classes=classes)

在这里插入图片描述

输出

预测结果是:cloudy

2.6 模型评估

"""模型评估"""
best_model.eval()
epoch_test_acc, epoch_test_loss = test(test_dl, best_model, loss_fn)
# 查看是否与我们记录的最高准确率一致
print(epoch_test_acc, epoch_test_loss)

输出


0.92 0.35305984411388636

3 知识点详解

3.1 YOLOv5详解

3.1.1 YOLOv5算法概述

Yolov5是一种目标检测算法,采用基于Anchor的检测方式,属于单阶段目标检测方法。相比于Yolov4,Yolov5有着更快的速度和更高的精度,是目前业界领先的目标检测算法之一。

3.1.2 YOLOv5算法基本原理

Yolov5基于目标检测算法中的one-stage方法,其主要思路是将整张图像划分为若干个网格,每个网格预测出该网格内物体的种类和位置信息,然后根据预测框与真实框之间的IoU值进行目标框的筛选,最终输出预测框的类别和位置信息。

3.1.3 特点

Yolov5具有以下几个特点:

高效性:相比于其他目标检测算法,Yolov5在保证高精度的前提下,速度更快,尤其是在GPU环境下可以实现实时检测。

精度高:通过使用多尺度预测和CIoU loss等机制,Yolov5可以提高目标检测的精度。

易用性强:Yolov5开源且易于使用,提供了PyTorch版本和ONNX版本,可以在不同的硬件上运行。

Yolov5可以应用于各种实际场景中的目标检测任务,例如物体检测、人脸检测、交通标志检测、动物检测等等。

3.1.4 YOLOv5模型结构

yolov5有五个版本:yolov5s、yolov5m、yolov5l、yolov5x和yolov5nano。其中,yolov5s是最小的版本,yolov5x是最大的版本。它们的区别在于网络的深度、宽度和参数量等方面。

下面以yolov5s为模板详解yolov5。其具有较高的精度和较快的检测速度,同时参数量更少。
YOLOv5s 模型主要由 Backbone、Neck 和Head 三部分组成,网络模型见下图。其中:
Backbone 主要负责对输入图像进行特征提取。
Neck 负责对特征图进行多尺度特征融合,并把这些特征传递给预测层。
Head 进行最终的回归预测。
在这里插入图片描述

3.1.5 Backbone骨干网络

骨干网络是指用来提取图像特征的网络,它的主要作用是将原始的输入图像转化为多层特征图,以便后续的目标检测任务使用。在Yolov5中,使用的是CSPDarknet53或ResNet骨干网络,这两个网络都是相对轻量级的,能够在保证较高检测精度的同时,尽可能地减少计算量和内存占用。
Backbone中的主要结构有Conv模块、C3模块、SPPF模块。

3.1.5.1 Focus模块

在这里插入图片描述从卷积的过程上来看如下所示:

在这里插入图片描述
Focus模块将每个2x2的相邻像素划分为一个patch,然后将每个patch中相同位置(同一颜色)像素给拼在一起就得到了4个feature map,然后在接上一个3x3大小的卷积层。YOLOv5在v6.0版本后相比之前版本有一个很小的改动,把网络的第一层(原来是Focus模块)换成了一个6x6大小的卷积层。两者在理论上其实等价的,但是对于现有的一些GPU设备(以及相应的优化算法)使用6x6大小的卷积层比使用Focus模块更加高效。

3.1.5.1 Conv模块

Conv模块是卷积神经网络中常用的一种基础模块,它主要由卷积层、BN层和激活函数组成。下面对这些组成部分进行详细解析。
在这里插入图片描述

  • 卷积层是卷积神经网络中最基础的层之一,用于提取输入特征中的局部空间信息。卷积操作可以看作是一个滑动窗口,窗口在输入特征上滑动,并将窗口内的特征值与卷积核进行卷积操作,从而得到输出特征。卷积层通常由多个卷积核组成,每个卷积核对应一个输出通道。卷积核的大小、步长、填充方式等超参数决定了卷积层的输出大小和感受野大小。卷积神经网络中,卷积层通常被用来构建特征提取器。
  • BN层是在卷积层之后加入的一种归一化层,用于规范化神经网络中的特征值分布。它可以加速训练过程,提高模型泛化能力,减轻模型对初始化的依赖性。BN层的输入为一个batch的特征图,它将每个通道上的特征进行均值和方差的计算,并对每个通道上的特征进行标准化处理。标准化后的特征再通过一个可学习的仿射变换(拉伸和偏移)进行还原,从而得到BN层的输出。
  • 激活函数是一种非线性函数,用于给神经网络引入非线性变换能力。常用的激活函数包括sigmoid、ReLU、LeakyReLU、ELU等。它们在输入值的不同范围内都有不同的输出表现,可以更好地适应不同类型的数据分布。其中SiLU (x)=x∗ sigmoid(x)。

综上所述,Conv模块是卷积神经网络中常用的基础模块,它通过卷积操作提取局部空间信息,并通过BN层规范化特征值分布,最后通过激活函数引入非线性变换能力,从而实现对输入特征的转换和提取。

3.1.5.2 C3模块

C3模块是YOLOv5网络中的一个重要组成部分,其主要作用是增加网络的深度和感受野,提高特征提取的能力。C3/C3_False的区别在于Botttleneck是否使用shortcut,其结构如下图
在这里插入图片描述
C3采用的是CSP结构,CSP的思想是将通道拆分成两个部分,分别走上图左右两条路,而源码中是将全部的输入特征利用两路1x1进行transition,比直接划分通道能够进一步提高特征的重用性,并且在输入到resiudal block之前也确实通道减半,减少了计算量。

3.1.5.2 SPPF模块

SPP模块是一种池化模块,通常应用于卷积神经网络中,旨在实现输入数据的空间不变性和位置不变性,以便于提高神经网络的识别能力。其主要思想是将不同大小的感受野应用于同一张图像,从而能够捕捉到不同尺度的特征信息。在SPP模块中,首先对输入特征图进行不同大小的池化操作,以得到一组不同大小的特征图。然后将这些特征图连接在一起,并通过全连接层进行降维,最终得到固定大小的特征向量。SPP结构如下图所示,是将输入并行通过多个不同大小的MaxPool,然后做进一步融合,能在一定程度上解决目标多尺度问题。
在这里插入图片描述
SPP模块通常由三个步骤组成:

池化:将输入特征图分别进行不同大小的池化操作,以获得一组不同大小的特征图。
连接:将不同大小的特征图连接在一起。
全连接:通过全连接层将连接后的特征向量降维,得到固定大小的特征向量。
v6.0版本后首先是将SPP换成成了SPPF(Glenn Jocher自己设计的),这个改动我个人觉得还是很有意思的,两者的作用是一样的,但后者效率更高。
在这里插入图片描述

3.1.6 Neck特征金字塔

由于物体在图像中的大小和位置是不确定的,因此需要一种机制来处理不同尺度和大小的目标。特征金字塔是一种用于处理多尺度目标检测的技术,它可以通过在骨干网络上添加不同尺度的特征层来实现。在Yolov5中,采用的是FPN(Feature Pyramid Network)特征金字塔结构,通过上采样和下采样操作将不同层次的特征图融合在一起,生成多尺度的特征金字塔。自顶向下部分主要是通过上采样和与更粗粒度的特征图融合来实现不同层次特征的融合,而自下向上则是通过使用一个卷积层来融合来自不同层次的特征图。

在目标检测算法中,Neck模块通常被用于将不同层级的特征图结合起来,生成具有多尺度信息的特征图,以提高目标检测的准确率。在 YOLOv5 中,使用了一种名为 PANet 的特征融合模块作为 Neck 模块。
在这里插入图片描述

YoloV5提取多特征层进行目标检测,一共提取三个特征层。
三个特征层位于主干部分CSPdarknet的不同位置,分别位于中间层,中下层,底层,当输入为(640,640,3)的时候,三个特征层的shape分别为feat1=(80,80,256)、feat2=(40,40,512)、feat3=(20,20,1024)。

在获得三个有效特征层后,利用这三个有效特征层进行FPN层的构建,构建方式为:

1.feat3=(20,20,1024)的特征层进行1次1X1卷积调整通道后获得P5,P5进行上采样UmSampling2d后与feat2=(40,40,512)特征层进行结合,然后使用CSPLayer进行特征提取获得P5_upsample,此时获得的特征层为(40,40,512)。
2.P5_upsample=(40,40,512)的特征层进行1次1X1卷积调整通道后获得P4,P4进行上采样UmSampling2d后与feat1=(80,80,256)特征层进行结合,然后使用CSPLayer进行特征提取P3_out,此时获得的特征层为(80,80,256)。
3.P3_out=(80,80,256)的特征层进行一次3x3卷积进行下采样,下采样后与P4堆叠,然后使用CSPLayer进行特征提取P4_out,此时获得的特征层为(40,40,512)。
4.P4_out=(40,40,512)的特征层进行一次3x3卷积进行下采样,下采样后与P5堆叠,然后使用CSPLayer进行特征提取P5_out,此时获得的特征层为(20,20,1024)。

特征金字塔可以将不同shape的特征层进行特征融合,有利于提取出更好的特征。

具体来说,自顶向下部分是通过上采样和与更粗粒度的特征图融合来实现不同层次特征的融合,主要分为以下几步:

1.对最后一层特征图进行上采样,得到更精细的特征图;
2.将上采样后的特征图与上一层特征图进行融合,得到更丰富的特征表达;
3.重复以上两个步骤,直到达到最高层。

自下向上部分主要是通过使用一个卷积层来融合来自不同层次的特征图,主要分为以下几步:

1.对最底层特征图进行卷积,得到更丰富的特征表达;
2.将卷积后的特征图与上一层特征图进行融合,得到更丰富的特征表达;
3.重复以上两个步骤,直到达到最高层。

最后,自顶向下部分和自下向上部分的特征图进行融合,得到最终的特征图,用于目标检测。

3.1.7 Head目标检测头

目标检测头是用来对特征金字塔进行目标检测的部分,它包括了一些卷积层、池化层和全连接层等。在 YOLOv5 模型中,检测头模块主要负责对骨干网络提取的特征图进行多尺度目标检测。该模块主要包括三个部分,此外,Yolov5还使用了一些技巧来进一步提升检测精度,比如GIoU loss、Mish激活函数和多尺度训练等。

  • Anchors:用于定义不同大小和长宽比的目标框,通常使用 K-means 聚类对训练集的目标框进行聚类得到,可以在模型训练之前进行计算,存储在模型中,用于预测时生成检测框。
  • Classification:用于对每个检测框进行分类,判断其是否为目标物体,通常采用全连接层加 Softmax 函数的形式对特征进行分类。
  • Regression:用于对每个检测框进行回归,得到其位置和大小,通常采用全连接层的形式对特征进行回归。
    YOLOv5的检测层由几个重要的组成部分构成,包括:

Anchors(锚框):
锚框是预定义的一组边界框,用于在特征图上生成候选框。
YOLOv5通过提前定义不同比例和尺寸的锚框来适应不同大小的目标。

Convolutional Layers(卷积层):
YOLOv5的检测层包含一系列卷积层,用于处理特征图和提取特征。
这些卷积层可以通过调整通道数和核大小来适应不同的检测任务。

Prediction Layers(预测层):
每个预测层负责预测一组边界框和类别。
每个预测层通常由卷积层和一个输出层组成。
输出层的通道数和形状决定了预测的边界框数量和类别数量。

Non-Maximum Suppression (NMS)(非极大值抑制):
在输出的边界框中,使用非极大值抑制算法来抑制重叠的边界框,只保留最具有代表性的边界框。

YOLOv5 的检测头模块采用了多层级特征融合的方法,首先将骨干网络输出的特征图经过一个 Conv 模块进行通道数的降维和特征图的缩放,然后再将不同层级的特征图进行融合,得到更加丰富的特征信息,从而提高检测性能。

3.1.8 YOLOv5总结

Yolov5是目标检测领域中的一种深度学习算法,是对Yolov4的改进版本,其在速度和精度方面都取得了很大的提升。Yolov5的整体架构由主干网络、FPN、Neck、Head等模块组成。
主干网络部分采用的是CSPDarknet53,通过使用残差结构和特征重用机制,可以有效地提高模型的特征提取能力。
FPN部分采用的是基于高斯加权的特征金字塔,可以解决多尺度目标检测的问题。

Neck部分采用的是SPP和PAN结合的结构,能够在保持高效性的同时提升模型的性能。
Head部分采用的是YOLOv5头结构,可以输出网络的预测结果。

总的来说,Yolov5在各个模块上的设计都充分考虑了速度和精度的平衡,使得它在目标检测任务中表现出色。
参考链接:
【YOLOv5】Backbone、Neck、Head各模块详解
YOLOv5网络详解
从YOLOv5源码yolo.py详细介绍Yolov5的网络结构
YOLOv5网络详解

总结

通过本文的学习,对YOLOv5网络结构有了一个初步的了解。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/220609.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式软件测试(黑盒测试)---三年嵌入式软件测试的理解

文章内容为本人这三年来在嵌入式软件测试&#xff08;黑盒&#xff09;上的一些积累吧&#xff0c;说起来也挺快的&#xff0c;毕业三年的时间就这样过去了&#xff0c;在两家公司工作过&#xff08;现在这家是第二家&#xff09;&#xff0c;这几年的测试项目基本都是围绕着嵌…

类型转换(C++)

1.5 类型转换1.5.1 隐式类型转换1.5.2 显示类型转换 1.5 类型转换 类型转换分为隐式转换和显示转换 写C/C代码的时候&#xff0c;有时候不可避免的会使用类型转换&#xff0c;良好的代码风格中应该避免隐式转换&#xff0c;隐式转换有时候会产生不易察觉的问题。 1.5.1 隐式…

翻译: ChatGPT Token消耗粗略计算英文就是除以四分之三

在这个视频中&#xff0c;我想带你快速浏览一些例子&#xff0c;以建立对在软件应用中使用大型语言模型的实际成本的直观感受。让我们来看看。这是一些示例价格&#xff0c;用于从不同的大型语言模型获取提示和回应&#xff0c;这些模型对开发者可用。即&#xff0c;如果你在你…

【MYSQL】-库的操作

&#x1f496;作者&#xff1a;小树苗渴望变成参天大树&#x1f388; &#x1f389;作者宣言&#xff1a;认真写好每一篇博客&#x1f4a4; &#x1f38a;作者gitee:gitee✨ &#x1f49e;作者专栏&#xff1a;C语言,数据结构初阶,Linux,C 动态规划算法&#x1f384; 如 果 你 …

【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(四)用户管理、部门管理模块

第一篇&#xff1a;【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统&#xff08;一&#xff09;搭建项目 第二篇&#xff1a;【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统&#xff08;二&#xff09;日志输出中间件、校验token中间件、配置路由、基础工具函数。 …

第三讲GNSS相关时间系统和转换 第四讲观测值的产生和分类 | GNSS(RTK)课程学习笔记day2

说明&#xff1a;以下笔记来自计算机视觉life吴桐老师课程&#xff1a;从零掌握GNSS、RTK定位[链接]&#xff0c;从零掌握RTKLIB[链接]。非原创&#xff01;且笔记仅供自身与大家学习使用&#xff0c;无利益目的。 第三讲 GNSS相关时间系统和转换 GPS卫星的位置在时间过程中是…

Java基础语法之内部类

什么是内部类 就是在一个类中又定义了另一个类 分类 实例内部类 即未被static修饰的内部类 1.外部类中的任何成员都可以在内部类里面直接访问&#xff0c;不管这个成员是什么权限 2.内部类对象的创建必须是在有外部类成员的前提下 这是错误的&#xff0c;那如何实例化呢&a…

你知道在MyBatis中传参的#{}和${}的区别吗???

首先我们先将其区别列举出来&#xff1a; 首先演示sql注入&#xff1a; 基于上两篇博客的准备工作&#xff0c;继续开发&#xff1a;MyBatis的删除、修改、插入操作&#xff01;&#xff01;&#xff01;-CSDN博客 #{}的使用 UserMapper.java: User testLogin(User user); U…

时序预测 | Python实现GRU-XGBoost组合模型电力需求预测

时序预测 | Python实现GRU-XGBoost组合模型电力需求预测 目录 时序预测 | Python实现GRU-XGBoost组合模型电力需求预测预测效果基本描述程序设计参考资料预测效果 基本描述 该数据集因其每小时的用电量数据以及 TSO 对消耗和定价的相应预测而值得注意,从而可以将预期预测与当前…

手把手教你搭建谷歌Gemini

前言 谷歌上周推出了一款名为 Gemini 的多模态大模型&#xff0c;并且现在发布了免费开放的 Gemini API 供开发者使用。根据谷歌提供的定价信息&#xff0c;Gemini 有两种收费方式。免费版本每分钟可以进行 60 次请求&#xff0c;足够满足个人用户的需求。收费版本目前暂不可用…

【无语】Microsoft Edge 浏览器不显示后台返回的数值数据

Microsoft Edge 禁用 JSON 视图 写在前面禁用 JSON 视图 写在前面 遇到一个有意思的事情&#xff0c;在用 Microsoft Edge 浏览器发送请求测试时发现&#xff0c;后端返回的数值数据没有正常展示&#xff0c;而是类似查看源码的结果&#xff0c;只显示了一个行号1&#xff0c;…

visual stdio code运行vue3

npm init vuelatest 该命令初始化vue项目 使用visual stdio code创建vue项目 ,这边是vue-project文件夹 vs code打开项目 vscode操作vue项目 vscode操作vue项目

Java 数据结构篇-实现二叉搜索树的核心方法

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 二叉搜索树的概述 2.0 二叉搜索树的成员变量及其构造方法 3.0 实现二叉树的核心接口 3.1 实现二叉搜索树 - 获取值 get(int key) 3.2 实现二叉搜索树 - 获取最小…

大创项目推荐 深度学习 大数据 股票预测系统 - python lstm

文章目录 0 前言1 课题意义1.1 股票预测主流方法 2 什么是LSTM2.1 循环神经网络2.1 LSTM诞生 2 如何用LSTM做股票预测2.1 算法构建流程2.2 部分代码 3 实现效果3.1 数据3.2 预测结果项目运行展示开发环境数据获取 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天…

升华 RabbitMQ:解锁一致性哈希交换机的奥秘【RabbitMQ 十】

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 升华 RabbitMQ&#xff1a;解锁一致性哈希交换机的奥秘【RabbitMQ 十】 前言第一&#xff1a;该插件需求为什么需要一种更智能的消息路由方式&#xff1f;一致性哈希的基本概念&#xff1a; 第二&…

很抱歉,Midjourney,但Leonardo AI的图像指导暂时还无人能及…至少目前是这样

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

【VScode】设置语言为中文

1、下载安装好vscode 2、此时可看到页面为英文&#xff0c;为方便使用可切换为中文 3、键盘按下 ctrlshiftP 4、在输入框内输入configure display language 5、选择中文&#xff0c;restart即可&#xff08;首次会有install安装过程&#xff0c;等待安装成功后重启即可&am…

【Python炫酷系列】一闪一闪亮星星,漫天都是小星星(完整代码)

文章目录 环境需求完整代码详细分析系列文章环境需求 python3.11.4及以上版本PyCharm Community Edition 2023.2.5pyinstaller6.2.0(可选,这个库用于打包,使程序没有python环境也可以运行,如果想发给好朋友的话需要这个库哦~)【注】 python环境搭建请见:https://want595.…

Python 爬虫之简单的爬虫(四)

爬取动态网页&#xff08;下&#xff09; 文章目录 爬取动态网页&#xff08;下&#xff09;前言一、大致内容二、基本思路三、代码编写1.引入库2.加载网页数据3.获取并保存4.保存文档 总结 前言 上篇主要讲了如何去爬取数据&#xff0c;这篇来讲一下如何在获取的同时将数据整…

利用canvas封装录像时间轴拖动(uniapp),封装上传uniapp插件市场

gitee项目地址,项目是一个空项目,其中包含了封装的插件,自己阅读,由于利用了canvas所以在使用中暂不支持.nvue,待优化; 项目也是借鉴了github上的一个项目,timeline-canvas,​​​​​​​ ​​​​​​​