news 2026/9/4 13:22:01

VGG-16复现马铃薯病害识别

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
VGG-16复现马铃薯病害识别
  • 我的环境:

  • 语言环境:Python 3.12.7
  • 编译器:jupyter notebook
  • 深度学习环境:TensorFlow 2.18.0

PyTorch version: 2.5.1+cu118
Torchvision version: 0.20.1+cu118


一、前期准备

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") device

import os,PIL,random,pathlib data_dir = './PotatoPlants/' data_dir = pathlib.Path(data_dir) data_paths = list(data_dir.glob('*')) classeNames = [str(path).split("\\")[1] for path in data_paths] classeNames

二、使用步骤

1.处理图片

  1. transforms.Resize([224, 224]):将输入图像调整到224x224像素的大小。这通常是为了适配预训练模型的输入尺寸。

  2. transforms.ToTensor():将PIL图像或NumPy数组转换为PyTorch张量(Tensor),并且自动将像素值从[0, 255]缩放到[0, 1]。

  3. transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]):对图像进行标准化处理,使其符合标准正态分布。这里的均值(mean)和标准差(std)是根据ImageNet数据集计算得出的,通常用于预训练模型。

# 关于transforms.Compose的更多介绍可以参考:https://blog.csdn.net/qq_38251616/article/details/124878863 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("E:/Jupyter/MR_K/7/PotatoPlants",transform=train_transforms) total_data

2.划分数据集

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

3.处理数据集

  1. train_datasettest_dataset:这些是包含训练和测试数据的数据集对象。你需要确保这些变量已经被正确定义并包含了相应的数据。

  2. batch_size=32:指定每个批次中包含的样本数量。在这个例子中,每个批次包含32个样本。

  3. shuffle=True:在每个epoch开始时,是否对数据进行随机打乱。这有助于模型训练的泛化能力。

  4. num_workers=1:指定加载数据时使用的子进程数量。这里设置为1,意味着数据加载将在主进程中进行。增加num_workers可以提高数据加载的效率,特别是在处理大型数据集时。

batch_size = 32 train_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) 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

4.配置模型

卷积块定义

每个卷积块由若干个卷积层、ReLU激活函数和最大池化层组成。这些层的配置如下:

  1. 卷积层(nn.Conv2d):用于提取图像特征。每个卷积层的参数包括输入通道数、输出通道数、卷积核大小、步长和填充。
  2. ReLU激活函数(nn.ReLU):用于引入非线性,帮助模型学习更复杂的特征。
  3. 最大池化层(nn.MaxPool2d):用于降低特征图的空间维度,减少参数数量和计算量。

全连接层分类器

在卷积块之后,你使用了一个全连接层分类器,它由三个全连接层组成:

  1. 第一个全连接层(nn.Linear):将特征图展平后映射到4096维。
  2. 第二个全连接层:将4096维映射到4096维。
  3. 第三个全连接层:将4096维映射到输出类别数(这里假设为3)。

前向传播

forward方法中,你定义了数据通过网络的流程:

  • 输入数据x依次通过五个卷积块。
  • 然后通过torch.flatten将多维的特征图展平成一维。
  • 最后,展平后的数据通过全连接层分类器。

设备选择

你检查了是否有可用的CUDA设备(GPU),如果有,则使用GPU进行计算,否则使用CPU。这是为了加速训练和推理过程。

模型实例化和设备分配

最后,你实例化了vgg16模型,并且将其移动到了选择的设备(GPU或CPU)上。

import torch.nn.functional as F class vgg16(nn.Module): def __init__(self): super(vgg16, self).__init__() # 卷积块1 self.block1 = nn.Sequential( nn.Conv2d(3, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(64, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) ) # 卷积块2 self.block2 = nn.Sequential( nn.Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(128, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) ) # 卷积块3 self.block3 = nn.Sequential( nn.Conv2d(128, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(256, 256, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) ) # 卷积块4 self.block4 = nn.Sequential( nn.Conv2d(256, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) ) # 卷积块5 self.block5 = nn.Sequential( nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.Conv2d(512, 512, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1)), nn.ReLU(), nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2)) ) # 全连接网络层,用于分类 self.classifier = nn.Sequential( nn.Linear(in_features=512*7*7, out_features=4096), nn.ReLU(), nn.Linear(in_features=4096, out_features=4096), nn.ReLU(), nn.Linear(in_features=4096, out_features=3) ) def forward(self, x): x = self.block1(x) x = self.block2(x) x = self.block3(x) x = self.block4(x) x = self.block5(x) x = torch.flatten(x, start_dim=1) x = self.classifier(x) return x device = "cuda" if torch.cuda.is_available() else "cpu" print("Using {} device".format(device)) model = vgg16().to(device) model

# 统计模型参数量以及其他指标 import torchsummary as summary summary.summary(model, (3, 224, 224))

5.循环训练

# 训练循环 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_loss
def 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_loss

6.训练和测试循环

  1. 初始化优化器和损失函数

    • 使用 Adam 优化器,学习率设置为1e-4
    • 使用交叉熵损失函数nn.CrossEntropyLoss
  2. 设置训练周期

    • epochs设置为 40,即模型将训练 40 个周期。
  3. 初始化记录列表

    • train_losstrain_acctest_losstest_acc用于记录每个周期的训练损失、训练准确率、测试损失和测试准确率。
  4. 最佳准确率初始化

    • best_acc初始化为 0,用于记录测试准确率最高的模型。
  5. 训练和测试周期

    • 对于每个周期,执行以下步骤:
      • 将模型设置为训练模式model.train()
      • 调用train函数进行训练,并记录训练准确率和损失。
      • 将模型设置为评估模式model.eval()
      • 调用test函数进行测试,并记录测试准确率和损失。
      • 如果当前周期的测试准确率超过best_acc,则更新best_acc并深拷贝当前模型到best_model
  6. 记录和打印信息

    • 将每个周期的准确率和损失添加到相应的列表中。
    • 打印当前周期的信息,包括周期编号、训练准确率、训练损失、测试准确率、测试损失和当前学习率。
  7. 保存最佳模型

    • 训练结束后,将最佳模型的参数保存到文件best_model.pth中。

注意事项和建议

  • 确保train_dltest_dl已经正确初始化,并且包含正确的数据。
  • 确保device变量已经定义,并且模型和数据已经移动到正确的设备上。
  • 在保存模型时,你保存的是model的状态字典,而不是整个模型对象。这是推荐的做法,因为它只保存模型参数,不包含模型架构。
  • 在打印信息时,你使用了格式化字符串来清晰地展示每个周期的结果。
  • 你可以考虑添加一个学习率调度器,例如torch.optim.lr_scheduler.StepLR,以在训练过程中调整学习率。
import copy optimizer = torch.optim.Adam(model.parameters(), lr= 1e-4) loss_fn = nn.CrossEntropyLoss() # 创建损失函数 epochs = 40 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(epoch+1, 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')

7.检测图像

函数参数

  • image_path:要预测的图片的文件路径。
  • model:已经训练好的 PyTorch 模型。
  • transform:用于将图片转换为模型输入格式的转换序列。
  • classes:类别名称列表,通常从数据集的class_to_idx属性中获取。

预测过程

  1. 加载图片

    • 使用PIL.Image.open加载图片,并将其转换为 RGB 格式。
  2. 展示图片

    • 使用matplotlib.pyplot.imshow展示图片。注意,这里需要导入matplotlib库。
  3. 应用转换

    • 使用transform将图片转换为模型可以接受的格式。
  4. 数据移动到设备

    • 将转换后的图片移动到指定的设备(GPU或CPU)。
  5. 模型评估模式

    • 将模型设置为评估模式model.eval(),以关闭 dropout 和 batch normalization 等仅在训练时使用的层。
  6. 模型预测

    • 使用模型对图片进行预测,得到输出output
  7. 获取预测结果

    • 使用torch.max获取预测结果中概率最高的类别索引。
    • 根据索引从classes列表中获取对应的类别名称。
  8. 打印预测结果

    • 打印预测的类别名称。
from PIL import Image classes = list(total_data.class_to_idx) def predict_one_image(image_path, model, transform, classes): test_img = Image.open(image_path).convert('RGB') plt.imshow(test_img) # 展示预测的图片 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}')


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 13:19:36

Word启动优化:隐藏导航栏实现简洁高效编辑界面

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 13:17:02

GPT镜像服务技术解析:反向代理架构与Python实践指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 13:16:39

KOReader 上手指南:E-ink 阅读器装好之后,先做这4件事

KOReader 上手指南:E-ink 阅读器装好之后,先做这4件事 【免费下载链接】koreader An ebook reader application supporting PDF, DjVu, EPUB, FB2 and many more formats, running on Cervantes, Kindle, Kobo, PocketBook and Android devices 项目地…

作者头像 李华
网站建设 2026/9/4 13:16:37

Maple Mono 字体教程:三步装好这款圆角连字 Nerd Font

Maple Mono 字体教程:三步装好这款圆角连字 Nerd Font 【免费下载链接】maple-font Maple Mono: Open source monospace font with round corner, ligatures and Nerd-Font icons for IDE and terminal, fine-grained customization options. 带连字和控制台图标的圆…

作者头像 李华
网站建设 2026/9/4 13:16:35

Cinema Studio免费上手:3组参数拍出电影级镜头效果

Cinema Studio免费上手:3组参数拍出电影级镜头效果 【免费下载链接】Open-Generative-AI Unrestricted Open-source alternative to AI video platforms — Free AI image & video generation studio with 600 models (Flux, Midjourney, Kling, Sora, Veo). No…

作者头像 李华