【转】神经网络初始化

南巴黎电信学院(Télécom SudParis)的在读博士生Nathan Hubens在训练CNN时遇到点难题。
使用在CIFAR10数据集上训练的VGG16模型进行实验的过程中,进行了50次迭代,最后发现模型没有学到任何东西

微信图片_20190716103902.jpg

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

实验本身

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

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)

这个模型遵循原始的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

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


image

结果有点出乎意料,也就是说在这个模型中,几乎没有任何梯度。作者表示,或许应该检查激活操作是如何沿着每一层进行的。
通过下面的代码再次得到它们的平均值和标准差:

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

结果和之前不一样了:

image

这不就朝着正轨又迈进一步了。
这一步中,每个卷积层的梯度计算方式如下:
image

其中Δx和Δy分别表示∂L/∂x和∂L/∂y,这里用反向传播算法和链式法则计算梯度,也就是说,需要从最后一层开始,向后传播到到前面的层中。
如果最后一层激活函数的值接近于0时,梯度在任何地方都趋近于0,因此无法反向传播,网络也无法学习任何东西。
作者认为,因为自己的网络没有批归一化,没有Dropout,也没有数据扩充,所以猜测问题主要出在初始化这一步上。
他读了何恺明此前的论文Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification,想看看能不能解开自己的疑惑。
image

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

初始化方法

初始化一直是深度学习研究中的重要领域,特别是随着架构和非线性研究的不断发展,一个好的初始化方法可能决定着网络最终的质量。
何恺明的论文中显示了初始化应具备的条件,也就是如何用ReLU激活函数正确将卷积网络初始化。这需要一点点数学基础,但也不难。
先考虑卷积层l的输出方式:


image

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


image

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

将上述公式变换为:
image

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


image

利用König-Huygens特性:
image

最终输出:
image

因为用的时ReLU激活函数:
[图片上传中...(image-1dc450-1563244715513-7)]
因此得到:
image

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

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

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


image

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

结果发现,使用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)

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

何恺明的初始化方法

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


image

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


image

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

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

结论

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

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

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

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

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

实验本身

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

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)

这个模型遵循原始的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

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

image

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

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

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

结果和之前不一样了:

image

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

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

image

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

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

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

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

image

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

初始化方法

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

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

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

image

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

image

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

image

将上述公式变换为:

image

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

image

利用König-Huygens特性:

image

最终输出:

image

因为用的时ReLU激活函数:

image

因此得到:

image

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

image

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

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

image

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

image

结果发现,使用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)

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

何恺明的初始化方法

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

image

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

image

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

image

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

结论

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

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343