NN权重初始值及其对各层激活值分布的影响,权值衰减

PyTorch 学习笔记(四):权值初始化的十种方法

NN中每一个Affine层的权重参数的初始值是非常重要的,甚至会影响学习是否成功。但幸好我们已经有很多前辈总结出了比较好的初始值,以及判定一组初始值是否可以取得好的学习效果的方法。

权值衰减

有一种抑制过拟合,提高泛化能力的技术,叫做权值衰减,weight decay, 它的目的是减小NN的权重参数。

但是虽然较小权值参数可以获得好的学习效果避免过拟合,我们却不可以把权重的初始值设置为0!!! 这样将无法正确学习!!比如一个有一个隐层的NN,架构是2-2-2,即输入层隐层输出层分别有2,2,2个神经元,如果隐层的unit权重初始化为0,则输入 x 1 , x 2 x_1,x_2 x1x2传到隐层两个unit的值都是这两个unit的偏置初始值 b 1 , b 2 b_1,b_2 b1,b2,再传到输出层的两个神经元获得的值只是输出层unit自己的偏置,这一路从第一步就已经把输入信息完全丢失了····还学个毛线

明确两点:

  • 一、权重参数应该尽量小且分布广

最好是NN每一层的所有权重参数取值不一样,因为取值一样两个神经元的相似性就太大了,干的一样的计算,计算结果也一样,实际上是在做无用功,学不到更多的东西;

有很多权重取相同的值的情况叫做“权重均一化”,这是非常不好的,这使得NN拥有许多不同权重的意义丧失了。所以实验中都是需要随机生成权重初始值的。

神经元激活值的分布有所偏向会造成NN“表现力受限”或梯度消失
  • 二、每层的激活值的分布应该尽量广

NN把输入数据的信息逐层往后传,每经过一个隐层就得到一组激活函数值,作为下一个隐层的输入,所以在NN这种学习过程中,我们不希望一层学完了得到的激活值挤到一坨,即大多数激活值都是相同的值,那样的话,就算权重参数分布广学到的也都是一样的东西,还是学不好。

只有传递多样性的数据,才能使NN高效学习。


而权重的初始值是会影响到隐层激活值分布的, 下面展示不同权重初始值下NN每一层激活值的分布情况(直方图):

  1. w = np.random.randn(node_num, node_num) * 1(较大权重初始值) && sigmoid激活函数

标准差为1的高斯分布作为权重初始值

可以看出激活值多为0或1,因为sigmoid的输出大多接近0或1,则导数都是0,这样就会造成梯度消失,即在反向传播中计算的梯度的值越来越小,直到变为0(消失),并且层数加深会更严重

在这里插入图片描述
2. w = np.random.randn(node_num, node_num) * 1 && relu激活函数

梯度消失。
在这里插入图片描述
3. w = np.random.randn(node_num, node_num) * 1 && tanh激活函数

激活值集中在1附近,虽然不会出现梯度消失,但是倾向性严重,表现力受限,不好。
在这里插入图片描述
4. w = np.random.randn(node_num, node_num) * 0.01 && sigmoid

标准差变为0.01的高斯分布

激活值呈现靠近0.5的分布,虽然不用担心梯度消失了,但是激活值的分布不是平均的广泛地,而是有所倾向,这会使得NN的表现力差!!就像上面说的,如果几个unit输出的计算值是一样的,那它们的存在有什么意义呢?没有。1000个输出相同的神经元所做的事情实际上由1个神经元就可以做到了。

激活值非常有倾向性,限制NN的表现力,不好。
在这里插入图片描述
5. w = np.random.randn(node_num, node_num) * 0.01 && relu

后面的激活值集中在0附近,反向传播的梯度也会大多为0,学习根本无法进行,问题非常严重。
在这里插入图片描述
6. w = np.random.randn(node_num, node_num) * 0.01 && tanh

后面的激活值集中在0附近,反向传播的梯度也会大多为0,学习根本无法进行,问题非常严重。
在这里插入图片描述
7. w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num) && sigmoid

Xavier初始值

是Xavier Glorot论文中推荐的权重初始值,在很多深度学习框架中已经作为标准被使用。

它是以激活函数是线性函数为前提推导出来的。所以只适用于sigmoid和tanh, 他俩左右对称,在中间近似线性。但不适用于relu, relu做激活函数时会使用何凯明等人推荐的初始值。

他们为了使得各层激活值呈现相同的广度,进入了一个权重尺度。即:

如果前一层节点数为n,则初始值使用标准差为 1 n \frac{1}{\sqrt n} n 1的高斯分布

所以前一层节点越多,权重初始值的标准差/尺度越小

从图像可以看到,每层激活值确实呈现了更广的分布,NN的表现力不受限制,有望高效学习
在这里插入图片描述
8. w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num) && relu

可以看到relu使用xavier初始值也取得了不错的分布,但是随着层数加深还是会倾向于0侧。因为relu的输入只要是负的,输出就是0,所以想办法尽量使得激活值正负相当,把affine层的输出的负值减少一些,所以就适量拉大了权重初始值的标准差,使用He初始值。
在这里插入图片描述
9. w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num) && tanh

使用tanh后,激活值分布呈现漂亮的吊钟型分布,解决了sigmoid中的歪斜

这是因为tanh是关于原点对称的s型曲线
而sigmoid却是关于(0,0.5)对称的S型曲线

用做激活函数的函数最好是具有关于原点对称的性质

在这里插入图片描述
11. w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num) && sigmoid

He初始值

前一层节点数为n,则初始值使用标准差为 2 n \sqrt\frac2 n n2 的高斯分布

在这里插入图片描述
12. w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num) && relu

可以看出relu配合He初始值,每层是有相当的广度的
在这里插入图片描述
13. w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num) && tanh
在这里插入图片描述

最后用mnist数据集搭配5层NN试了一试,得到的效果:

he初始值学习的明显快的多,用的relu激活函数;
xavier差一些,用的sigmoid激活函数
用的是SGD
在这里插入图片描述

我做了个很有意思的实验, 既然He初始值是在xavier基础上改进的,拉大了标准差,那我再拉大一点呢???于是我把那个系数改为3,得到下图,注意绿色的He实际是系数为3的改进版;而黄色的Xavier才是He初始值的结果,果然学的更快!

当然这只是兴趣使然,没有理论依据,也许过分拉大标准差会带来某些弊端,只是在这个实验中没有体现出来,不然何凯明大佬怎么会不再拉大点呢。仅供娱乐,或者自己调参时可以改改看看效果。
在这里插入图片描述

总结

纵观实验结果,发现:

1. He初始值很好用!!搭配relu是深层网络的上上签,到后层也依旧保持很好的广度;搭配tanh可用于Deep NN; 甚至搭配sigmoid也能获得不多的广度

2. Xavier 搭配sigmoid,tanh, 但very Deep NN不能用tanh,更不要搭配relu

直方图代码:

# coding: utf-8
# weight_init_activation_histogram.py
import numpy as np
import matplotlib.pyplot as plt


def sigmoid(x):
    return 1 / (1 + np.exp(-x))


def ReLU(x):
    return np.maximum(0, x)


def tanh(x):
    return np.tanh(x)


input_data = np.random.randn(1000, 100)  # 1000个输入数据
node_num = 100  # 各隐藏层的节点(神经元)数
hidden_layer_size = 5  # 隐藏层有5层
activations = {}  # 激活值的结果保存在这里

x = input_data

for i in range(hidden_layer_size):
    if i != 0:
        x = activations[i-1]

    # 改变初始值进行实验!
    # w = np.random.randn(node_num, node_num) * 1
    w = np.random.randn(node_num, node_num) * 0.01
    # w = np.random.randn(node_num, node_num) * np.sqrt(1.0 / node_num)
    # w = np.random.randn(node_num, node_num) * np.sqrt(2.0 / node_num)


    a = np.dot(x, w)


    # 将激活函数的种类也改变,来进行实验!
    # z = sigmoid(a)
    # z = ReLU(a)
    z = tanh(a)

    activations[i] = z

# 绘制直方图
for i, a in activations.items():
    plt.subplot(1, len(activations), i+1)
    plt.title(str(i+1) + "-layer")
    if i != 0: plt.yticks([], [])
    # plt.xlim(0.1, 1)
    # plt.ylim(0, 7000)
    plt.hist(a.flatten(), 30, range=(0,1))
plt.show()

不同权重初始值应用于mnist的部分代码:

# coding: utf-8
# weight_init_compare.py

import numpy as np
import matplotlib.pyplot as plt
from dataset.mnist import load_mnist
from util import smooth_curve
from MultiLayerNet import MultiLayerNet
from optimizer import SGD

# 0:读入MNIST数据==========
(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True)

train_size = x_train.shape[0]
batch_size = 128
max_iterations = 2000

# 1:进行实验的设置==========
weight_init_types = {'std=0.01': 0.01, 'Xavier': 'sigmoid', 'He': 'relu'}
optimizer = SGD(lr=0.01)

networks = {}
train_loss = {}
for key, weight_type in weight_init_types.items():
    networks[key] = MultiLayerNet(input_size=784, hidden_size_list=[100, 100, 100, 100],
                                  output_size=10, weight_init_std=weight_type)
    train_loss[key] = []

# 2:开始训练==========
for i in range(max_iterations):
    batch_mask = np.random.choice(train_size, batch_size)
    x_batch = x_train[batch_mask]
    t_batch = t_train[batch_mask]

    for key in weight_init_types.keys():
        grads = networks[key].gradient(x_batch, t_batch)
        optimizer.update(networks[key].params, grads)

        loss = networks[key].loss(x_batch, t_batch)
        train_loss[key].append(loss)

    if i % 100 == 0:
        print("===========" + "iteration:" + str(i) + "===========")
        for key in weight_init_types.keys():
            loss = networks[key].loss(x_batch, t_batch)
            print(key + ":" + str(loss))

# 3.绘制图形==========
markers = {'std=0.01': 'o', 'Xavier': 's', 'He': 'D'}
x = np.arange(max_iterations)
for key in weight_init_types.keys():
    plt.plot(x, smooth_curve(train_loss[key]), marker=markers[key], markevery=100, label=key)
plt.xlabel("iterations")
plt.ylabel("loss")
plt.ylim(0, 2.5)
plt.legend()
plt.show()
  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值