多変量解析の手法の1つです。
複数次元あるデータを要約して、データの関係性をわかりやすく要約してくれます。
よくある勘違いですが、クラスタリングと違い、データがどういうクラスターに属するかはわかりません。
クラスタ分けはk-meansなどで別途行う必要があります。
アルゴリズムに関しては、Qiitaのページを参照ください。
一言でいうと、高次元の距離情報を2次元の距離情報に落としたときの損失を最小限にするような点の探索と解釈しています。
t-SNEの中で重要なパラメーターとして、perplexityというものがあります。
これは、各点の有効な近傍点の個数を表します。値が小さいほど、より似てるものとの関係性しか考慮しないということ。
このページでは、
論文中で5〜50がいいと記載があること
RとPython両方のライブラリにおいてデフォルトの値が30であること
から、5, 30, 50の3つを探索するようにしています。
画像データとして有名な、MNISTデータを用います。
CSVへ変換した結果を公開しているリポジトリがあるので、ここからデータをダウンロードします。
データ全体の可視化はこのあと取り扱っていきますので、どのような規則でデータが入ってるかを可視化してみます。
1行目にどのようなデータが入っているかを可視化します。
#!/usr/bin/env python
#!pip install japanize_matplotlib # プロット中で日本語を使用するためにインストール
import itertools
import pandas as pd
import matplotlib.pylab as plt
from IPython.display import display
df = pd.read_csv("mnist_train.csv", header=None, index_col=0)
display(df.head())
xs = []
ys = []
for x, y in itertools.permutations(range(28), 2):
iloc = y * 28 + x
if df.iloc[0, iloc] != 0:
xs.append(x)
ys.append(28 - y)
plt.scatter(xs, ys)
df.head()
の出力結果plt.scatter(xs, ys)
の出力結果本来、色の濃さを表す数値が各ビットに入っているのですが、一旦それを無視して可視化しても数字の「5」が書いてあることがなんとなくわかるかと思います。
Pythonの場合は scikit-learnでt-SNEを行うことができます。
以下では、trainデータのうち、1000行だけ計算しています。
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.manifold import TSNE
df = pd.read_csv("mnist_train.csv", header=None, index_col=0)
df_1000 = df.sample(1000)
# perplexityは30がデフォルトが多い(Rtsneもsklearnも30)
# 5〜50がよいと言われているため、5,30,50
colors = ["r", "g", "b", "c", "m", "y", "k", "orange", "pink", "gray"]
col = ["x", "y"]
for perplexity in [5, 30, 50]:
model = TSNE(n_components=2, perplexity=perplexity, n_iter=500, verbose=3, random_state=1)
transformed = model.fit_transform(df_1000)
df_transformed = pd.DataFrame(transformed, index=df_1000.index, columns=col)
df_transformed["color"] = [colors[i] for i in df_transformed.index]
plt.scatter(df_transformed["x"], df_transformed["y"], c=df_transformed["color"])
plt.savefig(f"scatter.tsne.{perplexity}.pdf")
plt.clf()
結果は以下のように出力されます。
Rでt-SNEの場合、Rtsneというライブラリを使用します。
ライブラリのタイトルにも記載がありますが、Barnes-Hut法により、高速化されたt-SNEを用いているようです。
#!/usr/bin/env Rscript
# install.packages("Rtsne") # インストール
library("Rtsne")
set.seed(0)
setwd("/Users/nsmt/prv/hp/py_r_stats")
# input
df = read.csv("mnist_train.csv", header=FALSE)
df_1000 = df[1:1000, ]
vec_ans = df_1000$V1
df_1000 = df_1000[, colnames(df_1000) != "V1"]
# main
df_1000_tsne_pp5 = Rtsne(df_1000, perplexity=5)
df_1000_tsne_pp30 = Rtsne(df_1000, perplexity=30)
df_1000_tsne_pp50 = Rtsne(df_1000, perplexity=50)
# output
pdf("scatter.Rtsne.5.pdf")
plot(df_1000_tsne_pp5$Y, col=vec_ans)
dev.off()
pdf("scatter.Rtsne.30.pdf")
plot(df_1000_tsne_pp30$Y, col=vec_ans)
dev.off()
pdf("scatter.Rtsne.50.pdf")
plot(df_1000_tsne_pp50$Y, col=vec_ans)
dev.off()
結果は以下のように出力されます。
比較に使用したPCAのソースコードを載せておきます。
#!/usr/bin/env python
colors = ["r", "g", "b", "c", "m", "y", "k", "orange", "pink", "gray"]
pca = PCA(n_components=2, random_state=0)
df_pca = pd.DataFrame(pca.fit_transform(df_1000))
df_pca.columns = ["PC1", "PC2"]
df_pca["color"] = [colors[i] for i in df_1000.index]
#!/usr/bin/env Rscript
df_1000_pca = prcomp(df_1000)
pdf("scatter.RPCA.pdf")
plot(df_1000_pca$x, col=vec_ans)
text(df_1000_pca$x[, 1], df_1000_pca$x[, 2], vec_ans)
dev.off()