読者です 読者をやめる 読者になる 読者になる

プリキュアとセーラームーンはどちらの方がサザエさんに近いのか?

NLP Python R

この記事ではこんなことを書くよ

はじめに

計算機分野に所属する人たちは時々、本当に間抜けなことを真面目に取り組もうとします。

例えば、自然言語処理の最新手法"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_arraynumpy.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)

本題 プリキュアセーラームーンはどっちの方がサザエさんに近いのか?

さて、こんな図がプロットされます。

f:id:kensuke-mi:20150717051437p:plain

おやおや、セーラームーンの方がいくぶんか、サザエさんに近いようですね・・。

言い忘れてましたが、関係のないデータも入れておきました。どれくらい離れるか見たかったからです。ridergolgo_titlesultraが関係ないやつですね。仮面ライダーゴルゴ13ウルトラマンですが。

念のため、SVDの結果も見てみましょう。(SVDだけで2次元まで削減した。という意味です)

f:id:kensuke-mi:20150717051441p:plain

おおっ・・・これは・・・全然違う。

プリキュアセーラームーンが似たところにプロットされてたり、ゴルゴ13仮面ライダーが遠くに行ってたり、SVDの結果の方が直感によく合いますね・・・

こっちを採用しましょう。

ユークリッド距離で計算すると、プリキュアの方がわずかに0.12くらいサザエさんに近いです。

というわけで、プリキュアの方がセーラームーンより、わずかにサザエさんに似ているという結論が出ました。

うーん。。。サザエさんプリキュアも日曜に放送してるから。。かな?

まぁ、発火した素性がたまたまあったから、と言われるとそれまでなんだけど・・・

元の空間でcos類似度を計算した方がいいかもしれませんね。

ところで、何か忘れてないでしょうか。

そう、どうしてt-SNEはうまくプロットされなかったのか。

ちょっと試してみました。

高次元でなかったからうまくいかなかった?

実は元の空間は3,000次元ちょっとにすぎません。

これじゃ、高次元空間とは言えないだろ〜ということで、wikipedia青空文庫から文長が長そうな記事をコピペしまくります。

まずはt-SNE

f:id:kensuke-mi:20150717052455p:plain

形は綺麗そうなんだけど・・・・

chrchillの記事がはじっこなのはいいとしてmarxgermanbismarkが離れているのは気になります。

また、iran_rev2(イラン革命)khomeini(ホメイニ)の距離も違和感があります。

あ、そういえば、核問題が進展してよかったですね。

!اَلْحَمْدُ لِلَّهِ

さて、SVDの方をみてましょう。

f:id:kensuke-mi:20150717052501p:plain

t-SNEと違って、ホメイニイラン革命がほぼ同じところに位置したり、マルクスとドイツとビスマルクが同じところに位置するのは直感的に正しそうといえます。

anpan_characters(アンパンマンの登場人物)とkokoro(夏目漱石のこころ)がどっか行っているのは仕方ないですね。内容的も外れ値とみなしてよさそうです。

そういえば、アンパンマンの登場人物という記事はこまーめに整備されているようで感心するばかりです。

毎週、新しいキャラが出ると更新しているのでしょうか・・・・・

さて、この時の次元数は13,000次元でした。ちょい高次元と言ってもいいかな〜と思います。

じゃあ、どーしてt-SNEはビミョーな結果になったのでしょうか?

推測ですが、元の空間で記事に「非線形な塊」が存在してなかったんじゃないかと思います。

t-SNEがプロットした結果が、元の空間での距離を忠実に表現しているのかもしれません。

また、元の空間でノイズが多すぎたのが原因かもしれません。SVDで綺麗にノイズ除去できて、初めて、類似することにプロットできたとか・・・

でもどうやってそれを調べたらいいんだろう。元の空間での分散値でも調べましょうかね・・・。

まとめ

  • t-SNEを使ってみたよ。でも、うまくいかなかったよ。

    • 非線形な塊」が存在しないデータだった?
    • いずれにせよ、図は両方とも出すのがよさそう
  • プリキュアの方が若干、サザエさんに近いらしいよ

[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とか呼ぶみたいです