行列計算とバッチ処理による高速化を書いてみる

コンピュータ上では計算する方法や順序によって処理速度が大きく変わることがある。ここでは処理速度を上げるための2つの工夫として、行列とバッチ処理を挙げる。(これはGrasshopper等にも応用できる話だ。)

<1. 行列>
行列を使うとコードがシンプルになることもメリットだが、特にNumPyを使う場合には、行列の使用で計算速度が桁違いに変わる。注意すべきなのは、使い方によっては必ずしもNumPyが最速ではないということだ。

『Pythonのlistとnumpy.arrayとかの速度比較』

#5 手書きの数字の判別プログラムを実装する。でも使っていた通り、ディープラーニングは行列計算との相性が非常に良い。ニューラルネットワークの計算が行列の計算そのものであるためだ。

画像①行列とニューラルネットワーク

例えば上図のように、入力[x1, x2]に対して重みが[[a1, b1],[a2,b2],[a3,b3]]となる層が続くとする。ここで入力に対して重みがかかり次の層へ出力を3つ出すまでを、適当な数字を当てはめ実装してみる。

import numpy as np

x, y = 1, 2 #適当な入力
a1, a2, a3, b1, b2, b3 = 1, 4, 0, 6, 3, 8 #適当な重みパラメータ

#1つずつ計算
y1 = x*a1 + y*b1
y2 = x*a2 + y*b2
y3 = x*a3 + y*b3
y = np.array([y1, y2, y3]) #[15 18 21]

#行列で計算
input = np.array([x, y])
weight = np.array([[a1, a2, a3], [b1, b2, b3]])
z = np.dot(input, weight) #[15 18 21]

上の例は計算量が少ないため違いがわかりにくいが、計算量が増えても行列で解くコードは3行のままである。

<2.バッチ処理>

バッチ処理とは「入力を複数まとめて行うことで高速化させる処理」のことだ。手書き数字の判定で言えば、10000枚の画像について1枚1枚入力して10000回forループさせるより100枚ごと100回forループさせたほうが速い。これは多くのライブラリ(今回の場合はNumPy)が、大きな配列の計算を効率化するように最適化されているからである。

では、バッチ処理を実装するにはどうすれば良いか。入力の行列を1次元ではなくn次元にしてしまえば良い。これにより出力もn次元となり、n枚分のデータが同時に出てくる。手書き数字判定では784個の入力から10種類の数字に判定したが、話を簡略化するため、上で使った行列を使って「10枚を2個の入力から3種類に判別するもの」を5枚ずつ処理する考える。

//バッチ処理前

画像②単入力

//バッチ処理

画像③バッチ処理後イメージ

import numpy as np

x = [[1,2],[3,0],[5,6],[7,1],[9,2],[8,1],[1,4],[5,7],[7,1],[9,2]] #適当な10枚分の入力データ
a1, a2, a3, b1, b2, b3 = 1, 4, 0, 6, 3, 8 #適当な重みパラメータ
batch = 5 #5枚ずつ計算
weight = np.array([[a1, a2, a3], [b1, b2, b3]])
p = np.array([],int) #空配列をintで定義

for i in range(0,len(x),batch):
input = x[i:batch+i] #batch分をスライス
y = np.dot(input, weight)
p = np.append(p, np.argmax(y, axis=1)) #axis=1は、1次元目の要素ごとに対する処理であることを示す

print(p) #[2 1 2 1 1 1 2 2 1 1]

Leave a Comment