3.13 丢弃法

丢弃法

深度学习模型常常使用丢弃法(dropout) 来应对过拟合问题。本节中提到的丢弃法特指倒置丢弃法(inverted dropout
输入个数为4,隐藏单元个数为5,且隐藏单元(h_i)(i=1, ldots, 5))的计算表达式为

[h_i = phileft(x_1 w_{1i} + x_2 w_{2i} + x_3 w_{3i} + x_4 w_{4i} + b_i ight), ]

(phi)是激活函数,(x_1, ldots, x_4)是输入,隐藏单元(i)的权重参数为(w_{1i}, ldots, w_{4i}),偏差参数为(b_i),当对该隐藏层使用丢弃法时,该层的隐藏单元将有一定概率被丢弃掉。设丢弃概率为(p)
那么有(p)的概率(h_i)会被清零,有(1-p)的概率(h_i)会除以(1-p)做拉伸。丢弃概率是丢弃法的超参数。具体来说,设随机变量(xi_i)为0和1的概率分别为(p)(1-p)。使用丢弃法时我们计算新的隐藏单元(h_i')

[h_i' = frac{xi_i}{1-p} h_i. ]

由于(E(xi_i) = 1-p),因此

[E(h_i') = frac{E(xi_i)}{1-p}h_i = h_i. ]

丢弃法不改变其输入的期望值。(h_1, ldots, h_5)都有可能被清零,输出层的计算无法过度依赖(h_1, ldots, h_5)中的任一个,从而在训练模型时起到正则化的作用,并可以用来应对过拟合。在测试模型时,我们为了得到更加确定性的结果,一般不使用丢弃法。

从零开始实现

下面的dropout函数将以drop_prob的概率丢弃NDArray输入(X)中的元素。

#导包
import d2lzh as d2l
from mxnet import autograd, gluon, init, nd
from mxnet.gluon import loss as gloss, nn
#导包
#丢弃概率
def dropout(X, drop_prob):
    #在drop_prob不满足>=0&&<=1的时候触发异常
    assert 0 <= drop_prob <= 1
    #保留概率 = 1 - 丢弃概率
    keep_prob = 1 - drop_prob
    # 这种情况下把全部元素都丢弃
    # 特殊情况:如果保留概率为0,全部丢弃
    if keep_prob == 0:
        return X.zeros_like()
    # mask确定是0,还是1。也就是上文的xi_i,随机一个概率返回一个NDArray(X.shape)和keep_prob比较,决定这次是否保留
    # Draw random samples from a uniform distribution.使用均匀分布得到一个概率
    mask = nd.random.uniform(0, 1, X.shape) < keep_prob
    #$$h_i' = frac{xi_i}{1-p} h_i.$$
    return mask * X / keep_prob

定义模型参数

使用Fashion-MNIST数据集. 将定义一个包含两个隐藏层的多层感知机,其中两个隐藏层的输出个数都是(256)

#输入层单元大小:784,输出层单元大小:10,隐藏层1单元大小256,隐藏层2单元大小256.
num_inputs, num_outputs, num_hiddens1, num_hiddens2 = 784, 10, 256, 256
#初始化隐藏层1权重
W1 = nd.random.normal(scale=0.01, shape=(num_inputs, num_hiddens1))
#初始化隐藏层1偏置
b1 = nd.zeros(num_hiddens1)
#初始化隐藏层2权重
W2 = nd.random.normal(scale=0.01, shape=(num_hiddens1, num_hiddens2))
#初始化隐藏层2偏置
b2 = nd.zeros(num_hiddens2)
#初始化输出层权重
W3 = nd.random.normal(scale=0.01, shape=(num_hiddens2, num_outputs))
#初始化输出层偏置
b3 = nd.zeros(num_outputs)
#申请内存
params = [W1, b1, W2, b2, W3, b3]
for param in params:
    param.attach_grad()

定义模型

下面定义的模型将全连接层和激活函数(ReLU)串起来,并对每个激活函数的输出使用丢弃法。可以分别设置各个层的丢弃概率。通常的建议是把靠近输入层的丢弃概率设得小一点.把第一个隐藏层的丢弃概率设为(0.2),把第二个隐藏层的丢弃概率设为(0.5)is_training函数来判断运行模式为训练还是测试,并只需在训练模式下使用丢弃法。

#设置第一个隐藏层丢弃概率0.2,第二个隐藏层丢弃概率0.5
drop_prob1, drop_prob2 = 0.2, 0.5
#声明net函数
def net(X):
    #改一下x的shape
    X = X.reshape((-1, num_inputs))
    #relu(X*W1 + b1)
    H1 = (nd.dot(X, W1) + b1).relu()
    if autograd.is_training():  # 只在训练模型时使用丢弃法
        H1 = dropout(H1, drop_prob1)  # 在第一层全连接后添加丢弃层
    #relu(X*W2 + b2)
    H2 = (nd.dot(H1, W2) + b2).relu()
    if autograd.is_training():
        H2 = dropout(H2, drop_prob2)  # 在第二层全连接后添加丢弃层
    #在输出层返回
    return nd.dot(H2, W3) + b3

训练和测试模型

#设置迭代次数:5,学习率:0.5,mini批次大小:256
num_epochs, lr, batch_size = 5, 0.5, 256
#得到Loss函数
loss = gloss.SoftmaxCrossEntropyLoss()
#得到训练,测试数据集
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
#使用之前的函数进行计算
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size,
              params, lr)
epoch 1, loss 1.1106, train acc 0.573, test acc 0.786
epoch 2, loss 0.5772, train acc 0.784, test acc 0.838
epoch 3, loss 0.4895, train acc 0.821, test acc 0.845
epoch 4, loss 0.4423, train acc 0.838, test acc 0.855
epoch 5, loss 0.4179, train acc 0.847, test acc 0.864

简洁实现

net = nn.Sequential()
net.add(nn.Dense(256, activation="relu"),
        nn.Dropout(drop_prob1),  # 在第一个全连接层后添加丢弃层
        nn.Dense(256, activation="relu"),
        nn.Dropout(drop_prob2),  # 在第二个全连接层后添加丢弃层
        nn.Dense(10))
net.initialize(init.Normal(sigma=0.01))
trainer = gluon.Trainer(net.collect_params(), 'sgd', {'learning_rate': lr})
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, batch_size, None,
              None, trainer)
epoch 1, loss 1.1832, train acc 0.547, test acc 0.790
epoch 2, loss 0.5849, train acc 0.783, test acc 0.839
epoch 3, loss 0.4941, train acc 0.819, test acc 0.843
epoch 4, loss 0.4558, train acc 0.833, test acc 0.852
epoch 5, loss 0.4208, train acc 0.846, test acc 0.862
原文地址:https://www.cnblogs.com/strategist-614/p/14399002.html