在训练了 50 个 epoch 之后,本文作者惊讶地发现模型什么都没学到,于是开始深挖背后的问题,并最终从恺明大神论文中得到的知识解决了问题。

上个星期我做了一些实验,用了在 CIFAR10 数据集上训练的 VGG16。我需要从零开始训练模型,所以没有使用在 ImageNet 上预训练的版本。

我开始了 50 个 epoch 的训练,然后去喝了个咖啡,回来就看到了这些学习曲线:

模型什么都没学到!

我见过网络收敛得极其缓慢、振荡、过拟合、发散,但这是我第一次发现这种行为——模型根本就没有起任何作用。

因此我就深挖了一下,看看究竟发生了什么。

实验

这是我创建模型的方法。它遵循了 VGG16 的原始结构,但是,大多数全连接层被移除了,所以只留下了相当多的卷积层。

现在让我们了解一下是什么导致了我在文章开头展示的训练曲线。

学习模型过程中出现错误时,检查一下梯度的表现通常是一个好主意。我们可以使用下面的方法得到每层梯度的平均值和标准差:

然后将它们画出来,我们就得到了以下内容:

使用 Glorot 函数初始化的 VGG16 梯度的统计值

呀... 我的模型中根本就没有梯度,或许应该检查一下激活值是如何逐层变化的。我们可以试用下面的方法得到激活值的平均值和标准差:

然后将它们画出来:

使用 Glorot 函数进行初始化的 VGG16 模型的激活值

这就是问题所在!

提醒一下,每个卷积层的梯度是通过以下公式计算的:

其中Δx 和Δy 用来表示梯度∂L/∂x 和∂L/∂y。梯度是通过和链式法则计算的,这意味着我们是从最后一层开始,反向传递到较浅的层。但当最后一层的激活值接近零时会发生什么呢?这正是我们面临的情况,梯度到处都是零,所以不能反向传播,导致网络什么都学不到。

由于我的网络是相当简约的:没有,没有 Dropout,没有数据增强,所以我猜问题可能来源于比较糟糕的初始化,因此我拜读了何恺明的论文——《Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification》

论文链接:https://arxiv.org/pdf/1502.01852.pdf

下面简要描述一下论文内容。

初始化方法

初始化始终是深度学习研究中的一个重要领域,尤其是结构和非线性经常变化的时候。实际上一个好的初始化是我们能够训练深度神经网络的原因。

以下是何恺明论文中的关键思想,他们展示了初始化应该具备的条件,以便使用 ReLU 激活函数正确初始化 CNN。这里会需要一些数学知识,但是不必担心,你只需抓住整体思路。

我们将一个卷积层 l 的输出写成下面的形式:

接下来,如果偏置值被初始化为 0,再假设权重 w 和元素 x 相互独立并且共享相同的分布,我们就得到了:

其中 n 是一层的权重数目(例如 n=k²c)。通过独立变量的乘积的方差公式:

它变成了:

然后,如果我们让权重 w 的均值为 0,就会得到:

通过 König-Huygens 性质:

最终得到:

然而,由于我们使用的是 ReLU 激活函数,所以就有了:

因此:

这就是一个单独卷积层的输出的方差,到那时如果我们想考虑所有层的情况,就必须将它们乘起来,这就得到了:

由于我们做了乘积,所以现在很容易看到如果每一层的方差不接近于 1,网络就会快速衰减。实际上,如果它比 1 小,就会快速地朝着零消散,如果比 1 大,激活的值就会急剧增长,甚至变成一个你的计算机都无法表示的数字(NaN)。因此,为了拥有表现良好的 ReLU CNN,下面的问题必须被重视:

作者比较了使用标准初始化(Xavier/Glorot)[2] 和使用它们自己的解初始化深度 CNN 时的情况:

在一个 22 层的 ReLU CNN 上使用 Glorot(蓝色)初始化和 Kaiming 的初始化方法进行训练时的对比。使用 Glorot 初始化的模型没有学到任何东西。

这幅图是不是很熟悉?这就是我在文章开始向你们展示的图形!使用 Xavier/Glorot 初始化训练的网络没有学到任何东西。

现在猜一下 Keras 中默认的初始化是哪一种?

没错!在 Keras 中,卷积层默认是以 Glorot Uniform 分布进行初始化的:

所以如果我们将初始化方法改成 Kaiming Uniform 分布会怎么样呢?

使用 Kaiming 的初始化方法

现在来创建我们的 VGG16 模型,但是这次将初始化改成 he_uniform。

在训练模型之前,让我们来检查一下激活值和梯度。

所以现在,使用 Kaiming 的初始化方法时,我们的激活拥有 0.5 左右的均值,以及 0.8 左右的标准差。

可以看到,现在我们有一些梯度,如果希望模型能够学到一些东西,这种梯度就是一种好现象了。

现在,如果我们训练一个新的模型,就会得到下面的学习曲线:

我们可能需要增加一些正则化,但是现在,哈哈,已经比之前好很多了,不是吗?

结论

在这篇文章中,我们证明,初始化是模型中特别重要的一件事情,这一点你可能经常忽略。此外,文章还证明,即便像 Keras 这种卓越的库中的默认设置,也不能想当然拿来就用。

参考文献和扩展阅读:

[1]: Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification:https://arxiv.org/pdf/1502.01852.pdf

[2]: Understanding the difficulty of training deep feedforward neural networks:http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf

[3]: 吴恩达课程:https://www.youtube.com/watch?v=s2coXdufOzE

原文地址:https://towardsdatascience.com/why-default-cnn-are-broken-in-keras-and-how-to-fix-them-ce295e5e5f2

欢迎关注磐创博客资源汇总站:

http://docs.panchuang.net/

欢迎关注PyTorch官方中文教程站:

http://pytorch.panchuang.net/

为何Keras中的CNN是有问题的,如何修复它们?的更多相关文章

  1. [知乎作答]·关于在Keras中多标签分类器训练准确率问题

    [知乎作答]·关于在Keras中多标签分类器训练准确率问题 本文来自知乎问题 关于在CNN中文本预测sigmoid分类器训练准确率的问题?中笔者的作答,来作为Keras中多标签分类器的使用解析教程. ...

  2. 在Keras中可视化LSTM

    作者|Praneet Bomma 编译|VK 来源|https://towardsdatascience.com/visualising-lstm-activations-in-keras-b5020 ...

  3. 探索学习率设置技巧以提高Keras中模型性能 | 炼丹技巧

      学习率是一个控制每次更新模型权重时响应估计误差而调整模型程度的超参数.学习率选取是一项具有挑战性的工作,学习率设置的非常小可能导致训练过程过长甚至训练进程被卡住,而设置的非常大可能会导致过快学习到 ...

  4. keras中VGG19预训练模型的使用

    keras提供了VGG19在ImageNet上的预训练权重模型文件,其他可用的模型还有VGG16.Xception.ResNet50.InceptionV3 4个. VGG19在keras中的定义: ...

  5. keras中的mini-batch gradient descent (转)

    深度学习的优化算法,说白了就是梯度下降.每次的参数更新有两种方式. 一. 第一种,遍历全部数据集算一次损失函数,然后算函数对各个参数的梯度,更新梯度.这种方法每更新一次参数都要把数据集里的所有样本都看 ...

  6. keras中的模型保存和加载

    tensorflow中的模型常常是protobuf格式,这种格式既可以是二进制也可以是文本.keras模型保存和加载与tensorflow不同,keras中的模型保存和加载往往是保存成hdf5格式. ...

  7. keras中的loss、optimizer、metrics

    用keras搭好模型架构之后的下一步,就是执行编译操作.在编译时,经常需要指定三个参数 loss optimizer metrics 这三个参数有两类选择: 使用字符串 使用标识符,如keras.lo ...

  8. Keras中RNN不定长输入的处理--padding and masking

    在使用RNN based model处理序列的应用中,如果使用并行运算batch sample,我们几乎一定会遇到变长序列的问题. 通常解决变长的方法主要是将过长的序列截断,将过短序列用0补齐到一个固 ...

  9. 深度学习基础系列(十一)| Keras中图像增强技术详解

    在深度学习中,数据短缺是我们经常面临的一个问题,虽然现在有不少公开数据集,但跟大公司掌握的海量数据集相比,数量上仍然偏少,而某些特定领域的数据采集更是非常困难.根据之前的学习可知,数据量少带来的最直接 ...

随机推荐

  1. 热更新,App双开,App隐藏,App试用 -- Replugin的实际应用(原创)

    热更新,App双开,App隐藏,App试用 -- Replugin的实际应用(原创) RePlugin是Qihoo 360公司的开源框架,原本目的是用于热更新.但是,这个框架提供的功能远远超出了热更新 ...

  2. 这几个IDEA高级调试技巧,用完就是香

    一个项目启动两次 测试分布式项目时,经常要一个项目启动2次,不用将一个项目打开多次启动,配置一下即可 1.点击Edit Configurations 2.勾选Allow parallel run 3. ...

  3. IDEA打包web项目为war,通过本地Tomcat启动war

    1.打包 ①idea的打包很简单,网上教程也很多,简单说下:project struct-->artifact-->+-->Web Application:Archive--> ...

  4. 使用Properties配置文件进行配置读取

    #使用Properties配置文件进行配置读取: 例如:有一个配置文件的内容如下: # setting.properties last_open_file=/data/hello.txt auto_s ...

  5. python递归用法

    需求:4的阶乘 4*3*2*1计算.通过递归算法,c=4*getnums(4-1),然后调用自己本身的函数,形成递归,就等于3*getnums(3-1),2*getnums(2-1),依次递归调用,最 ...

  6. 07.深入浅出 Spring Boot - 数据访问之Mybatis(附代码下载)

    MyBatis 在Spring Boot应用非常广,非常强大的一个半自动的ORM框架. 代码下载:https://github.com/Jackson0714/study-spring-boot.gi ...

  7. nginx能访问html静态文件但无法访问php文件

    nginx.conf中红框部分修改成你的实际网站根目录

  8. 7-29 jmu-python-不同进制数 (10 分)

    输入一个十进制整数,输出其对应的八进制数和十六进制数.要求采用print函数的格式控制进行输出,八进制数要加前缀0o,十六进制数要加前缀0x. 输入格式: 输入一个十进制整数,例如:10 输出格式: ...

  9. 7-7 jmu_python_是否是数 (10 分)

    本题要求从键盘输入一个字符串,判断该串是否属于整数.浮点数或者复数的表示 输入格式: 输入一个字符串 输出格式: 输出yes或no 输入样例: -299 输出样例: yes a = input() t ...

  10. JavaScript常见的六种继承方式

    前言 面向对象编程很重要的一个方面,就是对象的继承.A 对象通过继承 B 对象,就能直接拥有 B 对象的所有属性和方法.这对于代码的复用是非常有用的. 大部分面向对象的编程语言,都是通过"类 ...