はじめに
こんにちは。Futureの関根です。アドベントカレンダー17日目になります。
院生時代にデータサイエンス系を専攻しており、今年のアドベントカレンダーを期に少し復習をしようかなと思い、本記事を書いています。
最初に叩き込まれた手法が特異値分解(SVD:Singular Value Decomposition)でした。
(中身はより広く知られている主成分分析(PCA:Principal Component Analysis)と似たものになります。)
本記事でPythonの実装を含めた、特異値分解が画像をどのように認識しているのか?を説明出来ればと思います。
今回やること
今回は画像データに対してSVDを適用して、得られた特異値、特異ベクトルから画像の再構成をしてみます。
SVDやPCAで画像の再構成する書籍や記事は結構あるので、今回はその 特異ベクトル一つひとつを取り出してみて、SVDが画像をどのように理解しているのか? まで今回はみてみようと思います。(厳密には特異ベクトルじゃないけど、)
扱うデータ
今回は以下の画像に対してSVDを適用してみます。
自分が撮影したカモカモ。(かわいい)
色情報自体は今回の分析ではあまり意味がないので、グレースケール化したものをSVDにかけていきます。
SVDしてみる(Python)
SVD自体は非常に簡単でPythonの場合、numpy.linalg.svd(X)
の1行でSVDの実行が出来ます。
今回は得られた特異ベクトルで画像の再構成を行うので、特異値Sはnp.diag(S)
で対角行列に変換しておきます。
U, S, VT = np.linalg.svd(X) # Xは画像
S = np.diag(S)
SVDの実行自体はこれで終わりになります。(簡単!)
得られた特異値、特異ベクトル画像の再構成をする
特異値S、特異ベクトルU, VTは特異値の大きい順に並んでいます。
分かりやすく伝えるならば 重要度の高い順番に並んでいる 、とも言えると思います。(厳密に言うと、分散が最大となる順)
そのため、再構成する際に、重要度の高い順にr個だけ使ってあげれば、重要度の高い特徴だけで画像の再構成をすることが出来ます。
今回は1,2,3,10,50,100個の特異値、特異ベクトルで画像の再構成を行ってみようと思います。
以下コードで画像の再構成を行い、再構成画像の表示を行ってみます。
reconstructs = [1, 2, 3, 10, 50, 100]
# 画像の再構成
for r in reconstructs:
X_tilde = U[:,:r] @ S[0:r,:r] @ VT[:r,:]
# 再構成画像の表示
fig, axes = plt.subplots(2, 3, figsize=(12, 6))
for i, r in enumerate(reconstructs):
X_tilde = U[:,:r] @ S[0:r,:r] @ VT[:r,:]
ax = axes[i//3, i % 3]
ax.imshow(X_tilde)
ax.set_title(f"reconstruct image(r={r})")
plt.gray()
plt.show()
特異ベクトルはデータXの行数分だけ得られます。今回は500個ちょっとの特異ベクトルが得られます。
その内の1,2,3つだけで再構成をした場合は、何がなんだか分かりませんが、たったの10個で再構成しただけでもカモの画像だ、と認識できることがわかります。
50個使用すると、この画像サイズでは元の画像とほぼ遜色がない画像が得られている事がわかります。
特異値、特異ベクトルを表示してみる
先ほどは画像の再構成を行いましたが、これを再構成せずに1つひとつ表示をしてみるとどうなるか。
それを見ればSVDがどのような順番で画像から特徴をとっているのか?がわかるかと思います。
以下コードでそちらの確認を行い、得られた結果がその下の画像になります。
for i, r in enumerate(reconstructs):
x_tilde = U[:,r-1:r] @ S[r-1:r,r-1:r] @ VT[r-1:r,:]
ax = axes[i//3, i % 3]
ax.imshow(x_tilde)
ax.set_title(f"reconstruct image(r={r})")
plt.gray()
plt.show()
r=1の時には画像の中の白・黒の情報を大雑把に捉えようとしています。
画像のどのあたりにカモが写っているのか?を捉えている、とも言えるかもしれません。
別々に見ているとわかりづらいので、再構成画像も一緒に表示をしてみます。
rがどんどん大きくなるにつれて、模様は細かくなっていき、r=3では画像のどの辺りに羽があるのか、クチバシがあるのか、顔があるのか?というのを捉えていると考えられそうです。
r=5あたりになると大きな特徴は得ておらず、より細かい情報(羽の模様や背景の波などでしょうか)を抽出しようとしていることがわかります。
50になるともう特徴らしいものは得られておらず、画像の非常に細かい情報やノイズのような物が得られているようにも見えます。
何に使えそうか、何が便利か
1枚の画像を扱う場合であれば、画像がノイズまみれの場合などに上位何%かだけで画像の再構成をすればノイズの除去なんかにも使えたりしそうです。
また今回は取り上げていませんが、複数枚の画像から共通する特徴を抽出したり、画像だけでなく時系列データから特徴抽出することもできます。
(後述する著者HPにある演習問題には単振動の動画データから、振動の振幅を取り出せたりします。練習問題として面白かったので是非とも。https://databookuw.com/page-17/ )
そしてSVDは教師無し学習の手法になります。つまりデータさえあれば、使えちゃうということです。
数ある手法の中から試しにSVDを使ってみる、みたいな使い方もできるのかなと。
(特にPythonの場合は1行だけで簡単に実行が出来るのも魅力です)
まとめ
今回は特異値分解(SVD)を取り上げてみました。
実際には今回のように画像1枚に対してSVDを使うことは少なく、複数枚の画像を使ったりだとか、時系列データなんかを扱いことの方が多いです。
しかし今回の記事のように画像に対して適応し、それを1つひとつ表示したりすることで、視覚的にどんなことをしているのか?を理解することができるかなと思っています。
おすすめ書籍・YouTube
SVDの詳細な理論は以下書籍とその著者のYouTube動画が非常におすすめです。
英語の書籍ですが、視覚的に説明をしたり、コードの例が豊富にあったりと分かりやすいです。(Python, MATLABのコードサンプルがあります)
特にEigenfaces(固有顔)という例はSVDがどんなことに使えるか?と言うのを理解する例として素晴らしかったです。
その他次元削減などで使われる高速フーリエ変換やウェーブレット変換などのほか、Deep Learningに関する話もあります。
興味がある方は是非ともこちらも見てみてください。
参考
- Data-Driven Science and Engineering: Machine Learning, Dynamical Systems, and Control