人脸识别回顾

数据准备

数据清洗:去除错误标签,图片质量不好的样本,如果有benchmark,还需要去除和benchmark重复的样本。

构造验证集: 对于给定的数据集,可能没有划分train set和validation set ,需要手动从给定的训练集中按照一定的比例分离出验证集(比如9:1)

数据均衡:数据集可能存在类别不均衡的问题,可以通过重新组合不均衡数据, 使之均衡,方式一: 复制或者合成(比如jitter操作)少数部分的样本, 使之和多数部分差不多数量; 方式二: 砍掉一些多数部分, 使两者数量差不多

数据扩充:对于一些样本数据比较少的数据集,为了更好的训练网络,有时候需要人为增加一些训练样本,比如随机的剪裁、缩放和旋转等。

预处理:常见的就是减均值、除方差。

训练模型

1. 模型选择:

根据具体任务和数据集规模选择合适的网络结构,对于分类任务来说,如果数据集的规模不大,则网络的层数不应太深,结构也不应太复杂。

2. 激励函数的选择

  • sigmoid函数:取值范围为(0,1),可以将一个实数映射到(0,1)的区间,可以用来做二分类,在特征相差比较复杂或是相差不是特别大时效果比较好,缺点是:激活函数计算量大,反向传播求梯度时,求导涉及除法,反向传播时,很容易出现梯度消失的情况

  • Tanh函数:取值范围为[-1,1],在特征相差明显时的效果会很好,在循环过程中会不断扩大特征效果,与sigmoid的区别是,tanh是0均值的,因此实际应用中,tanh会比sigmoid更好。

  • ReLU函数:使用ReLU得到的SGD的收敛速度会比sigmoid/tanh快很多,缺点是训练的时候很”脆弱”,很容易就”die”了例如,一个非常大的梯度流过一个 ReLU 神经元,更新过参数之后,这个神经元再也不会对任何数据有激活现象了,那么这个神经元的梯度就永远都会是 0.如果 learning rate 很大,那么很有可能网络中的 40% 的神经元都”dead”了。

    选择的时候,就是根据各个函数的优缺点来配置,例如:

    如果使用 ReLU,要小心设置 learning rate,注意不要让网络出现很多 “dead” 神经元,如果不好解决,可以试试 Leaky ReLU、PReLU 或者 Maxout.

    详细细节请参看常用激活函数比较

3. 卷积tricks

图片输入是2的幂次方,例如32、64、96、224等。

卷积核大小是33或者55。

输入图片上下左右需要用0补充,即padding,且假如卷积核大小是5,那么padding就是2(图片左右上下都补充2);卷积核大小是3,padding大小就是1。

4. pooling层tricks

poolin层也能防止过拟合,使用overlapped pooling,即用来池化的数据有重叠,但是pooling的大小不要超过3。max pooling比avg pooling效果会好一些。avg-global pooling进入全卷积时代。

5. Loss函数的选择

  • softmax loss

  • contrastive loss

  • triplet loss

  • center loss

    triplet loss比softmax的优势

    • 在于softmax不直接,(三元组直接优化距离),因而性能也不好。
    • softmax产生的特征表示向量都很大,一般超过1000维。
    • 利用triplet loss的metric learning目的在于减小类内的L2距离,同时增大类间的距离

    center loss VS triplet loss

    • triplet loss:dramatic data expansion
    • center loss:more directly and efficiently

神经网络的设计模式

  1. Architectural Structure follows the Application(架构遵循应用)

    应该根据自己的应用场景选择合适的网络架构。

  2. Proliferate Paths(路径扩增)

    每年ImageNet Challenge的赢家都比上一年的冠军使用更加深层的网络。从AlexNet 到Inception到Resnets,Smith和他的团队也观察到“网络的路径数量成倍增长”的趋势,ResNet可以是不同长度的网络的指数集合。

  3. Strive for Simplicity(简洁原则)

    更大的并不一定是更好的。在名为“Bigger is not necessarily better”的论文中,Springenberg 等人演示了如何用更少的单元实现最先进的结果。

  4. Increase Symmetry (增加对称性)

    无论是在建筑上,还是在生物上,对称性被认为是质量和工艺的标志。Smith 将 FractalNet 的优雅归功于网络的对称性。

  5. Pyramid Shape (金字塔型)

    在表征能力和减少冗余或者无用信息之间权衡。CNNs通常会降低激活函数的采样,并会增加从输入层到最终层之间的连接通道。

  6. Over-train (过训练)

    另一个权衡是训练准确率和泛化能力。用正则化的方法类似 drop-out 或 drop-path进行提升泛化能力,这是神经网络的重要优势。用比实际用例更难的问题训练你的网络,以提高泛化性能。

  7. Cover the Problem Space (覆盖问题空间)

    为了扩大你的训练数据和提升泛化能力,要使用噪声和人工增加训练集的大小,例如随机旋转、裁剪和一些图像操作。

  8. Incremental Feature Construction (递增的功能结构)

    当架构变得成功时,它们会简化每一层的“工作”。在非常深的神经网络中,每个 层只会递增地修改输入。在ResNets中,每一层的输出可能类似于输入。所以,在实践中,请在ResNet中使用短的跳过长度。

  9. Normalize Layer Inputs (规范化层输入)

    标准化是可以使计算层的工作变得更加容易的一条捷径,并且在实际中可以提升训练的准确性。批量标准化的发明者认为标准化发挥作用的原因在于处理内部的协变量,但Smith 认为,“标准化把所有层的输入样本放在了一个平等的基础上(类似于单位转换),这允许反向传播可以更有效地训练”。

  10. Input Transition (输入转换)

    研究表明,在Wide ResNets中,性能随着通道数量的增加而提升,但是要权衡训练成本与准确性。AlexNet,VGG,Inception和ResNets都是在第一层中进行输入变换,以保证多种方式检查输入数据。

  11. Available Resources Guide Layer Widths (可用资源决定层宽度)

    可供选择的输出数量并不明显,相应的是,这取决于您的硬件功能和所需的准确性。

  12. Summation Joining (求和连接)

    Summation是一种流行的合并分支的方式。在 ResNets 中,使用求和作为连接机制可以让每一个分支都能计算残差和整体近似。如果输入跳跃连接始终存在,那么Summation会让每一层学到正确地东西(例如:输入的差别)。在任何分支都可以被丢弃的网络(例如 FractalNet)中,你应该使用这种方式保持输出的平滑。

  13. Down-sampling Transition (下采样变换)

    在汇聚的时候,利用级联连接来增加输出的数量。当使用大于1的步幅时,这会同时处理加入并增加通道的数量。

  14. Maxout for Competition (用于竞争的Maxout)

    Maxout 用在只需要选择一个激活函数的局部竞争网络中。用的方法包含所有的激活函数,不同之处在于 maxout 只选择一个“胜出者”。Maxout 的一个明显的用例是每个分支具有不同大小的内核,而 Maxout 可以尺度不变。

Caffe调参

1. loss为nan(83.3365) [标签错误、学习率太大]

梯度爆炸

原因:梯度变得非常大,使得学习过程难以继续

现象:观察log,注意每一轮迭代后的loss。loss随着每轮迭代越来越大,最终超过了浮点型表示的范围,就变成了NaN。

措施

  1. 减小solver.prototxt中的base_lr,至少减小一个数量级。如果有多个loss layer,需要找出哪个损失层导致了梯度爆炸,并在train_val.prototxt中减小该层的loss_weight,而非是减小通用的base_lr。
  2. 设置clip gradient,用于限制过大的diff

不当的损失函数

原因:有时候损失层中loss的计算可能导致NaN的出现。比如,给InfogainLoss层(信息熵损失)输入没有归一化的值,使用带有bug的自定义损失层等等。

现象:观测训练产生的log时一开始并不能看到异常,loss也在逐步的降低,但突然之间NaN就出现了。

措施:看看你是否能重现这个错误,在loss layer中加入一些输出以进行调试。

示例:有一次我使用的loss归一化了batch中label错误的次数。如果某个label从未在batch中出现过,loss就会变成NaN。在这种情况下,可以用足够大的batch来尽量避免这个错误。

不当的输入

原因:输入中就含有NaN。

现象:每当学习的过程中碰到这个错误的输入,就会变成NaN。观察log的时候也许不能察觉任何异常,loss逐步的降低,但突然间就变成NaN了。

措施:重整你的数据集,确保训练集和验证集里面没有损坏的图片。调试中你可以使用一个简单的网络来读取输入层,有一个缺省的loss,并过一遍所有输入,如果其中有错误的输入,这个缺省的层也会产生NaN。

案例:有一次公司需要训练一个模型,把标注好的图片放在了七牛上,拉下来的时候发生了dns劫持,有一张图片被换成了淘宝的购物二维码,且这个二维码格式与原图的格式不符合,因此成为了一张“损坏”图片。每次训练遇到这个图片的时候就会产生NaN。良好的习惯是,你有一个检测性的网络,每次训练目标网络之前把所有的样本在这个检测性的网络里面过一遍,去掉非法值。

池化层中步长比核的尺寸大

如下例所示,当池化层中stride > kernel的时候会在y中产生NaN

1
2
3
4
5
6
7
8
9
10
11
layer {
name: "faulty_pooling"
type: "Pooling"
bottom: "x"
top: "y"
pooling_param {
pool: AVE
stride: 5
kernel: 3
}
}

2. 避免overfitting

  • simpler model structure

  • regularization

  • data augmentation

  • dropall(dropout+drop-path)

  • Bootstrap/Bagging

  • ensemble

  • early stopping

  • utilize invariance

  • Bayesian

人脸识别流程

一般流程:数据准备->人脸检测->人脸对齐->生成数据文件->训练模型->调参->model

tricks:

  • 在数据准备阶段,可以采用jitter等操作对样本数量较少的种类进行扩充,弥补类别不均衡造成的影响
  • 在生成模型阶段,可以采用ensemble的方式对多个模型进行融合,比如mirror。

参考及致谢

Common causes of nans during training

常用激活函数比较

深度学习之五常见tricks

《Deep convolutional neural network design patterns 》