首页 > 其他分享 >神经网络的k和b初始化方式问题笔记

神经网络的k和b初始化方式问题笔记

时间:2023-06-08 23:34:09浏览次数:58  
标签:初始化 torch 笔记 神经网络 var import 512 math mean

初始化是为了防止梯度消失和爆炸
编写代码,假设输入是512的行向量,经过10个512x512的矩阵,计算输出的平均值和标准差。
输入行向量,每个矩阵,都是标准正态分布

import torch

# 随机生成一个512的输入值,服从正态分布
x = torch.randn(512)

y = x
for i in range(10):
    a = torch.randn(512, 512)
    y = a @ y
print(y.mean())
print(y.std())

运行结果:

神经网络的k和b初始化方式问题笔记_正态分布


改成100直接算不出来了

神经网络的k和b初始化方式问题笔记_知乎_02


可以看到,平均值和标准差都非常大,因为假设是1x512的x和512x1的y做矩阵乘法,其中x和y都是标准正态分布,并且相互独立,则两个相乘是第二类修正贝塞尔函数

具体推导的知乎链接

XY其实也可以看作一个卡方分布减去两个卡方分布的结果

三个相乘也可以表示为梅耶尔G函数

因为数学太菜和编程功底不行,尝试了公式推导,python暴力算,以及蒙特卡洛枚举,都没成功。。。。太弱小了。

但是可以通过修改之前的代码,把每一步的矩阵的数绘制出直方图,便可以很直观地看到梯度爆炸。主要原因是方差变大了,虽然均值是0,但是分布会很广。
梯度爆炸的代码

import matplotlib.pyplot as plt
import torch

# 随机生成一个512的输入值,服从正态分布
x = torch.randn(512)

y = x
for i in range(1, 9):
    a = torch.randn(512, 512)
    y = a @ y
    plt.subplot(2, 4, i)
    plt.hist(y)
    print(i, y.mean())
    print(i, y.std())
plt.show()

神经网络的k和b初始化方式问题笔记_标准差_03


梯度消失,即在输出的地方乘0.01,即代码如下:

import matplotlib.pyplot as plt
import torch

# 随机生成一个512的输入值,服从正态分布
x = torch.randn(512)*0.01

y = x
for i in range(1, 9):
    a = torch.randn(512, 512)*0.01
    y = a @ y
    plt.subplot(2, 4, i)
    plt.hist(y)
    print(i, y.mean())
    print(i, y.std())
plt.show()

运行结果如下:

神经网络的k和b初始化方式问题笔记_正态分布_04


正态分布的表达式为

神经网络的k和b初始化方式问题笔记_标准差_05

神经网络的k和b初始化方式问题笔记_知乎_06


神经网络的k和b初始化方式问题笔记_标准差_07

神经网络的k和b初始化方式问题笔记_知乎_08

神经网络的k和b初始化方式问题笔记_标准差_09

而当

神经网络的k和b初始化方式问题笔记_标准差_10


神经网络的k和b初始化方式问题笔记_知乎_11

神经网络的k和b初始化方式问题笔记_知乎_12

比较上式,有

神经网络的k和b初始化方式问题笔记_正态分布_13

题目除了放缩系数不同以外,别的都一样。因此,防止梯度爆炸,就要减少方差,让k的取值主要在1附近。减小方差的手段有两种,一种是对方差直接变化,乘0.1。第二种是对x进行变化,然后乘一个幅值。代码如下:

import math
import numpy as np

u = 0   # 均值μ
sig = math.sqrt(1)  # 标准差δ
x = np.linspace(u - 3*sig, u + 3*sig, 50)   # 定义域
y1 = np.exp(-(x - u) ** 2 / (2 * sig ** 2)) / (math.sqrt(2*math.pi)*sig)  # 定义曲线函数
plt.plot(x, y1, "r", linewidth=2)    # 加载曲线

sig = math.sqrt(1)
x2 = np.linspace(u - 3*sig, u + 3*sig, 50) * sig * 0.1
y2 = np.exp(-(x - u) ** 2 / (2 * sig ** 2)) / (math.sqrt(2*math.pi)*sig)
plt.plot(x2, y2, "g", linewidth=2)    # 加载曲线

sig = 0.1 * sig
x = np.linspace(u - 3*sig, u + 3*sig, 50)
y3 = np.exp(-(x - u) ** 2 / (2 * sig ** 2)) / (math.sqrt(2*math.pi))
plt.plot(x, y3, "b", linewidth=2)

plt.grid(True)  # 网格线
plt.show()  # 显示

可以看到,绿线和蓝线完全重合,

神经网络的k和b初始化方式问题笔记_知乎_14


而且这个例子里,输出的每一个y都是512个x累加,而每次取的x都是独立分布的,因此输出的y实际上均值是0,方差是512,即标准差是神经网络的k和b初始化方式问题笔记_知乎_15

检验该结论的代码如下:

import matplotlib.pyplot as plt
import torch
import math
import numpy as np


mean = 0.0
var = 0.0
for i in range(10000):
    x = torch.randn(512)
    a = torch.randn(512, 512)
    y = a @ x
    # 用item()方法将tensor转换为float
    mean = mean + y.mean().item()
    var = var + y.pow(2).mean().item()
mean = mean/10000
var = math.sqrt(var/10000)
print(mean)
print(var)
print(math.sqrt(512))

神经网络的k和b初始化方式问题笔记_正态分布_16


要使得经过矩阵的输出值仍然保持1的标准差,则要给每个输出的值的方差进行调整,即乘个数量开根号的缩放系数。代码如下:

import matplotlib.pyplot as plt
import torch
import math
import numpy as np

mean = 0.0
var = 0.0
for i in range(10000):
    x = torch.randn(512)
    a = torch.randn(512, 512) * math.sqrt(1./512)
    y = a @ x
    # 用item()方法将tensor转换为float
    mean = mean + y.mean().item()
    var = var + y.pow(2).mean().item()
mean = mean/10000
var = math.sqrt(var/10000)
print(mean)
print(var)
print(math.sqrt(512))

运行结果如下:

神经网络的k和b初始化方式问题笔记_标准差_17


Xavier初始化,在这篇论文里提到 传统的做法是将网络里的k全部都初始化为-1到1的均匀分布

对于在[a,b]间均匀分布的x,其方差为

神经网络的k和b初始化方式问题笔记_正态分布_18

因此对于输入为标准差为神经网络的k和b初始化方式问题笔记_知乎_15的正态分布,再乘上均匀分布,其方差为

神经网络的k和b初始化方式问题笔记_标准差_20

和下面的python代码验证结果一致

import matplotlib.pyplot as plt
import torch
import math
import numpy as np

mean = 0.0
var = 0.0
for i in range(10000):
    x = torch.randn(512)
    a = torch.Tensor(512, 512).uniform_(-1, 1)
    y = a @ x
    # 用item()方法将tensor转换为float
    mean = mean + y.mean().item()
    var = var + y.pow(2).mean().item()
mean = mean/10000
var = math.sqrt(var/10000)
print(mean)
print(var)

神经网络的k和b初始化方式问题笔记_正态分布_21


但是用传统方法,初始化使用均匀分布,而不是高斯分布,即放缩神经网络的k和b初始化方式问题笔记_知乎_22然后再乘均匀分布,在输出使用激活函数时,效果并不是很好。会出现梯度消失

import matplotlib.pyplot as plt
import torch
import math
import numpy as np

x = torch.randn(512)

for i in range(100):

    ## Xavier
    # a = torch.Tensor(512, 512).uniform_(-1, 1) * math.sqrt(6./512*2)
    # traditional
    a = torch.Tensor(512, 512).uniform_(-1, 1) * math.sqrt(1. / 512)
    x = torch.tanh(x @ a)
    # x = x @ a

print(x.mean())
print(x.std())

神经网络的k和b初始化方式问题笔记_标准差_23


但是采用Xavier初始化,效果就要好得多。

它其实就是把缩放系数改成了下式

神经网络的k和b初始化方式问题笔记_知乎_24


神经网络的k和b初始化方式问题笔记_知乎_25是该层网络的扇入,神经网络的k和b初始化方式问题笔记_标准差_26是该层网络的扇出。效果不错

import matplotlib.pyplot as plt
import torch
import math
import numpy as np

x = torch.randn(512)


def xavier(fan_in, fan_out):
    return torch.Tensor(fan_in, fan_out).uniform_(-1, 1) * math.sqrt(6. / (fan_in + fan_out))


for i in range(100):
    ## Xavier
    # a = torch.Tensor(512, 512).uniform_(-1, 1) * math.sqrt(6./512*2)
    a = xavier(512, 512)
    x = torch.tanh(x @ a)
    # x = x @ a

print(x.mean())
print(x.std())

神经网络的k和b初始化方式问题笔记_知乎_27

何凯明的初始化
之前用sigmoid和tanh这种激活函数时,我们希望每一层的输出平均值为0,标准差为1(为啥要为1?以后好好推推xaiver的原论文)
如果不放缩,就会梯度爆炸。测试如下,经过十层网络直接爆炸

import matplotlib.pyplot as plt
import torch
import math
import numpy as np

def xavier(fan_in, fan_out):
    return torch.Tensor(fan_in, fan_out).uniform_(-1, 1) * math.sqrt(6. / (fan_in + fan_out))


def relu(x):
    # 钳位函数,小于0都设置成0
    return x.clamp_min(0.)

x = torch.randn(512)
mean = 0
var = 0

for i in range(10):
    a = torch.randn(512, 512)
    x = relu(x @ a)
    mean = mean + x.mean().item()
    var = var + x.pow(2).mean().item()

print(mean/10)
print(math.sqrt(var/10))

神经网络的k和b初始化方式问题笔记_知乎_28


而用何凯明的放缩系数,到了100层仍然活蹦乱跳

系数是

神经网络的k和b初始化方式问题笔记_知乎_29

import matplotlib.pyplot as plt
import torch
import math
import numpy as np

def xavier(fan_in, fan_out):
    return torch.Tensor(fan_in, fan_out).uniform_(-1, 1) * math.sqrt(6. / (fan_in + fan_out))


def relu(x):
    # 钳位函数,小于0都设置成0
    return x.clamp_min(0.)

x = torch.randn(512)
mean = 0
var = 0

for i in range(100):
    a = torch.randn(512, 512) * math.sqrt(2/512)
    x = relu(x @ a)
    mean = mean + x.mean().item()
    var = var + x.pow(2).mean().item()

print(mean/100)
print(math.sqrt(var/100))

神经网络的k和b初始化方式问题笔记_正态分布_30


所以还是有空回头去看看原论文推推公式。。。震撼到了


标签:初始化,torch,笔记,神经网络,var,import,512,math,mean
From: https://blog.51cto.com/u_16131692/6444249

相关文章

  • 《人件》读书笔记2
    改善工作环境:工作环境的质量直接关系开发者的效率。一般来说,除了新手,经验对产出效率影响不大。反倒是,和身边的人有关;如果他们表现好,你也会自然表现好。这也就是环境同化,好的工作环境真的很重要。好的工作环境:工作空间宽敞、光亮、安静、具有私密性、不容易受到打扰并且具有窗户(不......
  • 阅读笔记之《构建之法》四
    第八章需求分析8.1软件需求①获取和引导需求:软件团队需要找到软件的利益相关者,了解和挖掘他们对软件的需求,引导他们表达出对软件的需求;需求还可以来自各种管理机构;需求不仅来自外界,还可以来自软件企业本身;需求还可以来自技术团队本身;有些需求的目的是要更好地了解用户的行为和......
  • 阅读笔记之《构建之法》三
    第六章敏捷流程6.1敏捷的流程①敏捷开发原则:(1)尽早并持续地交付有价值的软件以满足顾客需求(2)敏捷流程欢迎需求的变化,并利用这些变化来提高用户的竞争优势(3)经常发布可用的软件,发布间隔可以从几周到几个月,能短则短(4)业务人员和开发人员在项目开发过程中应该每天共同工作(5)以有......
  • 读书笔记——人件1
     对于考虑改换工作的人而言,其理由与所涉及的个性一样纷繁复杂。对于病态的、人员流动率高(很高的大于 50% )的公司而言,有以下一些理由解释大多数人离开的原因: 1、一种混日子的思想:一起工作的人产生不想长期投入工作的想法。2、 一种任意处置的感觉:管理层认为它的员工只......
  • 读书笔记——人件2
    工作的度量对方法的改进、动机、提高工作的满意度方面是有用的工具,但是它几乎从没有用于这些目的。相反的是,度量方案有成为威胁和包袱的趋势。 为了让管理概念真正可行,管理本身必须有足够的洞察力和安全性以便让它自己脱离困境。这意味着有关个人的数据不能用于管理,公司......
  • 《人件》读书笔记
    感谢计算机,正因为有了它,我们不需要真的有水果,就能玩切水果的游戏。如果你是一个水果罐头制造商,那么你需要操心的是从哪里购买水果原料、怎样运输、怎样建造厂房、购买机器、怎样推广销售你的罐头以及最后的——招人,通过培训使他们成为流水线上合格的标准的零件。如果你是水果......
  • 路由学习笔记
    路由的基本概念和类型路由的几种类型:静态路由,动态路由动态路由又分为:距离矢量路由(RIP),链路状态路由(OSPF,IS-IS,BGP)路由的几个概念路由选路路由器收到数据包后,会根据数据包中的目的IP地址选择一条最优的路径,并将数据包转发到下一个路由器,路径上最后的路由器负责将数据包送交目......
  • 2023年4月阅读笔记1
    为什么巴比伦塔会失败巴比伦塔的制造是一个神话故事,但是其中的道理却对今天人们的协作有着重要的启示。软件系统的开发完全通过计算机执行,为什么还是很少有远程协作的企业,这是因为远程协作很容易导致交流的缺失。大型的软件项目开发需要团队中的每个人能及时了解到整个团队在做些......
  • 2023年4月阅读笔记2
    未雨绸缪我们在实现功能时往往有很多思路,但是哪种思路能行得通并且最适合情况就需要我们进行试验性开发。试验性开发确实会造成精力的消耗,或许大量的测试方案最终还会被舍弃,但是我们必须这样做。实际上如果不进行方案的实验,正式的开发反而可能遭遇返工和混乱的拆补,会严重分散重新......
  • 2023年4月阅读笔记3
    整体部分面向对象编程的“封装”思想和结构化编程的“精化”思想对于整个软件开发过程的各个粒度同样适用。整体的顺利运行离不开各个组成部分的优化。编码时各个信息隐藏的模块需要完成各自的任务,再通过接口互相配合。测试时需要从最小的单元测试开始,每一粒度都测试完全时,整个系......