【学习笔记3】Convolutional Pose Mashines在FashionAI中的应用——我的深度学习首秀

春学期开学至今这一个多月里,除了有序推进自己的科研任务,非专业的我还和协会志同道合的小伙伴跟着罗老师以参加比赛的方式在深度学习计算机视觉领域里进行实战演练。我们参加的是天池FashionAI服饰关键点定位全球挑战赛,今天(4月21日)是初赛截止的日子,凭借有限的精力和资源我们最终以test_b数据集不包含遮挡点NE(Normalized Error)7.49%的成绩排名70/2321,顺利进入复赛。虽然在复赛队伍里,我们的成绩并不拔尖,我也知道未来我们要做的还有很多很多,但是这是我第一次参赛,CPM(Convolutional Pose Machines)也是我第一个通篇读懂、调试、修改、完善的深度学习模型。和本科时候第一次参加电子设计大赛一样,第一次永远是最痛苦最煎熬的,但也是收获最大进步最显著的。从当初面试时的什么也不懂,到如今能够独当一面成为一名还算不错的深度学习炼丹师,碰了不少壁,踩了不少坑,但是我很享受这种钻研进步的过程。罗老师说,任重而道远,是的,还有很多要做,我也还有很多要学。那么就先写博客做一个阶段性的总结吧。

一、CPM——卷积姿态机

论文传送门:《Convolutional Pose Machines》。

作者源码传送门:https://github.com/shihenw/convolutional-pose-machines-release。

我们在比赛中用到的tensorflow实现的源码传送门:https://github.com/timctho/convolutional-pose-machines-tensorflow。

关于CPM的机理,这篇博客讲的已经很好了,CPM的结构简单,结构上复用很多,配合代码很快就能看懂,虽然我这个小白还是看了半个月,一句一句地推敲才读通了网络。在这里就不再多说原理,我想说一下我通过实践对CPM的一些理解,可能不准确,仅代表个人意见,欢迎各位指正。

优点:

  1. CPM通过将上一个stage预测的heatmap和当前stage提取的特征一起concate,有一种残差的思想,通过stack多个stage来refine预测的heatmap。Baseline挺高,在不同数据集上鲁棒性很强。
  2. 实践证明,CPM在学习关键点关联性上有先天的优势。在天池提供的数据集上,两个领口中间(neckline_left,neckline_right)就应该有中线点(center_front),左肩点(shoulder_left)对面就应该有右肩点(shoulder right)。因此很少有点明显跑偏到无关背景中的现象,哪怕有也是一些确实有可能混淆的角点。身边的有个好朋友也参加了这个比赛,他们先通过检测提取出bbox然后在bbox上预测关键点,他们起初点跑飞的现象依然很严重,来咨询我的时候,我直接说CPM应该能很大程度解决这个问题,于是他们转战CPM,最终获得了NE八点几的结果,也成功闯进复赛。因此,CPM不太会有明显的点跑飞现象,它可以很好的学习关键点之间的关联性。至少我这里的实践情况如此。
  3. CPM的中继监督优化,单独计算每个stage的loss,很好地解决了梯度弥散现象,因此多个stage网络也能训得动。

缺点:

  1. CPM网络虽然简单,但是结果复用、连续的卷积显得有些“啰嗦”,训练起来也很慢。当然网络性能好,硬件资源跟得上,这些也都不是啥大问题。
  2. CPM是没有Hourglass那样的多尺度特性的,因此需要在数据增广的时候加入scale的增广,而且实践证明,尺度的增广对于CPM来说是十分重要的。因为test上有很多全身照,人已经非常小,而我们只需要根据任务要求提取上衣或者裙子的关键点,所以需要网络从很小的区域提取图像特征。因此,尺度的增广对于没有多尺度特性的CPM来说是必要的。
  3. CPM具有残差的思想,通过多个stage不断refine预测结果,这种refine只能说是微调位置以及巩固强化heatmap对关键点的响应。但是一旦第一个stage发生很明显的预测不准确,或是根本没有响应关键点,那么后面的refine也是无济于事。

综上所述,CPM具有很好域迁移的鲁棒性,baseline挺高,但是它的缺点也决定了仅仅依靠CPM的baseline很难达到非常好的效果,即使加入完善的数据增广、加入检测给预测增加一个很强的constrain,也只能是涨一些点,要想跻身排行榜前列,需要结合一些更先进的算法,关于这一点,我们也在仔细研究。

二、我的炼丹修炼

深度学习炼丹过程是痛苦的,一种trick的效果验证的等待周期很长,先要代码实现,然后放到GPU上训练,等很长的时间一个模型训练完,然后做出结果,虽然有可视化的图片给出直观的感觉,但是还是需要提交一波结果让官方审核才放心,于是又得等待一天只有两次的测评。关键的是,我是个非常非常非常明显的急性子,等得我真的是......当然,我目前盲目的训练方法也存在问题,有很多弯路是完全可以规避的,这个后面会说。

不过,炼丹的过程却又是很有趣的,当结果显示一个小trick的加入让网络涨了哪怕只有半个百分点的时候,别提多高兴了。很多个trick都是之前从论文里看到的,知道机理却从没实践过,现在通过第一次尝试,对一些trick也有了更深的了解。后面就大致说一下我训练CPM baseline的过程,多少也能够表明训练一个网络都要做哪些事。有一点我是确信的,就是下一个网络,我绝不会再花这么久才做出第一个结果了(我的第一次结果提交是在4月4号,也就是说我差不多花了一个月)。

1、Datagenerator——数据生成器

我的师兄经常说数据预处理是训练一个网络的过程中最麻烦的,而且这个过程针对每个数据集的操作还不一样,因此针对不同网络需要写不同的数据生成器,所以,会做数据预处理,会写数据生成器,是深度学习实践的第一步。因此深度学习行业里有个岗位叫“Data provider”,它是专门做数据预处理的一个岗位。

将图片Feed进网络之前,一般需要做如下几个步骤数据预处理、数据增广、编写数据迭代器。我在CPM的实践过程中的数据生成器是团队小伙伴写的,大致做了如下操作:

  • 数据预处理。根据大赛提供的csv文件提取标签,生成包含所有图片名字的list,以及包含图片所有信息的dict,并按照一定百分比将训练集划分为训练集和验证集。这一步蛮简单的。
  • 数据增广。数据增广是一个很玄妙的学问:增广弱了不好,训练集规模又不够大,无法涵盖所有场景,test上的accuracy自然也不会高;增广太强呢也不好,一方面,增大了训练集的规模,需要增加网络训练的epoch,训练时间加长,另一方面过分地涵盖所有场景,同时又让训练的loss降到很低,网络很容易在我们设置的增广场景内过拟合,test一旦出现一个新奇的玩意儿,可能就一塌糊涂了,还不如不加增广的好。所以,增广的到底该怎么加,每种增广的幅度加多少,需要针对特定任务特定数据集认真确定,没办法一概而论。那么我在FashionAI的数据集中用的增广有这些:
  1. 尺度增广。在CPM缺点那一块提到了,CPM不具有提取多尺度特征的特性,而我们有需要网络有尺度上的鲁棒性,因此尺度增广是十分重要的。
  2. 旋转增广。test图片里的美女们经常摆pose嘛,pose一摆,关键点整体就偏移了一个角度,因此加入旋转的增广也是很重要的。不过,旋转没必要做到upside down这种程度,±30°、±45°足矣。要是直接upside down,我估计脚会被预测手,手会被预测称脚......
  3. 亮度、对比度、色度增广。test图片场景亮度变化很大,有的是在白天拍的,有的是在晚上拍的,有的是晚上拍的穿黑衣服,有的是穿着黄衣服背景还是差不多色儿的墙壁,因此对网络在亮度、对比度、色度上的要求也很高。当然这个增广也得有个度,没必要亮到自己看着都刺眼,也没必要暗到自己肉眼都看不着。我是用PIL的ImageEnhance函数库做的,区间设置为[0.5~1.8]。
  4. 其他增广的trick,由于涉及到小伙伴们还没发表的论文,时机成熟后再和大家分享。
  • 数据迭代器。数据迭代器很简单,一个while...和yield的组合就可以实现,在调用的时候通过next()函数,程序会自动返回到while中继续生成下一个batch的数据。

综上所述,一个简单的random batch的datagenerator的函数体大概如下:

def generator(self, batch_size=16, normalize=False, sample_set='train'):
        """ Auxiliary Generator
        """
        while True:
            train_img = np.zeros((batch_size, 256, 256, 3), dtype=np.float32)
            i = 0
            while i < batch_size:
                if sample_set == 'train':
                    name = random.choice(self.train_set)
                elif sample_set == 'valid':
                    name = random.choice(self.valid_set)
                img = self.open_img(name)
                
                ......
                data processing
                ......

                ......
                data augmentation
                ......

                train_img[i] = img.astype(np.float32)
                i = i + 1
            yield train_img

在主函数调用的时候用:

generator = dataset.generator(batchSize=FLAGS.batch_size, norm=False, sample='train')
batch_img_np = next(generator)

2、learning rate decay存在的问题

大家可以先看这篇干货:https://zhuanlan.zhihu.com/p/33107481?utm_medium=social&utm_source=wechat_session&wechatShare=1。谷歌爸爸都已经证明了增加Batch Size的同时保持学习率的策略既可以保证不掉点,还可以减少参数更新的次数。当然,我们的硬件资源有限,显存就那么点,没办法把batch size设置成那么大,没办法还是在用decayed learning rate。

关于learning rate decay网络上博客很多,这里就不再赘述了。learning rate decay设计的初衷就是为了让网络在开始训练的时候可以用一个较大的学习率迅速调节网络参数,之后再网络的后期通过一个小的学习率fine tune网络参数,使网络下降到全局或者局部最优,解决了固定学习率网络训练后期在全局或局部最优点附近震荡等问题。但是learning rate decay的参数很难调,decay rate和decay step很难调到和网络相匹配,decay周期长了,网络后期还在以一个较大的值在持续训练,可能出现过拟合甚至跳出全局/局部最优点造成loss回升;decay周期短了,网络还没有收敛完全,learning rate就非常非常小了,网络参数就更新不动了。这两种情况在实践过程中我都出现过。所以说,learning rate decay存在很严重的网络匹配问题。

同样的原因,我们的解决方法时机成熟再和大家分享。

3、适当的fine tune很重要

fine tune就是在网络训练的后期以一个合适的小学习率更新网络参数,使网络更好地拟合训练集,在全局/局部最优点处收敛得更好。实践证明,我们通过fine tune让模型在test a上增长了一个多点。

4、理解tensorflow命名空间

和小伙伴在写BN代码的时候总是会出现命名空间复用的问题,那时候不懂命名空间,一气之下干脆把bug相关的变量定义从tf.get_variable全部替换成了tf.Variable,并且名字都设置成了None,最后辛辛苦苦训练了一天多的网络保存的checkpoint在load进test网络的时候发生变量不存在的错误,训好的网络参数一点用都没有了,白白浪费了很多时间。

因此,以后管理变量尽量都用scope管理好,在不同的代码块用不同的scope管理代码。比如CPM中每个stage都定义一个单独的scope。并且用tf.get_variable定义时,每个变量定义一个自己唯一的名字,这样保存的checkpoint才能根据变量名成功地加载参数,也规范了网络的变量管理。

5、切忌“盲训”,要建立自己的validation机制

我们在训练的时候没有根据大赛官方的评价标准建立自己的验证机制,每做一次新的尝试,都要等待经过漫长的等待时间获得官方给出的test结果,这种盲目的时间浪费了我们大量的时间。如果在训练的过程中就建立和大赛官方标准一样的NE评价机制,就能直接在训练过程中通过观察验证集的NE值得到新的trick的性能,这样能减少浪费的时间,并且有据可依地尝试各种方法。做比赛嘛,要过拟合,也要在官方评价指标过拟合。

三、总结

上述是我自己在训练CPM的基础模型的过程中遇到的一些问题,对于大神来说可能不算什么,但对于第一次训练完整深度学习模型的我来说,都是第一次遇见。过程中确实走了不少弯路、碰了不少壁、踩了不少坑。在此总结一番,便于自己今后学习翻阅,也和大家一起交流分享。还有一些trick我会继续总结好再和大家一起分享,代码我也会在后面开源。未来还有很多要学,那就继续学下去吧。

最后,对于文中的错误,还请大家指正。

 

-------------------------------------------

Youzhi Gu, master student

Foresight Control Center

College of Control Science & Engineering

Zhejiang University

Email: [email protected]

 

 

你可能感兴趣的:(深度学习学习笔记)