機械学習勉強ログ

機械学習に関連した勉強を記録していこうと思います。

線形代数の進捗と機械学習へ向かうためのステップ

今回は主に自分のための備忘録です。

線形代数についてある程度、今読んでいる本の終わりが見えてきたので、 目的である「はじパタ」を読めるようになるには、何をしたら良いか考えていきたいと思います。

やったこと

まずは、ここまで勉強したことを整理しました。

  • 行列・ベクトルの基礎
    • ベクトル、行列の表現方法、基礎的な計算
  • 線形写像
    • やったこと
      • 拡大行列
      • 回転行列
      • 実際の応用例(アナログ時計)
      • 線型独立
    • 応用
      • 3Dグラフィック、ロボットアーム制御等

スカラーだけの表現から、ベクトル、行列を使った表記を使用できるようになったことで、 現実世界での事象を表現、計算しやすくなった。

現在進行形でブログに記載中ですが、連立一次方程式をAx=bという形に 一般化して、コンピュータで扱いやすい形で表現することができるようになった。

また、コンピュータを利用することで、建築で使われるような多数のトラス構造の 構造計算なども強力なマシンパワーで瞬時に解くことができるようになった。

  • 固有値問題
    • やったこと
      • 固有方程式の解き方
      • 固有値を利用した対角化(現在取り組み中)
    • 応用
      • 機械学習での主成分分析、次元削減
      • その他、工学系で多数の応用事例あり。

行列を因数分解する方法として、固有値分解を学んだ。 固有値分解することで、対角化が行え、べき乗が計算しやすくなります。

また、固有値には工学上の意味があり、機械学習の世界だと、PCAという次元削減の 手法に使われていたりする。

今後どうやって機械学習に結びつけるか?

まずは、線形代数の本をクリアする。

線形代数のGOAL

  • 最小二乗法により一番単純な回帰の原理を理解したい。
    • 最終章は最小二乗法による関数フィット(一番単純な回帰)があるので、多項式のモデルでパラメータを求める仕組みがわかると思う。

そこに至るまでに残しているもの

線形代数の次に取り組むことの候補

  • 線形モデルのところを中心に「はじめてのパターン認識」を読み始める。

  • 数理最適化について学ぶ?

    • 学習部分をもう少し突き詰めていくとなると、最適化問題の考え方について知っておいてもいいかもしれない。 どこまで深入りするか迷っている。
  • 確率モデルについての理解ができるように、大学レベルの確率・統計の基礎を学ぶ。

感想

線形代数を勉強したお陰か、ちょっとした数式が出てきても、お手上げ感はなくなりました。 数式はプログラミング言語に比べると凝縮度が高いので、数値を当てはめて式を展開したり、 項や因子ごとに意味を考えたりすることで、解釈していけば理解できることが分かりました。

やはり、ものづくりは、プログラミングもそうですが、 長い目で見れば、原理を知っていて作るのと、そうでないのとは雲泥の差がつくと私は思います。 今このタイミングで動作原理が頭の中に入ってきたことで、今後の応用が楽しみになってきました。

線形代数を学んだことで、他の工学系の資料を読んでも多少は意味がわかるようになってきました。 これは嬉しい副産物です。大学一年で線形、微積フーリエなどをなぜ学ぶのか今ようやっとわかったw。

まぁ、私は私の目的とペースで引き続きやっていこうと思います。

線形代数で連立方程式を解く(2) 〜余因子展開を使って解く〜

線形代数を使って、連立方程式を解くの2回目です。 今回は余因子展開を使った逆行列の求め方についての説明回となります。

図解とPythonによるプログラムをつけて数式を解説していきます。

余因子展開による方程式の解法

 A^{-1} = \frac{1}{|A|} \tilde{A}

  •  |A|は、行列式(determinant)で、 \det{A}とも書きます。
  •  \tilde{A} は余因子行列と言います。

これらを求めるためには、小行列余因子の求め方を知る必要があります。

小行列と余因子を求める

余因子の定義

 \tilde{a}_{ij} = (-1)^{i+j} |A_{ij}|

小行列を求める

まずは、小行列ですが  A_{ij} と表されている部分が小行列にあたります。 これは図解で見てもらったほうが早いですが、元の行列 A からi行j列を除いた部分の行列のことです。

つまり、 |A_{ij}| は現状の正方行列より、一回り小さい正方行列の行列式を求めることになります。 例えば、3次の正方行列だったら、2次の正方行列の行列式を求めることになります。

余因子を求める

次に余因子の係数の部分(下記)ですが、

 (-1)^{i+j}

この部分は、符号を決定している部分で行列の縦横のインデックスを足した値が偶数であれば正の符号、奇数であれば負の符号になります。

 \tilde{a}_{ij} = (-1)^{i+j} |A_{ij}|

余因子の式全体としては、n-1次の行列式を求め、行列のインデックスによって符号を付与しているということになります。

行列式( |A|)を求める

余因子を使って行列式は、以下のように表されます。

 |A| = \sum_{k=1}^{n} a_{ik} \tilde{a}_{ik} = \sum_{k=1}^{n} a_{kj} \tilde{a}_{kj}

計算方法についての解説ですが、まずはi, jどの行、列でもいいのですが任意の行か列を選びます。 その行の最初から最後まで、要素と余因子の積を \Sigmaで足し合わせます。

例: 3次正方行列の行列式

余因子展開で違和感があるのは、行列式を求める計算の中に行列式があるためですが、 3次元の行列式を求めるために、余因子展開をかけると2次元の行列式が出てきて、 2次元の行列式を求めるために、1次の行列式、つまり 要素そのものになるまで式の展開が続きます。

この方法であれば、どんなに高次であろうと行列式が求まりますね。😁

行列式(余因子展開)を求めるソースコード(参考)

プログラマには、ソースコードの方がわかりやすいかもしれないので、 自分の書いたコードも合わせて提示しておきます。

# 小行列を求める
def submatrix(m,i,j):
  return np.delete(np.delete(m, i, axis=0), j, axis=1)


# 余因子を求める
def cofactor(m,i,j):
  sign = np.power(-1,i+j) 
  sm = submatrix(m,i,j)
  
  return sign * det(sm)  

# 余因子展開により行列式を求める
def det(m, i=0):
  def term(m,k,i=0):
    return m[i,k] * cofactor(m,i,k)
  
  # スカラーの場合は、値そのものを返す
  if (m.shape == (1,1)):
    return m[0,0]
  
  # 2 x 2以上の正方行列の場合は、余因子展開で求める
  else:  
    return np.sum(
        [ term(m,k,i) for k in range(0, m[:,0].size)]
    )

要は、小行列がスカラーになるまで再帰的に、展開を続けるアルゴリズムになります。

余因子行列( \tilde{A})を求める

余因子行列の方は行列式(余因子展開)に比べれば楽です。 下記は、2次の例ですが余因子( \tilde{a}_{ij})を並べた行列を転置したものが余因子行列になります。


\tilde{A} = \begin{bmatrix}
\tilde{a_{11}} &  \tilde{a_{12}} \\\
\tilde{a_{21}} &  \tilde{a_{22}}
\end{bmatrix}^T

逆行列( A^{-1})を求める

  •  Aの各要素に対する余因子を求めます。
  • そこから、 |A| \tilde{A}を求めます。
  • 下記の公式に当てはめて逆行列を求めます。

 A^{-1} = \frac{1}{|A|} \tilde{A}

方程式の解を求める

 x = A^{-1} \cdot b を計算します。

まとめ

非常に長い手順でしたが、方程式の解を求める手順を紹介しました。 ここに書いてあることを定着させるには、一度は手を動かして解いた方がいいと思います。

今回やったこと

  • 余因子展開を用いた、逆行列の算出方法と方程式の解の算出方法を学んだ。
    • 余因子、小行列、余因子行列、行列式の計算方法について学んだ
    • 余因子展開については、理解を助けるためPythonコードを提示した。

わかったこと

  • 数式の読み方
    • 項や因子ごとに意味を考えると全体が見えてくる。
    • 具体的な値をいくつか当てはめて考えることも有効。そこから、抽象に立ち返っても良い。
  • 数式の意味さえ理解してしまえば、プログラムに変換するのは難しくない。(現状のところは)
  • (何冊か本を買ったのでわかったことだが)数学の解説書で実用に重きをおいている本と、理論の堅牢性や高度な抽象化に重きをおいているものがある。自分の目的にあった本を選択することが大切。

次回やること

余因子展開は行列式を求める手法としてはいいですが、 逆行列を求めるだけなら、もっといい方法をガウスさんが確立してくれています。😁

それをガウスの消去法というのですが、次回はそちらについてPythonコードも含めて解説していきます。

線形代数で連立方程式を解く(1)

はじめに

今回は連立方程式線形代数を使って解くという内容をアウトプットしていきます。 勉強しながら思い出したのは、遥か昔、高校の数Cの授業で習った時のことです。

中学で習った代入法で連立方程式自体は解けるのに、一体なんの為に これを覚えるんだろうと疑問に思いました。

幸い、今勉強してみて、行列ならではの価値が見出せたので、 その辺りから書いていく事にします。

行列化して連立方程式を解くことの意味

汎用的な表現能力

まず行列で表記することで、式が非常にコンパクトになります! どのぐらいコンパクトかというと、

 Ax = b

こんな感じで、実にシンプル!😂 しかも、未知数が2つ(2元)だろうが、100個だろうが同じ式で扱えます。

さすが、多次元を抽象化する行列ならではの表現能力の高さです!

さて、上記のような形にどうやって変形するかというと、

例えば2元の場合は、


\begin{align}
a_{11}x_1 + a_{12}x_2 = b_1 \\\
a_{21}x_1 + a_{22}x_2 = b_2 
\end{align}

という式があったとすると、


A = \begin{bmatrix}
a_{11} & a_{12} \\\ 
a_{21} & a_{22}
\end{bmatrix}



x = \begin{bmatrix}
x_1 \\\ 
x_2
\end{bmatrix}



b = \begin{bmatrix}
b_1 \\\ 
b_2
\end{bmatrix}

上記のような感じで係数の部分が、 Aの部分に来ます。

 A のことを係数行列(coefficient matrix) といい、  A^{-1}を求めることで連立方程式が解けます。

なぜ、逆行列を求める事で連立方程式が解けるか? 式を変形すると下記のようになります。


\begin{align}
Ax&=b\\\
  x&=A^{-1}b
\end{align}

つまり、 A^{-1}  bをかけたものが解になりうるからです。 (なお、 A^{-1}=0となるケースがある為、常に解があるわけではない。次回以降説明予定)

コンピュータと相性が良い

上記の通り、行列で連立一次方程式を解くには、 A^{-1} を求めます。 具体的な手段として、逆行列を求めるには、 余因子展開ガウスの消去法 などがあります。

余因子展開も、ガウスの消去法も本を読みながら手で計算してみましたが、非常に手間のかかる計算ですw。 しかし、計算は大変ですが、計算方法(アルゴリズム)は明確かつ単純です。つまり、プログラム化が簡単で、 コンピュータと相性が良いという事になります。 (次回あたり、実際にNumPyで書いてみようかと思います。)

まとめ

今回は短いですが、イントロということでここまでにしておきます。😁

  • n元連立一次方程式は、n次元の行列とベクトルを使って表現できる。
  • 係数行列の逆行列( A^{-1}) を求めることで、方程式の解が求まる。

次回は、余因子展開を使って、方程式を解いてみようと思います。

線形代数(回転行列)とPythonを使ってアナログ時計を表現してみる。

今回のテーマ

前回は、拡大行列に続いて、回転行列についてアウトプットしてきました。 今回は、応用としてアナログ時計の針を回転させるというのをやってみたいと思います。

実は、これ以前から、やってみたかったんですよね。それも随分昔から、、。

昔話

大学生のころ(1997年頃)、当時Windows上でゲーム作りにハマっていまして、 ちょうど当時流行っていた3D技術(Direct 3D)を使って何か作りたいと思っていました。(世の中的にも3Dというだけで持て囃されるような時代でした) それで、買ったのがこの本です。

Direct3Dプログラミングガイドブック

Direct3Dプログラミングガイドブック

この本には「地球を回してみよう」 という、エンジニアだったら誰しもあこがれるような魅力的なお題が設定されていました。 自分も回せるだろうと思って読み始めたのですが、当時は3Dの原理となる数学の部分が理解できなくて 結局回せなかったのでした orz。(もちろん、サンプルは動かしましたがw)

しかし、20年たった今、「線形代数」を学んだことで「あ、たぶん自分地球回せるわw」と思いまして。 そして、いきなり問題を3次元に拡張するのも、自分にとっては無謀なので、まずは2次元で時計の針を回そうと思い、 このテーマに行き当たった訳です。

時計の針を回してみる

まずは、問題を式として、表現することから手を付けていきます。

まず、時計の針は 秒針長針短針 とありますが、これは ベクトル としてあらわせそうです。

それぞれの長さは、3, 2, 1としておきましょうか。 そうすると、0時0分0秒の位置を初期位置とすると、それぞれのベクトルは。

こんな感じで表せそうです。

で、これらの針達が 躍動 回転する。

回転の速度はそれぞれ違うので、一気に考えるのではなく、 まずは、秒針の動きから考えていくことにします。

秒針の回転を考えてみる。

秒針の座標は、秒針の初期位置からの回転運動なので、次の式になると思います。

ただ、これだと角度を与えて秒針の位置を求める関数なので、

時間(s)の関数に直したほうがよさそうです。

そう考えると、秒針60秒時計回りに1週( -2\pi) するので

 \theta(s) = -2\pi \frac{s}{60}

時間と角度の関係は上式になり、時間sの時の秒針の位置は、

 n_s(s) = R(-2\pi \frac{s}{60}) \cdot n_{s0}

あっけないほど、簡単に表すことができてしまいました

短針長針に関しては、秒針との違いだけ考えればよさそうです。

違いは次の2点なので、

  • 針の長さ(針の初期位置のベクトル)
  • 針の回転速度

針の式は以下のようになります。

  • 短針の式

    •  n_m(m) = R(-2\pi \frac{m}{60}) \cdot n_{m0}
  • 長針の式

    •  n_h(h) = R(-2\pi \frac{h}{12}) \cdot n_{h0}

Pythonでのシミュレーション

さて、各針の動きの式ができたので、これを Pythonを使って シミュレーションしていきましょう。

シミュレーションで使っている環境の説明を簡単にしていきます。

まず、シミュレーションは、Google Colaboratory上で作成しています。

Google Coraboratoryは、 Jupyter notebookのマネージド環境です。 簡単にPythonの環境を用意できる上、Jupyterなのですぐに結果が確認できるので非常に便利です。 無償で利用できます。

NumPyの使い方

Pythonで行列演算を扱うためのPythonのライブラリです。 軽く触ってみましたが、今まで勉強した行列やベクトルの操作がライブラリ関数 として提供されているので、非常にシンプルに式をプログラムに変換できて便利です。

パッケージのインポート

import numpy as np

NumPy パッケージは、npと省略して使うのが慣習のようなので、 それに倣っていきます。以下、簡単な演算の数式とPythonの簡単な対応を書きます。

横ベクトル

 A=\begin{bmatrix}
1 & 2 & 3
\end{bmatrix}

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

縦ベクトル

 A=\begin{bmatrix}
1 \\\
2 \\\
3
\end{bmatrix}

A = np.array([[1,2,3]]).T # Tで転置(transpose)できる

ベクトルの内積

 \begin{bmatrix}
1 & 2 & 3
\end{bmatrix}
\cdot 
\begin{bmatrix}
1 \\\
2 \\\
3
\end{bmatrix}

np.dot(np.array([1,2,3]), np.array([1,2,3]))

ここまであれば、とりあえず時計の実装に必要な行列演算はほぼ手に入ったと思います。

Matplotlib

こちらは、データを可視化するためのライブラリです。 一般的な使い方はググってもらうとして、今回、ちょっと特殊な使い方をしているので その点だけ補足します。

FuncAnimation関数

通常 matplotlib は静的なグラフを表示するのに使いますが、今回は動的に時計の針のアニメーションさせるのに 使っています。これを実現するには、 FuncAnimation という機能を使います。

これは、下記のようにフレーム(n)を引数として、nフレーム目の静的なグラフを返却する関数を定義することで アニメーションを実現するものです。 下記に使用例を示します。

clock_animation = animation.FuncAnimation(
  fig=fig,
  func=update_needle,   # frameに基づいたレンダリングを行う関数
  interval=1000,        # 1秒ごとに描画を更新する
  frames=60*2,          # フレーム数
)

figで指定されるバッファを、funcというフレームを引数とする関数で更新していきます。

上記の例では、1秒ごとにupdate_needleを呼び出して、0フレーム~119フレームまで、合計120フレーム、 2分のアニメーションを生成しています。(clock_animation)

clock_animationは、JSプレーヤーで再生することもできるし、動画ファイルとして出力することも可能です。

注意点

  • 動的にアニメーションしているわけではなく、アニメーションデータを作ってから、データを再生する構造になっています。

    • 毎フレームごとに状態を取得するような処理は、書けません。(例えば、現在時刻を取得するなど)
    • フレーム数が多くなりすぎると、レンダリングに時間がかかることに注意。
  • レンダリング関数では、plot系の関数で全体を再描画してもよいが、公式のマニュアルにあるように、 オブジェクトのプロパティを書き換えて差分更新した方がよさそうです。

完成したアナログ時計

ソースコード

import numpy as np
from sympy import sin, cos, pi
import matplotlib.pyplot as plt
from matplotlib import rc
import matplotlib.animation as animation
from datetime import datetime
from pytz import timezone

# 針ベクトル
HOUR_NEEDLE     = np.array([[0,0], [0,1]])
MINUTE_NEEDLE  = np.array([[0,0], [0,2]])
SECOND_NEEDLE = np.array([[0,0], [0,3]])

def rotate(theta):
  return np.array([
    [cos(theta), -sin(theta)],
    [sin(theta), cos(theta)]          
  ])

# 各針の動きを表す関数
def seconds_needle(s):
  return rotate(-2 * pi * s / 60)

def minutes_needle(m):
  return rotate(-2 * pi * m / 60)

def hours_needle(h):
  return rotate(-2 * pi * h / 12)

# 針の位置をフレーム(1秒)ごとに更新する
def update_needle(frame):
  time = dt.second + dt.minute * 60 + dt.hour * 3600  + frame
  needles[0].set_data(
    np.dot(seconds_needle(time), SECOND_NEEDLE)
  )
  
  needles[1].set_data(
    np.dot(minutes_needle(time / 60),MINUTE_NEEDLE)
  )
  
  needles[2].set_data(
    np.dot(hours_needle(time / 3600),HOUR_NEEDLE)
  )
  
  return needles

def create_needle2d(ax, data, needle_width=3, color='red'):  
  # 針のLine2Dオブジェクトを生成
  needle2d = ax.plot([], [], lw=2)[0]
  needle2d.set_data(data)
  needle2d.set_linewidth(needle_width)
  needle2d.set_color(color)
  return needle2d

dt = datetime.now(timezone('Asia/Tokyo'))
fig, ax = plt.subplots()
ax.set_xlim(-3.5, 3.5)
ax.set_ylim(-3.5, 3.5)
ax.set_aspect('equal')

plt.close() # 余計なプロットがされるため追加

needles = [
    create_needle2d(ax, SECOND_NEEDLE, needle_width=1, color='red'), 
    create_needle2d(ax, MINUTE_NEEDLE, needle_width=2, color='black'), 
    create_needle2d(ax, HOUR_NEEDLE, needle_width=3, color='black')
]

# 針を1秒ごとに描画するためのイベントハンドラを設定
clock_animation = animation.FuncAnimation(
  fig=fig,
  func=update_needle,
  interval=1000,
  frames=60*2
)
rc('animation', html='jshtml')
clock_animation

colab.research.google.com

まとめ

線形代数を使って、アナログ時計を表現することができました。 とりあえず、線形代数を学んできて、やっと現実との接点が見えるサンプルができたので、 面白いですね。

地球は、いずれ回したいです😉。

現時点で把握している、時計と地球との違いは🤔、

  • 座標が3次元であること
  • 地球の回転運動について、自転と公転の2種類がある。
  • 公転運動について、楕円運動かつ面積速度が一定(ケプラーの法則)

機械学習とは離れていってしまうので、すぐにはやりませんが、いずれ気が向いたら やってみようと思っています。

工学基礎 はじめての線形代数学 (KS理工学専門書)

工学基礎 はじめての線形代数学 (KS理工学専門書)

こちらの本の方は、9章あたりまで解き進めましたので、 次回は、行列を使って連立方程式を解く ことについてアウトプットしていければと思います。

線形代数の基本的な計算(3) ~回転行列~

前回は、拡大行列を掛けることで長方形を拡大させることを表現してみました。 今回は、拡大ではなく、座標の回転に挑戦してみたい思います。

回転行列の紹介

次の行列を掛けることで、2次元座標の回転を行うことができます。


R(\theta) =\begin{pmatrix}
cos(\theta) & -sin(\theta) \\\
sin(\theta) & cos(\theta)
\end{pmatrix}

試しに、

 
p_1=(1,1)^T 


\theta_1=\frac{\pi}{2}

でやってみましょう。

まずは、回転する角度に対応した 回転行列を求めます。


R(\theta_1)= \begin{pmatrix}
0 & -1 \\\
1 & 0
\end{pmatrix}

上のような感じで求まります。

これを、 座標のベクトルにかけると、


p_2=\begin{pmatrix}
0 & -1 \\\
1 & 0
\end{pmatrix} \cdot \begin{pmatrix}
1 \\\
1
\end{pmatrix} = \begin{pmatrix}
-1 \\\
1
\end{pmatrix} 

回転後の座標 p_2が求まります。

今回の回転操作も前回の拡大と同様の考え方で

回転後の座標 = 回転変換 * 回転前の座標

求めることができました。

イメージで確認すると、

こんな感じですね。

なぜ、回転行列で回転できるのか?(導出方法)

やっぱり、なぜあの式で回転できるか気になったので調べてみました。

以外に簡単だったので過程も書いておきます。

  • 回転前の座標を p1 , 回転後の座標を p2
  • p1の角度をφ, p2の角度をθとします。

そうすると、p2 (回転後) の座標は次の式で表せているはずです。

これを 加法定理 (確か高校の数Aで習ったが忘れていた)を使って式を変形します😎。

これをベクトル行列の表記に直すと、

あら不思議、回転変換の式が導けました!🙌

これをどう使う?

せっかく回転の式を覚えたので、次回は、回転の式を使って アナログ時計 を表現してみたいと思います。

やっぱり、学校の勉強みたいにひたすら覚えるだけなのはつまらないのでね。

今回は短いですが、ここまでにしようと思います。

線形代数の基本的な計算(2) ~内積、対角行列、拡大変換~

前回は、ベクトル、行列の基本的な計算方法について記載しましたが、

内積の計算方法さえわかれば、行列の基本的な計算ができることが分かりました。

今回は、前回保留にしていた 内積 の意味を見出していこうと思います。

内積を使ってベクトルの成分を取り出す

今回は、点

 p_1=(1, 2)^T

のベクトルを例にとって、

内積を使って何ができるか見ていきたいと思います。

f:id:fugafigs:20190330161436p:plain:w300

まず、点p1 を図示していくと、上記のような感じになります。

例1 : ベクトルからXとYのそれぞれの成分をスカラーとして取り出してみる。

X成分の場合

f:id:fugafigs:20190330145458p:plain:w300

これは、係数として

 a_x=(1, 0)

をかけてあげれば、Xの成分 + Yの成分*0 になるので、結果的にX成分が残る ことになります。

式として表現すると下記になります。

 p_x=a_x \cdot p_1=1

Y成分の場合

f:id:fugafigs:20190331002124p:plain:w300

これは、先ほどと逆で、Y成分側を1にしたベクトルをかけてあげれば、Y成分が残ることになります。

 a_y=(0, 1)

式として表現すると下記になります。

 p_y=a_y \cdot p_1=2

例2: 成分を整数倍する

f:id:fugafigs:20190330150455p:plain:w300

試しにX成分を2倍しました。これは、

 a_x=(2, 0)

整数倍したい成分のところに倍数を入れるだけです。簡単ですね。

式として表現すると下記になります。

 p_x=a_x \cdot p_1=2

例3: Xを3倍して、Yを2倍にする(そして、ベクトルで結果を返す)

f:id:fugafigs:20190330152737p:plain:w300

これは、例1例2の組み合わせで表現できます。

式として表現すると下記になります。


A=\begin{pmatrix}
3 & 0\\\
0 & 2
\end{pmatrix}

 p=A \cdot p_1=(3,4)^T

Xの成分に3倍するベクトルYの成分を2倍するベクトル を行列にしてドット積をとると、

各成分に対する操作を合成できて、結果がそれぞれの要素となるベクトルになっています!

ちなみに、この結果をグラフでみると次のようになります。

f:id:fugafigs:20190330153444p:plain:w300

ちゃんと、座標が大きくなっていますね。 Aという行列を掛けるという操作が座標を拡大 に対応していることが分かりました。

ここで出てきた、Aのように行列の斜めの成分以外がゼロになっている行列を 対角行列 というそうです。

対角行列を使って、長方形を拡大する

先ほどは、点Pを拡大してみましたが、今度は点の集合体である長方形を拡大してみようと思います。

f:id:fugafigs:20190330155736p:plain:w300

試しに上図のような長方形を拡大しようと思います。長方形は、次のような4つの点のベクトルの集まりです。

 p_1=(1, 2)^T
 p_2=(0, 2)^T
 p_3=(0, 0)^T
 p_4=(1, 0)^T

これは、行列としても表現できます。


B=\begin{pmatrix}
1 & 0 & 0 & 1\\\
2 & 2 & 0 & 0
\end{pmatrix}

上のような感じ。(Bとしたのは、Boxを意図して)

先ほどの拡大行列を掛けると、拡大行列がそれぞれの点に対して分配されるので見事、長方形が拡大されます。

f:id:fugafigs:20190330155953p:plain:w300

グラフで見ると、下図のようになります。

f:id:fugafigs:20190331002651p:plain:w300

ちょっとだけ、これを抽象化すると

f:id:fugafigs:20190330160405p:plain:w400

点の集まりであるB1という長方形拡大行列であるAを掛けると、拡大後の長方形であるB2が得られるという感じになります。

これは、すごい。!

個別に式を作っていたら、このような簡潔な式にはならないと思いますが、これが 線形代数の表現力 の賜物なのかと思いました。😁

まとめ

  • まずは、内積が有効活用できる事が分かってよかったと思います。

  • ベクトルの内積とそれを発展させた行列のドット積を使う事で、座標の拡大という操作を行う 事ができました。

  • 座標の拡大は単一座標に対してだけでなく、複数の座標群に対してまとめて適用する 事もできました。

  • 抽象的な視点で見ると、ベクトルや行列を使うことで、複数の要素からなる数値の集合体に意味を与えて、まとめて操作することができることが分かった点も非常によかったと思います。

個人的な感想として線形代数を学んだ事で今まで数学に対して持っていた知識が手続き型プログラミングレベル ぐらいだったのが、構造化プログラミングレベル ぐらいにバージョンアップした感じがして嬉しいです。😄

次回は、今回の発展系として 回転行列 と、(可能であれば)ベクトルの次元を増やして 3次元の座標を扱ってみたい と思います。

線形代数の基本的な計算(1) ~ベクトルと行列の基本的な計算方法~

今回は、前回から1週間後の更新になりました。

最近は休日になるのが楽しみになっている状況です。

前回は、ベクトル・行列はプログラムにおける複合的なデータ構造(配列やハッシュ)と近しい という(個人的な理解)ところまで

話を進めてきました。今回は、それらの表現を使ってどんな操作ができるのか見ていきたいと思います。

工学基礎 はじめての線形代数学 (KS理工学専門書)

工学基礎 はじめての線形代数学 (KS理工学専門書)

ちなみに、僕はこの本を使って勉強していて、現状は5章まで解き進めた状態で、このブログを綴っています。

スカラー、ベクトル、行列の基本的な演算

まず、ベクトル行列の計算方法ですが、今読み進めていたところまでを振り返ると

最初は、まず次のことが理解できていれば問題ないと感じました。

  • (1)スカラーとベクトルの演算
  • (2)ベクトルとベクトルの演算
  • (3)ベクトルと行列の演算
  • (4)行列と行列の演算

いろいろな計算が出て来るのですが、

ベクトル行列の計算は、内積の計算を中心に計算方法が組み立てられている ように思えましたので、

その点を図解して説明していきたいと思います。

1. スカラー * ベクトル

f:id:fugafigs:20190330122246p:plain:w300

まず、スカラー * ベクトルですが、これは スカラーの値をベクトルの各要素に分配していく イメージです。これは簡単ですね!

2. ベクトル * ベクトル (内積 )

f:id:fugafigs:20190330134959p:plain:w300

次に、こいつがかなり重要な概念な 内積 ですが、

計算自体は簡単で、ベクトルのそれぞれの要素を掛け算して、足したもの です。

それぞれの要素を足し合わせて、1つの要素にするので、内積の結果はベクトルではなく スカラー値 になります。

ちなみに、ベクトルの要素数(次元)があっていないと内積という演算そのものができない

仕様となっておりますw。ご注意ください!

内積は、ドット積とも言うそうです。

ベクトルの 内積という操作がどういう意味であるか については重要な概念なので、

次回1回かけて掘り下げていこうかと思います。

3. ベクトル * 行列

f:id:fugafigs:20190330134709p:plain:w300

次は、ベクトルと行列のドット積となります。

これは、上の図で見た通り、左側ベクトルを右側のそれぞれの縦ベクトルに分配して内積をとっていくイメージとなります。

内積の計算イメージをしっかり頭に入れておけば、そんなに難しくないはずです。

結果は、スカラー値が横に2つ並んだ結果になります。

4. 行列 * ベクトル

f:id:fugafigs:20190330135218p:plain:w300

次は、行列とベクトルのドット積ですが、先ほどと考え方はほとんど一緒です。

左側の横ベクトルから、右側の縦ベクトルに対して内積をとっていく イメージとなります。

結果は、要素数が2(つまり、2次元)のベクトルが1つとなりました。

5. 行列 * 行列

f:id:fugafigs:20190330125918p:plain:w300

これは、見ればわかる通り、3と4で出てきた演算の組み合わせですね。

4つのベクトルの内積を縦横に分配しているとみることもできます。

結果は、4x4の行列、(要素数が2のベクトルが2つ並んでいる)となります。

高校で習ったときは、やたら複雑な計算だと思っていましたが、

4つのベクトルが並んでいると見れれば、案外簡単だと思いました。(計算はそれなりにめんどくさいですが、)

まとめ

今回は、計算方法を図解して説明しました。

次回は、今回覚えた計算が実際に何の役に立つのかについて、見ていきたいと思います。