手を動かして学ぶゼロからDL①メモ #1 – ニューラルネットワークを実装してみる
はじめに
勉強している本
コードの置き場
概要
この記事の目的は、自分の学習履歴を記録することに加えて、誰かの参考になればと思っています。
今回は第三章の内容についてまとめます。
書籍のサンプルコードをもとに、自分なりにアレンジを加えながら実装してみました。
活性化関数の実装
本のサンプルを元に、出力形式を少し変化しました
追加実装の内容
- config共通化
- configを関数内で自動展開
- plt(画像出力)の設定追加(フォント、タイトルなど)
- x=0 の垂直線を追加
- 複数ウィンドウを立ち上がります

ニューラルネットワークの実装
- graphvizを使って、ニューラルネットワークを可視化します
-
graphvizをダウンロードしてインストールする
- windowsでインストールする際に、青い背景の警告が出ることがありますが、気にせず進めて大丈夫です。
- 環境変数にパスを追加する選択肢にはチェックを入れてください
-
pip install graphvizでインストールします
# 入力
x = np.array([1.0, 0.5])
# 重み
network_W=[
np.array([[0.1,0.3,0.5],[0.2,0.4,0.6]]),
np.array([[0.1,0.4],[0.2,0.5],[0.3,0.6]]),
np.array([[0.1,0.3],[0.2,0.4]])
]
# バイアス
network_b=[
np.array([0.1,0.2,0.3]),
np.array([0.1,0.2]),
np.array([0.1,0.2])
]
def forward(Ws,bs,x):
W1,W2,W3=Ws[0],Ws[1],Ws[2]
b1,b2,b3=bs[0],bs[1],bs[2]
a1=np.dot(x,W1)+b1
z1=func.sigmoid_func(a1)
a2=np.dot(z1,W2)+b2
z2=func.sigmoid_func(a2)
a3=np.dot(z2,W3)+b3
y= identity_function(a3)
return y
# 出力
[0.31682708 0.69627909]
作成した画像

MNIST(手書き数字認識)
サンプルファイル(mnist_show.py)を実行してみます
目的:MNISTの1枚目の画像を表示する
処理内容:
- 初回実行する時、train-images-idx3-ubyte.gz, train-labels-idx1-ubyte.gz, t10k-images-idx3-ubyte.gz, t10k-labels-idx1-ubyte.gzがダウンロードします
- MNISTの1枚目の画像を取り出す
- 取るときに、
flatten=Trueで画像を1次元で格納します - 取り出したら、
reshapeで1次元で格納下画像を元に戻す
- 取るときに、
- 取り出した画像を表示します
結果:画像が表示されます
img = x_train[0]
label = t_train[0]
print(label) # 5
# img_show(img) # ここで画像表示のコードを追加すると、flattenの元データが見える
print(img.shape) # (784,)
img = img.reshape(28, 28) # 形状を元の画像サイズに変形
print(img.shape) # (28, 28)
img_show(img)
ニューラルネットワークの推論処理
目的:MNISTのニューラルネットワークの推論処理を行います
処理内容:
- MNISTデータを読み込みます
- 学習済みの重みパラメータを読み込みます
-
predict関数で推論処理を行います
本の図3.26で各層の配列の形状が分かります
{
'W1': ndarray(784, 50), # 第1層の重み
'b1': ndarray(1, 50), # 第1層のバイアス
'W2': ndarray(50, 100), # 第2層の重み
'b2': ndarray(1, 100), # 第2層のバイアス
'W3': ndarray(100, 10), # 第3層(出力層)の重み
'b3': ndarray(1, 10) # 第3層のバイアス
}
実際各層の配列形状を出力します
W1: (784, 50)
b1: (50,)
W2: (50, 100)
b2: (100,)
W3: (100, 10)
b3: (10,)
- 入力層の784:画像サイズ28*28=784
- 出力層の10:10クラス分類(0~9計10クラス)
結果:Accuracy:0.9352が表示されます。これは今のパラメータで、93.52%のデータが正しく分類されました意味です
ニューラルネットワークのバッチ推論処理
目的:バッチでMNISTのニューラルネットワークの推論処理を行います
処理内容:
-
MNISTデータを読み込みます
-
学習済みの重みパラメータを読み込みます
-
データを100枚ずつ学習を行います
-
y_batch:100枚でまとめて推論した結果
y_batch.shape = (100, 10) [ [0.1, 0.0, 0.2, 0.7, 0.0, 0, ...], ← 1枚目の画像に対する10クラスのスコア [0.0, 0.9, 0.0, 0.1, 0.0, 0, ...], ← 2枚目の画像 ... (以下100行) ]-
p = np.argmax(y_batch, axis=1):行方向(横の方向,axis=1)に対して最大値の位置を取得
画像1 → [ 0.1 0.0 0.2 0.7 ... ] → 最大値は 0.7 → クラス 3 画像2 → [ 0.0 0.9 0.0 0.1 ... ] → 最大値は 0.9 → クラス 1 画像3 → [ ... ] → クラス x ... p = [3, 1, x, x, ..., x] (100個)-
accuracy_cnt:合っている数だけカウンタに加算
-
-
正解枚数で正解率を計算します
結果:Accuracy:0.9352が表示されます。
バッチ処理のメリット
行列演算が一回で済む
1枚ずつ推論:10000枚の場合、重み行列 W と 10000回も掛け算する必要があります
for 1枚目:
y = x @ W
for 2枚目:
y = x @ W
...
バッチ処理(100枚ずつ):一度に 100 枚分の行列をまとめて計算できて、重み行列 W と掛け算する回数が 10000 → 100 回に減ります
Y_batch = X_batch @ W
CPU・GPU の並列処理に最適化されている
1枚ずつだと、毎回小さな計算でハードウェア性能を使い切れない
メモリ転送の回数が激減する
1枚ずつ:1枚読み込む → 推論 → 返す→ この処理を1万回繰り返す
バッチ:100枚まとめて読み込む → 推論 (行列演算をまとめて1回) → 返す→ この処理を100回繰り返す
レジを例にすると、
1枚ずつ推論
- コンビニで1人ずつレジに並び、毎回レジを開く
- 店員は10000回レジを開く
バッチ推論
- 100人ずつまとめて支払い
- 店員が1回の処理で100人分の計算
- 店員は100回レジを開く
ベクトル化により Python のループを減らせる
loop 10000回と100回の差です
後書き
本の内容はそれほど多くありませんが、学習メモとしてまとめると意外と時間がかかります。
次回は第4章の内容を整理する予定ですが、状況によっては先に MCP を勉強してから進めるかもしれません。
Discussion