Python
機械学習
scikit-learn
可視化

(t-SNE, DBSCAN, Decision Tree)で、(可視化 > クラスタリング > 特徴説明)を行う

More than 3 years have passed since last update.


まとめ


  • t-SNEというデータ可視化向けの次元削減アルゴリズムがある

  • t-SNEはデータのローカル構造を意識したアルゴリズムなので、同じくローカル構造を意識したクラスタリングメソッドであるDBSCANと相性がいい

  • DBSCANの結果をさらに決定木で分類すれば、t-SNEで可視化したデータに現れたクラスタの特徴を自動で説明できるのでは?

という発想でやってみました。実際、どういう風に応用できるかは、まだ思いついていません。


sklearnのload_bostonで実践してみる

まずは必要なものをimport

import numpy as np

from sklearn import datasets
from sklearn.manifold import TSNE
from matplotlib import pyplot as plt

bostonをロードしてTSNEで可視化してみる

幾つかのクラスタが現れるのを目視で分かる

boston = datasets.load_boston()

model = TSNE(n_components=2)
tsne_result = model.fit_transform(boston.data)
plt.plot(tsne_result[:,0], tsne_result[:,1], ".")

boston_tsne.png

比較するために、一度kmeansでクラスタリングしてみる

from sklearn.cluster import MiniBatchKMeans

# クラスタの数`n_clusters`はTSNEのグラフを見て、感覚で決めた
kmeans = MiniBatchKMeans(n_clusters=10, max_iter=300)
kmeans_tsne = kmeans.fit_predict(tsne_result)

#いい感じに色を付ける
color=cm.brg(np.linspace(0,1,np.max(kmeans_tsne) - np.min(kmeans_tsne)+1))
for i in range(np.min(kmeans_tsne), np.max(kmeans_tsne)+1):
plt.plot(tsne_result[kmeans_tsne == i][:,0],
tsne_result[kmeans_tsne == i][:,1],
".",
color=color[i]
)
plt.text(tsne_result[kmeans_tsne == i][:,0][0],
tsne_result[kmeans_tsne == i][:,1][0],
str(i), color="black", size=16
)

クラスタ(1,5)、(2,8)、(4,7,9)はそれぞれ分裂しているけど、構造的には繋がっているので、(私的には)あまり好ましくない

boston_tsne_kmeans.png

DBSCANでクラスタリングしてみる

from sklearn.cluster import DBSCAN

# `eps`は試行錯誤した結果
dbscan = DBSCAN(eps=3)
dbscan_tsne = dbscan.fit_predict(tsne_result)

#いい感じに色を付ける
color=cm.brg(np.linspace(0,1,np.max(dbscan_tsne) - np.min(dbscan_tsne)+1))
for i in range(np.min(dbscan_tsne), np.max(dbscan_tsne)+1):
plt.plot(tsne_result[dbscan_tsne == i][:,0],
tsne_result[dbscan_tsne == i][:,1],
".",
color=color[i+1]
)
plt.text(tsne_result[dbscan_tsne == i][:,0][0],
tsne_result[dbscan_tsne == i][:,1][0],
str(i), color="black", size=16
)

DBSCANでは、繋がっている島がちゃんと同じクラスタに入るので、望ましい

(-1は、外れているものたちが入るクラスタ)

boston_tsne_dbscan.png

さらに、決定木を生成して、それぞれのクラスタをうまく説明できないか試みる

from sklearn import tree

clf = tree.DecisionTreeClassifier()
# dbscanは-1のクラスタが生成されるので、ラベルは-1から始まる
clf.classes_ = np.max(dbscan_tsne) - np.min(dbscan_tsne) + 1
clf.fit(boston.data, dbscan_tsne)

# graphvizのdotファイルを生成する
with open("boston_tsne_dt.dot", 'w') as f:
tree.export_graphviz(
clf,
out_file=f,
feature_names=boston.feature_names,
filled=True,
rounded=True,
special_characters=True,
impurity=False,
proportion=False,
class_names=map(str, range(-1, np.max(dbscan_tsne) - np.min(dbscan_tsne)+1))
)

dot -T png boston_tsne_dt.dot > boston_tsne_dt.png

その結果が下記の図である

boston_tsne_dt.png

参考として、各クラスタのtarget (house price)を描画しておく

plt.boxplot([boston.target[dbscan_tsne == i]

for i in range(np.min(dbscan_tsne),
np.max(dbscan_tsne)+1)],
labels=range(np.min(dbscan_tsne),
np.max(dbscan_tsne)+1)
)

boston_tsne_price.png


考察

自分が気になったところをまとめると、


  • 税率(TAX)の高いクラスタ(6,7)は、値段も高くなるというわけではない


    • つまり、都市部だから値段が高くなるとは限らない?



  • 高値が跳ね上がるクラスタ(0,1,2)は、税率の低い場所にあって、それぞれ宅地(ZN)と年齢層(AGE)の割合で微妙に別れる


    • とはいえ、値段の範囲が広いので、クラスタと値段の関連性が薄れている



  • クラスタ(2,5)の値段分布は近いけど、税率で分かれている


    • 多分、税率によって、他の特徴もある程度連動しているから、TSNEの結果では両者をはっきりと区別した?



  • 税率の低いクラスタの中でも、3は特殊なクラスタである

ただ、実際これでなにか情報を提供できるかでいうと、かなり怪しい気がします。

ちなみに、元データにboston.targetを混ぜてみても、かなり近い結果となります。