ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

YOLOv5-C3模块实现

YOLOv5-C3模块实现 我的环境语言环境Python 3.12.7编译器jupyter notebook深度学习环境TensorFlow 2.18.0一、导入数据1.引入库import torch import torch.nn as nn import torchvision.transforms as transforms import torchvision from torchvision import transforms, datasets import os,PIL,pathlib,warnings warnings.filterwarnings(ignore) #忽略警告信息 device torch.device(cuda if torch.cuda.is_available() else cpu) device2.读入import pathlib # 使用 pathlib 处理路径 data_dir pathlib.Path(C:/Users/27437/Desktop/Data/3weather_photos/) # 获取所有子文件夹的路径 sub_dirs [d for d in data_dir.iterdir() if d.is_dir()] # 提取类别名称这里假设类别名称是文件夹名 classeNames [d.name for d in sub_dirs] # 打印类别名称 print(classeNames)3.数据处理train_transforms transforms.Compose([ transforms.Resize([224, 224]), # 将输入图片resize成统一尺寸 # transforms.RandomHorizontalFlip(), # 随机水平翻转 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] 从数据集中随机抽样计算得到的。 ]) test_transform 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(C:/Users/27437/Desktop/Data/3weather_photos/,transformtrain_transforms) total_datatotal_data.class_to_idx4. 划分数据集train_size int(0.8 * len(total_data)) test_size len(total_data) - train_size train_dataset, test_dataset torch.utils.data.random_split(total_data, [train_size, test_size]) train_dataset, test_dataset创建数据加载器DataLoader并遍历测试数据集test_dl以打印出一批数据的形状和标签的信息batch_size 4 train_dl torch.utils.data.DataLoader(train_dataset, batch_sizebatch_size, shuffleTrue, num_workers1) test_dl torch.utils.data.DataLoader(test_dataset, batch_sizebatch_size, shuffleTrue, num_workers1) 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二、搭建模型1. 搭建模型import torch.nn.functional as F def autopad(k, pNone): # kernel, padding # Pad to same if p is None: p k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad return p class Conv(nn.Module): # Standard convolution def __init__(self, c1, c2, k1, s1, pNone, g1, actTrue): # ch_in, ch_out, kernel, stride, padding, groups super().__init__() self.conv nn.Conv2d(c1, c2, k, s, autopad(k, p), groupsg, biasFalse) 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 bottleneck def __init__(self, c1, c2, shortcutTrue, g1, e0.5): # ch_in, ch_out, shortcut, groups, expansion super().__init__() c_ int(c2 * e) # hidden channels self.cv1 Conv(c1, c_, 1, 1) self.cv2 Conv(c_, c2, 3, 1, gg) self.add shortcut and c1 c2 def 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 convolutions def __init__(self, c1, c2, n1, shortcutTrue, g1, e0.5): # ch_in, ch_out, number, shortcut, groups, expansion super().__init__() c_ int(c2 * e) # hidden channels self.cv1 Conv(c1, c_, 1, 1) self.cv2 Conv(c1, c_, 1, 1) self.cv3 Conv(2 * c_, c2, 1) # actFReLU(c2) self.m nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e1.0) for _ in range(n))) def forward(self, x): return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim1)) class model_K(nn.Module): def __init__(self): super(model_K, self).__init__() # 卷积模块 self.Conv Conv(3, 32, 3, 2) # C3模块1 self.C3_1 C3(32, 64, 3, 2) # 全连接网络层用于分类 self.classifier nn.Sequential( nn.Linear(in_features802816, out_features100), nn.ReLU(), nn.Linear(in_features100, out_features4) ) def forward(self, x): x self.Conv(x) x self.C3_1(x) x torch.flatten(x, start_dim1) x self.classifier(x) return x device cuda if torch.cuda.is_available() else cpu print(Using {} device.format(device)) model model_K().to(device) model自动填充函数def autopad(k, pNone): # kernel, padding # Pad to same if p is None: p k // 2 if isinstance(k, int) else [x // 2 for x in k] # auto-pad return p这个函数用于自动计算卷积层的填充padding以保持输入和输出的空间维度相同same padding。如果p参数未指定则根据卷积核大小k自动计算填充值。标准卷积层类class Conv(nn.Module): # Standard convolution def __init__(self, c1, c2, k1, s1, pNone, g1, actTrue): # ch_in, ch_out, kernel, stride, padding, groups super().__init__() self.conv nn.Conv2d(c1, c2, k, s, autopad(k, p), groupsg, biasFalse) 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)))这个类定义了一个标准的卷积层包括卷积操作、批量归一化Batch Normalization和激活函数默认为 SiLU也称为 FReLU。c1和c2分别是输入和输出的通道数k是卷积核大小s是步长p是填充g是组数。瓶颈层类class Bottleneck(nn.Module): # Standard bottleneck def __init__(self, c1, c2, shortcutTrue, g1, e0.5): # ch_in, ch_out, shortcut, groups, expansion super().__init__() c_ int(c2 * e) # hidden channels self.cv1 Conv(c1, c_, 1, 1) self.cv2 Conv(c_, c2, 3, 1, gg) self.add shortcut and c1 c2 def forward(self, x): return x self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))这个类定义了一个瓶颈层它通过两个卷积层一个 1x1 和一个 3x3减少和恢复通道数同时根据shortcut参数决定是否添加输入和输出。C3 类class C3(nn.Module): # CSP Bottleneck with 3 convolutions def __init__(self, c1, c2, n1, shortcutTrue, g1, e0.5): # ch_in, ch_out, number, shortcut, groups, expansion super().__init__() c_ int(c2 * e) # hidden channels self.cv1 Conv(c1, c_, 1, 1) self.cv2 Conv(c1, c_, 1, 1) self.cv3 Conv(2 * c_, c2, 1) # actFReLU(c2) self.m nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e1.0) for _ in range(n))) def forward(self, x): return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim1))这个类定义了一个包含三个卷积层的 CSPCross Stage Partial瓶颈层其中两个 1x1 卷积层和一个 3x3 卷积层交替使用中间通过多个瓶颈层连接。模型类class model_K(nn.Module): def __init__(self): super(model_K, self).__init__() # 卷积模块 self.Conv Conv(3, 32, 3, 2) # C3模块1 self.C3_1 C3(32, 64, 3, 2) # 全连接网络层用于分类 self.classifier nn.Sequential( nn.Linear(in_features802816, out_features100), nn.ReLU(), nn.Linear(in_features100, out_features4) ) def forward(self, x): x self.Conv(x) x self.C3_1(x) x torch.flatten(x, start_dim1) x self.classifier(x) return x这个类定义了整个模型包括一个卷积层、一个 C3 层和一个全连接层。全连接层用于将特征图转换为类别预测。设备选择device cuda if torch.cuda.is_available() else cpu print(Using {} device.format(device))这段代码检查是否有可用的 CUDA 设备GPU如果有则使用 GPU否则使用 CPU。模型初始化和设备分配model model_K().to(device) model这段代码初始化模型并将模型移动到指定的设备上。# 统计模型参数量以及其他指标 import torchsummary as summary summary.summary(model, (3, 224, 224))三、 训练模型1. 编写训练函数# 训练循环 def train(dataloader, model, loss_fn, optimizer): size len(dataloader.dataset) # 训练集的大小 num_batches len(dataloader) # 批次数目, (size/batch_size向上取整) train_loss, train_acc 0, 0 # 初始化训练损失和正确率 for X, y in dataloader: # 获取图片及其标签 X, y X.to(device), y.to(device) # 计算预测误差 pred model(X) # 网络输出 loss loss_fn(pred, y) # 计算网络输出和真实值之间的差距targets为真实值计算二者差值即为损失 # 反向传播 optimizer.zero_grad() # grad属性归零 loss.backward() # 反向传播 optimizer.step() # 每一步自动更新 # 记录acc与loss train_acc (pred.argmax(1) y).type(torch.float).sum().item() train_loss loss.item() train_acc / size train_loss / num_batches return train_acc, train_lossdef test (dataloader, model, loss_fn): size len(dataloader.dataset) # 测试集的大小 num_batches len(dataloader) # 批次数目, (size/batch_size向上取整) test_loss, test_acc 0, 0 # 当不进行训练时停止梯度更新节省计算内存消耗 with torch.no_grad(): for imgs, target in dataloader: imgs, target imgs.to(device), target.to(device) # 计算loss target_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 / size test_loss / num_batches return test_acc, test_loss2. 正式训练import copy optimizer torch.optim.Adam(model.parameters(), lr 1e-4) loss_fn nn.CrossEntropyLoss() # 创建损失函数 epochs 20 train_loss [] train_acc [] test_loss [] test_acc [] best_acc 0 # 设置一个最佳准确率作为最佳模型的判别指标 for epoch in range(epochs): model.train() epoch_train_acc, epoch_train_loss train(train_dl, model, loss_fn, optimizer) model.eval() epoch_test_acc, epoch_test_loss test(test_dl, model, loss_fn) # 保存最佳模型到 best_model if epoch_test_acc best_acc: best_acc epoch_test_acc best_model copy.deepcopy(model) 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 optimizer.state_dict()[param_groups][0][lr] template (Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}) print(template.format(epoch1, 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)四、 结果可视化1. Loss与Accuracy图import matplotlib.pyplot as plt #隐藏警告 import warnings warnings.filterwarnings(ignore) #忽略警告信息 plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus] False # 用来正常显示负号 plt.rcParams[figure.dpi] 100 #分辨率 epochs_range range(epochs) plt.figure(figsize(12, 3)) plt.subplot(1, 2, 1) plt.plot(epochs_range, train_acc, labelTraining Accuracy) plt.plot(epochs_range, test_acc, labelTest Accuracy) plt.legend(loclower right) plt.title(Training and Validation Accuracy) plt.subplot(1, 2, 2) plt.plot(epochs_range, train_loss, labelTraining Loss) plt.plot(epochs_range, test_loss, labelTest Loss) plt.legend(locupper right) plt.title(Training and Validation Loss) plt.show()2. 模型评估best_model.eval() epoch_test_acc, epoch_test_loss test(test_dl, best_model, loss_fn) epoch_test_acc, epoch_test_lossepoch_test_acc总结提示这里对文章进行总结例如以上就是今天要讲的内容本文仅仅简单介绍了pandas的使用而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。
返回列表