果农需要根据水果的不同大小和质量进行产品的定价,所以每年收获的季节有大量的人工对水果分类的需求。基于人工智能模型的方案,收获的大堆水果会被机械放到传送带上,模型会根据摄像头拍到的图片,控制仪器实现水果的自动分拣,节省了果农大量的人力。
下面我们就看看如果采集到少量的桃子数据,如何基于PaddleHub对ImageNet数据集上预训练模型进行Fine-tune,得到一个更有效的模型。桃子分类数据集取自AI Studio公开数据集桃脸识别,该桃脸识别数据集中已经将所有桃子的图片分为2个文件夹,一个是训练集一个是测试集;每个文件夹中有4个分类,分别是B1、M2、R0、S3。
实现迁移学习,包括如下步骤:
下面将根据这四个主要步骤,展示如何利用PaddleHub实现finetune。
paddlehub安装可以使用pip完成安装,如下:
# 安装并升级PaddleHub,使用百度源更稳定、更迅速
pip install paddlehub==2.1 -i https://mirror.baidu.com/pypi/simple
在本次教程提供的数据文件中,已经提供了分割好的训练集、验证集、测试集的索引和标注文件。如果用户利用PaddleHub迁移CV类任务使用自定义数据,则需要自行切分数据集,将数据集切分为训练集、验证集和测试集。需要三个文本文件来记录对应的图片路径和标签,此外还需要一个标签文件用于记录标签的名称。
├─data: 数据目录
├─train_list.txt:训练集数据列表
├─test_list.txt:测试集数据列表
├─validate_list.txt:验证集数据列表
├─label_list.txt:标签列表
└─……
训练集、验证集和测试集的数据列表文件的格式如下,列与列之间以空格键分隔。
图片1路径 图片1标签
图片2路径 图片2标签
...
label_list.txt的格式如下:
分类1名称
分类2名称
...
准备好数据后即可使用PaddleHub完成数据读取器的构建,实现方法如下所示:构建数据读取Python类,并继承paddle.io.Dataset这个类完成数据读取器构建。在定义数据集时,需要预先定义好对数据集的预处理操作,并且设置好数据模式。在数据集定义中,需要重新定义__init__
,__getitem__
和__len__
三个部分。示例如下:
import os
import paddle
import paddlehub as hub
class DemoDataset(paddle.io.Dataset):
def __init__(self, transforms, num_classes=4, mode='train'):
# 数据集存放位置
self.dataset_dir = "./work/peach-classification" #dataset_dir为数据集实际路径,需要填写全路径
self.transforms = transforms
self.num_classes = num_classes
self.mode = mode
if self.mode == 'train':
self.file = 'train_list.txt'
elif self.mode == 'test':
self.file = 'test_list.txt'
else:
self.file = 'validate_list.txt'
self.file = os.path.join(self.dataset_dir , self.file)
with open(self.file, 'r') as file:
self.data = file.read().split('\n')[:-1]
def __getitem__(self, idx):
img_path, grt = self.data[idx].split(' ')
img_path = os.path.join(self.dataset_dir, img_path)
im = self.transforms(img_path)
return im, int(grt)
def __len__(self):
return len(self.data)
将训练数据输入模型之前,我们通常还需要对原始数据做一些数据处理的工作,比如数据格式的规范化处理,或增加一些数据增强策略。
构建图像分类模型的数据读取器,负责将桃子dataset的数据进行预处理,以特定格式组织并输入给模型进行训练。
如下数据处理策略,只做了两种操作:
对数据预处理及加载数据集的示例如下:
import paddlehub.vision.transforms as T
transforms = T.Compose(
[T.Resize((256, 256)),
T.CenterCrop(224),
T.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])],
to_rgb=True)
peach_train = DemoDataset(transforms)
peach_validate = DemoDataset(transforms, mode='val')
我们要在PaddleHub中选择合适的预训练模型来Fine-tune,由于桃子分类是一个图像分类任务,这里采用Resnet50模型,并且是采用ImageNet数据集Fine-tune过的版本。这个预训练模型是在图像任务中的一个“万金油”模型,Resnet是目前较为有效的处理图像的网络结构,50层是一个精度和性能兼顾的选择,而ImageNet又是计算机视觉领域公开的最大的分类数据集。所以,在不清楚选择什么模型好的时候,可以优先以这个模型作为baseline。
使用PaddleHub,不需要重新手写Resnet50网络,可以通过一行代码实现模型的调用。
#安装预训练模型
! hub install resnet50_vd_imagenet_ssld==1.1.0
import paddlehub as hub
model = hub.Module(name='resnet50_vd_imagenet_ssld', label_list=["R0", "B1", "M2", "S3"])
定义好模型,也准备好数据后,我们就可以开始设置训练的策略。Paddle2.2提供了多种优化器选择,如SGD, Adam, Adamax等。
from paddlehub.finetune.trainer import Trainer
import paddle
optimizer = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
trainer = Trainer(model, optimizer, checkpoint_dir='img_classification_ckpt', use_gpu=True)
trainer.train(peach_train, epochs=10, batch_size=16, eval_dataset=peach_validate, save_interval=1)
#打印
[2023-02-25 10:08:53,462] [ TRAIN] - Epoch=1/10, Step=10/375 loss=0.9796 acc=0.6250 lr=0.001000 step/sec=1.85 | ETA 00:33:46
[2023-02-25 10:08:54,244] [ TRAIN] - Epoch=1/10, Step=20/375 loss=0.6388 acc=0.7625 lr=0.001000 step/sec=12.78 | ETA 00:19:20
[2023-02-25 10:08:55,029] [ TRAIN] - Epoch=1/10, Step=30/375 loss=0.5733 acc=0.7375 lr=0.001000 step/sec=12.75 | ETA 00:14:31
[2023-02-25 10:08:55,827] [ TRAIN] - Epoch=1/10, Step=40/375 loss=0.2518 acc=0.9062 lr=0.001000 step/sec=12.53 | ETA 00:12:08
[2023-02-25 10:08:56,615] [ TRAIN] - Epoch=1/10, Step=50/375 loss=0.1935 acc=0.9250 lr=0.001000 step/sec=12.69 | ETA 00:10:41
[2023-02-25 10:08:57,428] [ TRAIN] - Epoch=1/10, Step=60/375 loss=0.1949 acc=0.9375 lr=0.001000 step/sec=12.31 | ETA 00:09:45
[2023-02-25 10:08:58,238] [ TRAIN] - Epoch=1/10, Step=70/375 loss=0.1502 acc=0.9563 lr=0.001000 step/sec=12.34 | ETA 00:09:05
[2023-02-25 10:08:59,023] [ TRAIN] - Epoch=1/10, Step=80/375 loss=0.1275 acc=0.9500 lr=0.001000 step/sec=12.73 | ETA 00:08:34
[2023-02-25 10:08:59,807] [ TRAIN] - Epoch=1/10, Step=90/375 loss=0.1811 acc=0.9187 lr=0.001000 step/sec=12.76 | ETA 00:08:09
其中Adam
:
learning_rate
: 全局学习率。默认为1e-3;parameters
: 待优化模型参数。Trainer
主要控制Fine-tune的训练,包含以下可控制的参数:
model
: 被优化模型;optimizer
: 优化器选择;use_gpu
: 是否使用gpu;use_vdl
: 是否使用vdl可视化训练过程;checkpoint_dir
: 保存模型参数的地址;compare_metrics
: 保存最优模型的衡量指标;trainer.train
主要控制具体的训练过程,包含以下可控制的参数:
train_dataset
: 训练时所用的数据集;epochs
: 训练轮数;batch_size
: 训练的批大小,如果使用GPU,请根据实际情况调整batch_size;num_workers
: works的数量,默认为0;eval_dataset
: 验证集;log_interval
: 打印日志的间隔, 单位为执行批训练的次数。save_interval
: 保存模型的间隔频次,单位为执行训练的轮数。当Fine-tune完成后,我们使用模型来进行预测,实现如下:
import paddle
import paddlehub as hub
result = model.predict(['./work/peach-classification/test/M2/0.png'])
print(result)
# 打印:
[{'M2': 0.99999964}]
以上为加载模型后实际预测结果(这里只测试了一张图片),返回的是预测的实际效果,可以看到我们传入待预测的是M2类别的桃子照片,经过Fine-tune之后的模型预测的效果也是M2,由此成功完成了桃子分类的迁移学习。