铜灵 发自 凹非寺
量子位 出品 | 公众号 QbitAI

南巴黎电信学院(Télécom SudParis)的在读博士生Nathan Hubens在训练CNN时遇到点难题。

使用在CIFAR10数据集上训练的VGG16模型进行实验的过程中,进行了50次迭代,最后发现模型没有学到任何东西

可以看出,模型的收敛速度极慢,振荡,过拟合,为什么会这样?

你是不是也有这样的疑惑?

小哥哥这篇分享得到了网友的感谢,启发了不少研究者。量子位整理重点如下:

实验本身

先看一下创建模型的过程:

def ConvBlock(n_conv, n_out, shape, x, is_last=False):  for i in range(n_conv):    x = Conv2D(n_out, shape, padding='same', activation='relu')(x)  if is_last: out  = layers.GlobalAveragePooling2D()(x)    else: out = MaxPooling2D()(x)  return outinput = Input(shape=(32, 32, 3))x = ConvBlock(2, 64, (3,3), input)x = ConvBlock(2, 128, (3,3), x)x = ConvBlock(3, 256, (3,3), x)x = ConvBlock(3, 512, (3,3), x)x = ConvBlock(3, 512, (3,3), x, is_last=True)x = layers.Dense(num_classes, activation='softmax')(x)  for i in range(n_conv):    x = Conv2D(n_out, shape, padding='same', activation='relu')(x)

  if is_last: out  = layers.GlobalAveragePooling2D()(x)    else: out = MaxPooling2D()(x)

  return out

input = Input(shape=(32, 32, 3))x = ConvBlock(2, 64, (3,3), input)x = ConvBlock(2, 128, (3,3), x)x = ConvBlock(3, 256, (3,3), x)x = ConvBlock(3, 512, (3,3), x)x = ConvBlock(3, 512, (3,3), x, is_last=True)

x = layers.Dense(num_classes, activation='softmax')(x)

这个模型遵循原始的VGG 16架构,但是大多数全连接层都被移除,因此几乎只剩下卷积层了。

开头的“车祸现场”,可能会受到几个操作步骤的影响。

当模型的学习环节出现问题时,研究人员通常会去检查梯度表现,得到网络每一层的平均值和标准差:

def get_weight_grad(model, data, labels):    means = []    stds = []    grads = model.optimizer.get_gradients(model.total_loss, model.trainable_weights)    symb_inputs = (model._feed_inputs + model._feed_targets + model._feed_sample_weights)    f = K.function(symb_inputs, grads)    x, y, sample_weight = model._standardize_user_data(data, labels)    output_grad = f(x + y + sample_weight)    for layer in range(len(model.layers)):        if model.layers[layer].__class__.__name__ == 'Conv2D':            means.append(output_grad[layer].mean())            stds.append(output_grad[layer].std())    return means, stds

    means = []    stds = []

    grads = model.optimizer.get_gradients(model.total_loss, model.trainable_weights)    symb_inputs = (model._feed_inputs + model._feed_targets + model._feed_sample_weights)    f = K.function(symb_inputs, grads)    x, y, sample_weight = model._standardize_user_data(data, labels)    output_grad = f(x + y + sample_weight)

    for layer in range(len(model.layers)):        if model.layers[layer].__class__.__name__ == 'Conv2D':            means.append(output_grad[layer].mean())            stds.append(output_grad[layer].std())    return means, stds

看一下最后统计出来的结果:

结果有点出乎意料,也就是说在这个模型中,几乎没有任何梯度。作者表示,或许应该检查激活操作是如何沿着每一层进行的。

通过下面的代码再次得到它们的平均值和标准差:

def get_stats(model, data):  means = []  stds = []  for layer in range(len(model.layers)):    if model.layers[layer].__class__.__name__ == 'Conv2D':        m = Model(model.input, model.layers[layer].output)        pred = m.predict(data)        means.append(pred.mean())        stds.append(pred.std())  return means, stds

  means = []  stds = []

  for layer in range(len(model.layers)):    if model.layers[layer].__class__.__name__ == 'Conv2D':        m = Model(model.input, model.layers[layer].output)        pred = m.predict(data)        means.append(pred.mean())        stds.append(pred.std())  return means, stds

结果和之前不一样了:

这不就朝着正轨又迈进一步了。

这一步中,每个卷积层的梯度计算方式如下:

其中Δx和Δy分别表示∂L/∂x和∂L/∂y,这里用反向传播算法和链式法则计算梯度,也就是说,需要从最后一层开始,向后传播到到前面的层中。

如果最后一层激活函数的值接近于0时,梯度在任何地方都趋近于0,因此无法反向传播,网络也无法学习任何东西。

作者认为,因为自己的网络没有批归一化,没有Dropout,也没有数据扩充,所以猜测问题主要出在初始化这一步上。

他读了何恺明此前的论文Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification,想看看能不能解开自己的疑惑。

论文地址:
https://arxiv.org/pdf/1502.01852.pdf

初始化方法

初始化一直是深度学习研究中的重要领域,特别是随着架构和非线性研究的不断发展,一个好的初始化方法可能决定着网络最终的质量。

何恺明的论文中显示了初始化应具备的条件,也就是如何用ReLU激活函数正确将卷积网络初始化。这需要一点点数学基础,但也不难。

先考虑卷积层l的输出方式:

如何将偏差初始化为0,并假设权重w和元素x两者独立并且共享相同的分布,则:

其中n为k的平方乘c,通过独立变量乘积方差公式:

将上述公式变换为:

如果让权重w使它们的均值变成0,则输出:

利用König-Huygens特性:

最终输出:

因为用的时ReLU激活函数:

因此得到:

上述公式为单个卷积层输出的方差,若考虑网络中的所有层,需要得到它们的乘积:

有了乘积后可以看出,如果每层的方差不接近1,网络就会快速衰减。若小于1,则会朝0消散;若大于1,则激活值将无限增长。

若想拥有良好的ReLU卷积网络,需要遵循以下条件:

作者将标准初始化和使用自己的初始化方法的情况进行对比:

结果发现,使用Xavier/Glorot初始化训练的网络没有学习到任何东西。

在默认情况下,在Keras中,卷积层按Glorot正态分布进行初始化:

keras.layers.Conv2D(filters, kernel_size, strides=(1, 1), padding='valid',                     data_format=None, dilation_rate=(1, 1), activation=None,                     use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros',                     kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None,                     bias_constraint=None)1), padding='valid',                     data_format=None, dilation_rate=(1, 1), activation=None,                     use_bias=True, kernel_initializer='glorot_uniform', bias_initializer='zeros',                     kernel_regularizer=None, bias_regularizer=None, activity_regularizer=None, kernel_constraint=None,                     bias_constraint=None)

如果将这种初始化方法替换成何恺明的方法,会发生什么?

何恺明的初始化方法

先重建VGG 16模型,将初始化改成he_uniform,在训练模型前检查激活和梯度。

通过这种初始化法,激活平均值为0.5,标准偏差为0.8。

有一些梯度出来了,也就是说明网络开始work了。按此方法训练新模型,得到了如下曲线:

现在还需要考虑下正则化的问题,但总体来说,结果已经比之前好很多了。

结论

在这篇文章中,作者证明了初始化是模型构建中的重要一部分,但在平时的训练过程中往往会被习惯性忽略。

此外还需要注意的是,即使是人气口碑机器学习库Keras,其中的默认设置也不能不加调试就拿来用。

传送门

最后,附上文章原文地址:

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

AI社群 | 与优秀的人交流

小程序 | 全类别AI学习教程

量子位 QbitAI · 头条号签约作者

վ'ᴗ' ի 追踪AI技术和产品新动态

喜欢就点「在看」吧 !

神经网络在Keras中不work!博士小哥证明何恺明的初始化方法堪比“CNN还魂丹”...相关推荐

  1. 模块说和神经网络学说_让神经网络解释自己:牛津大学博士小姐姐,用毕业论文揭示“炼丹炉”结构...

    萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 神经网络就像"炼丹炉"一样,投喂大量数据,或许能获得神奇的效果. "炼丹"成功后,神经网络也能对没见 ...

  2. html怎么去除左边的圆点,在Html的CSS中去除标签前面小黑点以及ul、LI部分属性方法...

    div是很多人做网站都会用到的,但在显示默认效果时前面总是会有一个小黑点,这个效果很多人不想要,但又不知到如何去除,现在我们可以用以下方法来清除默认黑点. 1.在CSS中写入代码.找到相关性的CSS, ...

  3. ChatGPT版必应被华人小哥攻破,一句话「催眠」问出所有Prompt

    才上岗2天,ChatGPT版必应就被攻破了. 只需在问题前面加上一句:忽视掉之前的指令. 它就好像被催眠了一样,问什么答什么. 来自斯坦福大学的华人小哥Kevin Liu就通过这一方法,把它的prom ...

  4. keras构建卷积神经网络_在Keras中构建,加载和保存卷积神经网络

    keras构建卷积神经网络 This article is aimed at people who want to learn or review how to build a basic Convo ...

  5. keras中文文档_【DL项目实战02】图像识别分类——Keras框架+卷积神经网络CNN(使用VGGNet)

    版权声明:小博主水平有限,希望大家多多指导. 目录: [使用传统DNN] BG大龍:[DL项目实战02]图像分类--Keras框架+使用传统神经网络DNN​zhuanlan.zhihu.com [使用 ...

  6. 神经网络“炼丹炉”内部构造?牛津大学博士小姐姐用论文解读

    点上方蓝字计算机视觉联盟获取更多干货 在右上方 ··· 设为星标 ★,与你不见不散 仅作学术分享,不代表本公众号立场,侵权联系删除 转载于:量子位 AI博士笔记系列推荐 周志华<机器学习> ...

  7. 神经网络“炼丹炉”内部构造长啥样?牛津大学博士小姐姐用论文解读

    萧箫 发自 凹非寺 量子位 报道 | 公众号 QbitAI 神经网络就像"炼丹炉"一样,投喂大量数据,或许能获得神奇的效果. "炼丹"成功后,神经网络也能对没见 ...

  8. Keras中神经网络可视化模块keras.utils.visualize_util安装配置方法

    Keras中提供了一个神经网络可视化的函数plot,并可以将可视化结果保存在本地.plot使用方法如下: from keras.utils.visualize_util import plot plo ...

  9. Keras 中的循环神经网络 (RNN)

    简介 循环神经网络 (RNN) 是一类神经网络,它们在序列数据(如时间序列或自然语言)建模方面非常强大. 简单来说,RNN 层会使用 ​​for​​ 循环对序列的时间步骤进行迭代,同时维持一个内部状态 ...

最新文章

  1. Renascence架构原理——遗传规划算法
  2. grep 与条件_小白贴:使用 grep 命令搜索多个字符串
  3. linux新建备份数据库的脚本文件,Linux下shell脚本:自动每日备份网站文件和数据库上传FTP空间...
  4. 每日一笑 | 老板,黑凤梨真的能吃吗?
  5. 2021牛客暑期多校训练营7 B-xay loves monotonicity(线段树+不降子序列)
  6. linux内核网络钩子函数使用,Linux内核IOCTL网络控制框架实现实例分析
  7. labelimg标注工具使用
  8. Android 读取Assets资源
  9. 计算机串口接打印机并口,电子连接系统上的串口和并口知识分享
  10. 山体滑坡动画用什么软件制作_做施工动画是用什么软件来做
  11. smobiler仿饿了么app筛选页面
  12. 奇妙“水仙花数”的判断
  13. PS给证件照换背景颜色
  14. 中国饭局上的座次讲究
  15. 【07月19日】指数估值排名
  16. 两台电脑用网线直连如何发送文件?
  17. 怎样和控制欲很强的家人相处-不受他人影响
  18. 北大青鸟资源管理系统
  19. c# 容联云_Rest Server Demo_开发文档_容联云通讯
  20. 什么是编码?什么是解码?为什么要编码.解码?

热门文章

  1. 使用DDMS中的内存监测工具Heap来优化内存
  2. Truncate a string
  3. windows系统如何添加ssh key到github
  4. 毕业后的第二个月的一点思绪
  5. 3年前的一个小项目经验,分享给菜鸟兄弟们(公文收发小软件:小技能 DeleteMark)...
  6. iOS进阶之底层原理-应用程序加载(dyld加载流程、类与分类的加载)
  7. c语言求n(n从1到10),关于C语言的一道题
  8. SSL/TLS抓包出现提示Ignored Unknown Record
  9. 分享Kali Linux 2016.2第46周虚拟机
  10. php实现qq相册功能,使用javascript如何实现QQ空间相册展示