Python

Numpyを使った行列演算

By 2021年7月22日No Comments

この記事では、Numpyを使った行列演算の方法を解説します。

Numpyを使うと行列演算(逆行列や行列式、固有値計算)といった線形代数の処理を簡単かつ高速に行うことができます。これらの線形代数は理工系の学問のほぼ全てにおいて必須であると言っても過言ではありません。また、画像処理では画素ごとの出力を行列計算によって処理したり、機械学習では多くの変数をひとまとめにして取り扱うために、行列が利用されたりと、注目されているAIにおいても行列演算はとても重要になります。この記事を読んで、Numpyを使った行列演算ができるように学習しましょう。

Numpy配列numpy.ndarrayとnumpy.matrix

Numpyではnumpy.ndarrayで配列を管理していました。また、numpy.ndarrayの二次元配列にはnumpy.matrixが用意されています。

numpy.ndarray

Numpy配列numpy.ndarrayはnumpy.array()などの関数(np.arange()、np.zeros()、np.eyes()など)で生成できます。

import numpy as np  # numpyをnpとしてインポートします。
array = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(array)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

print(type(array))
# <class 'numpy.ndarray'>

numpy.matrix

Numpy配列numpy.matrixはnumpy.matrix()で生成できます。

matrix = np.matrix([[0, 1, 2], [3, 4, 5], [6, 7, 8]])
print(matrix)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

print(type(matrix))
# <class 'numpy.matrix'>

np.matrix()は二次元配列を生成する関数なので、二次元以外の配列を作ろうとするとエラーになります。

matrix = np.matrix([[[0, 1, 2]]])  # [[[0, 1, 2]]]は三次元配列
# ValueError: matrix must be 2-dimensional

ndarrayとmatrixの動作の違い

np.ndarrayとnp.matrixはほとんど同じ動作をします。後述しますが、np.ndarrayでは*演算子では内積計算はできないのに対して、np.matrixでは*演算子で行列の積が計算できます。

print(array * array)  # ndarray * ndarrayは各行列要素をかけたものを出力します。
# [[ 0  1  4]
#  [ 9 16 25]
#  [36 49 64]]

print(matrix * matrix)  # matrix * matrixは行列の積を出力します。
# [[ 15  18  21]
#  [ 42  54  66]
#  [ 69  90 111]]

行列の積の求め方

行列の積を計算するにはnp.dot()や@演算子を使います。

print(array)
# [[0 1 2]
#  [3 4 5]
#  [6 7 8]]

array_2 = np.array([[1, 2], [3, 4], [5, 6]])

print(np.dot(array, array_2))
# [[13 16]
#  [40 52]
#  [67 88]]

print(array @ array_2)
# [[13 16]
#  [40 52]
#  [67 88]]

np.matrixでは、*演算子でも行列の積を計算できます。

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

matrix_2 = np.matrix([[1, 2], [3, 4], [5, 6]])

print(np.dot(matrix, matrix_2))
# [[13 16]
#  [40 52]
#  [67 88]]

print(matrix @ matrix_2)
# [[13 16]
#  [40 52]
#  [67 88]]

print(matrix * matrix_2)
# [[13 16]
#  [40 52]
#  [67 88]]

逆行列の求め方

逆行列を計算するときはnp.linalg.inv()を使います。linalgモジュールはScipyライブラリ内にもあるため

from scipy import linalg

でインポートする事もできます。今回はNumpyライブラリからlinalgモジュールを使います。

array = np.array([[1, 2], [3, 4]])

array_inv = np.linalg.inv(array)
print(array_inv)
# [[-2.   1. ]
#  [ 1.5 -0.5]]

matrix = np.matrix([[1, 2], [3, 4]])

matrix_inv = np.linalg.inv(matrix)
print(matrix_inv)
# [[-2.   1. ]
#  [ 1.5 -0.5]]

matrix_inv = matrix**-1  # np.matrixは**-1でも逆行列を求めることができます。
print(matrix_inv)
# [[-2.   1. ]
#  [ 1.5 -0.5]]

行列式の求め方

行列式の計算にはnp.linalg.det()を使います。

array = np.array([[1, 2], [3, 4]])

array_det = np.linalg.det(array)
print(array_det)

固有値と固有ベクトルの求め方

普段馴染みのないと思われる固有値や固有ベクトルは、Googleなどの検索エンジンのページランクの考え方に利用されています。

固有値と固有ベクトルを計算するにはnp.linalg.eig()を使います。引数には固有値と固有ベクトルを求めたい配列を指定します。この関数は固有値と固有ベクトルの2つの配列を返します。1つ目の配列が固有値を表し、2つ目の配列が固有ベクトルを表します。今回は固有値をw、固有ベクトルをvとします。

array = np.array([[1, 2], [3, 4]])

w, v = np.linalg.eig(array)  # 2つの配列が返されるため、変数を2つ用意しています。

print(w)
# [-0.37228132  5.37228132]

print(v)
# [[-0.82456484 -0.41597356]
#  [ 0.56576746 -0.90937671]]

2×2の行列なので、固有値が2つ出力されます。今回の場合、固有値-0.37228132に対応する固有ベクトルが [-0.82456484, 0.56576746] になります。 3×3の行列でもやってみます。

array = np.array([[0, 1, 2], [3, 4, 5], [6, 7, 8]])

w, v = np.linalg.eig(array)

print(w)
# [ 1.33484692e+01 -1.34846923e+00 -1.15433316e-15]

print(v)
# [[ 0.16476382  0.79969966  0.40824829]
#  [ 0.50577448  0.10420579 -0.81649658]
#  [ 0.84678513 -0.59128809  0.40824829]]

3×3の行列なので、固有値が3つ出力されます。今回の場合、固有値1.33484692e+01に対応する固有ベクトルが [0.16476382, 0.50577448, 0.84678513] になります。

まとめ

今回はNumpyを使った行列演算について解説しました。あらゆる分野の行列演算をNumpyを使うことで高速に簡単に行うことができます。ぜひ使いこなせるようになりましょう。

Udemyの動画学習でもPythonを勉強しよう!

「平日の夜の勉強会には時間が間に合わなくて参加できない」「通勤時間のわずかな隙間時間を勉強時間にあてたい」「本ではよく分からないところを動画で理解を深めたい」そんなあなたはUdemyの動画学習がお勧めです!

UdemyのPythonおすすめ33講座レビューリスト
HOSL

Author HOSL

More posts by HOSL