笔者最近在尝试接触飞桨框架
感觉百度开发的这个框架性能,架构都还是很不错的
相比其他的框架简洁很多
并且官网还有一些项目可供学习
今天写这篇博客是基于https://aistudio.baidu.com/aistudio/projectdetail/44338人脸识别项目
并对总体进行一个总结
图像数据有很多种处理方法
该项目选择以字典形式进行存储
import os
import json
data_path = './images/face'
class_detail = []
class_dirs = os.listdir(data_path)
# print(class_dirs)
class_label = 0
father_paths = data_path.split('/') #['', 'home', 'aistudio', 'images', 'face']
while True:
if father_paths[father_paths.__len__() - 1] == '':
del father_paths[father_paths.__len__() - 1]
else:
break
father_path = father_paths[father_paths.__len__() - 1]
# print(father_path)
data_list_path = './%s/'%father_path
isexist = os.path.exists(data_list_path)
if not isexist:
os.makedirs(data_list_path)
with open(data_list_path + "test.list", 'w') as f:
pass
with open(data_list_path + "trainer.list", 'w') as f:
pass
all_class_images = 0
for class_dir in class_dirs:
class_detail_list = {}
test_sum = 0
trainer_sum = 0
class_sum = 0
path = data_path + '/' + class_dir
img_paths = os.listdir(path)
for img_path in img_paths:
name_path = path + '/' + img_path
if class_sum % 10 == 0:
test_sum += 1
with open(data_list_path + "test.list", 'a') as f:
f.write(name_path + "\t%d"%class_label +'\n')
else:
trainer_sum +=1
with open(data_list_path + "trainer.list", 'a') as f:
f.write(name_path + "\t%d"%class_label + '\n')
class_sum += 1
all_class_images += 1
class_detail_list['class_name'] = class_dir
class_detail_list['class_label'] = class_label
class_detail_list['class_test_images'] = test_sum
class_detail_list['class_trainer_images'] = trainer_sum
class_detail.append(class_detail_list)
class_label += 1
all_class_sum = class_dirs.__len__()
readjson = {}
readjson['all_class_name'] = father_path #文件父目录
readjson['all_class_sum'] = all_class_sum #
readjson['all_class_images'] = all_class_images
readjson['class_detail'] = class_detail
jsons = json.dumps(readjson, sort_keys=True, indent=4, separators=(',', ': '))
with open(data_list_path + "readme.json",'w') as f:
f.write(jsons)
print ('生成数据列表完成!')
它数据集有三类人脸,并以9:1的比例划分训练集和测试集
然后以字典形式存储
face目录下会多出三个文件
分别是readme.json test.list trainer.list
飞桨框架提供了很多工具库来进行模型搭建
主要流程如下:
1.对数据进行处理
2.编写一个文件reader进行批量读入,从而避免内存爆炸
3.搭建网络结构
4.定义占位符,优化器,损失值
5.编写训练流程函数
它也是基于图机制的框架,所以大体操作跟tensorflow,mxnet类似、
数据集图片大小不一,我们进行统一的修剪
import paddle
import paddle.fluid as fluid
import numpy as np
import sys
import os
from multiprocessing import cpu_count
import matplotlib.pyplot as plt
def train_mapper(sample):
"""
:param sample:是一个元组,包含图片路径以及标签
:return:
"""
img, label = sample
img = paddle.dataset.image.load_image(img)
# 对图片进行统一修剪
img = paddle.dataset.image.simple_transform(im=img,
resize_size=100,
crop_size=100,
is_color=True,
is_train=True)
img = img.flatten().astype('float32')/255.0
return img, label
我们通过调用simple_transform进行图片修剪, is_color为true代表的是彩色图片
这是一个比较关键的一部分,刚开始学习该框架我也不是很懂得reader的编写方法
其实他就是返回一个有数据迭代器的函数
再通过调用xmap_reader,传入你自定义的函数,得到的就是一个reader
# 定义图片读取的reader
def train_r(train_list, buffered_size=1024):
def reader():
with open(train_list, 'r') as f:
lines = [line.strip() for line in f]
for line in lines:
img_path, lab = line.strip().split('\t')
yield img_path, lab
return paddle.reader.xmap_readers(train_mapper, reader, cpu_count(), buffered_size)
该操作是打开我们之前写好的list文件
然后进行分行,我们是用制表符\t来隔开label和path的
所以使用split方法分别得到path和label
最后再使用yield 方法迭代img_path, lab
再总方法最后
调用paddle.reader.xmap_readers, 第一个参数传递的是我们的预处理函数mapper,也就是刚刚我们定义的图像修剪函数,第二个传递的是我们自定义的reader函数,第三个传递的是处理线程数目,这里我们调用multiprocessing的cpu_count()方法获得线程数量,注:本项目训练量并不是很大,完全可以通过cpu进行训练。第四个传入的是缓冲大小
同样我们对测试集进行相同的处理,编写mapper和reader函数
注意这里在transform这里把is_train置为False
def test_mapper(sample):
img, label = sample
img = paddle.dataset.image.load_image(img)
img = paddle.dataset.image.simple_transform(im=img, resize_size=100,crop_size=100, is_color=True,
is_train=False)
img = img.flatten().astype('float32')/255.0
return img, label
def test_r(test_list, buffered_size=1024):
def reader():
with open(test_list, 'r') as f:
lines = [line.strip() for line in f]
for line in lines:
img_path, lab = line.strip().split('\t')
yield img_path, lab
return paddle.reader.xmap_readers(train_mapper, reader, cpu_count(), buffered_size)
然后我们进行调用这几个函数生成相应对象,并利用paddle.batch类让其能以批数量的形式进行数据的读取
BATCH_SIZE = 32
trainer_reader = train_r(train_list='./face/trainer.list')
trainer_reader = paddle.batch(paddle.reader.shuffle(
reader=trainer_reader, buf_size=300),
batch_size=BATCH_SIZE
)
tester_reader = test_r(test_list='./face/test.list')
test_reader = paddle.batch(
tester_reader, batch_size=BATCH_SIZE
)
这个模型用的也是很传统的卷积,池化,激活,批量归一化的形式
整体结构并没有什么特别点,这里就不再赘述
def convolutional_nerual_network(image, type_size):
conv_pool_1 = fluid.nets.simple_img_conv_pool(input=image,
filter_size=3,
num_filters=32,
pool_size=2,
pool_stride=2,
act='relu')
bn1 = fluid.layers.batch_norm(input=conv_pool_1, act='relu')
drop = fluid.layers.dropout(x=bn1, dropout_prob=0.3)
conv_pool_2 = fluid.nets.simple_img_conv_pool(input=drop,
filter_size=3,
num_filters=64,
pool_size=2,
pool_stride=2,
act='relu')
bn2 = fluid.layers.batch_norm(input=conv_pool_2, act='relu')
drop = fluid.layers.dropout(x=bn2, dropout_prob=0.5)
fc = fluid.layers.fc(input=drop, size=512, act='relu')
bn3 = fluid.layers.batch_norm(input=fc, act='relu')
drop = fluid.layers.dropout(x=bn3, dropout_prob=0.5)
predict = fluid.layers.fc(input=drop, size=type_size, act='softmax')
return predict
在这里我们要开始定义我们数据的输入维度
在tensorflow这种操作是叫定义占位符
而在paddle框架里面它专门有个层叫数据层
说白了就是定义好一个有维度的张量,流入计算图里
# 创建数据层
image = fluid.layers.data(name='image', shape=[3, 100, 100], dtype='float32')
label = fluid.layers.data(name='label', shape=[1], dtype='int64')
predict = convolutional_nerual_network(image, type_size=4)
我们这次采取的是交叉熵损失函数和Adam优化器
我们计算损失后利用mean取损失值的平均值
cost = fluid.layers.cross_entropy(input=predict, label=label)
avg_cost = fluid.layers.mean(cost)
accuracy = fluid.layers.accuracy(input=predict, label=label)
optimizer = fluid.optimizer.Adam(learning_rate=0.002)
optimizer.minimize(avg_cost)
这里我们需要初始化我们的计算图
并且将数据传入给feeder
feeder能接收我们传入的数据并将数据转换成图能读入的格式
# 开始训练模型
# 使用cpu
place = fluid.CPUPlace()
exe = fluid.Executor(place)
exe.run(fluid.default_startup_program())
feeder = fluid.DataFeeder(feed_list=[image, label], place=place)
我们创建几个空列表,方便后续损失值和准确率加入
最后我们根据这个列表进行损失和准确率图像的绘制
all_train_iter=0
all_train_iters=[]
all_train_costs=[]
all_train_accs=[]
def draw_train_process(title, iters, costs, accs, label_cost, label_acc):
plt.title(title, fontsize=24)
plt.xlabel("iter", fontsize=20)
plt.ylabel("cost/acc", fontsize=20)
plt.plot(iters, costs, color='red', label=label_cost)
plt.plot(iters, accs, color='green', label=label_acc)
plt.legend()
plt.grid()
plt.show()
我们这次分类任务较为简单,所以迭代轮次设置为20
我们的大轮次为EPOCH_NUM
小轮次就是总数据以批量读入的方式进行迭代
这里我们就用python自带的enumerate函数
他能返回的是一个序号和迭代器里的元素
这个序号就是我们的一个小轮次
fetch_list里面定义了我们想要得到的参数
我们需要得到损失值和准确率
然后方便后续绘制
所以这里fetch的是avg_cost 和accuracy
EPOCH_NUM = 20
print("START TRAINING")
model_save_dir = "./model_cnn"
for pass_id in range(EPOCH_NUM):
train_cost = 0
for batch_id, data in enumerate(trainer_reader()):
train_cost, train_acc = exe.run(
program=fluid.default_main_program(),
feed=feeder.feed(data),
fetch_list=[avg_cost, accuracy]
)
all_train_iter += BATCH_SIZE
all_train_iters.append(all_train_iter)
all_train_costs.append(train_cost[0])
all_train_accs.append(train_acc[0])
if batch_id% 10 == 0:
print("\n PASS %d, STEP %d, Cost: %f, Acc: %f"%(
pass_id, batch_id, train_cost[0], train_acc[0]
))
test_accs = []
test_costs = []
for batch_id, data in enumerate(test_reader()):
test_cost, test_acc = exe.run(program=fluid.default_main_program(),
feed=feeder.feed(data),
fetch_list=[avg_cost, accuracy])
test_accs.append(test_acc[0])
test_costs.append(test_cost[0])
test_cost = (sum(test_costs) / len(test_costs))
test_acc = (sum(test_accs) / len(test_accs))
print("TEST: %d, COST: %0.5f, ACC: %0.5f"%(pass_id, test_cost, test_acc))
if not os.path.exists(model_save_dir):
os.makedirs(model_save_dir)
# 保存训练的模型,executor 把所有相关参数保存到 dirname 中
fluid.io.save_inference_model(dirname=model_save_dir,
feeded_var_names=["image"],
target_vars=[predict],
executor=exe)
draw_train_process("training", all_train_iters, all_train_costs, all_train_accs, "trainning cost", "trainning acc")
print('训练模型保存完成!')
总的来说飞桨框架还是很不错的
现在还不支持在win系统上debug,以及没有可视化
但是各个函数,功能设计十分合理
期待它也能跻身进入机器学习流行框架