小样本利器4. 正则化+数据增强 Mixup Family代码实现

前三章我们陆续介绍了半监督和对抗训练的方案来提高模型在样本外的泛化能力,这一章我们介绍一种嵌入模型的数据增强方案 。之前没太重视这种方案,实在是方法过于朴实 。。。不过在最近用的几个数据集上mixup的表现都比较哇塞 , 所以我们再来聊聊~
Mixup

  • paper: mixup: Beyond Empirical Risk Minimization
  • TF源码:https://github.com/facebookresearch/mixup-cifar10
  • torch复现:ClassicSolution
原理mixup的实现非常简单 , 它从训练集中随机选择两个样本,对x和y分别进行线性加权 , 使用融合后的\(\tilde{x}, \tilde{y}\)进行模型训练
\[\tilde{x} = \lambda x_i + (1-\lambda) x_j \\\tilde{y} = \lambda y_i + (1-\lambda) y_j\]对x的融合比较容易理解,例如针对图像输入,mixup对输入特征层的线性融合可以被直观的展现如下 。
小样本利器4. 正则化+数据增强 Mixup Family代码实现

文章插图
对y的融合,如果是2分类问题且\(\lambda=0.3\), 一个y=1的样本融合一个y=0的样本后d得到\(\tilde{y}=[0.3,0.7]\),等价于两个样本损失函数的线性加权,既0.3 *CrossEntropy(y=0)+0.7 * CrossEntropy(y=1)
如何理解mixup会生效呢?作者是从数据增强的角度给出了解释 , 认为线性差值的方式拓展了训练集覆盖的区域,在原始样本未覆盖区域(in-between area)上让模型学到一个简单的label线性差值的结果,从而提高模型样本外的泛化效果~
不过我更倾向于从正则化的角度来理解,因为模型并不是在原始样本上补充差值样本进行训练,而是完全使用差值样本进行训练 。线性差值本身是基于一个简化的空间假设,既输入的线性加权可以映射到输出的线性加权 。这个简化的假设会作为先验信息对模型学习起到正则约束的作用,使得模型的分类边界更加平滑,且分类边界离样本高密度区更远 。这和我们上一章提到的半监督3大假设,平滑性假设 , 低密度分离假设相互呼应~
作者对比了原模型和mixup增强模型在对抗样本上的预测误差 , 验证了mixup可以有效提高模型在扰动样本上的鲁棒性,不过看误差感觉对抗训练可能可以和mixup并行使用,以后有机会尝试后再来补充~
小样本利器4. 正则化+数据增强 Mixup Family代码实现

文章插图
同时作者做了个有趣的实验,对比了在融合样本上的预测误差,举个栗子0.3个体育新闻融合0.7个娱乐新闻让模型去做预测,如果预测结果既非体育也非娱乐则判断为miss 。下图显示mixup可以有效降低miss率 。间接作证了mixup有提高in-between样本外预测的效果~
小样本利器4. 正则化+数据增强 Mixup Family代码实现

文章插图
实际应用中还有几个细节有待讨论
  1. 在哪一层进行mixup效果更好
作者在图像分类任务中对比了layer1~6 , 最终发现对最底层施加mixup的效果最好 。和FGM一样,如果对高层进行mixup,因为非线性程度较低,可能会导致模型欠拟合
  1. 只在同一个类别内部进行mixup还是对全类别进行随机mixup
作者对比了类内mixup,和所有类随机mixup,效果是随机mixup效果更好 。感觉不限制插值类别才是保证分类边界远离样本高密度区的关键,因为mixup会使得模型在两个分类cluster中间未覆盖的区域学到一个线性插值的分类,从而使得分类边界远离任意类别样本的覆盖区域
  1. 是否需要限制mixup样本之间的相似度,避免引入过多噪声作者尝试把mixup的范围限制在KNN200 , 不过效果没有随机mixup效果好
  2. 混合权重的选择论文并没有对应该如何选择插值的权重给出太多的建议,实际尝试中我也一般是从大往小了调,在一些小样本上如果权重太大会明显看到模型欠拟合 , 这时再考虑适当调低权重,和dropout扰动相同权重越大正则化效果越强,也就越容易欠拟合
实现def mixup(input_x, input_y, label_size, alpha):    # get mixup lambda    batch_size = tf.shape(input_x)[0]    input_y = tf.one_hot(input_y, depth=label_size)    mix = tf.distributions.Beta(alpha, alpha).sample(1)    mix = tf.maximum(mix, 1 - mix)    # get random shuffle sample    index = tf.random_shuffle(tf.range(batch_size))    random_x = tf.gather(input_x, index)    random_y = tf.gather(input_y, index)    # get mixed input    xmix = input_x * mix + random_x * (1 - mix)    ymix = tf.cast(input_y, tf.float32) * mix + tf.cast(random_y, tf.float32) * (1 - mix)    return xmix, ymix

推荐阅读