scikit-learn + クラスタリングに関してはこのブログのだいぶ初期にちょっとだけ触ったのですが、今にして思うと説明不足感が否めないですし、そもそもこれだけじゃ scikit-learn を思い通りの目的にあわせて使えないという意見もあるかと思います。そこで改めて scikit-learn による基本的なクラスタリングについて説明していきます。
といっても基本的な話としては本家のドキュメントを読めで話が終わってしまうのですが、日本語の情報があると何かと助かるということもあるかと思います。
生徒の成績をもとに班分けをおこなう
よくあるケースとしては例えば、生徒の国語・数学・英語の成績をもとにいくつかのグループに分けたいという場面です。このとき、各科目の合計点の上位から順番に分けてもいいのですが、中には国語が良く出来るけど数学はいまひとつな生徒、数学は得意だけど国語はいまひとつな生徒もいるでしょう。この生徒たちが合計点の上位から単純に班分けされたとしたら不幸です。そこでクラスタリング分析の出番です。成績の傾向のよく似た生徒同士をまとめることによって、単なる成績上位順ではなくより適したグルーピングができるようになります。
さっそく K-means クラスタリングのためのコードを書いて試してみましょう。なお K-means クラスタリングの仕様の説明としてはこちらの記事が詳しいのであわせて参考にしてみてください。今回は生徒たちを 3 つの班に分けてみるものとします。
import numpy as np
from sklearn.cluster import KMeans
# 生徒の国語・数学・英語の各得点を配列として与える
features = np.array([
[ 80, 85, 100 ],
[ 96, 100, 100 ],
[ 54, 83, 98 ],
[ 80, 98, 98 ],
[ 90, 92, 91 ],
[ 84, 78, 82 ],
[ 79, 100, 96 ],
[ 88, 92, 92 ],
[ 98, 73, 72 ],
[ 75, 84, 85 ],
[ 92, 100, 96 ],
[ 96, 92, 90 ],
[ 99, 76, 91 ],
[ 75, 82, 88 ],
[ 90, 94, 94 ],
[ 54, 84, 87 ],
[ 92, 89, 62 ],
[ 88, 94, 97 ],
[ 42, 99, 80 ],
[ 70, 98, 70 ],
[ 94, 78, 83 ],
[ 52, 73, 87 ],
[ 94, 88, 72 ],
[ 70, 73, 80 ],
[ 95, 84, 90 ],
[ 95, 88, 84 ],
[ 75, 97, 89 ],
[ 49, 81, 86 ],
[ 83, 72, 80 ],
[ 75, 73, 88 ],
[ 79, 82, 76 ],
[ 100, 77, 89 ],
[ 88, 63, 79 ],
[ 100, 50, 86 ],
[ 55, 96, 84 ],
[ 92, 74, 77 ],
[ 97, 50, 73 ],
])
# K-means クラスタリングをおこなう
# この例では 3 つのグループに分割 (メルセンヌツイスターの乱数の種を 10 とする)
kmeans_model = KMeans(n_clusters=3, random_state=10).fit(features)
# 分類先となったラベルを取得する
labels = kmeans_model.labels_
# ラベル (班) 、成績、三科目の合計得点を表示する
for label, feature in zip(labels, features):
print(label, feature, feature.sum())
#=>
# 2 [ 80 85 100] 265
# 2 [ 96 100 100] 296
# 0 [54 83 98] 235
# 2 [80 98 98] 276
# 2 [90 92 91] 273
# 1 [84 78 82] 244
# 2 [ 79 100 96] 275
# 2 [88 92 92] 272
# 1 [98 73 72] 243
# 2 [75 84 85] 244
# 2 [ 92 100 96] 288
# 2 [96 92 90] 278
# 1 [99 76 91] 266
# 2 [75 82 88] 245
# 2 [90 94 94] 278
# 0 [54 84 87] 225
# 1 [92 89 62] 243
# 2 [88 94 97] 279
# 0 [42 99 80] 221
# 0 [70 98 70] 238
# 1 [94 78 83] 255
# 0 [52 73 87] 212
# 1 [94 88 72] 254
# 1 [70 73 80] 223
# 2 [95 84 90] 269
# 2 [95 88 84] 267
# 2 [75 97 89] 261
# 0 [49 81 86] 216
# 1 [83 72 80] 235
# 1 [75 73 88] 236
# 1 [79 82 76] 237
# 1 [100 77 89] 266
# 1 [88 63 79] 230
# 1 [100 50 86] 236
# 0 [55 96 84] 235
# 1 [92 74 77] 243
# 1 [97 50 73] 220
さて、どうやら無事に 3 つの班に分かれたようです。中身を精査してみましょう。まずラベルが 2 となった生徒たちは得点がどれも高く比較的優秀なグループです。次にラベルが 1 となった生徒たちを見てみると、中間くらいではありますが、国語だけ得意な生徒や国語以外はそれほどでもない生徒も分類されています。どうやら文系寄りの生徒が集まるグループのようですね。最後にラベルが 0 となった生徒たちは、全体的にやや低めの成績か、あるいは数学が得意な生徒が分類されています。
まとめ
このように、合計得点の上位から三班に分けるのではなく、生徒ごとの傾向にあわせた班分けができました。
現実の問題においても、素性の選択をうまくおこなってこのように数値のベクトルさえ描いてしまえば、クラスタリング分析ができるというわけです。
scikit-learn を使えば K-means クラスタリング自体はたった 1 行で出来ますから、機械学習ライブラリの強力なパワーを実感できますね。