3.3ではNumpyを使って多次元配列の計算を行います。
###3.3.1 多次元配列
多次元配列は簡単にいうと**『数字の集合』**です。
具体的には以下のようなものがあります。
- 数字が
- ただそこに存在している(0次元配列)
- 一列に並んだものや長方形状に並んだもの(1、2次元配列)
- N次元状に並んでいるもの(3次元以上)
それではNumpyを使って多次元配列を作成していきましょう。
1. 1次元配列の作成
>>> import numpy as np
>>> A = np.array([1,2,3,4])
>>> print(A)
[1,2,3,4]
>>>np.ndim(A) #配列の次元数を取得
1
>>>A.shape #配列の形状をshapeというインスタンス変数で取得
(4,) #タプルになっていることに注意
>>>A.shape[0]
4
上の例ではAは1次元の配列であり、4つの要素から構成されていることがわかります。
※ここではA.shapeの結果がタプルになっていることに注意しましょう。
これは1次元配列の場合であっても、多次元配列の場合と同じ統一された結果を返すからです。
(4,3) #2次元配列の場合 → タプル
(4,3,2) #3次元配列の場合 → タプル
#1次元配列でも統一してタプルで表示
2. 2次元配列の作成
続いて2次元配列を作成していきます。
>>> B = np.array([[1,2], [3,4], [5,6]])
>>> print(B)
[[1 2]
[3 4]
[5 6]
>>> np.ndim(B)
2
>>> np.shape
(3,2)
ここでは「3✖️2」の配列であるBを作成しました。
「3✖️2」の配列とは
最初の次元に3つの要素があり、次の次元に要素が2つあるという意味です。
なお、最初の次元は0番目の次元、次の次元は1番目の次元に対応します。
(Pythonのインデックスは0から始まります。)
###3.3.2 行列の内積
続いて、行列(2次元配列)の内積について説明します。
行列の内積は計算の手順が定義されており、例えば次のように行ないます。
そして、この計算をPythonで実装すると次のようになります。
>>> A = np.array([[1,2], [3,4]])
>>> A.shape
(2,2)
>>> B = np.array([[5,6], [7,8]])
>>> B.shape
(2,2)
>>> np.dot(A,B) #引数としてNumPy配列を2つ取り、それらの配列の内積を結果として返す関数
array([[19,22], #結合法則,分配法則は成立するが,交換法則は成立しない
[43,50]])
ここで注意の必要なことがあります。
それは、行列の積では、被演算子(A,B)の順番が異なると結果も異なるということです。
先ほどは2✖️2の形状の行列についての計算を行いましたが、別の形状の行列についても同様に計算を行うことができます。
>>> A = np.array([[1,2,3], [4,5,6]])
>>> A.shape
(2,3)
>>> B = np.array([[1,2], [3,4], [5,6]])
>>> B.shape
(3,2)
>>>np.dot(A,B)
array([[22, 28],
[49, 64]])
2✖️3の行列と3✖️2の行列の積は上のように実装できます。
ここでも注意点があります。それは
行列の1次元目の要素数(列数)と行列Bの0次元目の要素数(行数)を同じ値にする必要がある
というものです。
もしこの値が異なれば、行列の計算はできません。
例えば、Pythonで行うとすれば、以下のようなエラーが出力されます。
>>> C = np.array([[1,2], [3,4]])
>>> C.shape
(2,2)
>>> A.shape
(2,3)
>>> np.dot(A, C)
Traceback (most recent call last):
File "<stdint>", line 1, in <module>
ValueError: shapes (2,3) and (2,2) not aligned: 3 (dim 1) !=2(dim 0 )
#↑行列Aの1次元目と行列Cの0次元目の次元数が一致していないという意味
つまり、多次元配列の積では、2つの行列で対応する次元を一致させる必要があるということです。
大切なことなのでもう一度図を用いて説明しておきます。
計算結果である行列Cは、行列Aの行数と行列Bの列数から構成される ←これも重要な点です。
なお、Aが2次元の行列で、Bが1次元の配列でも以下に示すように**「対応する次元の要素数を一致させる」**という同じ原則が成り立ちます。
Pythonで実装すると次のようになります。
>>> A = np.array([[1,2], [3,4], [5,6]])
>>> A.shape
(3,2)
>>> B =np.array([7,8])
>>> B.shape
(2,)
>>>np.dot(A, B)
array([23, 53, 83])
###3.3.3 ニューラルネットワークの内積
それでは次はNumpy行列を使ってニューラルネットワークの実装を行います。
ここでは以下の簡単なニューラルネットワークを対象とします。
※バイアスと活性化関数は省略し、重みだけがあるとする
実装に関してはX、W、Yの形状に注意しましょう。
特にXとWの対応する次元の要素数が一致していることが重要な点です。
>>> X = np.array([1,2])
>>> X.shape
(2,)
>>> W =np.array([[1,3,5], [2,4,6]])
>>> print(W)
[[1 3 5]
[2 4 6]
>>> W.shape
(2,3)
>>> Y = np.dot(X, W)
>>> print(Y)
[ 5 11 17]
np.dot(多次元配列のドット積)を使えば、Yの結果を一度に計算することができます。
これが意味することは、例えYの要素数が100や1000だとしても、一度に計算できるということです。
もし使わなければ、Yの要素を一つずつ取り出して計算(or for文を使って計算をしなければならない)のでとても面倒です。
そのため、行列の内積によって一度で計算ができるというテクニックは、実装上とても重要であると言えます。