NumPyは、プログラミング言語Pythonにおいて数値計算を効率的に行うための拡張モジュールである。

ref. https://ja.wikipedia.org/wiki/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]]
type(matrix)
# <class 'numpy.ndarray'> 
print(matrix.shape)
# (2, 3)
print(np.shape(matrix))
# (2, 3)
print(np.ndim(matrix))
# 2

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]]
type(matrix)
# <class 'numpy.ndarray'> 
print(matrix.shape)
# (2, 3)
print(np.shape(matrix))
# (2, 3)
print(np.ndim(matrix))
# 2

テンソル

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.array([matrix1, matrix2])
# or tensor = np.stack([matrix1, matrix2]) 
# [[[ 0 1 2],
#   [ 3 4 5]],
#  [[ 6 7 8],
#   [ 9 10 11]]]

tensor.shape
# (2, 2, 3)

np.ndim(tensor)
# 3

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

3 階テンソルをスライスする例です。

# 3 階テンソルを作成
tensor = np.arange(18).reshape(3, 3, 2)

print(tensor)
# [[[ 0  1]
#   [ 2  3]
#   [ 4  5]]
#  [[ 6  7]
#   [ 8  9]
#   [10 11]]
#  [[12 13]
#   [14 15]
#   [16 17]]]

# 各行列の 1 行目 を取得
tensor[:, 1, :]

# [[ 2,  3],
#  [ 8,  9],
#  [14, 15]])

# 各行列の最終行を取得
tensor[:, -1, :]
# [[ 4,  5],
#  [10, 11],
#  [16, 17]])

初期化

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 の配列」を新しく作成します。

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

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

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

ndarray メタ情報

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

import numpy as np

matrix = np.array([-1, 1, 2, 3, 4, 5]).reshape(2, 3)
print(matrix)
# array([[-1, 1, 2],
#       [3, 4, 5]])
print(matrix.shape)
# (2, 3)
print(matrix.ndim)
# 2
matrix0 = np.arange(0, 6).reshape(2, 3)
matrix1 = np.arange(6, 12).reshape(2, 3)

print(matrix0)
# [[0 1 2]
#  [3 4 5]]

print(matrix1)
# [[ 6  7  8]
#  [ 9 10 11]

tensor = np.stack([matrix0, matrix2]) # 3 階テンソル( 3 次元配列)

tensor.shape
#(2, 2, 3)
tensor.ndim
# 3 

ndarray.ndim は以下のとおり。

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

axis

NumPy の axis

axis は多次元配列で「どの方向に沿って処理するか」を指定するパラメータです。

  • axis=0:行方向(縦方向)に処理
  • axis=1:列方向(横方向)に処理
matrix = np.arange(6).reshape(2,3)

print(matrix)
# [[0 1 2]
#  [3 4 5]]

# axis = 0 行方向に処理
np.sum(matrix, axis=0)
# array([3, 5, 7])

# axis = 0 列方向に処理
np.sum(matrix, axis=1)
# array([3, 12])

Numpy の演算

スカラー積

import numpy as np

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

print(matrix * scalar)
# [[ 0 18]
#  [18 36]]

スカラー和

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

import numpy as np

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

print(matrix + scalar)
# [[ 9 11]
#  [11 13]]

積(*)

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

import numpy as np

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

print(m * v)
# [[0 40]
# [18 80]]

行列積(ドット積)

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

$@$ は Python3.5 以降で使用できます。

import numpy as np

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

print(np.dot(m1, m2))
# [[ 0  4]
#  [ 4 12]]

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

import numpy as np

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

print(m1 @ m2)
# [[ 0  4]
#  [ 4 12]]

スライス

import numpy as np

W = np.array([
    [0.1, 2, 0.3, 1],
    [2.3, 1, 4.3, 2],
    [1.1, 5, 1.3, 0],
    [5.1, 1, 2.9, 4],
    [0.9, 3, 1.6, 3]])

W.shape
# (5, 4)

W[1, 3] # 1 行 3 列を指定
# 2.0

# 行を抽出
W[[1, 3]] # W[[1, 3], :] と同じ
# [[2.3, 1, 4.3, 2],
#  [5.1, 1, 2.9, 4]]

# 列を抽出
W[:, [0, 2]]
# [[0.1, 0.3],
#  [2.3, 4.3],
#  [1.1, 1.3],
#  [5.1, 2.9],
#  [0.9, 1.6]]

# 1 行 0 列と 3 行 2 列を抽出
W[[1, 3], [0, 2]]
# [2.3, 2.9]

range

指定された範囲の連続する整数を作成するオブジェクトを返す関数です。
連続した整数値を直接返すわけではありません。
おもに for 文での繰り返し処理に用いられ, range(開始, 終了, ステップ)の形式で指定します。終了値は含まれず、メモリ効率が良いのが特徴です。

range(3)        # range(0, 3)  start から stop を含まない連続した整数値を作るオブジェクト。 0, 1, 2 を直接持っているわけではない
print(range(3)) # range(0, 3)  ← __str__ がこう表示される

for i in range(3): # 0, 1, 2 が生成される
# リストに変換してから入れる
a = list(range(3))
print(a)
# [0, 1, 2]

# アンパック(* を使う)
a = [*range(3)]
print(a)
# [0, 1, 2]

指定された範囲の連続する整数を生成する関数です。
おもに for 文での繰り返し処理に用いられ, range(開始, 終了, ステップ)の形式で指定します。終了値は含まれず、メモリ効率が良いのが特徴です。

Numpy.arange

numpy.arange は指定した範囲で等間隔の 配列 を作成する関数です。 stop を含みません。また整数に限らないことに注意します。

numpy.arange([start,] stop[, step], dtype=None)
np.arange(5)
# → [0 1 2 3 4]

//

2つの数値を割った商の整数部分を返します。

print( 10 // 3)
# 3

行列積の例:ミニバッチを使った全結合層

# 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): # range(5) は range(0, 5)
    xt = xs[:, t, :]        # (N, D)
    hs[:, t, :] = xt @ Wx   # (N, D) @ (D, H) = (N, H)

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