木薯树叶病识别与模型集成
广西多丘陵旱地,自然条件适宜木薯生产。广西一直大面积种植木薯,保持着全国木薯
生产前列的地位。木薯不仅是农民收入的主要来源,而且其加工产品丰富,产业链条长,经
济影响面广。然而,木薯病虫害诊断和防治问题给种植木薯的农民生产带来了巨大的挑战。
主要问题如下:
(1)农业专家的离线问诊,存在时效性不高的问题;
(2)农业专家的在线问诊,存在着农民拍摄的图片专业度不高,影响了专家在线问诊判别木薯病虫害的精度。
针对上述问题,基于 VGG-16 深度学习神经网络设计了识别出四种疾病、木薯健康
的叶子和非木薯叶子(共六类)的模型,通过大数据技术、云计算技术的帮助下,成功运行
模型识别分类了一万五千多张木薯图片。
原始数据集包含两个文件夹
1 包含所有图片的data文件夹
2 包含所有图片的名字和分类的csv文件,共6个类别
代码如下(示例):
os.makedirs('./data-raw/train/0')
os.makedirs('./data-raw/train/1')
os.makedirs('./data-raw/train/2')
os.makedirs('./data-raw/train/3')
os.makedirs('./data-raw/train/4')
os.makedirs('./data-raw/train/5')
os.makedirs('./data-raw/vel/0')
os.makedirs('./data-raw/vel/1')
os.makedirs('./data-raw/vel/2')
os.makedirs('./data-raw/vel/3')
os.makedirs('./data-raw/vel/4')
os.makedirs('./data-raw/vel/5')
读取train.csv文件
将image_id以列表保存到data_img_name
将label以列表保存到label
注意要去掉第一行
代码如下(示例):
import csv
f_csv = open('./data/raw/2021hwjx/train.csv', "r")
read = csv.reader(f_csv)
a = list(read)
data_img_name = []
label = []
for i in a:
data_img_name.append(i[0])
label.append(i[1])
data_img_name.remove('image_id')
label.remove('label')
将列表full_list按比例ratio(随机)划分为2个子列表sublist_1与sublist_2
:param full_list: 数据列表
:param ratio: 子列表1
:param shuffle: 子列表2
:return:
代码如下(示例):
def data_split(full_list, ratio, shuffle=False):
n_total = len(full_list)
offset = int(n_total * ratio)
if n_total == 0 or offset < 1:
return [], full_list
if shuffle:
random.shuffle(full_list)
sublist_1 = full_list[:offset]
sublist_2 = full_list[offset:]
return sublist_1, sublist_2
对数据集进行切片分类,按比例划分 验证集:训练集=2:8
对每个类别随机选取80%再合并到一个列表
(可直接从csv表格看出每个类别分别到第几行结束)
代码如下(示例):
train_0 = data_img_name[:978]
test_data0, train_data0 = data_split(train_0, ratio=0.2)
test_label_0 = [0]*len(test_data0)
train_label_0 = [0]*len(train_data0)
train_1 = data_img_name[978:2948]
test_data1, train_data1 = data_split(train_1, ratio=0.2)
test_label_1 = [1]*len(test_data1)
train_label_1 = [1]*len(train_data1)
train_2 = data_img_name[2948:5095]
test_data2, train_data2 = data_split(train_2, ratio=0.2)
test_label_2 = [2]*len(test_data2)
train_label_2 = [2]*len(train_data2)
train_3 = data_img_name[5095:10637]
test_data3, train_data3 = data_split(train_3, ratio=0.2)
test_label_3 = [3]*len(test_data3)
train_label_3 = [3]*len(train_data3)
train_4 = data_img_name[10637:12956]
test_data4, train_data4 = data_split(train_4, ratio=0.2)
test_label_4 = [4]*len(test_data4)
train_label_4 = [4]*len(train_data4)
train_5 = data_img_name[12956:]
test_data5, train_data5 = data_split(train_5, ratio=0.2)
test_label_5 = [5]*len(test_data5)
train_label_5 = [5]*len(train_data5)
# 训练集
train_data = train_data0 + train_data1 + train_data2 + train_data3 + train_data4 + train_data5
train_label = train_label_0 + train_label_1 + train_label_2 + train_label_3 + train_label_4 + train_label_5
# 验证集
test_data = test_data0 + test_data1 + test_data2 + test_data3 + test_data4 + test_data5
test_label = test_label_0 + test_label_1 + test_label_2 + test_label_3 + test_label_4 + test_label_5
将原始图片复制到分好类别的文件夹下
代码如下(示例):
import shutil
root_dir = './data/raw/2021hwjx/train_img'
a = 6
train_range = []
for j in range(a):
if j == 0:
train_range = train_data0
if j == 1:
train_range = train_data1
if j == 2:
train_range = train_data2
if j == 3:
train_range = train_data3
if j == 4:
train_range = train_data4
if j == 5:
train_range = train_data5
for i in range(len(train_range)):
img_dir = os.path.join(root_dir, train_range[i])
shutil.move(img_dir, './data-raw/train/{}'.format(j))
for j in range(a):
if j == 0:
test_range = test_data0
if j == 1:
test_range = test_data1
if j == 2:
test_range = test_data2
if j == 3:
test_range = test_data3
if j == 4:
test_range = test_data4
if j == 5:
test_range = test_data5
for i in range(len(test_range)):
img_dir = os.path.join(root_dir, test_range[i])
shutil.move(img_dir, './data-raw/vel/{}'.format(j))
对原始进行变换
包括裁剪
归一化
将PIL image转化为VGG16需要的tensor型
代码如下(示例):
from torch.utils.data import DataLoader
from torchvision import datasets, transforms
import torchvision
batch_size = 16
train_transforms = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((.5, .5, .5), (.5, .5, .5))
])
val_transforms = transforms.Compose([
transforms.RandomResizedCrop(224),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((.5, .5, .5), (.5, .5, .5))
])
train_dir = './data-raw/train'
train_datasets = datasets.ImageFolder(train_dir, transform=train_transforms)
train_data_loader = torch.utils.data.DataLoader(train_datasets, batch_size=batch_size, shuffle=True)
val_dir = './data-raw/vel'
val_datasets = datasets.ImageFolder(val_dir, transform=val_transforms)
val_data_loader = torch.utils.data.DataLoader(val_datasets, batch_size=batch_size, shuffle=True)
下载VGG16预训练模型
对模型经行修改
加上一层1000输入6输出的线性层,使其满足我们的分类需求
代码如下(示例):
import torchvision
from torch import nn
vgg16_true = torchvision.models.vgg16(pretrained=True)
vgg16_true.classifier.add_module('add_liner', nn.Linear(1000, 6))
包括是否支持GPU加速
学习速率
代码如下(示例):
if torch.cuda.is_available():
vgg16_true = vgg16_true.cuda()
loss_fn = nn.CrossEntropyLoss()
if torch.cuda.is_available():
loss_fn = loss_fn.cuda()
learning_rate = 0.0001 # 学习速率
optimizer = torch.optim.SGD(vgg16_true.parameters(), lr=learning_rate)
代码如下(示例):
from torch.utils.tensorboard import SummaryWriter
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 100
accuracy_bass = 0
train_data_size = len(train_datasets)
test_data_size = len(val_datasets)
# 添加tensorboard
writer = SummaryWriter("./logs")
for i in range(epoch):
print("-------第 {} 轮训练开始-------".format(i+1))
# 训练步骤开始
vgg16_true.train()
for data in train_data_loader:
imgs, targets = data
imgs = imgs.cuda()
targets = targets.cuda()
outputs = vgg16_true(imgs)
loss = loss_fn(outputs, targets)
# 优化器优化模型
optimizer.zero_grad()
loss.backward()
optimizer.step()
total_train_step = total_train_step + 1
if total_train_step % 100 == 0:
print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
# 测试步骤开始
vgg16_true.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad():
for data in val_data_loader:
imgs, targets = data
imgs = imgs.cuda()
targets = targets.cuda()
outputs = vgg16_true(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum()
total_accuracy = total_accuracy + accuracy
print("整体测试集上的Loss: {}".format(total_test_loss))
total = total_accuracy/test_data_size
print("整体测试集上的正确率: {}".format(total))
writer.add_scalar("test_loss", total_test_loss, total_test_step)
writer.add_scalar("test_accuracy", total, total_test_step)
total_test_step = total_test_step + 1
torch.save(vgg16_true, "./model_zoo/vgg16_true_{}.pth".format(i))
print("模型已保存")
writer.close()
将需要预测的图片放到docs文件下,需要重新预测图片时从这开始
代码如下(示例):
# 将需要预测的图片放到docs文件下,需要重新预测图片时从这开始
import torchvision
from PIL import Image
import os
imge_name = "58780369.jpg" # 要预测的图片的名字
root_dir = "./docs"
imge_path = os.path.join(root_dir, imge_name)
image_0 = Image.open(imge_path)
print(image_0)
imge_transforms = torchvision.transforms.Compose([
transforms.Resize((224, 224)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((.5, .5, .5), (.5, .5, .5))
])
image = imge_transforms(image_0)
print(image.shape)
image = torch.reshape(image, (-1, 3, 224, 224))
model = vgg16_true
model = torch.load("./model_zoo/vgg16_true_45.pth", map_location=torch.device('cpu'))
model.eval()
with torch.no_grad():
output = model(image)
print(output.argmax(1))
无