先前用Keras跑基于ResNet50的一些迁移学习的实验,遇到一些奇奇怪怪的问题,在GitHub上看到了相关的讨论,发现锅在BN层,国外已经有人总结并提了一个PR(虽然并没有被merge到Keras官方库中),并写了一篇博客(我翻译了一遍在下面),仔细阅读后我也觉得这篇博客写得并不清晰,按我的理解简单总结一下:Keras中通常用trainable这个参数来控制某一层的权重是否更新,例如trainable可以控制BN中
和
是否变化
TF为后端时,BN有一个参数是training,控制归一化时用的是当前Batch的均值和方差(训练模式)还是移动均值和方差(测试模式),这个参数由Keras的K.learning_phase控制。但是只设置trainable是不会影响BN的training参数。
冻结时某一层时,我们希望这一层的状态和预训练模型中的状态一致
我们通常希望训练和测试时网络中的配置一致,但BN训练和测试时的配置是不一样的,而frozen这个行为放大了这种不一致,导致精度下降。训练时用了新数据集的均值和方差去做归一化,测试时用了旧数据集的移动均值和方差去做归一化
为了让训练和测试尽量一致,避免精度下降,有两种方案,一种是在测试时也用新数据集的移动均值和方差(作者提的修复似乎就是这种方案,但这样回退到了作者所抨击的Keras2.1.3之前的做法,所以我不知道是不是我对博客理解上出了偏差,大家可以对照原文去看看)
另一种方案是在训练时也只用旧数据集的移动均值和方差,这是Keras作者方案:在定义模型时,手动将training参数设为False(可以通过显式设置BN的training参数,或者通过设置learning_phase来隐式改变training参数),我觉得其实这种workaround还是挺好用的,而且也更符合frozen的意图,即:
显式设置:
x = BatchNormalization()(y, training=False)
隐式设置:
# Set up inference-mode base
K.set_learning_phase(0)
inputs = Input(...)
x = layer1(...)(inputs)
x = layer2(...)(x)
...
x = layerN(...)(x)
# Add training-mode layers
K.set_learning_phase(1)
x = layerNp1(...)(x)
x = layerNp2(...)(x)
不可否认的是,默认的Frozen的BN的行为在迁移学习中确实是有training这个坑存在的,个人认为
下面是Vasilis Vryniotis的内容:
原文:The Batch Normalization layer of Keras is brokenblog.datumbox.com
译文:
虽然Keras节省了我们很多编码时间,但Keras中BN层的默认行为非常怪异,坑了我(此处及后续的“我”均指原文作者)很多次。Keras的默认行为随着时间发生过许多的变化,但仍然有很多问题以至于现在Keras的GitHub上还挂着几个相关的issue。在这篇文章中,我会构建一个案例来说明为什么Keras的BN层对迁移学习并不友好,并给出对Keras BN层的一个修复补丁,以及修复后的实验效果。
1. Introduction
这一节我会简要介绍迁移学习和BN层,以及learning_phase的工作原理,Keras BN层在各个版本中的变化。如果你已经了解过这些知识,可以直接跳到第二节(译者注:1.3和1.4跟这个问题还是比较相关的,不全是背景)。
1.1 迁移学习在深度学习中非常重要
深度学习在过去广受诟病,原因之一就是它需要太多的训练数据了。解决这个限制的方法之一就是迁移学习。
假设你现在要训练一个分类器来解决猫狗二分类问题,其实并不需要几百万张猫猫狗狗的图片。你可以只对预训练模型顶