一文教你学会如何使用PaddleHub在自定义数据集上进行迁移学习

  • 基于paddlehub实现驾驶员分心驾驶状态检测

  • 一个项目教你学会如何使用PaddleHub在自己数据集上实现迁移学习
  • 本项目已更新至PaddleHub2.0,使用旧版PaddleHub的同学可在本项目版本中查看上一版本

背景

  • 在我们的生活中,驾驶员疲劳驾驶的情况较常见,但因疲劳驾驶多出现于长途驾驶以及对应的限制制度的落地实施,疲劳驾驶情况呈下降趋势。而分心驾驶引发的交通事故却呈上升趋势,且分心驾驶具有不容易通过交警监管、驾驶员对此概念模糊等问题。在我们生活中常常发生的是,分心驾驶时驾驶员自身可能都不会有所察觉。当驾驶员处于分心驾驶状态时,由于注意力不集中,驾驶员在面对突发状况时会反应不及时出现呆滞和慌乱,以至于出现判断和操作机动车上的失误,从而引发交通事故。
  • paddlehub是一个功能强大的paddle预训练模型应用管理工具,同时提供Python API和命令行两种调用模式。Paddlehub的强大不必多说,但是官方提供做自己数据集迁移学习的demo较少,目前网上提供资料也不多且较散,这里我就以一个kaggle上经典的project为例,给大家讲解一下如何使用paddlehub进行自我数据集的迁移学习,实现驾驶员分心驾驶状态检测,并介绍paddlehub2.0.0的使用

本文涉及内容

    1. 数据分析
    1. 数据处理
    1. 训练配置
    1. 开始训练
    1. 导出结果

1.数据分析

import os
import cv2
import paddle
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
!unzip -o -q data/data38429/distracted_driver_detection.zip
print("done")
done

数据集说明

数据集来源于kaggle,解压后生成三个文件:
  • train
  • test
  • driver_imgs_list.csv
其中train文件夹下含有c0-c9十个子文件夹,包含训练对应的10种分心驾驶状态的图片,test文件夹包含测试集的图片,driver_imgs_list.csv文件则是标签信息,具体信息如下:
  • subject:拍摄的司机编号
  • classname:分心行为对应的标签
  • img:对应的图片数据名
下面通过程序可视化数据分布情况,可以看到,司机一共是26个,每个司机的数据分布并没有一个明显的特征
通过分析10类(c0-c9共十个类别)样本数据可以看到,样本数分布较为均匀,很奈斯,这样训练时每个类别都可学的差不太多,可以避免因数据不均衡让模型对某些类的学习产生过拟合。
#csv文件路径
dir = "driver_imgs_list.csv"

#读取csv中subject的数据
#subject对应的是司机的编号
img_csv = os.path.join(dir)
csv_data = pd.read_csv(img_csv)
class_name = csv_data['classname']

#储存所有类别
class_names = []
for name in class_name:
    if(name not in class_names):
        class_names.append(name)

#计算每个类别有多少张图片
class_num = csv_data.groupby('classname')['img'].count()

#显示每个类别图片数量
print(class_num)

#显示总共多少类
print("class count = {}".format(len(class_names)))

#可视化
plt.bar(x=class_names, height=class_num, width=0.8)
plt.show()
classname
c0    2489
c1    2267
c2    2317
c3    2346
c4    2326
c5    2312
c6    2325
c7    2002
c8    1911
c9    2129
Name: img, dtype: int64
class count = 10


/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/matplotlib/cbook/__init__.py:2349: DeprecationWarning: Using or importing the ABCs from 'collections' instead of from 'collections.abc' is deprecated, and in 3.8 it will stop working
  if isinstance(obj, collections.Iterator):

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0PbWRkX0-1616043558900)(output_6_2.png)]

十类对应的实际状态分别是:

C0:驾驶员正常驾驶C1:右手使用手机打字C2:使用右手打电话C3:左手使用手机打字C4:使用左手打电话
C5:调试车辆多媒体C6:驾驶员喝水或进食C7:驾驶员拿后排东西C8:整理头发和化妆C9:和其他乘客谈话

2.数据处理

2.1 生成train.txt 和 val.txt标签文件

因为提供的数据集中只有train、test没有val,所以人为的在训练集中划分一部分数据作为val集并生成储存图片位置与类别的txt文件。
  • 与旧版本相比,paddlehub2.0.0生成txt文件没有硬性格式要求了,只要保证之后定义数据集的自己在getitem方法里实现就可以
  • 没啥特殊需求的话这里生成txt文件的格式还是建议大家采用旧paddlehub读取规则,简单直观方便读取:图片1路径(空格)图片1标签
path = "train/"
folders_name = os.listdir(path)

a = open("train_list.txt", "w")
b = open("val_list.txt", "w")

count = 0
val_count = 0
train_count = 0

for name in folders_name:
    image_names = os.listdir(path+name)
    for img_name in image_names:
        if(count % 20 == 0):
            b.write(path+name+"/"+img_name+name.replace("c"," ")+'\n')
            val_count = val_count + 1
        else:
            a.write(path+name+"/"+img_name+name.replace("c"," ")+'\n')
            train_count = train_count + 1
        count = count + 1

a.close()
b.close()
print("train_list生成完毕,train数据集共{}个数据".format(train_count))
print("val_list生成完毕,val数据集共{}个数据".format(val_count))
print("合计{}个数据".format(count))
train_list生成完毕,train数据集共21302个数据
val_list生成完毕,val数据集共1122个数据
合计22424个数据

2.2 生成predict.txt待预测数据

将test集图片信息写入predict_list.txt文件中,并生成储存标签信息的label_list.txt文件,预测文件的格式就很简单了,常规的路径+文件名即可。
f = open("predict_list.txt", "w")
predict_data = []

path = "test/"
folders_name = os.listdir(path)
for name in folders_name:
    f.write(path+name+'\n')
    predict_data.append(path+name)
print("predict_list.txt文件成功生成")
f.close()
predict_list.txt文件成功生成

2.3 读取label全部标签种类

这里就是2.0.0和旧版的区别了,旧版label_list是在训练时读入,新版2.0.0则是在装载模型时载入,同时要用列表类型传入。这里就用列表类型变量储存标签
label = []
folders_name = os.listdir("train")
folders_name.sort()
for name in folders_name:
    label.append(name)
print("label_list成功读取")
print(label)
label_list成功读取
['c0', 'c1', 'c2', 'c3', 'c4', 'c5', 'c6', 'c7', 'c8', 'c9']

3.训练配置

3.1 安装PaddleHub2.0.0

  • 1. 安装最新的paddlehub==2.0.0
  • 2. 如果升级不了可以换清华源:

pip install paddlehub==2.0.0 --upgrade -i https://pypi.tuna.tsinghua.edu.cn/simple

!pip install paddlehub==2.0.0 -i https://mirror.baidu.com/pypi/simple

3.2 搜索模型

对于PaddleHub里的预训练模型,我们可以在命令行中输入:

hub search + model_name

model_name是我们想搜索的模型名字,这样就可以搜索到全部含模型名的预训练模型,再根据我们自己的需求选取模型即可。
import paddlehub as hub
!hub search resnet

3.3 装载选取的模型resnet50_vd_imagenet_ssld

  • 这里咱们是分类问题,就选择了在imagenet上训练好的resnet50的D版本分类预训练参数
  • 装载模型时也要注意,PaddleHub2.0中标签读取集成在了装载模型中,后面的label_list 参数就是读取标签数据的,如果不加label_list,默认是模型训练时输入的标签
model = hub.Module(name = "resnet50_vd_imagenet_ssld",label_list = label)
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1263: UserWarning: Skip loading for out.weight. out.weight receives a shape [2048, 1000], but the expected shape is [2048, 10].
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))
/opt/conda/envs/python35-paddle120-env/lib/python3.7/site-packages/paddle/fluid/dygraph/layers.py:1263: UserWarning: Skip loading for out.bias. out.bias receives a shape [1000], but the expected shape is [10].
  warnings.warn(("Skip loading for {}. ".format(key) + str(err)))


load pretrained parameters success

本步可能遇到的问题:

  • 1. AttributeError: ‘Parameter’ object has no attribute ‘gradient_clip_attr’

    hub库中部分模型仍未更新到PaddlePaddle2.0,所以无法和PaddleHub2.0兼容😂

  • 2. TypeError: init() got an unexpected keyword argument ‘label_list’

    问题原因同上

  • 解决方案:装载已升级模型即可,目前作者测试的常见的分类模型已升级的只有:resnet50_vd_imagenet_ssld

3.4 设置预处理操作

  • paddlehub2.0和paddlepaddle2.0相匹配,预处理操作也和paddle2.0高阶API保持一致,支持随机调整图像的亮度、对比度、饱和度、图像大小、归一化等,更多预处理操作可参考官方API文档
  • 这里根据我自己的需求,就选了图像大小修改和归一化。
#定义数据预处理方式
import paddlehub.vision.transforms as T
transforms = T.Compose([T.Resize((416, 416)), T.Normalize(mean=[0.485, 0.456, 0.406], std = [0.229, 0.224, 0.225])], )

3.5 定义自己数据类

划重点了划重点了,咱们在上一个模块将自己的数据集生成了txt文件,这里就要用上了。
这一步和使用paddle2.0自定义数据集相似,继承paddle2.0中的Dataset类,配置属性和方法,然后实例化。
  • mode :模式,即读取哪个数据集,这里咱们上面生成了训练、测试和预测三个集合,那这里mode就设三种状态保证都能把数据都出去
  • file:刚才生成的txt文件,注意要和mode相匹配
  • transforms:预处理操作参数,根据需求提前配置好
  • def getitem(self, idx)方法:定义了在idx时如何获取数据,并返回单条的数据,同时完成对应的数据预处理操作
  • def len(self)::实现__len__方法,返回数据集总数目
class DriverDataset(paddle.io.Dataset):
    def __init__(self, transforms, mode = 'train'):
        self.mode = mode
        self.transforms = transforms #预处理方式
        self.num_classes = 10 #类别数
        if self.mode == 'train':
            self.file = 'train_list.txt'
        elif self.mode == 'val':
            self.file = 'val_list.txt'
        else:
            self.file = 'predict_list.txt'
        with open(self.file, 'r') as file:
            self.data = file.read().split('\n')

    def __getitem__(self, idx):#这里的读取就要和之前生成txt那里相匹配
        img_path, grt = self.data[idx].split(' ')
        im = self.transforms(img_path)
        return im, int(grt)

    def __len__(self):
        return len(self.data)

定义好后我们实例化它,生成对应的三个数据集

train_dataset = DriverDataset(transforms,"train")
eval_dataset = DriverDataset(transforms,"val")
predict_dataset = DriverDataset(transforms,"predict")

3.6 设置优化策略和运行配置

  • 接下来先配置优化策略,此处优化策略选择和paddle2.0通用,都是在paddle.optimizer下进行选择,因为这里演示的任务是常规的图像分类任务,就直接选了Adam优化器,学习率设为固定学习率:0.001,更多优化器可参考此处
  • 配置好优化策略后就可以建立trainer了,主要控制Fine-tune的训练。在旧版是先建立config再建立task,而且不同的任务有对应不同task类型,新版里是建立trainer,具体参数如下:
参数名作用
model被优化模型
optimizer选择的优化器
use_gpu是否使用gpu
use_vdl是否使用vdl可视化
checkpoint_dir模型参数保存的地址
compare_metrics保存最优模型的衡量指标
配置训练策略总的来说和旧版的paddlehub差不太多,但是加大了和paddle2.0的联系,也可以说是paddlepaddle2.0API更好用了,确实是一个比较大的提升。
from paddlehub.finetune.trainer import Trainer

optimizer = paddle.optimizer.Adam(learning_rate=0.001, parameters=model.parameters())
trainer = Trainer(model, optimizer, use_gpu=True,checkpoint_dir='driver')
[2021-02-28 14:32:56,740] [ WARNING] - PaddleHub model checkpoint not found, start from scratch...

本步可能遇到的问题

  • ‘ModuleV1_9b84d0ff5bc6072a0db18dc325eb9873’ object has no attribute ‘parameters’

这时因为在装载模型时使用的是未升级的模型。同时装载模型忘记添加label_list就会在训练时就会报错如上,解决办法也是更换模型。

4.开始训练

最后配置训练过程参数就可以开始快乐炼丹了。
  • train_dataset: 训练使用的数据集;
  • epochs: 训练总轮数;
  • batch_size: 训练一个batch的大小;
  • num_workers: works的数量,默认为0;
  • eval_dataset: 验证集;
  • log_interval: 打印日志的间隔;
  • save_interval: 保存模型的间隔频次;
训练时PaddleHub也会将log全部打印出来,嘿嘿嘿,是不是很方便且easy?
trainer.train(train_dataset, epochs=5, batch_size=32, eval_dataset=eval_dataset, log_interval=20, save_interval=1)

5.模型预测

训练完成后所有参数数据都会保存在大家设置的checkpoint_dir文件夹下,大家就可以根据自己的需求去嘿嘿嘿了
预测也很简单,直接调用model.predict读取想要读取的模型参数来进行预测并进行后续操作。

result = model.predict(['test1.jpg'])[外链图片转存中...(img-tBQW6B6g-1616043558920)]



```python
result = model.predict(['test1.jpg'])
print(result)
[{'c1': 0.98667055}]
  • 预测图片如图为右手使用手机,对应类别为C1,模型预测状态判断为C1,置信度为0.98,还是较精准

  • 对于单帧图片预测,我们可以直接根据置信度来判断,当置信度大于0.8且状态判断是在C1-C9中的话,我们就认为在这帧图像中,驾驶员处于分心驾驶状态

总结

  • 千万不要以为PaddleHub只能完成图片分类任务嗷,PaddleHub功能远比我这里展示的强大,更多其他任务可以查看PaddleHub文档

  • 就目前来看【2021年2月14日】,paddlehub中仍有很多模型并没有转换成paddlepaddle2.0版本的,即无法使用paddlehub2.0进行操作,建议如果对模型选取有需求的伙伴可参考本项目上一个版本使用paddlehub1.8完成。

  • 本项目以简单常见的图片分类任务为例,希望能解决大家刚开始使用PaddleHub的一些问题快速入门,欢迎大家一起来讨论共同进步,fighting!

关于作者

  • 姓名:Fitz

  • 武汉理工大学信息工程专业2018级本科在读

  • 感兴趣方向:计算机视觉、推理部署、迁移学习

  • AIstudio主页 : Fitzie

  • 欢迎大家有问题一起交流讨论,共同进步~

运行代码请点击:https://aistudio.baidu.com/aistudio/projectdetail/782743

运行方式如下:

在这里插入图片描述

Eclipse是一个开放源代码的集成开发环境(IDE),可用于Java开发,但也可以用于其他编程语言的开发。在本文中,我将向你展示如何使用Eclipse进行Java开发。 1. 下载Eclipse 首先,你需要从Eclipse官方网站下载Eclipse IDE。下载页面上将提供几个不同的版本,包括Eclipse IDE for Java Developers、Eclipse IDE for JavaScript and Web Developers,以及Eclipse IDE for C/C++ Developers等。选择适合你的版本,然后按照安装向导进行安装。 2. 创建Java项目 一旦你安装了Eclipse,你可以启动它并创建一个新的Java项目。选择“File”->“New”->“Java Project”,然后按照向导创建一个新的Java项目。在创建项目时,你需要指定项目名称、项目类型以及JRE版本等信息。 3. 创建Java类 一旦你创建了一个Java项目,你就可以创建一个Java类。选择你的Java项目,在“src”文件夹上右键单击,然后选择“New”->“Class”。输入类名和选择要继承的类(如果有的话),然后点击“Finish”。 4. 编写Java代码 现在你已经创建了一个Java类,可以开始编写Java代码了。在Eclipse的编辑器中,你可以输入Java代码并保存它。当你保存Java文件时,Eclipse会自动编译你的代码,并在Problems视图中显示任何编译错误。 5. 运行Java程序 一旦你编写了Java代码并保存了它,你可以运行Java程序。右键单击Java文件,然后选择“Run As”->“Java Application”。如果一切顺利,你的Java程序将在控制台中输出结果。 6. 调试Java程序 如果你的Java程序出现了错误或不按预期运行,你可以使用Eclipse的调试器来调试它。在Eclipse的编辑器中,你可以设置断点并启动调试器。当程序执行到断点时,调试器会暂停程序并允许你检查变量、运行代码等。 7. 导入外部JAR包 有时,你可能需要使用外部JAR包来完成你的Java项目。在Eclipse中,你可以简单地将外部JAR包导入到你的项目中。右键单击Java项目,然后选择“Build Path”->“Configure Build Path”。在“Libraries”选项卡上,你可以添加外部JAR包。 总结 在本文中,我们介绍了如何使用Eclipse进行Java开发。我们学习了如何创建Java项目、创建Java类、编写Java代码、运行Java程序、调试Java程序以及导入外部JAR包。Eclipse具有强大的功能,可以大大提高Java开发的效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值