『ディープラーニング入門』第7章 実戦:手書き数字認識 - 畳み込みニューラルネットワーク

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


序文

最近、書籍「ディープラーニング入門 - Python ベースの理論と実装」の第 7 章を​​読みましたが、この章では主に畳み込みニューラル ネットワークについて説明されています。畳み込みニューラル ネットワーク (CNN) は、画像認識や音声認識のさまざまな場面で使用できます。


1. ちょっとした紹介

1. 全体構成

以前に学習したニューラル ネットワークと比較して、CNN には 1 つ多くの畳み込み層 (畳み込み層) と 1 つのプーリング層 (プーリング層) があります。

CNNの各層の接続順序は「Convolution - ReLU - (Pooling)」となっています(Pooling層は省略される場合もあります)。これは、従来の「Affine - ReLU」接続が「Convolution - ReLU - (Pooling)」接続に置き換えられたものと理解できます。
ここに画像の説明を挿入

2. 畳み込み層

全結合層では、データは 1 次元データに平坦化され、ネットワークに入力されます。したがって、全結合層では、データの形状は実際には無視されます。ただし、畳み込み層はデータの形状を変更しないで維持できます。

CNN では、畳み込み層の入力データと出力データを特徴マップと呼ぶこともあります。このうち、畳み込み層の入力データを入力特徴マップ(入力特徴マップ)と呼び、出力データを出力特徴マップ(出力特徴マップ)と呼びます。本書では、「産業連関データ」と「特徴量マップ」を同義語として使用しています。

畳み込み演算

畳み込み演算に関係するオブジェクトは、入力データとフィルターです。(今入力しているデータが2次元だとすると、対応するフィルターも2次元になります)
ここに画像の説明を挿入
畳み込み演算は入力データに対して、フィルターのウィンドウを一定間隔でスライドさせて適用します。ここで言うウィンドウとは、図 7-4 の灰色の 3 × 3 の部分を指します。図 7-4 に示すように、各位置のフィルターの要素は入力の対応する要素で乗算され、合計されます (この計算は積和演算と呼ばれることもあります)。次に、この結果を出力の対応する場所に保存します。このプロセスをすべての位置で実行して、畳み込み演算の出力を取得します。

完全に接続されたニューラル ネットワークでは、重みパラメータに加えてバイアスも存在します。CNN では、フィルターのパラメーターは前の重みに対応します。また、CNNには偏見があります。図 7-3 の畳み込み演算の例は、フィルターが適用される段階まで示されています。バイアスを含む畳み込み演算の処理フローを図 7-5 に示します。
ここに画像の説明を挿入
ここに画像の説明を挿入

充填

畳み込み層が処理される前に、入力データの周囲に固定データ (0 など) を埋める必要がある場合があります。これはパディング
と呼ばれます

ここに画像の説明を挿入

パディングにより、サイズ (4, 4) の入力データは (6, 6) の形状になります。次に、サイズ (3, 3) のフィルターが適用され、サイズ (4, 4) の出力データが生成されます。

この例では、パディングは 1 に設定されていますが、パディング値は 2、3 などの任意の整数に設定することもできます。図 7-5 の例では、パディングを 2 に設定すると入力データのサイズは (8, 8) になり、パディングを 3 に設定するとサイズは (10, 10) になります。

パディングは主に出力のサイズを変更するために使用されます。

ストライド

ここに画像の説明を挿入
複数のフィルターに基づく畳み込み演算:
ここに画像の説明を挿入

3. プーリング層

プーリングとは、高さ方向と長さ方向のスペースを削減する操作です。一般に、プーリングは最大プーリングと平均プーリングの 2 つのタイプに分類されます。

最大プーリング: 最大値を取得します。
平均プーリング: 平均値を取得します。

下図の例は、ストライド2に応じて2*2 Maxプーリングを行う場合の処理​​シーケンスです。
ここに画像の説明を挿入
プーリング層の特徴:
1. 学習するパラメータがありません。
プーリングは対象領域から最大値または平均値を取得するだけであり、学習するパラメータはありません。
2. チャンネル数は変わりません。
プーリング動作後も、入力データと出力データのチャネル数は変化しません。下図に示すように、チャネル数に応じて独立して計算が行われます。
ここに画像の説明を挿入

2. 畳み込み層とプーリング層の実装

このブログ投稿を参照してください: https://blog.csdn.net/LeungSr/article/details/127203161

3. すべてのコードと実行結果

from collections import OrderedDict

import sys, os

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

class SimpleConvNet:
    # input_dim 输入数据的尺寸,默认1通道,高28,长28
    # filter_num 滤波器数量
    # filter_size 滤波器大小
    # pad 填充
    # stride 步幅,默认为1
    # hidden_size 隐藏层(全连接)中的神经元数量
    # output_size 输出层(全连接)中的神经元数量
    # weight_init_std 初始化时权重的标准差
    def __init__(self, input_dim=(1,28,28),
                 conv_param={
    
    'filter_num':30, 'filter_size':5, 'pad':0, 'stride':1},
                 hidden_size=100, output_size=10, weight_init_std=0.01):
        filter_num = conv_param['filter_num']
        filter_size = conv_param['filter_size']
        filter_pad = conv_param['pad']
        filter_stride = conv_param['stride']
        input_size = input_dim[1]
        conv_output_size = (input_size - filter_size + 2*filter_pad) / filter_stride + 1
        pool_output_size = int(filter_num * (conv_output_size/2) * (conv_output_size/2))
        # 初始化权重参数
        self.params = {
    
    }
        self.params['W1'] = weight_init_std * np.random.randn(filter_num, input_dim[0], filter_size, filter_size)
        self.params['b1'] = np.zeros(filter_num)
        self.params['W2'] = weight_init_std * np.random.randn(pool_output_size, hidden_size)
        self.params['b2'] = np.zeros(hidden_size)
        self.params['W3'] = weight_init_std * np.random.randn(hidden_size, output_size)
        self.params['b3'] = np.zeros(output_size)
        # 生成必要的层
        self.layers = OrderedDict()
        self.layers['Conv1'] = Convolution(self.params['W1'], self.params['b1'], conv_param['stride'], conv_param['pad'])
        self.layers['Relu1'] = Relu()
        self.layers['Pool1'] = Pooling(pool_h=2, pool_w=2, stride=2)
        self.layers['Affine1'] = Affine(self.params['W2'], self.params['b2'])
        self.layers['Relu2'] = Relu()
        self.layers['Affine2'] = Affine(self.params['W3'], self.params['b3'])
        self.last_layer = SoftmaxWithLoss()

    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.last_layer.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 save_params(self, file_name="params.pkl"):
        params = {
    
    }
        for key, val in self.params.items():
            params[key] = val
        with open(file_name, 'wb') as f:
            pickle.dump(params, f)

    def load_params(self, file_name="params.pkl"):
        with open(file_name, 'rb') as f:
            params = pickle.load(f)
        for key, val in params.items():
            self.params[key] = val

        for i, key in enumerate(['Conv1', 'Affine1', 'Affine2']):
            self.layers[key].W = self.params['W' + str(i + 1)]
            self.layers[key].b = self.params['b' + str(i + 1)]


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

        return grads


# 读入数据
(x_train, t_train), (x_test, t_test) = load_mnist(flatten=False)

# 处理花费时间较长的情况下减少数据
# x_train, t_train = x_train[:5000], t_train[:5000]
# x_test, t_test = x_test[:1000], t_test[:1000]

max_epochs = 20

network = SimpleConvNet(input_dim=(1, 28, 28),
                        conv_param={
    
    'filter_num': 30, 'filter_size': 5, 'pad': 0, 'stride': 1},
                        hidden_size=100, output_size=10, weight_init_std=0.01)

trainer = Trainer(network, x_train, t_train, x_test, t_test,
                  epochs=max_epochs, mini_batch_size=100,
                  optimizer='Adam', optimizer_param={
    
    'lr': 0.001},
                  evaluate_sample_num_per_epoch=1000)
trainer.train()

# 保存参数
network.save_params("params.pkl")
print("Saved Network Parameters!")

# 绘制图形
markers = {
    
    'train': 'o', 'test': 's'}
x = np.arange(max_epochs)
plt.plot(x, trainer.train_acc_list, marker='o', label='train', markevery=2)
plt.plot(x, trainer.test_acc_list, marker='s', label='test', markevery=2)
plt.xlabel("epochs")
plt.ylabel("accuracy")
plt.ylim(0, 1.0)
plt.legend(loc='lower right')
plt.show()






操作結果:
ここに画像の説明を挿入

おすすめ

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