本次比赛赛题是进行人流密度的估计,由于以前看过不少人体姿态估计和目标检测的论文,隐约感受到能够用到此次比赛上来,因此趁着如今时间比较多,赶忙报名参加了一下比赛,比赛规定用paddlepaddle来进行开发,因此最近几天先学习一下paddlepaddle的相关流程,在这里记录一下,也好让本身真正的可以学到东西。html
在我看来,设计一个深度学习网络(主要是基于CNN的,其余的没怎么接触),主要有如下几方面:git
接下来就以上几部分进行学习,在次很是感谢Charlotte77和夜雨飘零1。他们的博文给予了我莫大的帮助,向大佬叩首。github
对于本次比赛来讲,个人数据是图片(各类监控的图片,大小不一样),标注是json格式的文件,因此接下来要讨论一下在paddlepaddle中如何以图片为输入。算法
参见大佬Charlotte77的博文,paddlepaddle主要是经过reader来进行数据的输入,这里我参考了paddlepaddle github 上的SSD的例子的例子,先看他们的代码:json
train_reader = paddle.batch( reader.train(data_args, train_file_list), batch_size=batch_size) test_reader = paddle.batch( reader.test(data_args, val_file_list), batch_size=batch_size)
其中reader是import来的,咱们以reader.train来看一下:api
def train(settings, file_list, shuffle=True): file_list = os.path.join(settings.data_dir, file_list) if 'coco' in settings.dataset: train_settings = copy.copy(settings) if '2014' in file_list: sub_dir = "train2014" elif '2017' in file_list: sub_dir = "train2017" train_settings.data_dir = os.path.join(settings.data_dir, sub_dir) return coco(train_settings, file_list, 'train', shuffle) else: return pascalvoc(settings, file_list, 'train', shuffle)
这里看得出来,是利用了以前定义的coco函数或者pascalvoc函数,就是从不一样的数据集读取数据,以coco为例,看一下,到底返回了什么,这里代码有点长,咱们主要看返回的是什么:网络
... if 'cocoMAP' in settings.ap_version: yield im, boxes, lbls, iscrowd, \ [im_id, im_width, im_height] else: yield im, boxes, lbls, iscrowd return reader
balabala一大堆,终于发现,返回的是一个生成器reader,可见,主要就在于生成这个生成器,下面来总结一下padlepaddle输入数据的生成:session
把你的数据(图片,标签)搞出来,而后用yield来产生一个生成器:reader。 框架
接下来就是送入网络了。ide
上面第三步就是将数据送入网络,这是如何办到的呢,用过tensorflow的童鞋们可能知道,咱们能够用一个palceholder(占位符)来连接咱们的原始数据和咱们的网络,在这里,也是一样的方法:
image = fluid.layers.data(name='image', shape=image_shape, dtype='float32') gt_box = fluid.layers.data(name='gt_box', shape=[4], dtype='float32', lod_level=1) gt_label = fluid.layers.data(name='gt_label', shape=[1], dtype='int32', lod_level=1)
用的是fluid.layers.data,等一下,lod_level是啥,这里paddlepaddle有个序列输入格式,这里lod_level为1说明这条数据是序列格式,可是这里感受应该不用加,这部分等我作过再说把。具体查看大佬Charlotte77的博文吧。
有了这个“占位符”以后,只需将咱们以前的那个batch_size的train_reader feed进去就行了,具体以下:
feeder = fluid.DataFeeder(place=place, feed_list=[image, gt_box, gt_label]) if args.parallel: loss_v, = train_exe.run(fetch_list=[loss.name],feed=feeder.feed(data))#这里的data就是以前train_reader的数据,fetch_list就是要执行的operation的名称,feed的顺序就是上面feed_list指定的 else: loss_v, = exe.run(fluid.default_main_program(), feed=feeder.feed(data), fetch_list=[loss]) #train_exe和exe是以前定义的,相似与tensorflow的session(我的感受,实际上仍是不同的)以下: #exe = fluid.Executor(place) #train_exe = fluid.ParallelExecutor(use_cuda=args.use_gpu, loss_name=loss.name) #其中place为指定设备(CPU GPU)
好了,总结一下,如何将数据送入网络(在有了reader的前提下):
看到这里,我老是感受paddlepaddle的fluid和tensorflow很像,先定义图模型,而后运行,可是看到官方说fluid是和TensorFlow Eager Execution很像,据我了解(没有用过,因此有多是错误的,望批评指正)TensorFlow Eager Execution是针对以前tensoflow不能实时出结果(必须sess.run)来设计的,可是如今看好像不是很像,之后看懂了再来解释。留坑。
对于如何将数据保存成RecordIO文件并读取,这篇会详细解释。
这部分咱们直接看代码吧,在SSD的例子中:
locs, confs, box, box_var = mobile_net(num_classes, image, image_shape)
调用了mobile_net模型,这个有兴趣本身看吧,主要是fluid.layers中各类层的应用,这个估计各个深度学习框架都差很少,这部分实现的仍是挺全的。
loss = fluid.layers.ssd_loss(locs, confs, gt_box, gt_label, box,box_var) loss = fluid.layers.reduce_sum(loss) ... optimizer = fluid.optimizer.RMSProp( learning_rate=fluid.layers.piecewise_decay(boundaries, values), regularization=fluid.regularizer.L2Decay(0.00005), ) optimizer.minimize(loss) place = fluid.CUDAPlace(0) if args.use_gpu else fluid.CPUPlace() exe = fluid.Executor(place) exe.run(fluid.default_startup_program())
咱们来看这一部分,定义了loss,而后指定了优化器,而后最小化loss,指定设备,而后启动咱们的程序。我感受这里是个大坑!有没有发现有些文档里面不是这么个流程,而是这样子的(来源paddlepaddle 03.image_classification):
place = fluid.CUDAPlace(0) if use_cuda else fluid.CPUPlace() trainer = fluid.Trainer( train_func=train_program, optimizer_func=optimizer_program, place=place) trainer.train( reader=train_reader, num_epochs=EPOCH_NUM, event_handler=event_handler, feed_order=['pixel', 'label'])
指定了一个trainer而后调用train。
还有一种:
parameters = paddle.parameters.create(cost) trainer = paddle.trainer.SGD(cost=cost, parameters=parameters,update_equation=momentum_optimizer)
先根据cost(loss)产生要优化的参数,而后指定这些参数进行优化。
这到底用哪种呢?幸亏有大佬夜雨飘零1的经验,是由于新版本Fluid的缘由,如今大部分都是用executor来进行编写的。因此之后也不用烦恼了,这里吐槽一下官方文档,感受维护人员要少吃一个鸡腿,不一样版本变化太大,然而官方只给最新的示例,可是对于以前的代码并无进行版本的说明,致使咱们学习起来有点混乱,但愿可以从新写一下book。
这部分官方文档资料挺全的,固然对于你们比较关心的如何加载ImageNet 预训练模型,也是有的,这里有例子,可是说实话这里有点问题,大佬在这里也作了讨论,原本想参考官方文档进行resnet的加载,可是一方面官方脚本执行时链接不上,再看模型加载会出现各类问题,因此暂时放弃了这种想法,等一下官方的优化。
这部分的主要代码:
exe = fluid.Executor(fluid.CPUPlace()) param_path = "./my_paddle_model" prog = fluid.default_main_program() fluid.io.save_params(executor=exe, dirname=param_path, main_program=None) ... exe = fluid.Executor(fluid.CPUPlace()) param_path = "./my_paddle_model" prog = fluid.default_main_program() fluid.io.load_params(executor=exe, dirname=param_path, main_program=prog)
可见是经过fluid.io来实现的。
这部分应该是paddlepaddle的优点了,一方面咱们训练的过程当中但愿可以进行测试,一方面当咱们的模型训练完之后咱们也但愿可以利用前向传播进行预测。paddlepaddle都有这两方面实现:第一种官方给了很好的示例,这里就不赘述了。对于第二种,paddlepaddle也进行了很好的封装:
inferencer = fluid.Inferencer( # infer_func=softmax_regression, # uncomment for softmax regression # infer_func=multilayer_perceptron, # uncomment for MLP infer_func=convolutional_neural_network, # uncomment for LeNet5 param_path=params_dirname, place=place) results = inferencer.infer({'img': img})
convolutional_neural_network就是你的模型里面生成predict的那个函数,params_dirname是保存参数的路径,可见,用paddlepaddle来进行前向传播十分简单,定义好数据以后,加载参数,而后调用infer就能够预测了。
paddlepaddle还有很好的部署能力,可是局限于我如今用的功能,这部分并无研究,这篇博客主要是串一下如何用paddlepadle搭建深度学习模型,其中有不少细节没有注意,并且有不少地方也不必定准确,但愿各位多批评指正。
参考:
https://blog.csdn.net/u010089444/article/details/76725843
http://www.javashuo.com/article/p-wwjagzao-gd.html
http://www.cnblogs.com/charlotte77/p/7906363.html
https://github.com/PaddlePaddle/models/tree/develop/fluid/object_detection
https://github.com/PaddlePaddle/book/blob/high-level-api-branch/03.image_classification/train.py