<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>numpy - まったり勉強ノート</title>
	<atom:link href="https://www.mattari-benkyo-note.com/tag/numpy/feed/" rel="self" type="application/rss+xml" />
	<link>https://www.mattari-benkyo-note.com</link>
	<description>shuの日々の勉強まとめ</description>
	<lastBuildDate>Fri, 30 Dec 2022 03:35:03 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.8.3</generator>
<site xmlns="com-wordpress:feed-additions:1">189243286</site>	<item>
		<title>生まれたばかりの息子をもっと理解したいので育児記録アプリの「ぴよログ」の記録を解析する(ファイル読み込み編)</title>
		<link>https://www.mattari-benkyo-note.com/2022/12/30/piyolog-reader/</link>
					<comments>https://www.mattari-benkyo-note.com/2022/12/30/piyolog-reader/#respond</comments>
		
		<dc:creator><![CDATA[Shuji Suzuki (shu)]]></dc:creator>
		<pubDate>Fri, 30 Dec 2022 03:33:22 +0000</pubDate>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[numpy]]></category>
		<category><![CDATA[python]]></category>
		<guid isPermaLink="false">https://www.mattari-benkyo-note.com/?p=1500</guid>

					<description><![CDATA[<p>Twitterで報告しましたが、11月に息子が生まれました。そして、ちょうど今週、妻が里帰りから帰ってきました。なので、ついに私も本格的に育児を開始したのですが、我が息子ながら何がどうなっているのか何もわからん！というこ [&#8230;]</p>
<p>The post <a href="https://www.mattari-benkyo-note.com/2022/12/30/piyolog-reader/">生まれたばかりの息子をもっと理解したいので育児記録アプリの「ぴよログ」の記録を解析する(ファイル読み込み編)</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>Twitterで報告しましたが、11月に息子が生まれました。そして、ちょうど今週、妻が里帰りから帰ってきました。なので、ついに私も本格的に育児を開始したのですが、我が息子ながら何がどうなっているのか何もわからん！ということで息子の理解度を高めるために使っていた育児記録アプリであるぴよログの記録データを解析してみようと思い立ちました。</p>



<p>今回はその第一弾としてぴよログの記録のデータ読み込みについてとそれを使った簡単なデータ解析についての記事になります。</p>



<p>ちなみに今回のぴよログのファイル読み込みライブラリはここに公開しています。</p>



<p><a href="https://github.com/shu65/piyolog_reader">https://github.com/shu65/piyolog_reader</a></p>



<h1 class="wp-block-heading">育児記録アプリのぴよログとは？</h1>



<p>ぴよログとは育児中のミルクをあげた時間や睡眠時間、うんちやおしっこの時間などいろいろなことを記録して、共有できるスマホアプリです。詳しくは以下のサイトを見てください。</p>



<p><a href="http://www.piyolog.com/" target="_blank" rel="noopener" title="">http://www.piyolog.com/</a></p>



<p>妻が出産前に調べて「これがよさそう！」ということで生まれてすぐから使い始めています。ぴよログに記録をとることで、いつミルクをあげたのかなどがスマホを見ればわかるので、妻と育児を交代するタイミングでも口頭で「〇時にミルクをあげたから次は〇時にお願い」みたいなやり取りが発生しないためスムーズに交代できるのですごく便利です。そんなぴよログですが、記録を出力することができます。</p>



<p>出力されるデータはCSVなどの構造化データではなく、ある程度規則的な形式のテキストで出力されます。おそらくEvernoteなど別のノート系のアプリに張り付けられるようにするための機能なのではないかと思っています。本来の用途とは少し違う気もしますが、テキストで出力されているので頑張ってパースすればデータ解析までもっていけそうだなと思いました。実際、調べてみるとQiitaで以下のようにぴよログの記録を読み込む記事を書いている方がすでにいます。</p>



<p><a href="https://qiita.com/yakipudding/items/11223f12a843e4399300">https://qiita.com/yakipudding/items/11223f12a843e4399300</a></p>



<p>簡単な読み込み関数はこの記事でも紹介されています。ただ、もう少し自分がデータ解析しやすい形で出力したいなということで今回は別の読み込み関数を書きました。</p>



<h1 class="wp-block-heading">ぴよログのファイルを出力してデータ解析する</h1>



<p>ここからはぴよログの記録をファイル出力して簡単なデータ解析するまでの流れを順に説明します。</p>



<h2 class="wp-block-heading">ぴよログの記録のファイル出力</h2>



<p>まず、ぴよログの記録の出力方法について説明します。</p>



<p>やり方としては「メニュー」→「記録の出力」→「データのエクスポート」で１日もしくは１か月分のデータを別のアプリに共有することができます。</p>



<p>後ほどGoogle Colabで解析しやすいようにこの記事では１か月分のデータをGoogle Driveにあげたという仮定で説明していきます。</p>



<h2 class="wp-block-heading">ぴよログのデータ読み込みの準備</h2>



<p>さて実際のデータ読み込みの前の準備です。まずは私の書いたぴよログの記録の読み込みライブラリのコードのインストールです。2022/12/30現在は以下のようにしてインストールするようになっています</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-file="install_piyolog_reader" data-lang="Bash"><code>pip install git+https://github.com/shu65/piyolog_reader.git</code></pre></div>



<p>ちなみに2022/12/30現在、Google Colabで解析する場合は <code>pandas</code> のパッケージが古く、timezoneの扱い周りでエラーが出てしまっているようなので以下のように最新版にあげておいたほうが良いと思います。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-file="install_pandas" data-lang="Bash"><code>pip install -U pandas</code></pre></div>



<p>次にGoogle ColabでGoogle Driveにアクセスできるようにマウントします。コードとしては以下の通りです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="gdrive" data-lang="Python"><code>from google.colab import drive
drive.mount(&quot;/gdrive&quot;)</code></pre></div>



<p>このコードを実行するとGoogle ColabからGoogle Driveにアクセスするための認証がでてきますので、許可します。</p>



<p>事前にGoogle Driveの <code>notes/piyolog</code> というディレクトリにぴよログのデータを入れておいたとすると、以下のように<code>ls</code> コマンドで <code>/gdrive/MyDrive/notes/piyolog/</code> にアクセスするとぴよログの記録データがあります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-bash" data-file="ls" data-lang="Bash"><code>!ls /gdrive/MyDrive/notes/piyolog/</code></pre></div>



<p>この<code>ls</code> の出力はこのような形になります。</p>



<pre class="wp-block-code"><code>【ぴよログ】2022年11月.txt  【ぴよログ】2022年12月.txt</code></pre>



<p>これで前準備としては終わりです。</p>



<h2 class="wp-block-heading">ぴよログの記録の読み込み</h2>



<p>さて、ここから本番のぴよログの記録の読み込みを行っていきます。今回はディレクトリに入っている全記録を読み込むようにします。コードとしては以下の通りです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="read_piyolog_text" data-lang="Python"><code>import glob
import piyolog_reader

log_pathes = glob.glob(&quot;/gdrive/MyDrive/notes/piyolog/*&quot;)
dfs = piyolog_reader.read_texts(log_pathes)</code></pre></div>



<p><code>dfs</code> の中に記録の情報を<code>pandas</code>の<code>DataFrame</code> を使って種類ごとに分けて格納しています。このように種類ごとに分けた理由としてはデータベースのスキーマ設計の正規化を意識してやりました。このあたりの仕様はあとで解析して使いにくかったら変える可能性があるので細かくは説明しませんが、記録の日時と記録の種類は　<code>dfs["event"]</code> という<code>DataFrame</code> にいれています。printするとこのようになります。</p>



<pre class="wp-block-code"><code>                      timestamp event
index                                
0     2022-12-01 01:00:00+09:00   起きる
1     2022-12-01 01:05:00+09:00  おしっこ
2     2022-12-01 01:05:00+09:00   うんち
3     2022-12-01 01:10:00+09:00   搾母乳
4     2022-12-01 01:15:00+09:00   ミルク
...                         ...   ...
1990  2022-11-30 21:35:00+09:00   起きる
1991  2022-11-30 21:40:00+09:00    母乳
1992  2022-11-30 21:40:00+09:00  おしっこ
1993  2022-11-30 21:55:00+09:00   ミルク
1994  2022-11-30 22:30:00+09:00    寝る

&#091;1995 rows x 2 columns]</code></pre>



<p>indexに記録ごとに一意の番号をふってあるので、他の記録をまとめた別の<code>DataFrame</code>とマージすると日時とその記録の情報のデータフレームを簡単に得ることができます。例えば体重の <code>DataFrame</code> は <code>dfs["weight"]</code> にまとまっているので、それとマージすると以下のようになります。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="merge" data-lang="Python"><code>import pandas as pd

weight_df = pd.merge(dfs[&quot;event&quot;], dfs[&quot;weight&quot;], left_index=True, right_index=True)
print(weight_df)</code></pre></div>



<p>出力は以下の通りです。</p>



<pre class="wp-block-code"><code>                      timestamp event  weight
index                                        
41    2022-12-01 17:40:00+09:00    体重    3.00
329   2022-12-07 09:40:00+09:00    体重    3.25
757   2022-12-16 09:10:00+09:00    体重    3.69
1347  2022-11-15 13:35:00+09:00    体重    2.76
1725  2022-11-25 16:10:00+09:00    体重    2.79</code></pre>



<p>これでぴよログのデータの読み込みをすることができました。</p>



<h2 class="wp-block-heading">ぴよログのデータを使った簡単なデータ解析</h2>



<p>さて、ぴよログのデータを使って簡単なデータの解析も行う例も示しておこうと思います。今回は以前に息子が順調に育っているのか心配になって体重の変化量がどの程度なのか？を調べたので似たようなことをぴよログの記録とPythonを使って簡単にやってみる例を示します。</p>



<p>まず、生まれた日と体重測定日の差の日数を以下のように先ほどの<code>weight_df</code>に追加します。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="compute_day" data-lang="Python"><code>birthday_str = &#39;2022-11-15T00:00&#39;
birthday = pd.to_datetime(birthday_str)
birthday = birthday.tz_localize(&#39;Asia/Tokyo&#39;)

weight_df[&quot;day&quot;] = (weight_df[&quot;timestamp&quot;] - birthday).dt.days
print(weight_df)</code></pre></div>



<p>出力としては以下の通りです。</p>



<pre class="wp-block-code"><code>                      timestamp event  weight  day
index                                             
41    2022-12-01 17:40:00+09:00    体重    3.00   16
329   2022-12-07 09:40:00+09:00    体重    3.25   22
757   2022-12-16 09:10:00+09:00    体重    3.69   31
1347  2022-11-15 13:35:00+09:00    体重    2.76    0
1725  2022-11-25 16:10:00+09:00    体重    2.79   10</code></pre>



<p>あとは<code>scikit-learn</code> で線形モデルにフィッティングさせて <code>day</code> の重みを見ると大体、一日の体重変化量が分かります。コードとしては以下の通り。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="linear_model_fitting" data-lang="Python"><code>import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error

model = LinearRegression(positive=True)
model.fit(X=weight_df[[&quot;day&quot;]].values, y=weight_df[&quot;weight&quot;])
y_pred_train = model.predict(X=weight_df[[&quot;day&quot;]].values)
mae_train = mean_absolute_error(y_pred=y_pred_train, y_true=weight_df[&quot;weight&quot;])
rmse_train = np.sqrt(mean_squared_error(y_pred=y_pred_train, y_true=weight_df[&quot;weight&quot;]))

print(&quot;coef&quot;, model.coef_, &quot;intercept&quot;, model.intercept_, &quot;mae_train&quot;, mae_train, &quot;rmse_train&quot;, rmse_train)</code></pre></div>



<p>出力としては以下の通り。</p>



<pre class="wp-block-code"><code>coef &#091;0.03083936] intercept 2.610738060781476 mae_train 0.10900144717800293 rmse_train 0.11535210050611226</code></pre>



<p><code>coef</code> の一つ目の要素が<code>day</code> に対する重みで約0.03となっています。単位はkgなので、大体30gくらい１日に増えていることがわかります。体重が増加してなかったらヤバイそうなので、その点は大丈夫そうということがわかりました。ついでに0日から現在データとしてあるところから10日ほど未来まで体重を予測してみた結果と実際の測定値を図を以下に示します。</p>


<div class="wp-block-image">
<figure class="aligncenter size-full"><img fetchpriority="high" decoding="async" width="386" height="262" src="https://www.mattari-benkyo-note.com/wp-content/uploads/2022/12/weight-1.png" alt="" class="wp-image-1503" srcset="https://www.mattari-benkyo-note.com/wp-content/uploads/2022/12/weight-1.png 386w, https://www.mattari-benkyo-note.com/wp-content/uploads/2022/12/weight-1-300x204.png 300w" sizes="(max-width: 386px) 100vw, 386px" /><figcaption class="wp-element-caption">実際の体重と予測した体重</figcaption></figure></div>


<p>図の描画のコードは以下の通りです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-lang="Python"><code>%matplotlib inline
import numpy as np

import matplotlib.pyplot as plt

plt.scatter(weight_df[&quot;day&quot;].values, weight_df[&quot;weight&quot;].values, label=&quot;actual value&quot;)

x = np.linspace(0, weight_df[&quot;day&quot;].max() + 10.0, 100).reshape((-1, 1))
weight_pred = model.predict(x)
plt.plot(x[:, 0], weight_pred, label=&quot;predicted value&quot;)

plt.xlabel(&quot;days&quot;)
plt.ylabel(&quot;wegiht&quot;)
plt.legend()
plt.grid()</code></pre></div>



<h1 class="wp-block-heading">おわりに </h1>



<p>今回は生まれたばかりの息子をもっと理解したいという思いから育児記録アプリのぴよログの記録データを読み込んで簡単な解析をした結果を紹介しました。まだまだ取り組みとしては始めたばかりなので、これからデータをいろいろ見てみようと思います。もし面白そうなネタがあったらまた記事にしようと思います。</p><p>The post <a href="https://www.mattari-benkyo-note.com/2022/12/30/piyolog-reader/">生まれたばかりの息子をもっと理解したいので育児記録アプリの「ぴよログ」の記録を解析する(ファイル読み込み編)</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.mattari-benkyo-note.com/2022/12/30/piyolog-reader/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1500</post-id>	</item>
		<item>
		<title>[勉強ノート] 「線形計算の数理」 7.7.1 Arnoldi法</title>
		<link>https://www.mattari-benkyo-note.com/2022/04/04/theoretical-numerical-linear-algebra-arnoldi-method/</link>
					<comments>https://www.mattari-benkyo-note.com/2022/04/04/theoretical-numerical-linear-algebra-arnoldi-method/#respond</comments>
		
		<dc:creator><![CDATA[Shuji Suzuki (shu)]]></dc:creator>
		<pubDate>Sun, 03 Apr 2022 23:48:06 +0000</pubDate>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[numpy]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[固有値問題]]></category>
		<category><![CDATA[線形計算の数理]]></category>
		<guid isPermaLink="false">https://www.mattari-benkyo-note.com/?p=1299</guid>

					<description><![CDATA[<p>前回に引き続き線形計算の数理の固有値問題で紹介されていたアルゴリズムについての解説になります。今日はその中のArnoldi法についての説明です。 このArnoldi法をはじめとしたRayleigh-Ritzの技法ベースの [&#8230;]</p>
<p>The post <a href="https://www.mattari-benkyo-note.com/2022/04/04/theoretical-numerical-linear-algebra-arnoldi-method/">[勉強ノート] 「線形計算の数理」 7.7.1 Arnoldi法</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>前回に引き続き線形計算の数理の固有値問題で紹介されていたアルゴリズムについての解説になります。今日はその中のArnoldi法についての説明です。</p>



<p>このArnoldi法をはじめとしたRayleigh-Ritzの技法ベースの手法は個人的には初見で「どれもアルゴリズムはわかるけど、なんでそれでうまくいくのさっぱり」という感じだったので、本の解説の中で自分のわからなかった部分を補足しながら説明をしていければと思います。</p>



<p>Pythonの実装はこちらにあります。<br><a href="https://github.com/shu65/theoretical-numerical-linear-algebra/blob/main/%E7%B7%9A%E5%BD%A2%E8%A8%88%E7%AE%97%E3%81%AE%E6%95%B0%E7%90%86_7_7_1_Arnoldi%E6%B3%95.ipynb" title="">https://github.com/shu65/theoretical-numerical-linear-algebra/blob/main/%E7%B7%9A%E5%BD%A2%E8%A8%88%E7%AE%97%E3%81%AE%E6%95%B0%E7%90%86_7_7_1_Arnoldi%E6%B3%95.ipynb</a></p>



<h2 class="wp-block-heading">Arnoldi法とは</h2>



<p>ある次元の不変部分空間\(E\)の近似空間\(F\)が与えられたとき、\(E\)に対応する固有値と固有ベクトルの近似値を近似空間\(F\)を使って求めるRayleigh-Ritzの技法というものがあります。</p>



<p>このRayleigh-Ritzの技法ベースの手法の一つで、Krylov部分空間と呼ばれる空間で近似空間を作る手法がArnoldi法です。大規模な行列の際、QR法などの固有値、固有ベクトルを求めるアルゴリズムを直接適用すると計算量や必要なメモリサイズの観点で適用が難しい場合があります。このような場合にArnoldi法は利用されます。</p>



<p>Arnoldi法のアルゴリズムやRayleigh-Ritzの技法、Krylov部分空間についてはこの後より詳しく説明していきます。</p>



<h2 class="wp-block-heading">Arnoldi法のアルゴリズム</h2>



<p>まずはArnoldi法のアルゴリズム説明します。本ではアルゴリズム全体についてちゃんと書かれておらず、いろいろなサイトを調べましたが、ちょっとずつ違うので、今回は近似固有値、近似固有ベクトルまで求めるシンプルなものを説明します。</p>



<p>Arnoldi法は入力として以下のものを取ります。</p>



<ul class="wp-block-list"><li>\( A\): 固有値、固有ベクトルを計算する複素\( n \)次正方行列</li><li>\(\boldsymbol{x}_0\): \(\boldsymbol{x}_0 \ne 0\)の\( n \)次元ベクトル</li><li>\(m\): 生成する部分空間の次元数。\(m \le n\)</li></ul>



<p>これらを入力として以下のように計算していきます。</p>



<ol class="wp-block-list"><li>\( A \) と \(\boldsymbol{x}_0\)を入力に取る。</li><li>\(H\)をすべての要素が0の行列として初期化する。</li><li>\(\boldsymbol{v}_0 := \boldsymbol{x}_0/||\boldsymbol{x}_0|| \)</li><li>\(k\)を\(0\) から \(m-1\)まで以下を繰り返す。<ol><li>\(\boldsymbol{w} := A\boldsymbol{v}_k\)</li><li>\(i\)を\(0\) から \(k\)まで以下を繰り返す。<ol><li>\(H_{i,k} := \boldsymbol{v}_i^H \boldsymbol{w}\)</li><li>\(\boldsymbol{w} := \boldsymbol{w} &#8211; H_{i,k} \boldsymbol{v}_i\)</li></ol></li><li>\(H_{k+1,k} := || \boldsymbol{w} ||\)</li><li>\(\boldsymbol{v}_{k+1} := \boldsymbol{w} / H_{k+1,k}\)</li></ol></li><li>\(H\)の固有値と固有ベクトルを求め、それぞれをRitz値(\(\theta_0, &#8230;, \theta_m\))とRitzベクトル(\( \boldsymbol{y}_0, &#8230;, \boldsymbol{y}_m\))とする</li><li>\( V = [\boldsymbol{v_0}, &#8230;, \boldsymbol{v}_m]\) とし、以下の計算を(k)が(0) から (m)まで繰り返す。<ol><li>\(\boldsymbol{z}_k = \boldsymbol{V}y_k\)</li></ol></li><li>Ritz値(\(\theta_0, &#8230;, \theta_m\))を\(A\)の固有値の近似値、\(\boldsymbol{z}_k\)を\(A\)の固有値の近似固有ベクトルとして出力する</li></ol>



<p>1-4は正規直交基底を生成しつつ、\(H\)を作っているためわかりにくいですが、正規直交基底を作っている部分は\( \boldsymbol{x}_0, A\boldsymbol{x}_0, A^2\boldsymbol{x}_0, &#8230;, A^{m-1}\boldsymbol{x}_0\) に対してグラムシュミットの正規直交化法を適用しているだけです。</p>



<p>なので、アルゴリズム自体は難しいものではないと思っています。</p>



<p>ちなみにこのアルゴリズムで\(H\)は対角成分の一つ下の要素も値が埋まっている上三角行列であるHessenberg行列になります。この際、アルゴリズムの4.3.行目が対角成分の一つ下の要素を代入しているところになります。ここで、この4.3.行目で代入する値は、ここまで生成した正規直交基底\(\boldsymbol{v}_0, &#8230;, \boldsymbol{v}_k\)で\(A^k\boldsymbol{x}_0\)を表しきれなかったベクトルのノルムを代入していると見ることができます。実はこのノルムが0に近ければ、ここまでで生成した正規直交基底\(\boldsymbol{v}_0, &#8230;, \boldsymbol{v}_k\)で\(A^k\boldsymbol{x}_0\)を表現できるとみることができ、これ以降のKrylov列の要素も表現できます。このため、アルゴリズムのバリエーションとしては4.3.行目のノルムの値をチェックして、0に近ければそこで正規直交基底の生成をストップするというものもあります。</p>



<p>このアルゴリズムにおいて\(m\)は何個の固有値、固有ベクトルを求めるかや近似精度をどこまで上げるか？など重要なものを決定づけています。ただ、実際にArnoldi法でいろいろ実験をしてみればわかりますが、\(m\)を事前に決めておくのは難しく、実用を考えると後ほど紹介する残差ベクトルを使って、近似空間を拡大するかどうか判断するようなアルゴリズムのほうが使い勝手は良いかと思います。</p>



<p>ちなみに1-4までの処理を指してArnoldi法と言っているサイトや講義資料もありました。なので、一体どこからどこまでの処理がArnoldi法かはわからないですが、今回は固有値を求めるところまでをArnoldi法と呼びます。</p>



<h2 class="wp-block-heading">Arnoldi法によってなぜ固有値、固有ベクトルの近似値が求まるのか？</h2>



<p>先ほど説明した通り、Arnoldi法のアルゴリズム自体は難しいことをしてないのですが、それがなぜうまくいくのかは本を読んでもわからないことが多かったので詳しく説明していきます。</p>



<h3 class="wp-block-heading">Rayleigh-Ritzの技法 / 射影法</h3>



<p>Arnoldi法がなぜうまくいくかを説明するために、Rayleigh-Ritzの技法（または射影法と呼ぶ場合もあります)をまずは説明していきます。</p>



<p>Rayleigh-Ritzの技法は不変部分空間\(E\)の近似空間\(F\)が与えられたとき、\(E\)に対する固有値、固有ベクトルの近似値を\(F\)を使って求める手法全般を指します。</p>



<p>もう少し具体的に説明していきます。ある\(m\)次元不変部分空間\(E\)を\(A\)の\(m\)本の固有ベクトル \(\boldsymbol{z}_{i_1}, &#8230; ,\boldsymbol{z}_{i_m}\)で張られた空間であるとします。ここでポイントですが、\(E\)は\(A\)のすべての固有ベクトルで張られた空間である必要はなく、一部の固有ベクトルだけで張られた空間であることを注意してください。また、\(\boldsymbol{z}_{i_1}, &#8230;, \boldsymbol{z}_{i_m}\)に対応する\(A\)の固有値を\(\lambda_{i_1}, &#8230; \lambda_{i_m}\)とします。このとき、固有値、固有ベクトルの性質から以下の関係が成り立ちます。</p>



<p>$$ \begin{align*} <br>AZ_m = Z_m\Lambda_m \tag{7.42.1}<br>\end{align*} $$</p>



<p>ただし、\(Z_m = [\boldsymbol{z}_{i_1}, &#8230; \boldsymbol{z}_{i_m}]\)、\(\Lambda_m = \text{diag}(\lambda_{i_1}, &#8230; \lambda_{i_m})\)です。</p>



<p>次に\(E\)の任意の正規直交基底\( \boldsymbol{u}_1, &#8230; , \boldsymbol{u}_m\)をとって、\(U=[\boldsymbol{u}_{1}, &#8230; \boldsymbol{u}_{m}]\)とします。\( \boldsymbol{u}_1, &#8230; , \boldsymbol{u}_m\)はAの固有ベクトルとは別もので大丈夫です。\(U\)は正規直交基底なので以下が成り立ちます。</p>



<p>$$ \begin{align*} <br>U^HU = I_m \tag{7.42.2}<br>\end{align*} $$</p>



<p>この\(U\)を使うと\(Z_m\)はある\(m\)次行列\(Y\)を使って以下のように書けます。</p>



<p>$$ \begin{align*} <br>Z_m = UY \tag{7.42.3}<br>\end{align*} $$</p>



<p>これは単純に\(Z_m\)を\(\boldsymbol{u}_{1}, &#8230; \boldsymbol{u}_{m}\)を使って表すことができることを指しています。</p>



<p>そして、式(7.42.1)の左から\(U^H\)を掛け、式(7.42.2)、(7.42.3)を使って変形すると以下のようになります。</p>



<p>$$ \begin{align*} <br>U^HAZ_m =&amp; U^HZ_m\Lambda_m \\<br>U^HAUY =&amp; U^HUY\Lambda_m \\<br>U^HAUY =&amp; I_mY\Lambda_m \\<br>U^HAUY =&amp; Y\Lambda_m<br>\tag{7.42}<br>\end{align*} $$</p>



<p>ここで分かりやすいように\(U^HAU=B\)と置くと以下のようになります。</p>



<p>$$ \begin{align*} <br>BY =&amp; Y\Lambda_m<br>\tag{7.42.4}<br>\end{align*} $$</p>



<p>これは\(m\)次行列\(B\)に対する固有値問題の形になっています(\(Y\)の列ベクトルが固有ベクトル、\(\Lambda_m\)の対角成分が固有値)。ここで\(m \le n\)であることを思い出すと、\(A\)よりも小さい行列\(B\)の固有値を求めれば\(A\)の固有値の一部がわかることを示しています。また、\(A\)の固有ベクトルに関しても、式(7.42.3)から簡単に求めることができます。</p>



<p>ここまでは不変部分空間\(E\)を使っていましたが、実際には\(E\)は未知です。なので、\(E\)を近似する部分空間\(F\)を適当に選ぶことにします。\(F\)の正規直交基底\(\boldsymbol{v}_{1}, &#8230; \boldsymbol{v}_{m}\)を並べた行列\(V=[\boldsymbol{v}_{1}, &#8230; \boldsymbol{v}_{m}]\)とします。この\(V\)を使って式(7.42)の\(U\)を\(V\)に置き換えたような以下の式を考えます。</p>



<p>$$ \begin{align*} <br>V^HAV\boldsymbol{y} =&amp; \theta\boldsymbol{y}<br>\tag{7.42}<br>\end{align*} $$</p>



<p>これは\(m\)次行列\(V^HAV\)の固有値問題になっています。この\(V^HAV\)の固有値問題を何等かの方法(QR法など)で解き、\(\theta\)を\(A\)の固有値の近似値、\(\boldsymbol{z} = V\boldsymbol{y}\)を\(A\)の固有ベクトルの近似とします。</p>



<p>これをRayleigh-Ritzの技法といい、近似固有値\(\theta\)はRitz値、近似固有ベクトル\(\boldsymbol{z}\)をRitzベクトルといいます。</p>



<p>通常は\(m\)を事前に決めておくのではなく、以下の残差ベクトル\(\boldsymbol{r}\)を導入して近似部分空間\(F\)を拡大するかどうかを判断します。</p>



<p>$$ \begin{align*} <br>\boldsymbol{r} =&amp; A\boldsymbol{z} &#8211; \theta\boldsymbol{z}<br>\tag{7.43.1}<br>\end{align*} $$</p>



<p>\(\theta\)と\(\boldsymbol{z}\)がAの固有値、固有ベクトルに十分近ければ\(r\)は十分小さくなります。このため、\(r\)が十分小さければ\(\theta\)と\(\boldsymbol{z}\)を採用し、\(r\)が大きいときは近似部分空間\(F\)を拡大します。</p>



<p>ちなみにRayleigh-Ritzの技法は近似部分空間をどういう風に作るかは特に言及されていません。ただ、このRayleigh-Ritzの技法ではどういう部分空間を作るかによって、\(A\)の固有値のどの部分の固有値を近似するのか（絶対値が最大となる固有値など）や近似精度などが決定いします。このため、この近似部分空間をどう作るかは重要なポイントになります。</p>



<p>Arnoldi法はこの近似部分空間としてKrylov部分空間を利用している手法になります。また、Arnoldi法以外のRayleigh-Ritzの技法ベースの手法としては以下のものがあります。</p>



<ul class="wp-block-list"><li>Lanczos法: エルミート行列に対するArnoldi法</li><li>Jacobi-Davidson法: Krylov部分空間を使わずに部分空間の列を構成する手法</li></ul>



<h3 class="wp-block-heading">Krylov部分空間</h3>



<p>ここではArnoldi法で利用されているKrylov部分空間について説明します。</p>



<p>ベクトル\(\boldsymbol{x}\)に行列\(A\)のべき乗掛けて生成される列 \( \boldsymbol{x}, A\boldsymbol{x}, A^2\boldsymbol{x},&#8230;\) (Krylov列と言います)の最初の\(k\)個のベクトルの張る部分空間</p>



<p>$$ \begin{align*} <br>\mathcal{K}_k(A, \boldsymbol{x}) = \text{span}(\boldsymbol{x}, A\boldsymbol{x}, A^2\boldsymbol{x}, &#8230;, A^{k-1}\boldsymbol{x})<br>\tag{4.94}<br>\end{align*} $$</p>



<p>を\(k\)次のKrylov部分空間と呼びます。</p>



<p>Krylov部分空間の特徴として本で紹介されていることとしてはKrylov部分空間は\(\boldsymbol{x}\)を含む最小の\(A\)不変部分空間に達するまで単調増加するという特徴があります。(詳しくは「線形計算の数理」の4.3 Krylov部分空間を参照)</p>



<p>これに加え、固有値問題を解くアルゴリズムの一つであるべき乗法ではKrylov列のベクトルを正規化したものが\(A\)の固有ベクトルに収束することを利用していることからわかる通り、Krylov部分空間は\(A\)の固有ベクトル（特に絶対値が最大となる固有値の固有ベクトル）の成分を多く持つような空間を生成することができます。</p>



<p>べき乗法についてはこちらで詳しく紹介していますので、参考にしてみてください。</p>



<figure class="wp-block-embed is-type-wp-embed is-provider-まったり勉強ノート wp-block-embed-まったり勉強ノート"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="rKhLtn7uym"><a href="https://www.mattari-benkyo-note.com/2022/03/24/theoretical-numerical-linear-algebra-power-method/">[勉強ノート] 「線形計算の数理」 7.2.1 べき乗法 基本形</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;[勉強ノート] 「線形計算の数理」 7.2.1 べき乗法 基本形&#8221; &#8212; まったり勉強ノート" src="https://www.mattari-benkyo-note.com/2022/03/24/theoretical-numerical-linear-algebra-power-method/embed/#?secret=GJBXXnK25e#?secret=rKhLtn7uym" data-secret="rKhLtn7uym" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<p>この二つの特性からKrylov部分空間を利用することでArnoldi法は\(A\)の固有値、固有ベクトルを\(A\)の次元よりもはるかに小さい行列を利用して計算することが可能となります。</p>



<p>また、Krylov列からグラムシュミットの正規直交化法によって正規直交基底\(\boldsymbol{v}_{1}, &#8230; \boldsymbol{v}_{m}\)を並べた行列\(V=[\boldsymbol{v}_{1}, &#8230; \boldsymbol{v}_{m}]\)とします。このとき、</p>



<p>$$ \begin{align*} <br>H = V^HAV <br>\tag{4.50}<br>\end{align*} $$</p>



<p>という風に作った\(H\)は\(m \times m\)のHessenberg行列で，上三角行列に下1行を追加した形の行列になることが知られています。Arnoldi法のアルゴリズムでは式(4.50)計算を直接やらずに、Krylov列のグラムシュミットの正規直交化を計算しながら\(H\)の生成も同時にやっています。ちなみに、この\(H\)と\(V\)を求める部分のアルゴリズムを本ではArnoldi算法という名前で紹介しています。<br>この\(H\)はHessenberg行列であるため、行列の対角成分の2つ以下が0になっています。このため、QR法なで固有値を求める際、QR分解で0の部分をスキップするなど高速化が可能となり、固有値を求めるには都合の良い行列になっています。</p>



<p>QR法についてはこちらの記事で紹介していますので、参考にしてみてください。</p>



<figure class="wp-block-embed is-type-wp-embed is-provider-まったり勉強ノート wp-block-embed-まったり勉強ノート"><div class="wp-block-embed__wrapper">
<blockquote class="wp-embedded-content" data-secret="xQCpPw62w6"><a href="https://www.mattari-benkyo-note.com/2022/03/29/theoretical-numerical-linear-algebra-qr-method/">[勉強ノート] 「線形計算の数理」7.4 QR法</a></blockquote><iframe class="wp-embedded-content" sandbox="allow-scripts" security="restricted"  title="&#8220;[勉強ノート] 「線形計算の数理」7.4 QR法&#8221; &#8212; まったり勉強ノート" src="https://www.mattari-benkyo-note.com/2022/03/29/theoretical-numerical-linear-algebra-qr-method/embed/#?secret=dhT00Hc3Bc#?secret=xQCpPw62w6" data-secret="xQCpPw62w6" width="600" height="338" frameborder="0" marginwidth="0" marginheight="0" scrolling="no"></iframe>
</div></figure>



<h2 class="wp-block-heading">Pythonによる実装</h2>



<p>ここからは紹介したアルゴリズムをPythonで実装したものを説明します。</p>



<p>実装は以下の通りです。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="arnoldi_iteration" data-lang="Python"><code>import numpy as np

def arnoldi_method(a, x0, m):
  if m &gt; a.shape[0]:
    raise ValueError
  n = a.shape[0]
  h = np.zeros((m, m))
  v = np.zeros((n, m))

  v0 = x0 / np.linalg.norm(x0)
  v[:, 0] = v0

  k = 0
  for k in range(m):
    k = k
    w = a @ v[:, k]
    for j in range(k+1):
        v_j = v[:, j]
        h[j,k] = v_j.T @ w
        w = w - h[j,k]*v_j

    if k+1 &lt; m:
        h[k+1, k] = np.linalg.norm(w)
        v[:, k+1] = w/h[k+1, k]
  
  ritz_values, ritz_vectors = np.linalg.eig(h)
  eigenvectors = v @ ritz_vectors
  return ritz_values, eigenvectors</code></pre></div>



<p>これを以下のように実行して絶対値最大となる固有値とその固有ベクトル、そのときの残差のノルムを出力してみます。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="arnoldi_iteration_main" data-lang="Python"><code>import numpy as np

np.random.seed(1)
a_matrix = np.random.rand(8, 8)

n = a_matrix.shape[0]
x0 = np.ones(n)
eigenvalues, eigenvectors = arnoldi_method(a_matrix, x0, 4)
orders = np.argsort(-np.abs(eigenvalues))

print(&quot;eigenvalue:&quot;, eigenvalues[orders[0]])
print(&quot;eigenvector:&quot;)
print(eigenvectors[:, orders[0]])
r = a_matrix @ eigenvectors[:, orders[0]] - eigenvalues[orders[0]] * eigenvectors[:, orders[0]]
norm_r = np.linalg.norm(r)
print(&quot;||r||:&quot;, norm_r)</code></pre></div>



<p>この出力は以下のようになります。</p>



<pre class="wp-block-preformatted">eigenvalue: (3.4995258474334907+0j)
eigenvector:
[0.2046176 +0.j 0.38710413+0.j 0.42003848+0.j 0.32821558+0.j
 0.48492918+0.j 0.38776328+0.j 0.18715778+0.j 0.32183775+0.j]
||r||: 0.004572773990371693</pre>



<p>また、\(m\)を変化させると近似固有値、近似固有ベクトルの近似精度が良くなるのでそれも見てみます。以下のように\(m\)を変化させながら、絶対値が最大となる固有値の残差ノルムをプロットすると以下のようになります。</p>



<div class="wp-block-image"><figure class="aligncenter size-full is-resized"><img loading="lazy" decoding="async" src="https://www.mattari-benkyo-note.com/wp-content/uploads/2022/04/norm_r_plot.png" alt="" class="wp-image-1380" width="415" height="275" srcset="https://www.mattari-benkyo-note.com/wp-content/uploads/2022/04/norm_r_plot.png 395w, https://www.mattari-benkyo-note.com/wp-content/uploads/2022/04/norm_r_plot-300x199.png 300w" sizes="auto, (max-width: 415px) 100vw, 415px" /><figcaption>絶対値が最大となる近似固有値の残差ノルム</figcaption></figure></div>



<p>このように\(m\)が大きくなるにつれて残差が0に近づいていくことがわかります。</p>



<h2 class="wp-block-heading">終わりに</h2>



<p>今回はArnoldi法について紹介しました。今回のArnoldi法は読んだだけではわからないことが多く、実際に実装していろいろ値を変えてみてわかることが多かった印象があります。このため、実装まで作ってよかったと考えています。</p><p>The post <a href="https://www.mattari-benkyo-note.com/2022/04/04/theoretical-numerical-linear-algebra-arnoldi-method/">[勉強ノート] 「線形計算の数理」 7.7.1 Arnoldi法</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.mattari-benkyo-note.com/2022/04/04/theoretical-numerical-linear-algebra-arnoldi-method/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">1299</post-id>	</item>
		<item>
		<title>[勉強ノート] 「線形計算の数理」 7.2.1 べき乗法 基本形</title>
		<link>https://www.mattari-benkyo-note.com/2022/03/24/theoretical-numerical-linear-algebra-power-method/</link>
					<comments>https://www.mattari-benkyo-note.com/2022/03/24/theoretical-numerical-linear-algebra-power-method/#respond</comments>
		
		<dc:creator><![CDATA[Shuji Suzuki (shu)]]></dc:creator>
		<pubDate>Wed, 23 Mar 2022 23:37:09 +0000</pubDate>
				<category><![CDATA[プログラミング]]></category>
		<category><![CDATA[numpy]]></category>
		<category><![CDATA[python]]></category>
		<category><![CDATA[固有値問題]]></category>
		<category><![CDATA[線形計算の数理]]></category>
		<guid isPermaLink="false">https://www.mattari-benkyo-note.com/?p=911</guid>

					<description><![CDATA[<p>今日の記事では「線形計算の数理」の7.2.1で紹介されているべき乗法について解説とPythonによる実装を紹介します。基本的には自分があとで見返したときに納得できるように説明を書いたので、かなり基本的なところから書いてあ [&#8230;]</p>
<p>The post <a href="https://www.mattari-benkyo-note.com/2022/03/24/theoretical-numerical-linear-algebra-power-method/">[勉強ノート] 「線形計算の数理」 7.2.1 べき乗法 基本形</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></description>
										<content:encoded><![CDATA[<p>今日の記事では「線形計算の数理」の7.2.1で紹介されているべき乗法について解説とPythonによる実装を紹介します。基本的には自分があとで見返したときに納得できるように説明を書いたので、かなり基本的なところから書いてあると思います。</p>



<p>実数に対応したPythonによる実装はこちらに置いてあります。</p>



<p><a href="https://github.com/shu65/theoretical-numerical-linear-algebra/blob/main/%E7%B7%9A%E5%BD%A2%E8%A8%88%E7%AE%97%E3%81%AE%E6%95%B0%E7%90%86_7_2_1_%E3%81%B9%E3%81%8D%E4%B9%97%E6%B3%95_%E5%9F%BA%E6%9C%AC%E5%BD%A2.ipynb" target="_blank" rel="noreferrer noopener">https://github.com/shu65/theoretical-numerical-linear-algebra/blob/main/%E7%B7%9A%E5%BD%A2%E8%A8%88%E7%AE%97%E3%81%AE%E6%95%B0%E7%90%86_7_2_1_%E3%81%B9%E3%81%8D%E4%B9%97%E6%B3%95_%E5%9F%BA%E6%9C%AC%E5%BD%A2.ipynb</a></p>



<h2 class="wp-block-heading">固有値問題とは</h2>



<p>まずはべき乗法の前に固有値問題について触れます。</p>



<p>複素\( n \)次正方行列 \( A \) に対して以下のような関係を満たす 複素数の\( \lambda \) と 複素ベクトル\( z \) の組を全部、またはいくつか求める問題を固有値問題と言います。 </p>



<p>$$ \begin{align*} <br>A \boldsymbol{z} = \lambda \boldsymbol{z} \tag{7.1} <br>\end{align*} $$</p>



<p>このとき、 \( \lambda \)を固有値、\( z \)を固有ベクトルと言います。</p>



<p>この固有値問題を解くアルゴリズムの一つが今回紹介するべき乗法です。</p>



<h2 class="wp-block-heading">べき乗法とは</h2>



<p>べき乗法は固有値問題を解くアルゴリズムで、固有値のうち、絶対値最大となる固有値とその固有ベクトル を求めるアルゴリズムになります。</p>



<h3 class="wp-block-heading">アルゴリズムの概要</h3>



<p>具体的なPythonコードは後ほどしめしますが、基本的には以下のようなアルゴリズムになります。</p>



<ol class="wp-block-list"><li>\( A \) と任意の単位ベクトル \( \boldsymbol{x}_0 \)を入力に取る</li><li>以下を\( \boldsymbol{x}_{k} \) が収束するまで繰り返す<ol><li>\(  \boldsymbol{y}_{k} := A \boldsymbol{x}_{k-1}\)</li><li>\( \boldsymbol{x}_{k} :=  \boldsymbol{y}_{k} / ||  \boldsymbol{y}_{k} || \) </li></ol></li><li>収束した\( \boldsymbol{x}_{k}\) を\( \boldsymbol{x} \) とし、\( \boldsymbol{x} \) と \( \boldsymbol{y} \) (\(=A \boldsymbol{x}\)) の要素比の絶対値 \( | y_i / x_i | \) が最大となる \( y_i / x_i  \) (ただし、 \( x_i \ne 0 \) ) を求める。</li><li>3で求めた\( y_i / x_i  \)を絶対値最大となる固有値、その固有ベクトルを\( \boldsymbol{x} \) として出力する</li></ol>



<h3 class="wp-block-heading">絶対値最大の固有値の固有ベクトルに収束する理由</h3>



<p>先ほど紹介したアルゴリズムでは\( \boldsymbol{x}_{k} \)が絶対値最大となる固有値の固有ベクトルに収束することを利用していました。そもそもなぜ \( \boldsymbol{x}_{k} \)が固有ベクトルに収束していくのか？を示します。</p>



<p>まず、複素\( n \)次正方行列\(A\)の固有値 \( \lambda_i \) を以下のように絶対値が大きい順に番号付けします。</p>



<p>$$ \begin{align*} <br>|\lambda_1| \geq  |\lambda_2|  \geq &#8230; \geq |\lambda_n|\tag{7.6} <br>\end{align*} $$</p>



<p>また、固有ベクトルを単位ベクトルとし、それぞれの固有値に対応する固有ベクトルを固有値の絶対値が大きい順に \( \boldsymbol{z}_{1}, \boldsymbol{z}_{2}, &#8230;, \boldsymbol{z}_{n} \) とします。ここで\(A\) が対角化可能(つまり\(n\)個の1次独立な固有ベクトルを持つ)という仮定がおけるとします。つまり、\(n\)個の固有ベクトルを用いると\(n\)次元の任意のベクトルを固有値ベクトルを使って表すことができます。</p>



<p>これを利用して任意の単位ベクトル \( \boldsymbol{x}_0 \)を以下のように固有ベクトルを使って表します。</p>



<p>$$ \begin{align*} <br>\boldsymbol{x}_0 = \sum_{i=1}^n c_i \boldsymbol{z}_i \tag{7.7.1} <br>\end{align*} $$</p>



<p>ここで\( c_i \) は複素数です。この(7.7.1)で\(A^k\)を左からかけ、(7.1)を利用すると以下のように変形できます。</p>



<p>$$ \begin{align*} <br>A^k \boldsymbol{x}_0 =&amp; \sum_{i=1}^n c_i A^k \boldsymbol{z}_i \\<br>=&amp; \sum_{i=1}^n c_i \lambda_i^k \boldsymbol{z}_i \\<br>=&amp; \lambda_1^k \left[ c_1 \boldsymbol{z}_1 + \sum_{i=2}^n c_i \left( \frac{\lambda_i}{\lambda_1} \right)^k \boldsymbol{z}_i \right] <br>\tag{7.7} <br>\end{align*} $$</p>



<p>このとき\( \lambda_1 \) は絶対値最大の固有値であるため、 \( |\frac{\lambda_i}{\lambda_1}| &lt; 1 \) が成り立ちます。このため、sumの項に関しては\( k \to \infty \) のとき0に収束することがわかります。</p>



<p>この式を両辺を \( || A^k \boldsymbol{x}_0 || \) で割り、\( k \to \infty \) のときにどうなるかを見てみます。固有ベクトルが単位ベクトルということを利用すると以下のように固有ベクトル \( \boldsymbol{z}_1 \)だけにすることができます。</p>



<p>$$ \begin{align*} <br>\lim_{k \to \infty} \frac{A^k \boldsymbol{x}_0}{|| A^k \boldsymbol{x}_0 ||} =&amp; \lim_{k \to \infty} \frac{\lambda_1^k \left[ c_1 \boldsymbol{z}_1 + \sum_{i=2}^n c_i \left( \frac{\lambda_i}{\lambda_1} \right)^k \boldsymbol{z}_i \right]}{|| \lambda_1^k \left[ c_1 \boldsymbol{z}_1 + \sum_{i=2}^n c_i \left( \frac{\lambda_i}{\lambda_1} \right)^k \boldsymbol{z}_i \right] ||} \\<br>=&amp; \lim_{k \to \infty} \frac{\lambda_1^k \left[ c_1 \boldsymbol{z}_1 + \sum_{i=2}^n c_i \left( \frac{\lambda_i}{\lambda_1} \right)^k \boldsymbol{z}_i \right]}{\lambda_1^k || \left[ c_1 \boldsymbol{z}_1 + \sum_{i=2}^n c_i \left( \frac{\lambda_i}{\lambda_1} \right)^k \boldsymbol{z}_i \right] ||} \\<br>=&amp; \lim_{k \to \infty} \frac{\left[ c_1 \boldsymbol{z}_1 + \sum_{i=2}^n c_i \left( \frac{\lambda_i}{\lambda_1} \right)^k \boldsymbol{z}_i \right]}{|| \left[ c_1 \boldsymbol{z}_1 + \sum_{i=2}^n c_i \left( \frac{\lambda_i}{\lambda_1} \right)^k \boldsymbol{z}_i \right] ||} \\<br>=&amp; \frac{c_1 \boldsymbol{z}_1}{||c_1 \boldsymbol{z}_1||} \\<br>=&amp; \frac{c_1 \boldsymbol{z}_1}{c_1 ||\boldsymbol{z}_1||} \\<br>=&amp; \boldsymbol{z}_1<br>\tag{7.7.2} <br>\end{align*} $$</p>



<p>この式の左辺の \( \frac{A^k \boldsymbol{x}_0}{|| A^k \boldsymbol{x}_0 ||} \) は\( \boldsymbol{x}_k \) の定義から \( \frac{A^k \boldsymbol{x}_0}{|| A^k \boldsymbol{x}_0 ||} = \boldsymbol{x}_k \)です。このため、\( \boldsymbol{x}_k \) が収束すると \( \boldsymbol{z}_1 \) の近似ベクトルになります。</p>



<h3 class="wp-block-heading">固有ベクトルから固有値を求める</h3>



<p>次に固有値の求め方を説明します。先ほどの説明で \( \boldsymbol{x}_k \) が収束すると固有値ベクトル \( \boldsymbol{z}_1 \) に収束することがわかりました。このため、収束した\( \boldsymbol{x}_k \)を \( \boldsymbol{x} \) とし、\( \boldsymbol{y} = A\boldsymbol{x} \) と置きます。この時、\( \boldsymbol{x} \) は固有ベクトルの近似なので以下の関係が成り立ちます。</p>



<p>$$ \begin{align*} <br>\boldsymbol{y} = A \boldsymbol{x} \approx \lambda_1 \boldsymbol{x} \tag{7.7.3} <br>\end{align*} $$</p>



<p>このため、\( \boldsymbol{x}\) と\( \boldsymbol{y}\)の要素をそれぞれ \( x_i\)、\( y_i\)とするとき以下の式が成り立ちます。</p>



<p>$$ \begin{align*} <br>y_i \approx \lambda_1 x_i \tag{7.7.4} <br>\end{align*} $$</p>



<p>式 (7.7.4)から\( x_i \ne 0 \) のときは以下の関係が成り立ちます。</p>



<p>$$ \begin{align*} <br>\lambda_1　\approx  \frac{y_i}{x_i} \tag{7.7.5} <br>\end{align*} $$</p>



<p>本では詳しく理由は書かれていないですが、\( |\frac{y_i}{x_i}| \) が最大となるような \( i \) を選ぶとよいとあります。</p>



<p>また\( A \) がエルミート行列の時は以下の式(レイリー商)のほうが良い近似らしいです。</p>



<p>$$ \begin{align*} <br>\lambda_1　\approx  \frac{\boldsymbol{x}^H A \boldsymbol{x}}{\boldsymbol{x}^H \boldsymbol{x}} \tag{7.9} <br>\end{align*} $$</p>



<p>他の方のblog記事を読むとレイリー商を利用して固有値を出しているケースが多い印象でした。</p>



<h2 class="wp-block-heading">Pythonによる実装</h2>



<p>アルゴリズムの概要で示したものをPythonで具体的に書きます。今回は複素数ではなく実数のみ対応しています。また、簡単な行列演算はnumpyを使用しています。</p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="power_method" data-lang="Python"><code>def power_method(a, eps=1e-7, max_iterations=100):
  x0 = np.ones((a.shape[1],))/np.sqrt(a.shape[1])
  x_k = x0
  for i in range(max_iterations):
    y_k = a @ x_k
    x_kp1 = y_k / np.linalg.norm(y_k)
    relative_residual_norm = np.linalg.norm(x_kp1 - x_k)
    if relative_residual_norm &lt; eps:
      break
    x_k = x_kp1 
  x = x_k
  y = a @ x
  eigenvalue = None
  for x_i, y_i in zip(x, y):
    if np.abs(x_i) &gt;= eps:
      tmp = y_i / x_i
      if (eigenvalue is None) or (np.abs(eigenvalue) &lt; np.abs(tmp)):
        eigenvalue = tmp
  eigenvector = x
  return eigenvalue, eigenvector</code></pre></div>



<p>このコードを実行すると以下のようになります。<br></p>



<div class="hcb_wrap"><pre class="prism line-numbers lang-python" data-file="main" data-lang="Python"><code># 入力のAの行列
a_matrix = np.array(
    [[1, 0],
     [0, 2]]
)
eigenvalue, eigenvector = power_method(a_matrix)
print(&quot;eigenvalues:&quot;, eigenvalue)
print(&quot;eigenvectors:&quot;)
print(eigenvector)</code></pre></div>



<pre class="wp-block-preformatted"># 出力
eigenvalues: 2.0
eigenvectors:
[1.1920929e-07 1.0000000e+00]</pre>



<p>numpyの固有値と固有ベクトルを求める <code>np.linalg.eig()</code>でも近い値が得られているので想定通りの動作をしていると考えています。</p>



<h2 class="wp-block-heading">終わりに</h2>



<p>最近固有値問題について勉強していて、同僚に良い本として「線形計算の数理」を教えてもらったので読んでいました。この際、アルゴリズムの背景にある数学の証明が面白いと感じたのと、他の方のべき乗法の記事を読んでもわからないところがあったので、自分があとで見返したときに納得できるように説明を書きました。</p>



<p>実は「線形計算の数理」で紹介されている固有値問題のアルゴリズムは大部分を勉強がてら実装してあるので、随時今回のような記事にしていければと思っています。</p><p>The post <a href="https://www.mattari-benkyo-note.com/2022/03/24/theoretical-numerical-linear-algebra-power-method/">[勉強ノート] 「線形計算の数理」 7.2.1 べき乗法 基本形</a> first appeared on <a href="https://www.mattari-benkyo-note.com">まったり勉強ノート</a>.</p>]]></content:encoded>
					
					<wfw:commentRss>https://www.mattari-benkyo-note.com/2022/03/24/theoretical-numerical-linear-algebra-power-method/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
		<post-id xmlns="com-wordpress:feed-additions:1">911</post-id>	</item>
	</channel>
</rss>
