Pytorch教程目录
Torch and Numpy
变量 (Variable)
激励函数
关系拟合(回归)
区分类型 (分类)
快速搭建法
批训练
加速神经网络训练
Optimizer优化器
卷积神经网络 CNN
卷积神经网络(RNN、LSTM)
RNN 循环神经网络 (分类)
RNN 循环神经网络 (回归)
自编码 (Autoencoder)
DQN 强化学习
生成对抗网络 (GAN)
为什么 Torch 是动态的
GPU 加速运算
过拟合 (Overfitting)
批标准化 (Batch Normalization)
目录
什么是批标准化 (Batch Normalization)
普通数据标准化
Batch Normalization
, 批标准化, 和普通的数据标准化类似, 是将分散的数据统一的一种做法, 也是优化神经网络的一种方法. 具有统一规格的数据, 能让机器学习更容易学习到数据之中的规律.
每层都做标准化
在神经网络中, 数据分布对训练会产生影响. 比如某个神经元 x
的值为1, 某个 Weights
的初始值为 0.1, 这样后一层神经元计算结果就是 Wx = 0.1
; 又或者 x = 20
, 这样 Wx
的结果就为 2.
现在还不能看出什么问题, 但是, 当我们加上一层激励函数, 激活这个 Wx
值的时候, 问题就来了. 如果使用 像 tanh
的激励函数, Wx
的激活值就变成了 ~0.1
和 ~1
, 接近于 1 的部已经处在了 激励函数的饱和阶段, 也就是如果 x 无论再怎么扩大, tanh 激励函数输出值也还是 接近1. 换句话说, 神经网络在初始阶段已经不对那些比较大的 x 特征范围 敏感了.
这样很糟糕, 想象我轻轻拍自己的感觉和重重打自己的感觉居然没什么差别, 这就证明我的感官系统失效了. 当然我们是可以用之前提到的对数据做 normalization 预处理
, 使得输入的 x 变化范围不会太大, 让输入值经过激励函数的敏感部分.
但刚刚这个不敏感问题不仅仅发生在神经网络的输入层, 而且在隐藏层中也经常会发生. 只是 x 换到了隐藏层当中, 我们能不能对隐藏层的输入结果进行像之前那样的normalization 处理
呢? 答案是可以的, 因为大牛们发明了一种技术, 叫做 batch normalization
, 正是处理这种情况.
BN 添加位置
Batch normalization
的 batch
是批数据, 把数据分成小批小批进行 stochastic gradient descent
. 而且在每批数据进行前向传递 forward propagation
的时候, 对每一层都进行 normalization
的处理,
BN 效果
Batch normalization
也可以被看做一个层面. 在一层层的添加神经网络的时候, 我们先有数据 X
, 再添加全连接层
, 全连接层
的计算结果会经过 激励函数
成为下一层的输入
, 接着重复之前的操作. Batch Normalization (BN)
就被添加在每一个全连接和激励函数之间.
之前说过, 计算结果在进入激励函数前的值很重要, 如果我们不单单看一个值, 我们可以说, 计算结果值的分布对于激励函数很重要. 对于数据值大多分布在这个区间的数据, 才能进行更有效的传递. 对比这两个在激活之前的值的分布. 上者没有进行 normalization
, 下者进行了 normalization
, 这样当然是下者能够更有效地利用 tanh
进行非线性化的过程.
没有 normalize
的数据 使用 tanh
激活以后, 激活值大部分都分布到了饱和阶段, 也就是大部分的激活值不是-1, 就是1, 而 normalize
以后, 大部分的激活值在每个分布区间都还有存在. 再将这个激活后的分布传递到下一层神经网络进行后续计算, 每个区间都有分布的这一种对于神经网络就会更加有价值. Batch normalization
不仅仅 normalize
了一下数据, 他还进行了反 normalize
的手续. 为什么要这样呢?
BN 算法
我们引入一些 batch normalization
的公式. 这三步就是我们在刚刚一直说的 normalization 工序
, 但是公式的后面还有一个反向操作
, 将 normalize 后的数据再扩展和平移. 原来这是为了让神经网络自己去学着使用和修改这个扩展参数 gamma 和 平移参数 β, 这样神经网络就能自己慢慢琢磨出前面的 normalization 操作
到底有没有起到优化的作用, 如果没有起到作用, 我就使用 gamma 和 β 来抵消一些 normalization 的操作.
最后我们来看看一张神经网络训练到最后, 代表了每层输出值的结果的分布图. 这样我们就能一眼看出 Batch normalization 的功效啦. 让每一层的值在有效的范围内传递下去.
Batch Normalization 批标准化
批标准化通俗来说就是对每一层神经网络进行标准化 (normalize) 处理, 我们知道对输入数据进行标准化能让机器学习有效率地学习. 如果把每一层后看成这种接受输入数据的模式, 那我们何不 批标准化 所有的层呢?
做点数据
自己做一些伪数据, 用来模拟真实情况. 而且 Batch Normalization (之后都简称BN) 还能有效的控制坏的参数初始化 (initialization), 比如说 ReLU
这种激励函数最怕所有的值都落在附属区间, 那我们就将所有的参数都水平移动一个 -0.2 (bias_initialization = -0.2
), 来看看 BN 的实力.
import torch
from torch import nn
from torch.nn import init
import torch.utils.data as Data
import torch.nn.functional as F
import matplotlib.pyplot as plt
import numpy as np
# 超参数
N_SAMPLES = 2000
BATCH_SIZE = 64
EPOCH = 12
LR = 0.03
N_HIDDEN = 8
ACTIVATION = F.tanh # 你可以换 relu 试试
B_INIT = -0.2 # 模拟不好的 参数初始化
# training data
x = np.linspace(-7, 10, N_SAMPLES)[:, np.newaxis]
noise = np.random.normal(0, 2, x.shape)
y = np.square(x) - 5 + noise
# test data
test_x = np.linspace(-7, 10, 200)[:, np.newaxis]
noise = np.random.normal(0, 2, test_x.shape)
test_y = np.square(test_x) - 5 + noise
train_x, train_y = torch.from_numpy(x).float(), torch.from_numpy(y).float()
test_x = torch.from_numpy(test_x).float()
test_y = torch.from_numpy(test_y).float()
train_dataset = Data.TensorDataset(train_x, train_y)
train_loader = Data.DataLoader(dataset=train_dataset, batch_size=BATCH_SIZE, shuffle=True, num_workers=2,)
# show data
plt.scatter(train_x.numpy(), train_y.numpy(), c='#FF9359', s=50, alpha=0.2, label='train')
plt.legend(loc='upper left')
plt.show()
搭建神经网络
这里就教你如何构建带有 BN
的神经网络. BN
其实可以看做是一个 layer (BN layer)
. 我们就像平时加层一样加 BN layer
就好了. 注意, 我还对输入数据进行了一个 BN
处理, 因为如果你把输入数据看作是 从前面一层来的输出数据, 我们同样也能对她进行 BN
. momentum
不是用来更新缩减和平移参数的, 而是用来平滑化 batch mean and stddev
的
class Net(nn.Module):
def __init__(self, batch_normalization=False):
super(Net, self).__init__()
self.do_bn = batch_normalization
self.fcs = [] # 太多层了, 我们用 for loop 建立
self.bns = []
self.bn_input = nn.BatchNorm1d(1, momentum=0.5) # 给 input 的 BN
for i in range(N_HIDDEN): # 建层
input_size = 1 if i == 0 else 10
fc = nn.Linear(input_size, 10)
setattr(self, 'fc%i' % i, fc) # 注意! pytorch 一定要你将层信息变成 class 的属性! 我在这里花了2天时间发现了这个 bug
self._set_init(fc) # 参数初始化
self.fcs.append(fc)
if self.do_bn:
bn = nn.BatchNorm1d(10, momentum=0.5)
setattr(self, 'bn%i' % i, bn) # 注意! pytorch 一定要你将层信息变成 class 的属性! 我在这里花了2天时间发现了这个 bug
self.bns.append(bn)
self