ヒント: 記事を作成した後、目次を自動的に生成できます。生成方法は、右側のヘルプドキュメントを参照してください。
記事ディレクトリ
序文
最近、書籍「ディープラーニング入門 - 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()
操作結果: