本文主要是介绍【Intel校企合作课程】基于ResNet50的杂草检测,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
目录
- 1.项目简介
- 1.1项目描述
- 1.2数据集展示
- 1.3部分图像展示
- 1.4预期处理方案
- 2.数据预处理
- 2.1 数据集结构
- 2.2数据集提取
- 2.3构建数据集
- 3.使用ResNet50残差神经网络识别杂草
- 3.1ResNet网络简介
- 3.2ResNet50网络模型简介
- 3.3查看Pytorch提供的ResNet50模型
- 3.3自己定义ResNet50网络模型
- 3.4使用Pytorch提供的ResNet50预训练模型进行训练
- 3.5使用OneAPI组件进行加速
- 4.模型训练(迭代二十次)
- 5.计算推理时间和二分类准确度(F1分数)
- 6.模型预测
- 7.OneAPI组件的使用
- 8.项目总结
1.项目简介
1.1项目描述
杂草是农业经营中不受欢迎的入侵者,它们通过窃取营养、水、土地和其他关键资源来破坏种植,这些入侵者会导致产量下降和资源部署效率低下。一种已知的方法是使用杀虫剂来清除杂草,但杀虫剂会给人类带来健康风险。我们的目标是利用计算机视觉技术可以自动检测杂草的存在,开发一种只在杂草上而不是在作物上喷洒农药的系统,并使用针对性的修复技术将其从田地中清除,从而最小化杂草对环境的负面影响。
1.2数据集展示
数据集链接:https://filerepo.idzcn.com/hack2023/Weed_Detection5a431d7.zip
1.3部分图像展示
杂草图像:
标签:1 0.508789 0.489258 0.869141 0.861328
作物图像:
标签:0 0.478516 0.560547 0.847656 0.625000
1.4预期处理方案
我们期待您将其部署到模拟的生产环境中——这里推理时间和二分类准确度(F1分数)将作为评分的主要依据。
2.数据预处理
2.1 数据集结构
本项目数据集共由两部分组成,分别包含文件夹data和classes.txt。
data文件夹下包含了杂草和作物的图像以及它们的标签数据,如果标签是1开头则它就是杂草是0开头为作物。
2.2数据集提取
将文件名写入data.txt中:
# 指定图片所在的文件夹路径
image_folder = '../Weed_detection/data'# 获取文件夹下所有以.jpeg结尾的文件
image_files = [f for f in os.listdir(image_folder) if f.endswith('.jpeg') ]# 提取文件名的前缀并保存到data.txt
with open('../Weed_detection/data.txt', 'w') as file:for filename in image_files:# 获取文件名的前缀prefix = os.path.splitext(filename)[0]# 写入前缀到data.txt文件file.write(prefix + '\n')
提取部分结果展示:
对图片数据进行预处理,并将处理后的数据分为训练集和测试集。
transformer = transforms.Compose([transforms.ToTensor(),transforms.ColorJitter(contrast=0.5), # 增强对比度transforms.Normalize(mean=[0.5], std=[0.5]) # 归一化
])train_images_tensor = []
with open(r'../Weed_detection/data.txt','r') as f:file_name_url=[i.split('\n')[0] for i in f.readlines()]
for i in range(len(file_name_url)):image = Image.open('../Weed_detection/data/'+file_name_url[i]+'.jpeg')tensor = transformer(image.convert('L')).type(torch.float16)train_images_tensor.append(tensor)
image_train = []
image_test = []
for i in range(len(train_images_tensor)):if i <=len(train_images_tensor)*0.7:image_train.append(train_images_tensor[i])else:image_test.append(train_images_tensor[i])优化代码
读取文本文件中的标签数据,并将其转换为PyTorch张量格式,最后将数据分为训练集和测试集。
transformerlab = transforms.Compose([transforms.ToTensor()
])train_lables_tensor = []
with open(r'../Weed_detection/data.txt','r') as f:file_name_url=[i.split('\n')[0] for i in f.readlines()]
train_lables_tensor = []for i in range(len(file_name_url)):image = open('../Weed_detection/data/' + file_name_url[i] + '.txt')labels = image.readline()[0]labels = float(labels)tensor = torch.tensor(labels, dtype=torch.float16) # 使用float16数据类型train_lables_tensor.append(tensor)lables_train = []
lables_test = []
for i in range(len(train_lables_tensor)):if i <=len(train_lables_tensor)*0.7:lables_train.append(train_lables_tensor[i])else:lables_test.append(train_lables_tensor[i])
2.3构建数据集
rain_datas_tensor = torch.stack(image_train)
train_labels_tensor = torch.stack(lables_train)
test_datas_tensor = torch.stack(image_test)
test_labels_tensor = torch.stack(lables_test)
train_dataset = TensorDataset(train_labels_tensor, train_datas_tensor)
train_dataloader = DataLoader(train_dataset, batch_size=32, shuffle=True)
test_dataset = TensorDataset(test_labels_tensor, test_datas_tensor)
test_dataloader = DataLoader(test_dataset, batch_size=32, shuffle=True)
3.使用ResNet50残差神经网络识别杂草
3.1ResNet网络简介
残差连接通常以跳跃连接的形式实现,即将某一层的输出直接连接到下一层的输入。通过这种方式,网络的每一层都可以得到其输入和上一层输出的组合,从而增加了网络的非线性表达能力。如下图所示,假设输入图像为 x,输出为H(x),中间经过卷积之后的输出为F(x)的非线性函数,那最终的输出为H(x) = F(x) + x,这样的输出仍然可以进行非线性变换,残差指的是“差”,也就是F(x),而网络也就转化为求残差函数F(x) = H(x) - x,这样残差函数要比 F(x) = H (x) 更加容易优化。残差神经网络具有易于优化、能够有效训练深层的优点,因此在计算机视觉、自然语言处理等领域得到了广泛应用。
3.2ResNet50网络模型简介
如下图所展示的网络模型所示,ResNet50网络总共有50层分别是49层卷积层和1层全连接层。网络的输入是224x224x3,经过前面5部分的卷积计算可以得到7x7x2048,因为全连接层的输入不能直接是多维图像所以要将其转化为一个一维的特征向量,最后分类器对这个特征向量进行计算并输出类别的概率。
3.3查看Pytorch提供的ResNet50模型
model = models.resnet50(pretrained=True)
model.eval() # 设置模型为评估模式 # 打印模型结构
print(model)
残差网络结构:输入➡ 卷积层 ➡ 标准化层 ➡ 激活层 ➡ 最大池化层 ➡ 4个残差网络模块 ➡ 全局平均池化层 ➡ 全连接层 ➡输出
Pytorch提供的ResNet50网络结构:
卷积层:
标准化层:
激活层:
最大池化层:
Stage1有3个残差块:
Stage2有4个残差块:
Stage3有6个残差块:
Stage4有3个残差块:
全局平均池化层:
全连接层:
3.3自己定义ResNet50网络模型
import torch
import torch.nn as nn
import torch.nn.functional as F class BasicBlock(nn.Module): expansion = 1 def __init__(self, in_channels, out_channels, stride=1): super(BasicBlock, self).__init__() self.conv1 = nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(out_channels) self.conv2 = nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False) self.bn2 = nn.BatchNorm2d(out_channels) self.shortcut = nn.Sequential() if stride != 1 or in_channels != self.expansion*out_channels: self.shortcut = nn.Sequential( nn.Conv2d(in_channels, self.expansion*out_channels, kernel_size=1, stride=stride, bias=False), nn.BatchNorm2d(self.expansion*out_channels) ) def forward(self, x): out = F.relu(self.bn1(self.conv1(x))) out = self.bn2(self.conv2(out)) out += self.shortcut(x) out = F.relu(out) return out class ResNet(nn.Module): def __init__(self, block, num_blocks, num_classes=1000): super(ResNet, self).__init__() self.in_channels = 64 self.conv1 = nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False) self.bn1 = nn.BatchNorm2d(64) self.layer1 = self._make_layer(block, 64, num_blocks[0], stride=1) self.layer2 = self._make_layer(block, 128, num_blocks[1], stride=2) self.layer3 = self._make_layer(block, 256, num_blocks[2], stride=2) self.layer4 = self._make_layer(block, 512, num_blocks[3], stride=2) self.fc = nn.Linear(512*block.expansion, num_classes) def _make_layer(self, block, out_channels, num_blocks, stride): layers = [] layers.append(block(self.in_channels, out_channels, stride)) self.in_channels = out_channels * block.expansion for i in range(1, num_blocks): layers.append(block(self.in_channels, out_channels)) return nn.Sequential(*layers) def forward(self, x): out = F.relu(self.bn1(self.conv1(x))) out = self.layer1(out) out = self.layer2(out) out = self.layer3(out) out = self.layer4(out) out = F.avg_pool2d(out, 4) # change to global pooling if needed out = out.view(out.size(0), -1) # flatten the tensor to pass to fully connected layer out = self.fc(out) # fully connected layer (output layer) with softmax activation (if needed) return out
3.4使用Pytorch提供的ResNet50预训练模型进行训练
首先加载预训练的ResNet-50模型,更改第一个卷积层
# 加载预训练的ResNet-50模型
net = models.resnet50(pretrained=True) # 更改第一个卷积层的输入通道数
net.conv1 = nn.Conv2d(1, 64, kernel_size=7, stride=2, padding=3, bias=False) # 获取最后一个全连接层的输入特征数
num_features = net.fc.in_features # 替换最后一层全连接层为2个输出单元
net.fc = nn.Linear(num_features, 2) # 添加Softmax激活函数
net.add_module("softmax", nn.Softmax(dim=1)) device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
net.to(device)
net.float() criterion = nn.CrossEntropyLoss()
# 使用Adam优化器
optimizer = torch.optim.Adam(net.parameters(), lr=0.001)
3.5使用OneAPI组件进行加速
# 使用IPEX优化器优化模型
net,optimizer = ipex.optimize(net,optimizer=optimizer)
4.模型训练(迭代二十次)
for epoch in range(1, 20):running_loss = 0.0num_images = 0loop = tqdm(enumerate(train_dataloader, 0))for step, data in loop:labels, inputs = data[0].float(), data[1].float()optimizer.zero_grad()inputs = inputs.float()outputs = net(inputs)# 创建包含相同数量的目标值的示例目标张量target = labels # 使用实际标签作为目标# 使用 MSE 损失函数loss = criterion(outputs, target.long())loss.backward()optimizer.step()num_images += inputs.size(0)running_loss += loss.item()loop.set_description(f'Epoch [{epoch}/10]')loop.set_postfix(loss=running_loss / (step + 1))print('Compete training!!!')
5.计算推理时间和二分类准确度(F1分数)
因为电脑不是NVIDIA显卡不支持cuda环境所以无法使用GPU进行训练,只能用CPU训练时间较长。
from sklearn.metrics import f1_score
import time
correct = 0
total = 0
all_predictions = []
all_labels = []
# 开始推理
start_time = time.time()
with torch.no_grad():for data in test_dataloader:images, labels = data[1].to('cpu').float(), data[0].to('cpu').long() # 将标签转换为整数类型net = net.float() outputs = net(images)_, predicted = torch.max(outputs.data, 1)total += labels.size(0)correct += (predicted == labels).sum().item() all_predictions.extend(predicted.cpu().numpy())all_labels.extend(labels.cpu().numpy())
accuracy = 100 * correct / total
# 计算推理时间
inference_time = time.time() - start_time
print(f'Accuracy on test set: {accuracy:.2f}%')
print(f"Inference Time: {inference_time} seconds")
f1 = f1_score(all_labels, all_predictions, average='binary') # 适用于二分类问题
print(f'F1分数为: {f1:.4f}')
平均精度在97%以上
6.模型预测
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
import torchvision.transforms.functional as TF
import torch
import matplotlib.image as mpimg# 保存模型
PATH = './model_weights.pth'
net.load_state_dict(torch.load(PATH)) # 加载一张图片进行测试
image_path = 'data/agri_0_9952.jpeg'
image = Image.open(image_path).convert('L') # 替换为你的图片路径
image = TF.to_tensor(image) # 将图片转换为tensor
image = image.unsqueeze(0) # 添加批处理维度
image = image.to('cpu') # 转移到GPU上 # 在图片上进行预测
with torch.no_grad(): outputs = net(image) # outputs为预测结果,即各个类别的概率分布 _, predicted = torch.max(torch.abs(outputs), 1)# 获取概率最高的类别作为预测结果 print('Predicted:', predicted.item()) # 打印预测结果 if predicted.item() == 0: print('判断结果:作物') elif predicted.item() == 1: print('判断结果:杂草') else: print('预测结果不在0和1之间,可能存在错误')# 读取图片
img = mpimg.imread(image_path) # 显示图片
plt.imshow(img)
plt.show()
预测结果与标签一致
7.OneAPI组件的使用
OneAPI Math Kernel Library (MKL):ntel的数学核心函数库,包含一系列优化的数学函数,用于高性能计算和机器学习应用。
OneDNN:这是Intel的另一个高性能计算库,专门为深度学习应用提供优化。它提供了各种深度学习相关的操作,如卷积、池化、归一化等,都经过优化以提高运行速度。
8.项目总结
有很多模型都可以运用到杂草检测的案例中,本次使用的模型是ResNet50。通过自己重新搭建ResNet50的网络结构,对于这个模型也更加了解。使用了OneAPI组件进行加速,让推理时间有了一个很明显的减少。这次涉及到的组件主要是OneAPI Math Kernel Library和OneDNN,还有很多其它的组件没有使用到,后面也会去深入学习其他组件将它们更好的运用在项目中。
这篇关于【Intel校企合作课程】基于ResNet50的杂草检测的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!