1-5.基于paddle2.0的木薯叶病分类

原创
2020/12/21 14:14
阅读数 618

数据来源于kaggle上的木薯病叶分类比赛,这里用paddle2.0实现一个训练部分的baseline方案,以及使用VDL2.0工具辅助分析。

比赛链接:木薯叶病分类

数据集下载:木薯叶病分类数据集

可一键fork运行的项目地址:木薯叶病分类

下载安装命令

## CPU版本安装命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle

## GPU版本安装命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu

项目简介:

  • 一.数据准备:
    • 这边只有训练集,比赛测试集是隐藏的,预测部分需要在kaggle上的notebook环境中完成。因此,该项目中从训练集中划分一部分数据作为验证集(测试集)。
  • 二.数据预处理
  • 三.模型搭建(使用paddle2.0rc版本)
  • 四.模型训练
  • 五.用VisualDL2.0可视化数据
  • 六.总结
#引用需要用到的库
import numpy as np
import glob
import cv2
import os
import pandas as pd
import matplotlib.pyplot as plt
import shutil
import paddle
import math
from paddle.static import InputSpec
from visualdl import LogWriter

 

#解压数据集
!unzip data/data62371/cassava-leaf-disease-classification.zip -d work/dataset

一.数据准备

  • 共有21397个样本,图片大小为:(600, 800, 3)
  • 类别是不均衡的
    • 0 -> 1087
    • 1 -> 2189
    • 2 -> 2386
    • 3 -> 13158
    • 4 -> 2577
  • 原数据已打乱,从中划分出前3000个样本作为验证集
class Data():
    def __init__(self, 
        file_path="/home/aistudio/work/dataset",
        is_train=True,
        label_file="train.csv",
        new_train_path = "new_train",
        new_val_path = "new_val",
        image_path="train_images"):

        self.file_path = file_path
        #创建训练集、验证集目录
        if not os.path.exists(os.path.join(file_path, new_train_path)):
            os.mkdir(os.path.join(file_path, new_train_path))
        if not os.path.exists(os.path.join(file_path, new_val_path)):
            os.mkdir(os.path.join(file_path, new_val_path)) 

        datas = pd.read_csv(os.path.join(file_path, label_file))#读取图片和标签
        print("样本数:", datas.shape)#(样本数,2列),第一列为图片路径,第二列为图片类别
        print("图片类别:对应样本个数")
        print(datas.label.value_counts())#统计类别个数
        
        '''
        划分训练集和验证集。并且将数据按照 root/class_a/1.ext 格式存储,用于DatasetFolder方法读取数据
        csv文件中的图片顺序已经是打乱过的,不需要重新打乱               
        '''
        for index,row in datas.iterrows():
            #划分前3000张图片作为验证集
            if index < 3000:
                #创建每一个类别的文件夹
                if not os.path.exists(os.path.join(file_path, new_val_path, str(row[-1]))):
                    os.mkdir(os.path.join(file_path, new_val_path, str(row[-1])))
                #将原路径下的数据复制到新路径下
                shutil.move(os.path.join(file_path, image_path, str(row[0])),os.path.join(file_path, new_val_path, str(row[-1]), str(row[0])))
            else:
                #创建每一个类别的文件夹
                if not os.path.exists(os.path.join(file_path, new_train_path, str(row[-1]))):
                    os.mkdir(os.path.join(file_path, new_train_path, str(row[-1])))
                #将原路径下的数据复制到新路径下
                shutil.move(os.path.join(file_path, image_path, str(row[0])),os.path.join(file_path, new_train_path, str(row[-1]), str(row[0])))

    #查看图像
    def show_img(self):
        to_bgr = cv2.imread(os.path.join(self.file_path, "test_images/2216849948.jpg"))
        print("图片大小信息:", to_bgr.shape)
        to_rgb = to_bgr[:, :, ::-1]#读取是按照bgr格式读取,转化为rgb格式
        plt.imshow(to_rgb)
        plt.show()

data = Data()
data.show_img()        

二.数据预处理

  • 定义数据读取类,继承paddle.io.Dataset类
  • 定义构造方法
  • 重写__getitem__方法、__len__方法
from paddle.io import Dataset
from paddle.vision.datasets import DatasetFolder
from paddle.vision.transforms import Compose, Resize, Transpose
class CassavaDataset(Dataset):
    """
    步骤一:继承paddle.io.Dataset类
    """
    def __init__(self,
        train_dir = "/home/aistudio/work/dataset/new_train",
        val_dir = "/home/aistudio/work/dataset/new_val",
        is_test = False,
        is_train = True):
        """
        步骤二:实现构造函数,定义数据读取方式,划分训练和测试数据集
        """
        super(CassavaDataset, self).__init__()

        transform_train = Compose([Resize(size=(600,800)), Transpose()])
        transform_eval = Compose([Resize(size=(600,800)), Transpose()])
        train_data_folder = DatasetFolder(train_dir, transform=transform_train)
        val_data_folder = DatasetFolder(val_dir, transform=transform_eval)
        self.is_train = is_train
        self.is_test = is_test
        if self.is_train  == True:
            self.data = train_data_folder
        elif self.is_train  == False:
            self.data = val_data_folder


    def __getitem__(self, index):
        """
        步骤三:实现__getitem__方法,定义指定index时如何获取数据,并返回单条数据(训练数据,对应的标签)
        """
        data = np.array(self.data[index][0]).astype('float32')

        if self.is_test  == True:
            return None
        else:
            label = np.array([self.data[index][1]]).astype('int64')
            return data, label

    def __len__(self):
        """
        步骤四:实现__len__方法,返回数据集总数目
        """
        return len(self.data)

# 测试定义的数据集
train_dataset = CassavaDataset()
val_dataset = CassavaDataset(is_train = False)
num = 0
print('=============train dataset=============')
for data, label in train_dataset:
    #num += 1
    print(data, label)
    break
#print(num)
num = 0
print('=============evaluation dataset=============')
for data, label in val_dataset:
    #num += 1
    print(data, label)
    break
#print(num)

三,模型搭建

#初始化模型
#使用内置的模型,这边可以选择多种不同网络
Cassava = paddle.vision.models.mobilenet_v1(pretrained=True, num_classes=5)
#创建LeNet日志文件
writer = LogWriter("./work/log")

 

## 查看模型结构
model = paddle.Model(Cassava)
print('飞桨框架内置模型:', paddle.vision.models.__all__)
model.summary((-1, 3, 600, 800))

四.模型训练

#定义数据读取器
batch_size = 32
train_loder =  paddle.io.DataLoader(train_dataset, places=paddle.CUDAPlace(0), batch_size=batch_size, shuffle=True)
#开启训练
Cassava.train()
#设置epoch数
epochs = 5
step = 0
#定义优化器
opt = paddle.optimizer.Adam(learning_rate=0.001, parameters = Cassava.parameters())
#定义损失函数
loss_fn = paddle.nn.CrossEntropyLoss()

for epoch in range(epochs):
    for batch_id, data in enumerate(train_loder()):
        #分出img和label
        x_data = data[0]
        y_data = data[1]
        #预测结果
        predict = Cassava(x_data)
        #传入损失函数
        loss = loss_fn(predict, y_data)
        #loss_sum += loss.numpy().sum()
        #查看acc
        acc = paddle.metric.accuracy(predict, y_data)
        #acc_sum += acc.numpy().sum()
        #反向传播
        loss.backward()
        #打印输出
        if batch_id % 20 is 0:
            print("epoch:{}, batch:{}, loss:{}, acc:{}".format(epoch, batch_id, loss.numpy(), acc.numpy()))

        #生成VDL日志
        step += 1
        if step % 20 is 0:
            #添加acc
            writer.add_scalar(tag="train/acc", step=step, value=float(acc.numpy()))
            #添加loss
            writer.add_scalar(tag="train/loss", step=step, value=float(loss.numpy()))
            
            #记录每一个批次第一张图片
            img = np.reshape(np.array(data[0][0].numpy()), [600, 800, 3])
            writer.add_image(tag="train/input", step=step, img=img)

            '''
            #画P-R曲线
            labels = np.array(data, dtype='int32')[0, 1]
            prediction = np.where(np.squeez(np.array(predict[0, 1])) == 1)
            writer.add_pr_curve(tag='train/class_{}_pr_curve'.format(i),
                labels=labels,
                predictions=prediction,
                step=step,
                num_thresholds=20)
            '''

        #更新梯度
        opt.step()
        #清除梯度
        opt.clear_grad()
    
    #保存模型参数和优化器参数
    if not os.path.exists(os.path.join("/home/aistudio/work/save_model")):
        os.mkdir(os.path.join("/home/aistudio/work/save_model"))
    if not os.path.exists(os.path.join("/home/aistudio/work/save_model", str(epoch))):
        os.mkdir(os.path.join("/home/aistudio/work/save_model", str(epoch)))
    paddle.save(Cassava.state_dict(), os.path.join("work/save_model", str(epoch), str(epoch) + ".pdparams"))
    paddle.save(opt.state_dict(), os.path.join("work/save_model", str(epoch), str(epoch) + ".pdopt"))
    #保存模型结构
    #fluid.io.save_inference_model(dirname=os.path.join("work/save_model", str(epoch)), feeded_var_names=['img'],target_vars=[predictions], executor=exe)

 

## 模型在验证集上评估
#模型载入
Cassava_state_dict = paddle.load("/home/aistudio/work/save_model/4/4.pdparams")
opt_state_dict = paddle.load("/home/aistudio/work/save_model/4/4.pdopt")
Cassava.set_state_dict(Cassava_state_dict)
opt.set_state_dict(opt_state_dict)

# 加载验证数据集
val_loader = paddle.io.DataLoader(val_dataset, places=paddle.CUDAPlace(0), batch_size=32, shuffle=True)
loss_fn = paddle.nn.CrossEntropyLoss()

Cassava.eval()

for batch_id, data in enumerate(val_loader()):
    x_data = data[0]            # 数据
    y_data = data[1]            # 数据标签
    predicts = Cassava(x_data)    # 预测结果

    # 计算损失与精度
    loss = loss_fn(predicts, y_data)
    acc = paddle.metric.accuracy(predicts, y_data)

    # 打印信息
    if (batch_id+1) % 20 == 0:
        print("batch_id: {}, loss is: {}, acc is: {}".format(batch_id, loss.numpy(), acc.numpy()))

 

'''
## 模型预测结果
# 加载测试数据集
test_loader = paddle.io.DataLoader(test_dataset, places=paddle.CUDAPlace(0), batch_size=32, shuffle=True)

mnist.eval()
for batch_id, data in enumerate(test_loader()):
    x_data = data[0]
    predicts = Cassava(x_data)
    # 获取预测结果
    
print("predict finished")
'''

五.用VisualDL2.0可视化数据


六.总结:

下载安装命令

## CPU版本安装命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/cpu paddlepaddle

## GPU版本安装命令
pip install -f https://paddlepaddle.org.cn/pip/oschina/gpu paddlepaddle-gpu

本文同步分享在 博客“我不是坏人...真的”(CSDN)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部