paddlehub
paddlehub是百度paddlepaddle深度学习框架提供的配套预训练模型应用管理工具,提供Python API和命令行两种调用模式,目前的预训练模型覆盖了图像分类、语义模型、视频分类、图像生成、图像分割、文本审核、关键点检测等业界主流模型,功能十分强大。Github地址:https://github.com/PaddlePaddle/PaddleHub
paddlehub的强大,但是官方提供做自己数据集迁移学习的demo较少,目前网上提供资料也不多且较散,这里我就以我自己的一个简单的project为例,给大家讲解一下paddlehub的迁移学习使用入门。
数据集
完整项目地址:https://aistudio.baidu.com/aistudio/projectdetail/782743
数据集可在kaggle官网下载,但有的同学嫌翻墙麻烦下载慢,我也在百度网盘和aistudio上放了一份:
百度网盘:链接: https://pan.baidu.com/s/12RbvTpOfUL8eM9vNwWW72A提取码: 4fe4
aistudio:https://aistudio.baidu.com/aistudio/datasetdetail/38429
kaggle:https://www.kaggle.com/c/state-farm-distracted-driver-detection
运行环境
paddlepaddle 1.8.0 + paddlehub 1.8.4
1.数据分析
!unzip -o -q data/data38429/distracted_driver_detection.zip
print("done")
解压数据集,可以看到最后生成:train文件夹+test文件+driver_imgs_list.csv
我们先来分析一下mgs_list.csv文件,打开可以看到共有三个信息:
- subject:拍摄的司机编号
- classname:分心行为对应的标签
- img:对应的图片数据名
先来看看司机数据的数据分布:
#csv文件路径
dir = "driver_imgs_list.csv"
#读取csv中subject的数据
#subject对应的是司机的编号
img_csv = os.path.join(dir)
csv_data = pd.read_csv(img_csv)
driver_name = csv_data['subject']
#储存所有司机的编号
names = []
for name in driver_name:
if(name not in names):
names.append(name)
#计算每个司机有多少张图片
driver_num = csv_data.groupby('subject')['img'].count()
#显示每个司机图片数量
print(driver_num)
#显示总共多少个司机
print("drivers count = {}".format(len(names)))
#可视化
plt.bar(x=names, height=driver_num, width=0.8)
plt.show()
从输出可以看到,司机一共是26个,每个司机的数据分布并没有一个明显的特征,下面再分析分析10个分类的样本数据
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()
从输出结果可以看到,分类是c0-c9共十个类别,且样本数分布较为均匀,诶很奈斯。
提供的数据集中除了储存标注信息的csv文件外,只有train文件夹+test文件,无val集,所以下一步要对数据进行处理,划分出val集。
2.数据处理
因为提供的数据集中只有train和test无val集,所以人为的在train集中划分一部分数据作为val集并生成储存图片位置与类别的txt文件
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))
将test集图片信息写入predict_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()
生成储存标签信息的label_list.txt文件
w = open("label_list.txt", "w")
path = "train/"
folders_name = os.listdir(path)
for name in folders_name:
w.write(name+'\n')
print("label_list.txt文件成功生成")
w.close()
3.训练配置
装载选中的模型(这边主要是举例,所以随便选的加载的模型)
import paddlehub as hub
module = hub.Module(name="mobilenet_v2_imagenet")
input_dict, output_dict, program = module.context(trainable=True)
定义了一个DemoDataset读取自己的数据集
-
train_list_file:训练集
-
validate_list_file:val集
-
test_list_file:测试集
-
predict_file:实际中做的要预测的数据
因为没有predict_file就可以注释掉不管他了
from paddlehub.dataset.base_cv_dataset import BaseCVDataset
class DemoDataset(BaseCVDataset):
def __init__(self):
# 数据集存放位置
self.dataset_dir = ""
super(DemoDataset, self).__init__(
base_path=self.dataset_dir,
train_list_file="train_list.txt",
validate_list_file="val_list.txt",
test_list_file="predict_list.txt",
#predict_file="predict_list.txt",
label_list_file="label_list.txt",
)
dataset = DemoDataset()
这里是于图像分类问题,所以选择ImageClassificationReader:图像分类数据预处理器,会修改输入数据集图像的尺寸、进行标准化处理、图像增广处理等操作。
data_reader = hub.reader.ImageClassificationReader(
image_width=module.get_expected_image_width(),
image_height=module.get_expected_image_height(),
images_mean=module.get_pretrained_images_mean(),
images_std=module.get_pretrained_images_std(),
dataset=dataset)
配置训练的一些超参数,在strategy里可以配置很多优化的参数,这里的优化策略选取的默认优化策略。其对应参数如下:
-
learning_rate: 全局学习率。默认为1e-4。
-
optimizer_name: 优化器名称。默认adam。
-
regularization_coeff: 正则化的λ参数。默认为1e-3。
config = hub.RunConfig(
use_cuda=True, #是否使用GPU训练
num_epoch=10, #Fine-tune的轮数
checkpoint_dir="source", #模型checkpoint保存路径
batch_size=16, #训练的批大小
eval_interval=100, #模型评估的间隔
strategy=hub.finetune.strategy.DefaultFinetuneStrategy()) #Fine-tune优化策略;
把设置好的参数写入并建立ImageClassifierTask任务
feature_map = output_dict["feature_map"]
feed_list = [input_dict["image"].name]
#ImageClassifierTask:通用的分类任务Task,该Task基于输入的特征,添加一个或多个全连接层来创建一个分类任务用于Fine-tune
task = hub.ImageClassifierTask(
data_reader=data_reader, #提供数据的reader
feed_list=feed_list, #待feed变量的名字列表
feature=feature_map, #输入的特征矩阵
num_classes=dataset.num_labels, #分类任务的类别数量
config=config) #运行配置
这两步可能会报的错误:
- AttributeError: ‘Parameter’ object has no attribute
‘gradient_clip_attr’
paddlehub不是最新版导致的,建议升级paddlehub至1.8.2再试一试,如果还不行就检查一下自己paddlepaddle版本是不是1.8以上的 - Error: Blocking queue is killed because the data reader raises an
exception 这个报错问题出在reader上,在这个报错向上找,如果能找到类似:ValueError: invalid
literal for int() with base 10: 'c2’的语句,就铁锤是因为便签设置的问题
我这里是十分类问题,对应标签是c0-c9十个,但是最后分类对应的标签应该是0-9这种整数,在前面生成txt那里进行修改就可以解决了
4.开始训练
激活task的finetune和eval
run_states = task.finetune_and_eval()
5.结果预测
调用task.predict来进行预测,最后返回的预测结果是一个batch一个batch大小返回的
import numpy as np
label_map = dataset.label_dict()
index = 0
run_states = task.predict(data=predict_data)
results = [run_state.run_results for run_state in run_states]
images_data= []
res_data= []
for batch_result in results:
batch_result = np.argmax(batch_result, axis=2)[0]
for result in batch_result:
index += 1
result = label_map[result]
print("input %i is %s, and the predict result is %s" %
(index, predict_data[index - 1], result))
images_data.append(predict_data[index - 1])
res_data.append(result)
print(res_data)
paddlehub功能强大学会使用可以给自己做项目省很多精力,希望这篇博客能解决大家刚开始使用paddlehub的一些问题。
项目地址如下,欢迎大家fork、讨论
项目地址:https://aistudio.baidu.com/aistudio/projectdetail/782743