Python × AI - t-SNE(最適なPerplexity探索)

t-SNEにとって重要なパラメータであるPerplexityの最適値を調べます。

Perplexityとは、どれだけ近傍の点を考慮するかを決めるためのパラメータであり、データの局所的な特性と全体的な特性のどちらをより考慮するかというバランスを表します。

デフォルトは30であり、5から50の間の値を選択することが推奨されています。

複数のPerplexityを設定して結果を確認することが、基本的なアプローチになります。

最適なPerplexityを探索 (2次元)

最適なPerplexityを調べるための関数を定義し、結果を2次元で表示します。

(前回読み込んだMNISTデータを使用しています)

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import time
def create_2d_tsne(target_X, y, y_labels, perplexity_list= [2, 5, 30, 50, 100]):
fig, axes = plt.subplots(nrows=1, ncols=len(perplexity_list),figsize=(5*len(perplexity_list), 4))
for i, (ax, perplexity) in enumerate(zip(axes.flatten(), perplexity_list)):
start_time = time.time()
tsne = TSNE(n_components=2, random_state=0, perplexity=perplexity)
Y = tsne.fit_transform(target_X)
for each_label in y_labels:
c_plot_bool = y == each_label
ax.scatter(Y[c_plot_bool, 0], Y[c_plot_bool, 1], label="{}".format(each_label))
end_time = time.time()
ax.legend()
ax.set_title("perplexity: {}".format(perplexity))
print("perplexity {} is {:.2f} seconds.".format(perplexity, end_time - start_time))
plt.show()

create_2d_tsne(digits.data, digits.target, digits.target_names)

定義した関数の引数には次の3パラメータを設定します。

  • 元データ
  • ラベル名
  • ラベル名のユニークリスト

Perplexityにそれぞれ2,5,30,50,100を設定(2行目)し、t-SNEを実行(6,7行目)します。

それぞれの結果を2次元で可視化したものが下記になります。

[実行結果]

2次元ではPerplexityが30,50の時に、うまく分類されていることが分かりました。

t-SNEの結果を3次元表示

今度は3次元で、最適なPerplexityを調べるための関数を定義します。

[Google Colaboratory]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def create_3d_tsne(target_X, y, y_labels, perplexity_list= [2, 5, 30, 50, 100]):
fig = plt.figure(figsize=(5*len(perplexity_list),4))
for i, perplexity in enumerate(perplexity_list):
ax = fig.add_subplot(1, len(perplexity_list), i+1, projection="3d")
start_time = time.time()
tsne = TSNE(n_components=3, random_state=0, perplexity=perplexity)
Y = tsne.fit_transform(target_X)
for each_label in y_labels:
c_plot_bool = y == each_label
ax.scatter(Y[c_plot_bool, 0], Y[c_plot_bool, 1], label="{}".format(each_label))
end_time = time.time()
ax.legend()
ax.set_title("Perplexity: {}".format(perplexity))
print("perplexity {} is {:.2f} seconds.".format(perplexity, end_time - start_time))
plt.show()

create_3d_tsne(digits.data, digits.target, digits.target_names)

projectionパラメータ“3d”を設定(4行目)し、3次元のグラフを表示します。

また、n_componentsパラメータを2から3に変更しています。(6行目)

[実行結果]

3次元の表示からは、Perplexityが30の方がより特徴ごとに分かれているようです。

今回はPerplexityのデフォルト値である30でうまく分類できることが分かりましたが、データセットによって最適なPerplexityの設定値が異なりますので、今回実施したように複数の結果を作成し比較することをお勧めします。

Python × AI - t-SNE(次元削減)

t-SNE(ティースニー)は、2次元または3次元への圧縮に特化しているアルゴリズムです。

(次元数4以上の場合の結果は保証されていません)

MNISTデータの読み込み

0~9の手書き数字の画像データセットMNIST(エムニスト)を読み込みます。

画像データはベクトル化すると高次元になるため、次元削減アルゴリズムがとても有効です。

[Google Colaboratory]

1
2
3
4
from sklearn.datasets import load_digits
digits = load_digits()
print(digits.data.shape)
print(digits.data)

[実行結果]

1797個の画像データがあり、各データは 8 × 8 = 64 個の数値配列、つまり64次元のデータセットになります。

この64次元のデータセットをt-SNEで2次元に削減し可視化します。

データの先頭から100文字を画像として表示するコードは下記の通りです。

[Google Colaboratory]

1
2
3
4
5
import matplotlib.pyplot as plt 
fig, axes = plt.subplots(10, 10, figsize=(8, 8),subplot_kw={"xticks":[], "yticks":[]})
for i, ax in enumerate(axes.flat):
ax.imshow(digits.images[i], cmap="binary", interpolation="nearest")
ax.text(0, 0, str(digits.target[i]))

[実行結果]

手書き数字の画像データが確認できました。

PCAで次元削減

まずはPCAで次元削減を実行し(2行目)、可視化を行います。

[Google Colaboratory]

1
2
3
4
5
6
7
from sklearn.decomposition import PCA
X_reduced = PCA(n_components=2).fit_transform(digits.data)
for each_label in digits.target_names:
c_plot_bool = digits.target == each_label
plt.scatter(X_reduced[c_plot_bool, 0], X_reduced[c_plot_bool, 1], label="{}".format(each_label))
plt.legend()
plt.show()

[実行結果]

数字ごとに分類されて欲しかったのですが、うまく分類できていないようです。

これはPCAが非線形データに対応できていないためです。

t-SNEで次元削減

次にt-SNEで次元削減を実行し(2行目)、可視化を行います。

[Google Colaboratory]

1
2
3
4
5
6
7
from sklearn.manifold import TSNE
X_reduced = TSNE(n_components=2, random_state=0).fit_transform(digits.data)
for each_label in digits.target_names:
c_plot_bool = digits.target == each_label
plt.scatter(X_reduced[c_plot_bool, 0], X_reduced[c_plot_bool, 1], label="{}".format(each_label))
plt.legend()
plt.show()

[実行結果]

t-SNEは、非線形データに対応できるためうまく分類されています。

1,3,8,9がやや混じっているように見えますが、これは人の目から見ても形が似ている数字があるためしかたがないように思えます。

入力データが複雑になればなるほどPCAはうまく次元削減できなくなることが多いのですが、t-SNEは複雑なデータでもかなり高精度な次元削減を行うことができます。

ただし、次のようなデメリットもあるので注意が必要です。

  • 処理に時間がかかる。
  • 4次元以上には不向き。
  • パラメータ調整が必要になる。

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×