『ディープラーニング入門』第5章 実戦:手書き数字認識~誤差逆伝播

ヒント: 記事を作成した後、目次を自動的に生成できます。生成方法は、右側のヘルプドキュメントを参照してください。


序文

最近、書籍「ディープラーニング入門 - Python ベースの理論と実装」の第 5 章を読んだのですが、この章では主に重みパラメータの勾配法を効率的に計算できる誤差逆伝播法について説明しています。


1. ちょっとした紹介

誤差逆伝播法は、ニューラル ネットワークの重みパラメータの勾配を計算するために使用されます。この方法は、ネットワーク内のすべてのモデル パラメーターに対する損失関数の勾配を計算できるように、ニューラル ネットワークを後ろから前に横断することを目的としています。

(1) 計算グラフ

本書では誤差逆伝播法を計算グラフを使って解説しています。計算グラフを使用する最大の理由は、逆伝播によって導関数を効率的に計算できることです。例えば、下の図で説明されている苹果价格的上涨会在多大程度上影响最终支付金额問題「 」、つまり「リンゴの価格に対する支払額の微分値」の問題です。

では、なぜバックプロパゲーションを介して導関数を計算するのでしょうか? 導関数が計算できればニューラルネットワークの重みパラメータの傾きが分かるので、その傾きに応じてパラメータを更新して最適なパラメータを見つけることができる。
ここに画像の説明を挿入
ここに画像の説明を挿入

(2) バックプロパゲーション

計算グラフについて簡単に説明した後、加算ノードのバックプロパゲーションと乗算ノードのバックプロパゲーションに焦点を当ててバックプロパゲーションの方法についても説明しました。

  • 加算ノードのバックプロパ
    ゲーション 加算ノードのバックプロパゲーションでは、例として z=x+y を取り上げます。この例の目的は、x の変化が z にどの程度影響するか、y の変化が z にどの程度影響するかを計算することです。つまり、x に関する z の導関数と y に関する z の導関数です。
    ここに画像の説明を挿入
    ここに画像の説明を挿入
    ここに画像の説明を挿入

  • 乗算ノードの逆伝播
    ここに画像の説明を挿入
    ここに画像の説明を挿入

(3) バックプロパゲーションのコード表現

上記では加算ノードと乗算ノードのバックプロパゲーション手法を紹介しましたが、以下のコードの形式でバックプロパゲーションを実装します。
以下、計算グラフを実現するための乗算ノードを「乗算層」、加算ノードを「加算層」と呼ぶ。

  • 乗算層のコード実装
class MulLayer:
    def __init__(self):
        self.x = None
        self.y = None
	
	# 这是正向传播
    def forward(self, x, y):
        self.x = x
        self.y = y                
        out = x * y

        return out
	
	# 这里就是反向传播
    def backward(self, dout):
        dx = dout * self.y # 根据(二)中所讲的,乘法节点的反向传播需要把x和y进行翻转
        dy = dout * self.x

        return dx, dy
  • 追加層のコード実装
class AddLayer:
    def __init__(self):
        pass	# 加法层无需进行初始化,所以直接pass即可

    def forward(self, x, y):
        out = x + y

        return out

    def backward(self, dout):
        dx = dout * 1
        dy = dout * 1

        return dx, dy

概念を読み、コードと組み合わせて再度理解した後、突然少し気づきました。ここでは紹介しませんが、活性化関数reluやシグモイド関数などのバックプロパゲーション導出についても解説しています。

2. 誤差バックプロパゲーションを使用した手書き数字の認識

この例では、ニューラル ネットワークは TwoLayerNet クラスを使用して実装された 2 層ニューラル ネットワークです。TwoLayerNet クラスの変数は次のように説明されます。

params : ニューラルネットワークパラメータを保持する辞書型変数。params['W1'] は最初の層の重み、params['b1'] は最初の層のバイアス、params['W2'] は 2 番目の層の重み、params['b2'] は2番目の層はバイアスです。

layers : ニューラルネットワークの各層の順序付き辞書変数を保存し、Affine1層、ReLu1層、Affine2層を順に保存します。

lastLayer : ニューラル ネットワークの最後の層。この場合、SoftmaxWithLoss レイヤーです。

いくつかの主要な関数について説明します。
次の関数は、TwoLayerNet クラスの初期化関数です。コードからわかるように、ニューラル ネットワークは 2 層のニューラル ネットワークを使用しています。手前からAffine層、ReLu層、Affine層、SoftmaxWithLoss層です。

def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
    # 初始化权重
    self.params = {
    
    }
    self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
    self.params['b1'] = np.zeros(hidden_size)
    self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
    self.params['b2'] = np.zeros(output_size)
    # 生成层
    self.layers = OrderedDict()
    self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
    self.layers['Relu1'] = Relu()
    self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
    self.lastLayer = SoftmaxWithLoss()

次の関数はdef gradient(self, x, t)主に重みパラメータの勾配を計算するために使用されます。x は画像データ、t は画像のラベルです。self.loss(x, t)予測損失を計算するために使用される順伝播プロセスです。次に、逆方向バックプロパゲーションのプロセスがあります。逆伝播のプロセスでは、layers.reverse()まずこれらの層の順序を逆にして、次に層ごとに逆伝播し、最後に doout を取得し、更新されたパラメータ値を grad{} に入れます。

    def gradient(self, x, t):
        # forward
        self.loss(x, t)
        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)
        # 设定
        grads = {
    
    }
        grads['W1'] = self.layers['Affine1'].dW
        grads['b1'] = self.layers['Affine1'].db
        grads['W2'] = self.layers['Affine2'].dW
        grads['b2'] = self.layers['Affine2'].db

def predict(self, x)予測には次の関数が使用されます。ニューラルネットワークの順伝播は、要素を追加した順に各層の forward メソッドを呼び出すだけで処理が完了します。

def predict(self, x):
    for layer in self.layers.values():
        x = layer.forward(x)
    return x

Kang Yikang の「メイン関数」のロジックを見てみましょう。手順はコメントに書かれています。

	# 加载数据
	(x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
    network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

    # 超参数
    iters_num = 10000	# 随机抽数据,抽10000次
    train_size = x_train.shape[0]	# 训练集的大小
    batch_size = 100	# 随机抽数据,每次抽100个数据
    learning_rate = 0.1	# 学习率
    train_loss_list = []# 记录训练集的损失值
    train_acc_list = []	# 记录训练集的准确度
    test_acc_list = []	# 记录测试集的准确度

    # 平均每个epoch的重复次数
    iter_per_epoch = max(train_size / batch_size, 1)
    for i in range(iters_num):
        # 获取mini-batch
        batch_mask = np.random.choice(train_size, batch_size)
        x_batch = x_train[batch_mask]
        t_batch = t_train[batch_mask]

        # 计算梯度
        grad = network.gradient(x_batch, t_batch)
        # 更新参数
        for key in ('W1', 'b1', 'W2', 'b2'):
            network.params[key] -= learning_rate * grad[key]

        # 记录学习过程
        loss = network.loss(x_batch, t_batch)
        train_loss_list.append(loss)

        # 计算每个epoch的识别精度
        if i % iter_per_epoch == 0:
            train_acc = network.accuracy(x_train, t_train)
            test_acc = network.accuracy(x_test, t_test)
            train_acc_list.append(train_acc)
            test_acc_list.append(test_acc)

すべてのコード

PS: 本のコードを基に次のコードを入力しました。コードを実行する前に、対応するパッケージ common と dataset を公式 Web サイトからダウンロードし、プロジェクト パス venv\Lib\site- に配置することを忘れないでください。パッケージ。
公式ウェブサイトのリンク: http://www.ituring.com.cn/book/1921
次に、最初に右側にある「Download with book」をクリックし、次に 2 番目の「Download」をクリックします。
ここに画像の説明を挿入
次のように記述します。
ここに画像の説明を挿入
コードを見てください。

import sys, os

sys.path.append(os.pardir)
import numpy as np
import matplotlib.pyplot as plt
from common.functions import *
from common.gradient import numerical_gradient
from common.layers import *
from collections import OrderedDict
from dataset.mnist import load_mnist
from dataset.two_layer_net import TwoLayerNet


def cross_entropy_error(y, t):
    if y.ndim == 1:
        t = t.reshape(1, t.size)
        y = y.reshape(1, y.size)
        batch_size = y.shape[0]
        return -np.sum(t * np.log(y + 1e-7)) / batch_size


class TwoLayerNet:
    def __init__(self, input_size, hidden_size, output_size, weight_init_std=0.01):
        # 初始化权重
        self.params = {
    
    }
        self.params['W1'] = weight_init_std * np.random.randn(input_size, hidden_size)
        self.params['b1'] = np.zeros(hidden_size)
        self.params['W2'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b2'] = np.zeros(output_size)
        # 生成层
        self.layers = OrderedDict()
        self.layers['Affine1'] = Affine(self.params['W1'], self.params['b1'])
        self.layers['Relu1'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W2'], self.params['b2'])
        self.lastLayer = SoftmaxWithLoss()

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

    def softmax(a):
        exp_a = np.exp(a)
        sum = np.sum(exp_a)
        y = exp_a / sum
        return y

    def predict(self, x):
        for layer in self.layers.values():
            x = layer.forward(x)
        return x

    # x是输入数据,t是标签
    def loss(self, x, t):
        y = self.predict(x)
        return self.lastLayer.forward(y, t)

    def accuracy(self, x, t):
        y = self.predict(x)
        y = np.argmax(y, axis=1)
        if t.ndim != 1:
            t = np.argmax(t, axis=1)
        accuracy = np.sum(y == t) / float(x.shape[0])
        return accuracy

    def numerical_gradient(self, x, t):
        loss_W = lambda W: self.loss(x, t)
        grads = {
    
    }
        grads['W1'] = numerical_gradient(loss_W, self.params['W1'])
        grads['W2'] = numerical_gradient(loss_W, self.params['W2'])
        grads['b1'] = numerical_gradient(loss_W, self.params['b1'])
        grads['b2'] = numerical_gradient(loss_W, self.params['b2'])
        return grads

    def gradient(self, x, t):
        # forward
        self.loss(x, t)
        # backward
        dout = 1
        dout = self.lastLayer.backward(dout)
        layers = list(self.layers.values())
        layers.reverse()
        for layer in layers:
            dout = layer.backward(dout)
        # 设定
        grads = {
    
    }
        grads['W1'] = self.layers['Affine1'].dW
        grads['b1'] = self.layers['Affine1'].db
        grads['W2'] = self.layers['Affine2'].dW
        grads['b2'] = self.layers['Affine2'].db

        return grads

    (x_train, t_train), (x_test, t_test) = load_mnist(normalize=True, one_hot_label=True)
    network = TwoLayerNet(input_size=784, hidden_size=50, output_size=10)

    # 超参数
    iters_num = 10000
    train_size = x_train.shape[0]
    batch_size = 100
    learning_rate = 0.1
    train_loss_list = []
    train_acc_list = []
    test_acc_list = []

    # 平均每个epoch的重复次数
    iter_per_epoch = max(train_size / batch_size, 1)
    for i in range(iters_num):
        # 获取mini-batch
        batch_mask = np.random.choice(train_size, batch_size)
        x_batch = x_train[batch_mask]
        t_batch = t_train[batch_mask]

        # 计算梯度
        grad = network.gradient(x_batch, t_batch)
        # 更新参数
        for key in ('W1', 'b1', 'W2', 'b2'):
            network.params[key] -= learning_rate * grad[key]

        # 记录学习过程
        loss = network.loss(x_batch, t_batch)
        train_loss_list.append(loss)

        # 计算每个epoch的识别精度
        if i % iter_per_epoch == 0:
            train_acc = network.accuracy(x_train, t_train)
            test_acc = network.accuracy(x_test, t_test)
            train_acc_list.append(train_acc)
            test_acc_list.append(test_acc)

    # 导入数据
    m = list(np.arange(1, iters_num + 1))
    n = list(np.arange(1, len(train_acc_list) + 1))
    t = list(np.arange(1, len(test_acc_list) + 1))
    # 绘图命令
    print(train_loss_list)
    print(train_acc_list)
    print(test_acc_list)
    # 画第一个图
    plt.subplot(221)
    plt.title("Train Loss List")
    plt.plot(m, train_loss_list)
    # 画第二个图
    plt.subplot(222)
    plt.title("Train Acc List")
    plt.plot(n, train_acc_list)
    # 画第三个图
    plt.subplot(223)
    plt.title("Test Acc List")
    plt.plot(t, test_acc_list)
    # show出图形
    plt.show()

演算結果

ここに画像の説明を挿入
誤差バックプロパゲーションのトレーニング速度は数値微分よりもはるかに速いと言わざるを得ません。

おすすめ

転載: blog.csdn.net/rellvera/article/details/128038383