1. 程式人生 > >動手學深度學習(三)——丟棄法(從零開始)

動手學深度學習(三)——丟棄法(從零開始)

文章作者:Tyan
部落格:noahsnail.com  |  CSDN  |  簡書

注:本文為李沐大神的《動手學深度學習》的課程筆記!

丟棄法的概念

在現代神經網路中,我們所指的丟棄法,通常是對輸入層或者隱含層做以下操作:

  • 隨機選擇一部分該層的輸出作為丟棄元素;
  • 把丟棄元素乘以0;
  • 把非丟棄元素拉伸。

丟棄法的實現

import mxnet as mx
from mxnet import nd
from mxnet import gluon
from mxnet import autograd
from utils import load_data_fashion_mnist, accuracy, evaluate_accuracy, SGD

# 設定隨機種子
mx.random.seed(2) # 實現dropout def dropout(X, drop_probability): # 計算保留資料的比例 keep_probability = 1 - drop_probability # 確保drop_probability的輸入合法 assert 0 <= keep_probability <= 1 # 丟棄所有元素 if keep_probability == 0: return X.zeros_like() # 隨機生成一個相同緯度的矩陣, 根據隨機值和keep_probability的對比確定是否丟棄該元素
mask = nd.random.uniform(0, 1.0, X.shape, ctx=X.context) < keep_probability # 保證 E[dropout(X)] == X, 對剩下的資料進行縮放 scale = 1 / keep_probability return mask * X * scale
# 測試dropout
A = nd.arange(20).reshape((5,4))
dropout(A, 0.0)
[[  0.   1.   2.   3.]
 [  4.   5.   6.   7.]
 [  8.   9.  10.  11.]
 [ 12.  13.  14.  15.]
 [ 16.  17.  18.  19.]]
<NDArray 5x4 @cpu(0)>
dropout(A, 1.0)
[[ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]
 [ 0.  0.  0.  0.]]
<NDArray 5x4 @cpu(0)>
dropout(A, 0.5)
[[  0.   2.   4.   0.]
 [  8.   0.  12.   0.]
 [ 16.  18.   0.   0.]
 [  0.   0.   0.  30.]
 [  0.  34.  36.   0.]]
<NDArray 5x4 @cpu(0)>

丟棄法的本質

一般來說,在整合學習裡,我們可以對訓練資料集有放回地取樣若干次並分別訓練若干個不同的分類器;測試時,把這些分類器的結果整合一下作為最終分類結果。事實上,丟棄法在模擬整合學習。丟棄法實質上是對每一個這樣的資料集分別訓練一個原神經網路子集的分類器。與一般的整合學習不同,這裡每個原神經網路子集的分類器用的是同一套引數。因此丟棄法只是在模擬整合學習。使用丟棄法的神經網路實質上是對輸入層和隱含層的引數做了正則化:學到的引數使得原神經網路不同子集在訓練資料上都儘可能表現良好。

資料獲取

# 批資料大小
batch_size = 256

# 載入資料
train_data, test_data = load_data_fashion_mnist(batch_size)

含兩個隱藏層的多層感知機

# 模型輸入大小
num_inputs = 28 * 28

# 模型輸出大小
num_outputs = 10

# 第一個隱藏層節點數量
num_hidden1 = 256

# 第二個隱藏層節點數量
num_hidden2 = 256

# 隨機資料時的標準差
weight_scale = 0.01

# 第一個隱藏層權重
W1 = nd.random_normal(shape=(num_inputs, num_hidden1), scale=weight_scale)
# 第一個隱藏層偏置
b1 = nd.zeros(num_hidden1)

# 第二個隱藏層權重
W2 = nd.random_normal(shape=(num_hidden1, num_hidden2), scale=weight_scale)
# 第二個隱藏層偏置
b2 = nd.zeros(num_hidden2)

# 輸出層權重
W3 = nd.random_normal(shape=(num_hidden2, num_outputs), scale=weight_scale)
# 輸出層偏置
b3 = nd.zeros(num_outputs)

# 引數陣列
params = [W1, b1, W2, b2, W3, b3]

# 需要計算梯度, 新增自動求導
for param in params:
    param.attach_grad()

定義包含丟棄層的模型

# 第一個隱藏層的丟棄概率
drop_prob1 = 0.2
# 第二個隱藏層的丟棄概率
drop_prob2 = 0.5

# 定義網路
def net(X):
    X = X.reshape((-1, num_inputs))
    # 第一層全連線
    h1 = nd.relu(nd.dot(X, W1) + b1)
    # 在第一層全連線後新增丟棄層
    h1 = dropout(h1, drop_prob1)
    # 第二層全連線
    h2 = nd.relu(nd.dot(h1, W2) + b2)
    # 在第二層全連線後新增丟棄層
    h2 = dropout(h2, drop_prob2)
    # 返回輸出
    return nd.dot(h2, W3) + b3

訓練

# 定義交叉熵損失
softmax_cross_entropy = gluon.loss.SoftmaxCrossEntropyLoss()

# 定義學習率
learning_rate = 0.5

# 訓練
for epoch in range(5):
    # 訓練損失
    train_loss = 0.0
    # 訓練準確率
    train_acc = 0.0
    # 迭代訓練
    for data, label in train_data:
        with autograd.record():
            # 計算輸出
            output = net(data)
            # 計算損失
            loss = softmax_cross_entropy(output, label)
        # 梯度反向傳播
        loss.backward()
        # SGD更新梯度
        SGD(params, learning_rate / batch_size)
        # 記錄訓練損失
        train_loss += nd.mean(loss).asscalar()
        # 記錄訓練準確率
        train_acc += accuracy(output, label)
    # 計算測試準確率
    test_acc = evaluate_accuracy(test_data, net)
    print("Epoch %d. Loss: %f, Train acc %f, Test acc %f" % (epoch, train_loss / len(train_data), train_acc / len(train_data), test_acc))
Epoch 0. Loss: 1.221062, Train acc 0.528746, Test acc 0.754006
Epoch 1. Loss: 0.598503, Train acc 0.774890, Test acc 0.813101
Epoch 2. Loss: 0.499490, Train acc 0.818493, Test acc 0.840244
Epoch 3. Loss: 0.457343, Train acc 0.832699, Test acc 0.835036
Epoch 4. Loss: 0.426575, Train acc 0.846070, Test acc 0.849159