MDS&文字列カーネルの可視化を試す
はじめに
せっかく文字列カーネルで遊んでみたので、可視化も試してみる。
Multidimensional Scalingとは
MDSを試す
アメリカの飛行場をプロットするのが流行らしいので、自分もやってみる。
使ったもの
- Eigen 3.1.2
- gnuplot
- 結果を表示するため
コード
#include <iostream> #include <vector> #include <Eigen/Dense> using namespace std; using namespace Eigen; struct ST { double lambda; VectorXd v; }; bool operator<(const ST& a, const ST& b){ return a.lambda < b.lambda; } int main(){ int N; cin >> N; MatrixXd m(N,N); //2点間の二乗の行列 MatrixXd one(N,N), e(N,N); //要素が全て1の行列、単位行列 for(int i=0; i<N; i++){ e(i,i) = 1; for(int j=0; j<N; j++){ double tmp; cin >> tmp; m(i,j) = tmp * tmp; one(i,j) = 1; } } //centering(Young-Householder transformation) MatrixXd P = -0.5 * (e - 1/(double)N * one) * m * (e - 1/(double)N * one); //固有値固有ベクトルの計算 SelfAdjointEigenSolver<MatrixXd> eigensolver(P); if(eigensolver.info() != Success) return 1; vector<ST> res; VectorXd vv = eigensolver.eigenvalues(); MatrixXd mm = eigensolver.eigenvectors(); for(int i=0; i<vv.size(); i++){ res.push_back((ST){vv(i),mm.col(i)}); } //固有値でソート sort(res.begin(), res.end()); //座標のプロット for(int i=0; i<N; i++){ cout << res[N-1].v(i) << " " << res[N-2].v(i) << endl; } return 0; }
実行&結果
$ less dist.txt 10 0 587 1212 701 1936 604 748 2139 2182 543 587 0 920 940 1745 1188 713 1858 1737 597 1212 920 0 879 831 1726 1631 949 1021 1494 701 940 879 0 1374 968 1420 1645 1891 1220 1936 1745 831 1374 0 2339 2451 347 959 2300 604 1188 1726 968 2339 0 1092 2594 2734 923 748 713 1631 1420 2451 1092 0 2571 2408 205 2139 1858 949 1645 347 2594 2571 0 678 2442 2182 1737 1021 1891 959 2734 2408 678 0 2329 543 597 1494 1220 2300 923 205 2442 2329 0 $ g++ mds_dist.cc -I/usr/local/include/eigen3 $ ./a.out < dist.txt > dist.out $ gnuplot > set terminal dumb > unset key > plot "./dist.out" using 1:2 0.5 ++------+------+-------+-------+------+-------+-------+------+------++ + A + + + + + + + + + 0.4 ++ A ++ | | 0.3 ++ ++ | A A | 0.2 ++ ++ | | 0.1 ++ ++ 0 ++ A ++ | | -0.1 ++ A ++ | A | -0.2 ++ ++ | | -0.3 ++ A ++ | | -0.4 ++ ++ + + + + + + A + + + A + -0.5 ++------+------+-------+-------+------+-------+-------+------+------++ -0.5 -0.4 -0.3 -0.2 -0.1 0 0.1 0.2 0.3 0.4
カーネルの可視化を試す
「カーネル法によるパターン解析」にカーネル行列Kの可視化が載っていたので、それを試す。
カーネル行列Kとは、(i,j)成分がK(i,j)=<φ(i),φ(j)>となっているようなN*N行列をいう。
方法
コード
MDSのコードを少し修正して利用。
#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> using namespace std; using namespace Eigen; struct ST { double lambda; VectorXd v; }; bool operator<(const ST& a, const ST& b){ return fabs(a.lambda) < fabs(b.lambda); } int main(){ int N; cin >> N; MatrixXd K(N,N); //カーネル行列K MatrixXd D(N,N); //対角行列D for(int i=0; i<N; i++){ D(i,i) = 0; for(int j=0; j<N; j++){ cin >> K(i,j); D(i,i) += K(i,j); } } MatrixXd L = D - K; //Kのラプラシアン //固有値固有ベクトルの計算 SelfAdjointEigenSolver<MatrixXd> eigensolver(L); if(eigensolver.info() != Success) return 1; vector<ST> res; VectorXd vv = eigensolver.eigenvalues(); MatrixXd mm = eigensolver.eigenvectors(); for(int i=0; i<vv.size(); i++){ res.push_back((ST){vv(i),mm.col(i)}); } //固有値でソート sort(res.begin(), res.end()); //for(int idx=0; idx<N; idx++){ // cout << res[idx].lambda << endl; //} //座標のプロット for(int i=0; i<N; i++){ cout << res[N-1].v(i) << " " << res[N-2].v(i) << endl; } return 0; }
実行&結果
「今日は晴れ」「明日は晴れ」「今日は雨」「こんにちは」の全部分列カーネル(の正規化カーネル)について試してみる。
$ less kernel.txt 4 1 0.5 0.353553 0.0625 0.5 1 0.176777 0.0625 0.353553 0.176777 1 0.0883883 0.0625 0.0625 0.0883883 1 $ g++ kernel_vis.cc -I/usr/local/include/eigen3 $ ./a.out < kernel.txt > kernel.out $ gnuplot > set terminal dumb > unset key > gnuplot> set xrange [-1:1] > gnuplot> set yrange [-1:1] > plot "./kernel.out" using 1:2 1 ++---------------+-----------------+----------------+---------------++ + + + + + | A | | | | | 0.5 ++ ++ | | | | | | 0 ++ ++ | A | | A | | | | | -0.5 ++ ++ | A | | | | | + + + + + -1 ++---------------+-----------------+----------------+---------------++ -1 -0.5 0 0.5 1
左3つが「今日は晴れ」「明日は晴れ」「今日は雨」で、右1つが「こんにちは」。
大まかに取れてるみたいだけど、微妙。。。?
比率的なのは大体あってそう。眺めて知見を得ることが目的なので、大まかにこれでもよさそうかな。