Multidimensional Scaling (多次元尺度構成法, MDS) の計算方法と実装
Multidimensional Scaling (MDS) について少し勉強したのでメモしておきます。
Multidimensional Scaling とは
はじめに
日本語では「多次元尺度構成法」と呼ばれる統計テクニックの一つです。英語版ウィキペディアの記事が詳細です。
多次元尺度構成法 - Wikipedia
Multidimensional scaling - Wikipedia
MDS には色々な派生がありますが、ここでは Classical MDS (古典的多次元尺度構成法) と呼ばれる最も基礎的で古典的なものについてのみ記述します。
どういうときに使えるか
いくつか要素があり、要素間の距離がなんらかの形で定義できる場合に、その要素を多次元空間に配置する場合に使えます。
具体例
例えば、いま目の前に日本酒が10銘柄(鳳凰美田、花陽浴、...)あるとし、これらを一つずつ味見し「どの銘柄とどの銘柄がどのぐらい似ているか(似ていないか)」という指標によって、以下のように任意の2銘柄間の距離が定義できるとします。
要素間の距離の定義のイメージ*1。は要素間の距離を表すとする。
(農口, 村祐) | (農口, 鳳凰美田) | (農口, 花陽浴) | ... |
(十四代, 村祐) | (十四代, 呼友) | (十四代, 花陽浴) | ... |
... | ... | ... | ... |
MDS を用いると、これらの距離の情報から、例えば2次元の座標系に銘柄の「マップ」を可視化することができたりします。ここで、各点の座標は MDS によって計算され、その際に各点間の距離は先ほど定義した距離が反映されます。
MDS の使用イメージ*2。MDS によって各点の座標が計算される。
MDS の計算方法
行列 を求めるためには、まず
を満たす行列 *7*8を、入力として与えられた距離行列 から計算できるとします。すると、この行列 は固有値分解 (eigenvalue decomposition) によって
と分解することができます*9*10。ここで は固有ベクトルを並べた行列、 は固有値を対角成分に持つ行列です。これらを用いて、
とすることで、所望の を計算することができます*11。ここで は要素毎に累乗根をとることを意味しています*12。また、このままでは空間の次元は ですが、これをより小さい次元 に削減するには、固有値の上位 個と、それに対応する固有ベクトルのみを使って前式を計算することで実現できます*13。
与えられた距離行列 からカーネル行列 を計算する方法ですが、これは
によって算出するようです。ただし
は中心化行列*14と呼ばれ、得られる結果の原点を重心に移す効果があるそうです*15*16。
MDS の実装
C++ によって MDS を実装した例を以下に示します。行列演算ライブラリとして Eigen を用いています。
#include <Eigen/Core> #include <vector> #include <utility> using namespace std; using namespace Eigen; // This function computes low-dimensional embedding by using multi-dimensional scaling (MDS) // - Input: A distance (dissimilarity) matrix and a target dimension for embedding // - Output: A coordinate matrix whose i-th column corresponds to the embedded coordinates of the i-th entry MatrixXd computeMDS(const MatrixXd &D, unsigned dim) { assert(D.rows() == D.cols()); assert(D.rows() >= dim); const unsigned n = D.rows(); const MatrixXd H = MatrixXd::Identity(n, n) - (1.0 / static_cast<double>(n)) * VectorXd::Ones(n) * VectorXd::Ones(n).transpose(); const MatrixXd K = - 0.5 * H * D * H; const EigenSolver<MatrixXd> solver(K); VectorXd S = solver.eigenvalues().real(); MatrixXd V = solver.eigenvectors().real(); extractNLargestEigen(dim, S, V); const MatrixXd X = DiagonalMatrix<double, Dynamic>(sqrt(S)) * V.transpose(); return X; }
以下にテストコード等を含めた git repository 公開しました:
github.com
参考にした文献
ウェブ検索等で見つけた以下の文献を、それぞれ断片的に参考にしつつ勉強しました(特に参考にした順にソート):
- 多次元尺度構成法イントロダクション - kohta blog
- http://www.math.uwaterloo.ca/~aghodsib/courses/f10stat946/notes/lec10-11.pdf
- http://www.aichi-gakuin.ac.jp/~chino/part-time/handai.pdf
- Multidimensional scaling - Wikipedia
- 多様体学習紹介 中川研機械学習勉強会 2008/6/19
- Toward a perceptual space for gloss
- https://classes.soe.ucsc.edu/cmps261/Fall13/papers/jcosborn/bp_empmdsld_06.pdf
- [1105.1033] Adaptively Learning the Crowd Kernel
他によくまとまっている文献などがあれば是非ご教示ください。
*1:これらの銘柄は実在するものですが、その距離として与えられている値には全く根拠はなく、極めて乱数に近いものであると考えてください。
*2:この図はあくまでイメージを掴むためのもので、実際のデータから計算されたものではありません。
*3:この部分について明示していない文献も多く私は途中まで勘違いしていたのですが、距離行列は距離の二乗を要素として持つようです。
*5:文献によっては 番目の列を とする行列 として表現したりすることもあります。
*6:この操作は Principal Component Analysis (PCA) と同値?
*8:文献によって と表記されることもあります。
*9: は対称行列であるため、特異値分解 (SVD) を用いても同様の計算ができます。
*10: がユークリッド距離に基づいているとき、まとそのようなときに限り は半正定値行列となる?
*11:
*12:ただし、 が半正定値行列でない場合には固有値に負の値が現れます。その場合はその固有値を 0 だとみなして以降の計算を行います。これはある種の近似を行っていると考えられるそうです。
*13:そのようにして良い根拠はこの資料で説明されています。
*14:文献によって と表記されることもあります。
*15:このあたりはよく理解できていませんが、距離を一定に保った点群には剛体移動(平行移動と回転移動)の自由度があるので、そのうち平行移動については重心が原点にくるように制約をかけるということだと思います。
*16:この変換は「ダブル・センタリング変換」と呼ばれる?