こんにちは!
現役パラレルワーカー フクコです。
前回の記事↓に続き
www.fukuko-parallel-work.com
来年の2月の試験に向けてE資格試験勉強中のため
E資格とは?の記事はコチラ↓
ゼロつくシリーズでおなじみ
オーライリーから出版されている
ディープラーニングの本格的な入門書でよくおススメされる
「ゼロからつくる Deep Learning」本
この本↑を毎日5ページずつコツコツこなすと
約2か月間で今年中に終了するので
来年のE資格試験までにこれで基礎力をつけることにしました。(^^)
ついつい私は何もないとだらけてしまうので(笑)
毎日5ページ終わった後の記録とまとめを書いていこうと思います。
と、まとめに入る前に…
やる気を出すためのコトバをシェアします!!(主に私のやる気を出すために 笑)
「刀の打ち方を知ってるか。
刀はな、叩いて叩いて叩き上げて不純物や余分なものを飛ばし
鋼の純度を高め強靭な刀を造るんだ。」
by じいちゃん(桑島慈悟郎)
私の大好きな「鬼滅の刃」から
じいちゃんこと、桑島慈悟郎(くわじま じごろう)の名言です。
そう!
たとえわからなくても、間違っても
何度も、何度も、ひたすら続けているうちに
どんどん精度が上がってきて
気がついたら周囲の誰よりもすごく出来るようになっているんですよね。
そうなるためには…
ひたすらコツコツ努力、毎日精進あるのみ!!!
じいちゃん、どうもありがとう! (^0^)
今日もやる気が出てきました!
よし!! 今日も頑張るぞ~! お~!!
というコトで、
私も叩いて叩いて叩きあげて、ゼッタイE資格、受かってみせますよ!
なので、今日もノルマはゼッタイこなします!笑
ではでは、いい加減まとめに入ります。笑
その前に本の目次の紹介です。
ゼロつくディープラーニングは、下記↓の合計8章で構成されています。
本の目次
- 1章 Python入門
- 2章 パーセプトロン
- 3章 ニューラルネットワーク
- 4章 ニューラルネットワークの学習
- 5章 誤差伝播法
- 6章 学習に関するテクニック
- 7章 畳み込みニューラルネットワーク
- 8章 ディープラーニング
ちなみに…
ゼロつくディープラーニングの第1章はPython入門のセクション(20ページ分)なので、
とりあえず今回私は飛ばし、第2章からまとめています。
現在は、第7章 畳み込みニューラルネットワークで~す。
第7章 畳み込みニューラルネットワーク つづき
第7章のテーマは、
畳み込みニューラルネットワーク(Convolutional Neural Network: CNN)。
7.5 CNNの実装のつづき
昨日の続きで、
単純なCNNのネットワークを構成してみる。
↓の図が例。
このCNN↑のネットワークを、SimpleConvNetというクラスで実装してみる。
Pythonで実装すると↓のとおり。
# 7.5 CNNの実装 # 単純なCNNのネットワークを構成 # coding: utf-8 # まずはライブラリインポート import sys, os sys.path.append(os.pardir) # 親ディレクトリのファイルをインポートするための設定 import pickle import numpy as np from collections import OrderedDict from common.layers import * from common.gradient import numerical_gradient class SimpleConvNet: # ""単純なConvNet # conv - relu - pool - affine - relu - affine - softmax # Parameters # ---------- # input_size : 入力サイズ(MNISTの場合は784) # hidden_size_list : 隠れ層のニューロンの数のリスト(e.g. [100, 100, 100]) # output_size : 出力サイズ(MNISTの場合は10) # activation : 'relu' or 'sigmoid' # weight_init_std : 重みの標準偏差を指定(e.g. 0.01) # 'relu'または'he'を指定した場合は「Heの初期値」を設定 # 'sigmoid'または'xavier'を指定した場合は「Xavierの初期値」を設定 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'] = 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_year = SoftmaxWithLoss() def predict(self, x): for layer in self.layers.values(): x = layer.forward(x) return x def loss(self, x, t): """損失関数を求める 引数のxは入力データ、tは教師ラベル """ y = self.predict(x) return self.last_layer.forward(y, t) def accuracy(self, x, t, batch_size=100): if t.ndim != 1 : t = np.argmax(t, axis=1) acc = 0.0 for i in range(int(x.shape[0] / batch_size)): tx = x[i*batch_size:(i+1)*batch_size] tt = t[i*batch_size:(i+1)*batch_size] y = self.predict(tx) y = np.argmax(y, axis=1) acc += np.sum(y == tt) return acc / x.shape[0] def numerical_gradient(self, x, t): """勾配を求める(数値微分) Parameters ---------- x : 入力データ t : 教師ラベル Returns ------- 各層の勾配を持ったディクショナリ変数 grads['W1']、grads['W2']、...は各層の重み grads['b1']、grads['b2']、...は各層のバイアス """ loss_w = lambda w: self.loss(x, t) grads = {} for idx in (1, 2, 3): grads['W' + str(idx)] = numerical_gradient(loss_w, self.params['W' + str(idx)]) grads['b' + str(idx)] = numerical_gradient(loss_w, self.params['b' + str(idx)]) return grads def gradient(self, x, t): """勾配を求める(誤差逆伝搬法) Parameters ---------- x : 入力データ t : 教師ラベル Returns ------- 各層の勾配を持ったディクショナリ変数 grads['W1']、grads['W2']、...は各層の重み grads['b1']、grads['b2']、...は各層のバイアス """ # 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'], grads['b1'] = self.layers['Conv1'].dW, self.layers['Conv1'].db grads['W2'], grads['b2'] = self.layers['Affine1'].dW, self.layers['Affine1'].db grads['W3'], grads['b3'] = self.layers['Affine2'].dW, self.layers['Affine2'].db return grads 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)]
↑のようにSimpleConvNetで初期化をしてしまえば、
推論を行うpredictメソッド
と
損失関数の値を求めるlossメソッドを実装。
その後
最後の層のSoftmaxWithLossレイヤまでforward処理を行う。
パラメータの勾配は、誤差逆伝播法によって求める。
最後に
gradsというディクショナリに各重みパラメータの勾配を格納。
7.6 CNNの可視化
畳み込み層の可視化を通じて、
CNNで何が行われているのか探索していく。
7.6.1 1層目の重みの可視化
MNISTデータセットにたいして単純なCNNの学習を行ってみる。
Pythonで実装すると↓のとおり。
# 7.6.1 1層目の重みの可視化 # coding: utf-8 import numpy as np import matplotlib.pyplot as plt from simple_convnet import SimpleConvNet def filter_show(filters, nx=8, margin=3, scale=10): """ c.f. https://gist.github.com/aidiary/07d530d5e08011832b12#file-draw_weight-py """ FN, C, FH, FW = filters.shape ny = int(np.ceil(FN / nx)) fig = plt.figure() fig.subplots_adjust(left=0, right=1, bottom=0, top=1, hspace=0.05, wspace=0.05) for i in range(FN): ax = fig.add_subplot(ny, nx, i+1, xticks=[], yticks=[]) ax.imshow(filters[i, 0], cmap=plt.cm.gray_r, interpolation='nearest') plt.show() network = SimpleConvNet() # ランダム初期化後の重み filter_show(network.params['W1']) # 学習後の重み network.load_params("params.pkl") filter_show(network.params['W1'])
学習前と学習後の1層目の画像の表示は次↓のとおり。
学習前
学習後
↑の画像データが示すように
タダのぼんやりした映像に見えるが(笑)
規則性のあるフィルターが「何を見ている」かというと、
エッジ=色が変化する境目
と
ブロブ=局所的に塊のある領域
を見ている。
このように
畳み込み層のフィルターは
エッジやブロブなどのプリミティブ(原始的、基本的)な情報を
抽出することがわかる。