プリキュアとセーラームーンはどちらの方がサザエさんに近いのか?
この記事ではこんなことを書くよ
はじめに
計算機分野に所属する人たちは時々、本当に間抜けなことを真面目に取り組もうとします。
例えば、自然言語処理の最新手法"word2vec"で艦これ加賀さんから乳を引いてみるとか眼鏡っ娘分類器を作る話とか。
ぼくも多分に漏れず、間抜けなことをよく取り組もうとします。 きっと、データサイエンティストと言っているけど、実際は、データマゾヒストなのですね。
さて、きょう、取くんで見る課題は「プリキュアとセーラームーンはどっちの方がサザエさんに近いのだろう?」というお題です。
実にどーでもいいお題ですが、簡単に答えられる人もそうそういないでしょう。
Googleにも聞いてみましたが、満足な答えは得られませんでした。
それでは、計算機に聞いてみることにします。
やってみること
最近、t-SNEという新しいおもちゃを知ったので、これを試してみたいのが、本音です。
類似度を計算するなら、word2vec
とかdoc2vec
とかもっとスマートな方法があります。
でも、そ・う・じゃ・な・く・て。ぼくは2次元平面にプロットしたいのです。
ときどき、「えっ、ベクトル空間とかなにそれ?めんどくせーからグラフで出してよ。xとyがあるやつ(鼻くそほじほじ)」とか言われるたびに「くっそーいつかfxxxkしてやる!」と思っていたので、2次元平面で表現したいのです。
t-SNEは高次元空間を2次元平面に射影するのを得意としているようです。著者のHPをみると、とてもクールな2次元プロットの例がたくさん見れます。
今回は、wikipediaの記事からデータを作って、tSNEに突っ込んで見ることを目標とします。
そもそもtSNEって何なん?
ここに詳しく書いてありますが、長いんで面倒くさいですよね。ぼくが理解したことをかいつまんで書いていこうと思います。
そもそもSNEって何なん?
そもそもSNEの話をしなくてはいけません。
SNE(Stochastic Neighbor Embedding)は高次元空間のデータを低次元(だいたい目標は2次元)に射影するのを目的とするアルゴリズムです。
「えっ、それPCAでいいじゃん」っていう方、もう少しお待ちくださいね。あとで書きます。
SNEの基本原理は、こんな感じです。
- 高次元空間と低次元空間の両方で条件付き確率を考える。
- 高次元空間で、ある点x_iがすべての点からx_jをサンプリングする確率を延々を求めます。この分布をp_(j|i)とします。
- 低次元空間でも、ある点y_iがすべての点からy_jをサンプリングする確率を延々を求めます。この分布をq_(j|i)とします。
- p(j|i)とq(j|i)の差が最小になるように最適化をします。具体的には、分布の差分ですから、カルバック・ライブナー距離の最小化です。この最小化式をコスト関数とします。
- コスト関数にStochastic Descendentを実行します。アルゴリズムの名前の通りですね。
しかし、SNEには欠点があります。
それは、パラメタの探索範囲が広いことです。
SNEは最適な射影を探すときにlocal最適化に陥るのを避けるために、ガウシアンノイズを加えます。[1]
ガウシアンノイズのパラメタがまた別のパラメタに影響を与えて・・・みたいな感じで、「つまり遅い!」ということです。
t-SNEはこの点を改善しています。コスト関数の中身をとっかえて、条件付き確率の計算をガウス分布でなく、Student-t分布にしてます。
どうして、これでパラメタチューニング問題が解決できるのか、まだわかってません。が、解決できたと書いてあります。
次元削減だったらPCAでいーじゃん
話が前後してしまいました。
高次元データでは、「非線形なデータの塊」[2]みたなものが存在(することがある)します。
scikit-learnの画像なんかはイメージしやすいと思います。
こういった「非線形な塊」を低次元に射影するには、「塊どうしは近くに配置したいけど、同時に塊でない点は遠くに配置したい」という希望を満たす射影でないといけないわけです。
PCAのような線形なテクニックではうまくいきません。というのも、「似てない点はとりあえずどっかやってまえ〜」という思想なので、「非線形な塊」をうまく低次元に射影できないのです。
というのが論文に書いてあった内容です。
個人的な理解だと、「PCAは共分散行列の固有値分解をえいや!ってやってるだけじゃん。分解した後をみてないじゃん。SNEは違うもんね。ちゃんと射影後の分散をみてるもんね(^^)」by 筆者 だと解釈してます。
使ってみる
さて、実装はすでに公開されているので、使うだけです。
作者のページでも公開されてますが、アルゴリズムを忠実に再現しているので遅いです。pythonならscikit-learnが良いでしょう。
ここでもscikit-learnを使います。
t-SNEを使うまえに、次元削減をしておくと良いです。とここに書いてある。
密な行列ならPCAをスパースな行列ならSVDを使います。
ここでは単語出現数matrix、通称term document matrixなので、スパースです。
なのでSVDを使います。
svdオブジェクトの入力はscipy.sparse.csr.csr_matrix
なので、うまいこと変換していてくださいね。
from sklearn.decomposition import TruncatedSVD X = csr_matrix(csc_matrix) svd = TruncatedSVD(n_components=low_dims, random_state=0) X_input = svd.fit_transform(X)
後は、tSNEに入れるだけです。
from sklearn.manifold import TSNE model = TSNE(n_components=2, perplexity=40, verbose=2) two_dimension_array = model.fit_transform(X_input)
two_dimension_array
はnumpy.array
です。
おしまい!
せっかくなので、二次元平面にプロットしてみましょう。
Rでプロットすることにします。
two_dimension_array
にラベルをつけて、csv
に保存します。
Rではcsvファイルを読み込んで、directlabels
ライブラリでラベル付きの散布図をプロットします。
ラベルに日本語をつけると豆腐(文字が四角になっちゃう現象)になるので、英数でラベルつけてくださいね。
par(family = "") plot_data <- read.table(file = path_plot_data, sep = ",", header = T) q <- ggplot(plot_data, aes(x=x, y=y)) + geom_point(aes(colour=label)) aa <- directlabels::direct.label(q) ggsave(file = path_png_file, plot = aa, dpi = 100, width = 10, height = 8)
本題 プリキュアとセーラームーンはどっちの方がサザエさんに近いのか?
さて、こんな図がプロットされます。
おやおや、セーラームーンの方がいくぶんか、サザエさんに近いようですね・・。
言い忘れてましたが、関係のないデータも入れておきました。どれくらい離れるか見たかったからです。rider
とgolgo_titles
とultra
が関係ないやつですね。仮面ライダーとゴルゴ13とウルトラマンですが。
念のため、SVDの結果も見てみましょう。(SVDだけで2次元まで削減した。という意味です)
おおっ・・・これは・・・全然違う。
プリキュアとセーラームーンが似たところにプロットされてたり、ゴルゴ13と仮面ライダーが遠くに行ってたり、SVDの結果の方が直感によく合いますね・・・
こっちを採用しましょう。
ユークリッド距離で計算すると、プリキュアの方がわずかに0.12くらいサザエさんに近いです。
というわけで、プリキュアの方がセーラームーンより、わずかにサザエさんに似ているという結論が出ました。
うーん。。。サザエさんもプリキュアも日曜に放送してるから。。かな?
まぁ、発火した素性がたまたまあったから、と言われるとそれまでなんだけど・・・
元の空間でcos類似度を計算した方がいいかもしれませんね。
ところで、何か忘れてないでしょうか。
そう、どうしてt-SNEはうまくプロットされなかったのか。
ちょっと試してみました。
高次元でなかったからうまくいかなかった?
実は元の空間は3,000次元ちょっとにすぎません。
これじゃ、高次元空間とは言えないだろ〜ということで、wikipediaと青空文庫から文長が長そうな記事をコピペしまくります。
まずはt-SNE
形は綺麗そうなんだけど・・・・
chrchillの記事がはじっこなのはいいとしてmarxとgermanとbismarkが離れているのは気になります。
また、iran_rev2(イラン革命)とkhomeini(ホメイニ)の距離も違和感があります。
あ、そういえば、核問題が進展してよかったですね。
!اَلْحَمْدُ لِلَّهِ
さて、SVDの方をみてましょう。
t-SNEと違って、ホメイニとイラン革命がほぼ同じところに位置したり、マルクスとドイツとビスマルクが同じところに位置するのは直感的に正しそうといえます。
anpan_characters(アンパンマンの登場人物)とkokoro(夏目漱石のこころ)がどっか行っているのは仕方ないですね。内容的も外れ値とみなしてよさそうです。
そういえば、アンパンマンの登場人物という記事はこまーめに整備されているようで感心するばかりです。
毎週、新しいキャラが出ると更新しているのでしょうか・・・・・
さて、この時の次元数は13,000次元でした。ちょい高次元と言ってもいいかな〜と思います。
じゃあ、どーしてt-SNEはビミョーな結果になったのでしょうか?
推測ですが、元の空間で記事に「非線形な塊」が存在してなかったんじゃないかと思います。
t-SNEがプロットした結果が、元の空間での距離を忠実に表現しているのかもしれません。
また、元の空間でノイズが多すぎたのが原因かもしれません。SVDで綺麗にノイズ除去できて、初めて、類似することにプロットできたとか・・・
でもどうやってそれを調べたらいいんだろう。元の空間での分散値でも調べましょうかね・・・。
まとめ
[1] 具体的には、射影後の点yにノイズをのせるのだと思います" In addition, in the early stages of the optimization, Gaussian noise is added to the map points after each iteration."と論文に書いてありました〜
[2] non-linear manifoldとか呼ぶみたいです