#線形代数をpythonで実装していきます。
勉強したてなのでまだ理解ができていない部分もありますがやっていきたいと思います。
まず簡単なベクトル・行列の理解
まず先に今回使うライブラリをインポートしていきます。
import numpy as np
import scipy as sp
import sympy as sy
スカラーとベクトルについて
スカラーとは大きさのみを持ったもののことを指します。例えば普段何気なく使っている5や-10と言った実数がこれに該当します。
次にベクトルですがこちらは先程のスカラーの大きさに加えて向きを追加してものです。よくaやbと言った文字で表されます。
ノルムについて
ノルムとはベクトル(例えばa)の大きさのことを指し||a||と表すことができます。
逆ベクトルとゼロベクトル、単位ベクトルについて
ベクトルは実数のようにある変数を用いて元の値をk倍することができます。例えばkという文字を使用して、k=5と置きます。そして元のベクトルをaと置きます。この元のaベクトルをスカラー(k)倍することができます。これを利用して、k=−1の時スカラー倍してあげると-aとなる。このベクトルをaの逆ベクトルと言います。
またk=0と置き、同様にスカラー倍してあげると0となり、これを零ベクトルと呼びます。
またある大きさのベクトルを正規化したのを単位ベクトルと呼びます。どういうことかとうと大きさを1に変換したベクトルのことを指します。単位ベクトルは次のように表現することができます。
$$e=\frac{1}{||a||}a$$
例えば大きさ4のベクトルがあったとします。上記の式に当てはめればちゃんと1になります。
では早速pythonでやっていきます。
a = np.array([1, 2, 3])
k = [4, 1.5, -1, 0]
print(a * k[0]) #[4, 8, 12]
print(a * k[1]) #[1.5, 3, 4.5]
print(a * k[2]) #[-1, -2, -3]逆ベクトル
print(a * k[3]) # [0, 0, 0]零ベクトル
では次にノルムを実装します。今回はscipyからノルムをインポートしてきていますがnumpyにもあるのでそちらでも実装できます。
from scipy.linalg import norm
a = np.array([3, 4])
n = linalg.norm(a)
print(n)## 5
上記のプログラムで何が起こったかというと次の様なことが起きます。各値を二乗して平方根をとっています。これがノルムです。実際に機械学習などではニューラルネットワークなどのパラメータを調節するの使われます。(例えばL1ノルムやL2ノルムなど)
$$a = \sqrt{3^2 + 4^2} = \sqrt{25} = 5$$
では次に単位行列を実装してみます。先程さらっと確認しましたが、単位ベクトルとは元の向きを保ったベクトルを長さが1のベクトルに正規化するということです。先程の実装したaベクトルを元に実装してみます。
a = np.array([3, 4])
n = norm(a)
b = a / n
print(n) # [0.6, 0.8]
内積
では次にベクトルの内積をとっていきます。内積とは二つのベクトル(a,b)の掛け算のことを指します。
ではa, bそれぞれのベクトルを次の様に定義してみます。ではpythonで実装してみます。numpyにあるdotを使用して求めることができます。
$$a = \array{[1, 2, 3]}$$$$b = \array{[4, 5, 6]}$$
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
c = np.dot(a, b)
print(c)#32
このプログラムで何が起きているでしょう。次の様なことが起こっています。
$$c = 1 \times 4 + 2 \times 5 + 3 \times 6=32$$
各成分同士をかけて足しています。これが内積です。
$$a=\array[a_1, b_1, c_1]$$$$b=\array[a_2, b_2, c_3]$$$$c=a_1 \cdot a_2+b_1 \cdot b_2+c_1 \cdot c_2$$
またそれぞれのベクトルで生じた角度を実際に求めてみます。高校の教科書では次のような式で表されていました。では実際に求めてみます。
$$\cos\theta= \frac{a \cdot b}{||a||||b||}$$
def get_cos(a, b):
inner_product = np.dot(a, b)
a_norm = norm(a)
b_norm = norm(b)
cos_theta = inner_product / (a_norm * b_norm)
return cos_theta
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
get_cos(a,b)#0.6220084679281461
求めることはできましたが。小数でなんだかかわかりにくいですね。ここで登場するのがSymPyです。SymPyとはpythonで数式処理を行うライブラリです。そう言われてもピント来ないと思うので、実際に触れてみます。Sympyを使うと下のように綺麗な状態で返してくれます。多少numpyと書き方は異なりますが、大体は一緒なので使ってみてください。
import sympy as np
a = sy.Matrix([[1, 2, 3]])
print(a.shape)
display(a)
#(1, 3)
#numpyだと次のように返ってくる:
array([[1, 2, 3]])
少し逸れてしまいましがこちらを使用して実際に角度を求めてみましょう。
def get_angle(a, b):
a_v = sy.Matrix(a)
b_v = sy.Matrix(b)
norm_a = a_v.norm(ord=2)
norm_b = b_v.norm(ord=2)
inner_product = a_v.dot(b_v)
return inner_product / (norm_a * norm_b)
a = [5, 2, 5]
b = [4 ,1, 1]
答えは次のようになります。
$$\frac{\sqrt{3}}{2}$$
これを角度に直すと30°になります。
このコサインを求めることで正射影と言うものを求めることができます。
正射影とは次のように二つのベクトルが与えられた時、aに垂直に光が射した際に、bがaに落とす影のことを射します。
正射影は次の式で求めることができます。
$$\frac{a \cdot b}{||a||}=\frac{||a||||b||\cos\theta}{||a||}=||b||\cos\theta$$
行列
次に行列についてみていきます。pythonを触れたことがある方はよく使っていると思います。次のようなのを行列と言います。1,2の組み合わせと3,4と組み合わせで構成されている部分が行と呼ばれる部分で一行目、二行目と呼びます。また1,3と2,4で構成される部分を列と呼びます。合わせて下記のような行列を2行2列の行列と表現します。そして下記の行列に含まれている数字や文字を成分と呼びます。例えば3と言う数字は2行目1列目に位置しています。その場合を**(2, 1)成分**と言います。他にも例をあげると(2,2)の成分は4、(1,1)の成分を1と捉えることができます。
A =
\left(
\begin{matrix}
1 & 2\\\
3 & 4
\end{matrix}
\right)
先程も少し紹介しましたが、pythonを使用して行列を生成してみます。sympyを使用することで綺麗な行列を生成することができます。(GoogleColaboratoryではsympyが表示されないっぽい?)
a = np.array([[1, 2,
3, 4]])
print(a.shape)#(2, 2):2行2列の行列という意味
print(a)
a = sy.Matrix([[1, 2,
3, 4]])
零行列と単位行列
次に紹介するのが零行列と単位行列です。ベクトルの部分でもさらっと紹介しました。零行列とは全ての成分が0の行列のことを指します。単位行列とは対角成分が全て1の行列のことを指します。では早速pythonで実装していきます。
O = np.zeros((2, 2))
print(O.shape)
print(O)
#(2, 2)
[[0. 0.]
[0. 0.]]
E = np.eye(2, 2)
print(E.shape)
print(E)
#(2, 2)
[[1. 0.]
[0. 1.]]
O = sy.zeros(2, 2)
print(O.shape)
O
E = sy.eye(2, 2)
print(E.shape)
E
O =
\left(
\begin{matrix}
0 & 0\\\
0 & 0
\end{matrix}
\right)
E =
\left(
\begin{matrix}
1 & 0\\\
0 & 1
\end{matrix}
\right)
逆行列
次の紹介するのが逆行列です。AB=BA=Eを満たす行列をAの逆行列と言います。逆行列を次のように表します。$$A^{-1}$$$$AA^{-1}=A^{-1}A=E$$
しかし次のようにはならないです。
$$A^{-1}=\frac{1}{A}$$
A行列が次のように表される時逆行列は次のように表すことができます。
$$A =
\left(
\begin{matrix}
a & b\
c & d
\end{matrix}
\right)$$
$$
A^{-1}= \frac{1}{ad-bc}
\left(
\begin{matrix}
d & -b\
-c & a
\end{matrix}
\right)
$$
$$ad-bcを\Deltaや|A|で表すこともできます。$$
もしad-bc=0の場合は逆行列は存在しません。この逆行列を使用することで連立方程式を簡単に解くことができます。(今回は扱いません)
ケーリー・ハミルトンの定理
次にケーリー・ハミルトンの定理を紹介します。次のように行列が与えられた時、ケーリー・ハミルトンの定理を次のようにに表すことができます。
$$A= \left(
\begin{matrix}
a & b\
c & d
\end{matrix}
\right)$$
$$A^2-(a+d)+(ad-bc)E=O$$
一次変換
次に扱うのが一次変換です。行列の掛け算により点や直線などを移動することができます。では典型的な点
の移動について紹介します。
点の移動には次の4種類のやり方があります。
・ x軸に関して対称移動させる
・ y軸に関して対称移動させる
・ 原点関して対称移動させる
・ y=xに関して対称移動させる
x軸に関して対称移動させる
$$\left(
\begin{matrix}
1 & 0\
0 & -1
\end{matrix}
\right)$$
y軸に関して対称移動させる
$$\left(
\begin{matrix}
-1 & 0\
0 & 1
\end{matrix}
\right)$$
原点に関して対称移動させる
$$\left(
\begin{matrix}
-1 & 0\
0 & -1
\end{matrix}
\right)$$
y=xに関して対称移動させる
$$\left(
\begin{matrix}
0 & 1\
1 & 0
\end{matrix}
\right)$$
上記の行列を元の行列にかけることで点移動が可能になります。今回は以上です。pythonで実装してみます。
def x_axis(x):
transformed = np.dot(x, np.array([[1, 0], [0, -1]]))
return transformed
def y_axis(x):
transformed = np.dot(x, np.array([[-1, 0]], [0, 1]))
return transformed
def origin(x):
transformed = np.dot(x, np.array([[-1, 0], [0, -1]]))
return transformed
def y_x(x):
transformed = np.dot(x, np.array([[0, 1], [1, 0]]))
return transformed
x = np.array([2, 3])
x_axis(x)#array([ 2, -3])
a = np.array([2, -3])
y_x(a)#array([-3, 2])
できました。
次回も引き続き線形代数の基礎をやっていきます。
参考資料
・