【入門シリーズ】PySparkでクラスタリングを体験してみよう 〜Irisデータセットを使って〜

こんにちは、クラウドデータ研究所です。
当ブログではこれまでに 重回帰ロジスティック回帰 など、PySparkを使った機械学習の手法を取り上げてきました。

今回はシリーズの一つとして、「クラスタリング」を紹介します。
クラスタリングは「答え(ラベル)が与えられていないデータをグループに分ける手法」で、教師なし学習と呼ばれます。

今回扱う題材:Irisデータセット

「Irisデータセット」は、アヤメという花の品種データです。
花びらやがく片の長さ・幅といった特徴量があり、実際には Setosa, Versicolor, Virginica の3種類の品種ラベルがついています。

アヤメの花の品種について 出典:https://rpubs.com/Jay2548/519589

今回はラベルを使わずに、クラスタリングだけで「3種類に分かれるか?」を試します。

Sparkセッションの準備

Pysparkを使う時は、まず「セッション」を立ち上げて処理の窓口を作っていきます。

from pyspark.sql import SparkSession
spark = SparkSession.builder.master("local").appName("kmeans_iris").getOrCreate()

データの読み込み

次にローカルに存在するirisのcsvファイルを読み込んでSparkのデータフレームを生成します。

data = spark.read.csv("./data/iris.csv", header=True, inferSchema=True)
data.show(5)

CSVファイルを読み込んで、最初の数行を確認します。

+------------+-----------+------------+-----------+-------+
|sepal_length|sepal_width|petal_length|petal_width|variety|
+------------+-----------+------------+-----------+-------+
|         5.1|        3.5|         1.4|        0.2| Setosa|
|         4.9|        3.0|         1.4|        0.2| Setosa|
|         4.7|        3.2|         1.3|        0.2| Setosa|
|         4.6|        3.1|         1.5|        0.2| Setosa|
|         5.0|        3.6|         1.4|        0.2| Setosa|
|         5.4|        3.9|         1.7|        0.4| Setosa|
|         4.6|        3.4|         1.4|        0.3| Setosa|
|         5.0|        3.4|         1.5|        0.2| Setosa|
|         4.4|        2.9|         1.4|        0.2| Setosa|
|         4.9|        3.1|         1.5|        0.1| Setosa|
|         5.4|        3.7|         1.5|        0.2| Setosa|
|         4.8|        3.4|         1.6|        0.2| Setosa|


カラムは次の通りです。

  • sepal_length(がく片の長さ)
  • sepal_width(がく片の幅)
  • petal_length(花びらの長さ)
  • petal_width(花びらの幅)
  • variety(品種ラベル ※今回は使わない)

varietyのグルーピングをしてカウント値を見てみると、50個ずつアヤメの種類があることがわかります。

data.groupby("variety").count().show()
+----------+-----+
|   variety|count|
+----------+-----+
| Virginica|   50|
|    Setosa|   50|
|Versicolor|   50|
+----------+-----+

特徴量をまとめる

機械学習には、予測する為の変数として「目的変数」といい、その為のインプットになる変数の事を「説明変数」といいます。

PySpark MLでは、複数の説明変数、つまり特徴量を「1つのベクトル」にまとめる必要があります。
ここでは VectorAssembler を使います。

https://spark.apache.org/docs/latest/api/python/reference/api/pyspark.ml.feature.VectorAssembler.html

これは複数のカラムを、一つのベクトルのカラムに変換するメソッドです。

from pyspark.ml.feature import VectorAssembler
assemble = VectorAssembler(
    inputCols=["sepal_length", "sepal_width", "petal_length", "petal_width"],
    outputCol="features"
)
pred = assemble.transform(data)

KMeansでクラスタリング

KMeansとはものすごく簡単に説明すると

  • たくさんのデータを「似たもの同士」でグループ分けする方法
  • そのとき、グループの真ん中の点(平均=centroid) を目印にして分ける

つまり「人を背の高さと体重で分けたら、背が高くて重い人は同じグループになる」みたいな感じです。

クラスタ数 kを変えて試し、クラスタリングの良さを「シルエット係数」で評価します。

from pyspark.ml.clustering import KMeans
from pyspark.ml.evaluation import ClusteringEvaluator

for k in range(2, 7):
    kmeans = KMeans().setK(k).setSeed(1)
    model = kmeans.fit(pred)
    prediction = model.transform(pred)

    evaluator = ClusteringEvaluator()
    sil = evaluator.evaluate(prediction)
    print(f"k={k}, シルエット係数={sil}")

結果を見てみると、k=3のときに最も良い値になります。
これは、実際の品種が3種類あることと対応していて面白いですね。

シルエット変数 k=3 で実行してみる

シルエット変数を3として、クラスタリングつまりグループ分けをしてみましょう。

kmeans = KMeans().setK(3).setSeed(1)
model = kmeans.fit(pred)
prediction = model.transform(pred)

prediction.groupby("prediction").count().show()

出力例:

+----------+-----+
|prediction|count|
+----------+-----+
|         0|   38|
|         1|   62|
|         2|   50|
+----------+-----+

ちゃんと3つのグループに分けられていることが分かります。

本来は、50個ずつアヤメの種類が散らばっていましたが、PysparkのMLのモジュールを使用してクラスタリングをすることによって上記のように3種類のグルーピングをすることができました。

まとめ

  • クラスタリングは「正解ラベルがなくてもグループ分けできる」教師なし学習の代表例
  • PySparkでは、VectorAssembler で特徴量をまとめてから KMeans に渡す
  • 評価には「シルエット係数」を使うと便利
  • Irisデータでは、実際の品種数と同じ「k=3」が最適

最新情報をチェックしよう!