NumPy:【機械学習】で使う行列計算


サマリ

  • np.dot
    • dot積
  • np.inner
    • 内積
  • np.outer
    • 外積
  • np.linalg.inv
    • 逆行列
  • np.linalg.norm
    • L1ノルム
    • L2ノルム

前提

np.dot(dot積)

特徴

  • 行列計算できる
  • 前の最後の次元の数と後の最初の次元の数を一致させる
    • 行列計算の決まり
    • (2,2) x (2,2)とか、(2,3) x (3,4)とか、(2,4,2) x (2,5)とか
  • AIで使う行列計算(np.dot)の勘所を書いた記事(外部リンク)

例1:普通に行列計算

データ作成

import numpy as np
a = np.array([[1, 0], [0, 1]])
b = np.array([[4, 1], [2, 2]])
print(a)
print(a.shape)
print(b)
print(b.shape)
[[1 0]
 [0 1]]
(2, 2)
[[4 1]
 [2 2]]
(2, 2)
  • (2,2)行列を2つ作成

dot積実行(手計算でもできる簡単な計算)

c = np.dot(a, b)
print(c)
print(c.shape)
[[4 1]
 [2 2]]
(2, 2)
  • (2,2)行列 x (2,2)行列 = (2,2)行列になる

例2:形状が異なる場合

データ作成

import numpy as np
a = np.arange(12).reshape(2,2,3)
b = np.arange(12).reshape(3,4)
print(a)
print(a.shape)
print(b)
print(b.shape)
[[[ 0  1  2]
  [ 3  4  5]]

 [[ 6  7  8]
  [ 9 10 11]]]
(2, 2, 3)
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]
(3, 4)
  • (2, 2, 3)テンソルと(3, 4)行列を作成
  • 参考
    • テンソルについては下記ページに説明あり

dot積実行

c = np.dot(a, b)
print(c)
print(c.shape)
[[[ 20  23  26  29]
  [ 56  68  80  92]]

 [[ 92 113 134 155]
  [128 158 188 218]]]
(2, 2, 4)
  • (2, 2, 3)テンソル x (3, 4)行列 = (2, 2, 4)テンソルになる
  • 計算された数字より形状に注目
      • (2,2,3)の最後の次元(3次元目)の要素数3に注目
    • 後ろ
      • (3,4)の最初の次元(1次元目)の要素数3に注目
    • dot積のルールとして前の最後の次元と後ろの最初の次元を一致させる必要がある
    • 結果は一致したものを除く形状になる
      • (2,2,3)と(3,4)で(2,2,4)になる

np.inner(内積)

説明

  • 同じサイズのリスト同士、対応する場所でかけて足す
    • [2,3] x [1,4] = 14、[3,2] x [2,1] = 8
      ※この例の[]はリストの囲み(以降の()と混同しないように)
       数値は入っている具体的な数字
  • ndarray(NumPy配列)とリストの表記の違い
    • (2,3,2)は(2,3)と(2,)に分けて考える
    • [10,15](=2次元)のようなndarrayが(2,3)個ある
  • ndarrayの内積計算では、最後の次元のサイズを合わせる
    • (1,1,2)と(3,2) …… これは2次元
    • (2,3)と(4,3) …… これは3次元
  • 内積計算後は、最後の次元を除いた全部
    • (2,3,4)と(4,)なら最後の4を除いた(2,3)
    • (1,1,2)と(3,2)なら最後の2を除いた(1,1,3)

例1:シンプル(1次元同士)

データ作成

import numpy as np
a = np.array([1,2,3])
b = np.array([0,1,0])
print(a)
print(a.shape)
print(b)
print(b.shape)
[1 2 3]
(3,)
[0 1 0]
(3,)
  • 1次元のndarray(NumPy配列)を作成

内積計算実行

c = np.inner(a, b)
print(c)
print(c.shape)
2
()
  • 向かい合う場所を掛けて足す
    • 1×0+2×1+3×0=2
  • 1次元同士の内積計算なので、最後はスカラー(数字のみ)になった

例2:3次元 x 1次元

データ作成

import numpy as np
a = np.arange(24).reshape((2,3,4))
b = np.arange(4)
print(a)
print(a.shape)
print(b)
print(b.shape)
[[[ 0  1  2  3]
  [ 4  5  6  7]
  [ 8  9 10 11]]

 [[12 13 14 15]
  [16 17 18 19]
  [20 21 22 23]]]
(2, 3, 4)
[0 1 2 3]
(4,)
  • 形状(2,3,4)の3次元ndarray(NumPy配列)を作成
  • 形状(4, )の1次元ndarrayを作成

内積計算実行

c = np.inner(a, b)
print(c)
print(c.shape)
[[ 14  38  62]
 [ 86 110 134]]
(2, 3)
  • 最後の次元の「4」がなくなって形状(2,3)のndarrayができた

例3:3次元 x 2次元

データ作成

import numpy as np
a = np.arange(2).reshape((1,1,2))
b = np.arange(6).reshape((3,2))
print(a)
print(a.shape)
print(b)
print(b.shape)
[[[0 1]]]
(1, 1, 2)
[[0 1]
 [2 3]
 [4 5]]
(3, 2)
  • 形状(1,1,2)の3次元のndarray(NumPy配列)を作成
  • 形状(3,2)の2次元のndarrayを作成

内積計算を実行

c = np.inner(a, b)
print(c)
print(c.shape)
[[[1 3 5]]]
(1, 1, 3)
  • 最後の次元の「2」がなくなって残った(1,1,3)が計算結果のshape
  • 補足:(1,1,3)について
    • (3,)が(1,1)個(=1個)あると考えるので、(3,)と中身の数字は同じ
    • [[[1 3 5]]] …… (1,1,3)
    • [1 3 5] …… (3,)
    • []を取る(=次元削除)はnp.squeezeで、以下の記事にて説明

例4:ブロードキャスト

import numpy as np
a = np.inner(np.eye(2), 7)
print(a.shape)
print(a)
(2, 2)
[[7. 0.]
 [0. 7.]]
  • (2,2)とスカラーの計算
  • ブロードキャスト
    • 7が全体に掛けられている

np.outer(外積)

特徴

  • メッシュ(全部)で掛け合わせる
  • (5, ) x (5, )なら(5,5)になる
    • 5個 x 5個 = 25個

例1:一般的な計算

データ作成

import numpy as np
a = np.ones((5,))
b = np.linspace(-2, 2, 5)
print(a)
print(a.shape)
print(b)
print(b.shape)
[1. 1. 1. 1. 1.]
(5,)
[-2. -1.  0.  1.  2.]
(5,)
  • 形状(5, )の1次元ndarray(NumPy配列)を2つ作成

外積計算を実行

c = np.outer(a, b)
print(c)
print(c.shape)
[[-2. -1.  0.  1.  2.]
 [-2. -1.  0.  1.  2.]
 [-2. -1.  0.  1.  2.]
 [-2. -1.  0.  1.  2.]
 [-2. -1.  0.  1.  2.]]
(5, 5)
  • (5,5)行列になった

例2:文字と複合

データ作成

import numpy as np
a = np.array(['p', 'q', 'r'], dtype=object)
b = np.array([1, 2, 3])
print(a)
print(a.shape)
print(b)
print(b.shape)
['p' 'q' 'r']
(3,)
[1 2 3]
(3,)
  • 形状(3, )の文字列を内包する1次元ndarray(NumPy配列)を作成
  • 形状(3, )の数値を内包する1次元ndarray(NumPy配列)を作成

外積計算を実行

c = np.outer(a, b)
print(c)
print(c.shape)
[['p' 'pp' 'ppp']
 ['q' 'qq' 'qqq']
 ['r' 'rr' 'rrr']]
(3, 3)
  • 計算後は(3,3)行列になった
  • 文字列が数値の数だけ繰り返される

np.linalg

np.linalg.inv(逆行列)

データ作成

import numpy as np
a = np.array([[2,3],[1,-4]])
print(a)
print(a.shape)
[[ 2  3]
 [ 1 -4]]
(2, 2)
  • 形状(2,2)のndarray(NumPy配列)を作成

逆行列作成と検算

b = np.linalg.inv(a)
c = np.dot(a, b)
print(b)
print(b.shape)
print(c)
print(c.shape)
[[ 0.36363636  0.27272727]
 [ 0.09090909 -0.18181818]]
(2, 2)
[[1. 0.]
 [0. 1.]]
(2, 2)
  • 形状(2,2)のndarrayの逆行列が作成される
  • 検算
    • 元の行列と逆行列でdot積(np.dot)を求める
    • 単位行列Eになった
      • 逆行列の性質からbが逆行列であることが証明できた

numpy.linalg.norm(L1、L2ノルム)

データ作成

import numpy as np
from numpy import linalg as LA
a = (np.arange(9) - 4).reshape((3, 3))
print(a)
print(a.shape)
[[-4 -3 -2]
 [-1  0  1]
 [ 2  3  4]]
(3, 3)
  • 形状(3,3)のndarray(NumPy配列)を作成

ノルム計算

l2 = LA.norm(a, 2)
l1 = LA.norm(a, 1)
print(l2)
print(l1)
7.3484692283495345
7.0
  • L2ノルムとL1ノルムそれぞれ取得できた


Posted by futa