Numpy を使った演算

リスト(配列)

Python はリスト [ ] を使って生の配列を定義します。

arr = ['a', 'b', 'c']
print(arr)
['a', 'b', 'c']
type(arr)
# <clas `list`>

Numpy ndarray ( N-dimensional array )

Numpy はベクトル・行列・テンソルを扱う ndarray を提供しています。

ベクトル

import numpy as np

arr = np.array([0, 2, 3, 4, 5])
print(arr)
# [0, 2, 3, 4, 5]
print(type(arr))
<class 'numpy.ndarray'>

行列

行列を以下のように定義する。

import numpy as np

matrix = np.array([
    [1, 3, 4],
    [2, 2, 1]
])
print(matrix)
# [[1 3 4]
#  [2 2 1]]
print(matrix.shape)
# (1, 3)
print(np.shape(matrix))
# (1, 3)
print(np.ndim(matrix))
# 1

np.array().reshape() でも作成できます。

import numpy as np

matrix = np.array([1, 3, 4, 2, 2, 1]).reshape(2, 3)
print(matrix)
# [[1 3 4]
#  [2 2 1]]
print(matrix.shape)
# (1, 3)
print(np.shape(matrix))
# (1, 3)
print(np.ndim(matrix))
# 1

Numpy の演算

スカラー積

import numpy as np

matrix = np.array([
    [0, 2],
    [2, 4]
])
scalar = 9
print(matrix * scalar)

# [[0 20]
#  [18 40]]

スカラー和

ブロードキャストされます。

import numpy as np

matrix = np.array([
    [0, 2],
    [2, 4]
])
scalar = 9
print(matrix + scalar)

# [[10 12]
# [12 14]]

行列積

Numpy は行列積を $@$ またはドット積( np.dot() )で求めます。

$@$ は Python3.5 以降されました。

[!NOTE] ndarray の * を使った積は後述するように各要素をかけ合せる演算を表します。

import numpy as np

m1 = np.array([
    [0, 2],
    [2, 4]
])
m2 = np.array([
    [2, 2],
    [0, 2]
])

print(np.dot(m1, m2))

# [[ 4  6]
# [12 14]]

$@$ でも同様に行列積を求めることができます。

import numpy as np

m1 = np.array([
    [0, 2],
    [2, 4]
])
m2 = np.array([
    [2, 2],
    [0, 2]
])

print(m1 @ m2)

# [[ 4  6]
# [12 14]]

積( * )

* 演算子は各要素の積を算出する。
このときシェイプが異なる場合はブロードキャストされる。

import numpy as np

m = np.array([
    [0, 2],
    [2, 4]
])
v = np.array([9, 20])

print(m * v)

# [[9 40]
# [29 80]]

ndarray メタ情報

ndarray メタ情報は ndarray.shape 、 ndarray.ndim で求めます。

import numpy as np

matrix = np.array([0, 1, 2, 3, 4, 5]).reshape(2, 3)
print(x)
# array([[0, 1, 2],
#       [3, 4, 5]])
print(x.shape)
# (2, 3)
print(x.dim)
# 2
matrix1 = np.arange(0, 6).reshape(2, 3)
matrix2 = np.arange(6, 12).reshape(2, 3)
print(martix1)
# [[0 1 2]
#  [3 4 5]]
print(matrix2)
# [[ 6  7  8]
#  [ 9 10 11]
tensor = np.stack([matrix1, matrix2]) # 3 階テンソル( 3 次元配列)
tensor.shape
(2, 2, 3)
tensor.ndim
3 

ndarray.ndim は以下のとおり。

  • 1 => ベクトル
  • 2 => 行列
  • 3 => 3 階テンソル

テンソル

matrix1 = np.arange(0, 6).reshape(2, 3)
# [[0 1 2]
#  [3 4 5]]
matrix2 = np.arange(6, 12).reshape(2, 3)
# [[ 6  7  8]
#  [ 9 10 11]

# 3 階テンソル( 3 次元配列)
tensor = np.stack([matrix1, matrix2]) 
tensor = np.array([matrix1, matrix2])
# [[[ 0 1 2],
#   [ 3 4 5]],
#  [[ 6 7 8],
#   [ 9 10 11]]]

np.arange(12).reshape(2, 2, 3)
# [[[ 0 1 2],
#   [ 3 4 5]],
#  [[ 6 7 8],
#   [ 9 10 11]]]

ndarray の初期化

np.random.randn

matrix = np.random.randn(2, 3) # 平均 0, 標準偏差 1 で初期化
print(matrix)
# [[ 0.34865773 -0.11704851  0.66940347]
#  [ 1.42817345 -1.23289827 -1.46875888]]

matrix = np.random.randn(2, 3) / np.sqrt(100)
>>> print(matrix)
# 標準偏差: 1/√D = 1/√100 = 0.1
# 分散:     1/D  = 1/100  = 0.01
# [[-0.14391807  0.12261132  0.02211319]
#  [-0.1235343  -0.15567155  0.14128749]]

np.zeros_like

NumPy で使用される関数で、「指定した配列と同じ形状(shape)とデータ型(dtype)を持ち、要素がすべて 0 の配列」を新しく作成します。

import numpy as np

# 元となる配列
a = np.array([[1, 2, 3], [4, 5, 6]])

# a と同じ形・データ型で、中身が 0 の配列を作成
b = np.zeros_like(a)

print(b)
# 出力:
# [[0 0 0]
#  [0 0 0]]

Numpy axis

Numpy の axis の覚え方のコツは axis は「動く方向」として捉えて、番号の軸に沿って走ると理解する。

  • axis=0 => 行の軸を走る 縦方向(行から行へ)に移動しながら計算します。結果として「列ごと」の集計値が出ます。
  • axis=1 => 列の軸を走る 横方向(列から列へ)に移動しながら計算します。結果として「行ごと」の集計値が出ます。

ミニバッチを使った全結合層

# N バッチ数
# T RNN のステップ数
# D 単語 ID の分散表現の要素数
# H 隠れ状態の要素数

import numpy as np

N, T, D, H = 10, 5, 100, 10

xs = np.arange(N*T*D, dtype=float).reshape(N, T, D)
Wx = np.random.randn(D, H)  # 重み行列 (D, H)

hs = np.zeros((N, T, H))    # 結果を格納 hs = np.empty((N, T, H)) などでも良い

for t in range(T):
    xt = xs[:, t, :]        # (N, D)
    hs[:, t, :] = xt @ Wx   # (N, D) @ (D, H) = (N, H)

print(hs.shape)  # (10, 5, 10)