Mahoutで体感する機械学習の実践

第5回K-MeansとCanopyクラスタリングでセグメンテーション分析を行う

入力データを用意する

サンプルデータを確認する

前回はセグメンテーション分析の理論編として、セグメンテーション分析と代表的なクラスタリングアルゴリズムであるK-MeansとCanopyクラスタリングについて解説しました。

今回は、実践編として、K-MeansとCanopyクラスタリングによるセグメンテーション分析をMahoutを用いて実際に行います。

なお、本連載の3回目同様、Mahoutのバージョンは現時点の最新版である0.7を対象とします。

今回利用するサンプルデータは、ARFF形式[1]で顧客のRFMが記載されています。@DATA以降が、実際のデータです。

  • サンプルデータ

今回のデータは、以下のような構造になっています。

  • 1行が顧客1人のデータを意味する
  • カンマ区切りで、左から「R(最終購買日⁠⁠F(購買頻度⁠⁠M(購入金額⁠⁠」を表す
(省略)

@DATA
顧客1の最終購買日, 顧客1の購買頻度, 顧客1の購買金額
顧客2の最終購買日, 顧客2の購買頻度, 顧客2の購買金額
顧客3の最終購買日, 顧客3の購買頻度, 顧客3の購買金額

(省略)

今回のデータはR、F、Mの3つとも、本来の値を1から100の範囲に収まるように正規化しています。

ARFF形式をVector形式へ変換する

MahoutでK-Meansを実行する場合、入力ファイルはMahoutのVector形式である必要があります。

サンプルデータはARFF形式なので、下記のようにMahoutのarff.vectorコマンドでVector形式に変換します。

hadoop fs -put gihyo-mahout-kmeans-sample.arff .
mahout arff.vector --input gihyo-mahout-kmeans-sample.arff --output gihyo-mahout-kmeans-sample.vector --dictOut gihyo-mahout-kmeans-sample.dict

arff.vectorコマンドの各パラメータは、それぞれ以下を意味します。

  • --input ⇒ 入力ファイル
  • --output ⇒ 出力ファイル
  • --dictOut ⇒ dictionaryファイルの出力先

生成されたVectorファイルは、以下のように、mahoutのseqdumperコマンドや、vectordumpコマンドで、中身を確認することができます。

mahout seqdumper -i gihyo-mahout-kmeans-sample.vector 
mahout vectordump -i gihyo-mahout-kmeans-sample.vector

なお、CSVファイルを入力ファイルに用いたい場合は、いったんCSVファイルをARFF形式に変換して、上記手順を踏んでください。Mahout自体には、CSV形式をVector形式に変換する機能は用意されていないためです。

K-Meansをコマンドラインから実行する

kmeansコマンドを実行する

それでは、サンプルデータを用いて実際にK-Meansを実行してみましょう。

MahoutのK-Means実装は、以下のようにkmeansコマンドで実行します。

mahout kmeans

kmeansコマンドのおもなパラメータとして以下があります。

  • --input(-i) ⇒ 入力元
  • --output(-o) ⇒ 出力先
  • --distanceMeasure(-dm) ⇒ 距離計算方法の指定。デフォルト値はユークリッド平方距離
  • --numCluster(-n) ⇒ 生成するクラスタの数
  • --clusters(-c) ⇒ 初期クラスタ
  • --convergenceDelta ⇒ 収束閾値。クラスタ重心の1回の移動距離が閾値以下になった場合、計算を終了する
  • --maxIter ⇒ クラスタ重心点の最大計算回数
  • --overwrite(-ow) ⇒ 出力先にファイルがすでにあった場合に上書きを行う
  • --clustering ⇒ clusteredPointsの出力。clusteredPointsには、各要素がどのクラスタに属するかが記載されている
  • --method ⇒ mapreduceを指定した場合はMapReduceによる計算、sequentialを指定した場合はローカルマシンによる計算を行う。デフォルト値はmapreduce

今回は、以下の設定で実行してみます。

  • クラスタ重心点の最大計算回数 ⇒ 50回
  • 生成するクラスタの数 ⇒ 10個
  • クラスタの初期座標 ⇒ ランダム
  • clusteredPointsの出力 ⇒ 有り
  • 距離計算方法 ⇒ ユークリッド距離

以下のコマンドを入力してみてください。

mahout kmeans --input gihyo-mahout-kmeans-sample.vector --output gihyo-kmeans-output --maxIter 50 --numClusters 10 --clusters gihyo-kmeans-null-cluster --clustering -dm org.apache.mahout.common.distance.EuclideanDistanceMeasure

今回は出力先にhdfsを利用しているので、出力結果のファイル構成を確認するには以下のように実行します。

hadoop fs -lsr gihyo-kmeans-output

以下のような出力が表示されたでしょうか?

-rw-r--r--   1 yamakatu supergroup        194 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/_policy
drwxr-xr-x   - yamakatu supergroup          0 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusteredPoints
-rw-r--r--   1 yamakatu supergroup      46560 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusteredPoints/part-m-0
drwxr-xr-x   - yamakatu supergroup          0 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusters-0
-rw-r--r--   1 yamakatu supergroup        194 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusters-0/_policy
-rw-r--r--   1 yamakatu supergroup        359 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusters-0/part-00000

(省略)

-rw-r--r--   1 yamakatu supergroup        359 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusters-0/part-00009

(省略)

drwxr-xr-x   - yamakatu supergroup          0 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusters-20-final
-rw-r--r--   1 yamakatu supergroup        194 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusters-20-final/_policy
-rw-r--r--   1 yamakatu supergroup        332 2013-06-01 11:41 /user/yamakatu/gihyo-kmeans-output/clusters-20-final/part-00000

(省略)

MahoutのKmaensコマンドは、クラスタの計算回数ごとにclusters-xフォルダを作成します。clusters-0フォルダは初期状態、clusters-1フォルダは1回目の計算終了後の結果を含みます。

最終結果には、postfixとして、finalの文字列が付加されます。 上記出力では、clusters-20-finalが最終結果なので、20回クラスタの計算が行われたことがわかります[2]⁠。

--clusteringパラメータにより生成されるgihyo-kmeans-output/clusteredPointsフォルダには、各要素がどのクラスタに属しているかの情報が保存されています。

K-Meansの実行結果を確認する

次に、実際に生成されたクラスタを確認してみましょう。

生成されたクラスタは、以下のようにclusterdumpコマンドを利用することで出力できます。

mahout clusterdump

clusterdumpコマンドのおもなパラメータとして、以下があります。

  • --input(-i) ⇒ 入力元
  • --output(-o) ⇒ 出力先
  • --outputFormat(-of) ⇒ 出力フォーマット。デフォルトのTEXT以外にCSV、GRAPH_ML(GraphML形式)を指定可能
  • --pointsDir(-p) ⇒ clusteredPointsの指定
  • --dictionary(-d) ⇒ dictionaryファイル指定

今回は、先ほど実行したkmeansの出力結果のうち、最終結果を確認してみましょう。以下のコマンドを入力してみてください。

mahout clusterdump --input gihyo-kmeans-output/clusters-20-final --output ~/gihyo-kmeans-dump.text

次に、clusterdumpで出力したファイルの中身を確認してみます。

cat ~/gihyo-kmeans-dump.text
VL-959{n=103 c=[22.165, 82.806, 27.854] r=[12.907, 11.927, 15.057]}
VL-969{n=98 c=[73.776, 85.929, 26.561] r=[14.760, 9.117, 15.672]}

(省略)

ここで表示される出力結果は、以下の意味になります。

  • VL-x ⇒ クラスタ
  • n ⇒ そのクラスタに属する要素の数
  • c ⇒ そのクラスタの重心
  • r ⇒ そのクラスタの半径

クラスタ数を10で実行していますので、VLで始まる行が10行生成されているはずです。

各クラスタに属する要素を同時に出力したい場合は、以下のようにclusteredPointsを指定します。

mahout clusterdump --input gihyo-kmeans-output/clusters-20-final --output ~/gihyo-kmeans-dump.text -p gihyo-kmeans-output/clusteredPoints

clusteredPointsの内容だけを確認したい場合は、Mahoutのseqdumperコマンドで確認することができます。

mahout seqdumper --input gihyo-kmeans-output/clusteredPoints

CanopyクラスタリングをK-Meansの前処理として実行する

canopyコマンドを実行する

前回でもとりあげたように、K-Meansでは生成するクラスタ数を指定する必要があります。そのため、生成するクラスタの大きさを指定してクラスタリングを行いたい場合、K-Meansのみでは実現が困難です。これは、生成するクラスタのおよその大きさを指定できるCanopyクラスタリングをK-Meansの前処理として利用することで解決が可能です。

Mahoutでは、以下のようにしてCanopyクラスタリングを実行します。

mahout canopy

canopyコマンドのおもなパラメータとして、以下があります。

  • --input(-i) ⇒ 入力元
  • --output(-o) ⇒ 出力先
  • --distanceMeasure(-dm) ⇒ 距離計算方法の指定。デフォルト値はユークリッド平方距離
  • --t1 ⇒ t1値
  • --t2 ⇒ t2値
  • --overwrite(-ow) ⇒ 出力先にファイルが既にあった場合に上書きを行う
  • --clustering ⇒ clusteredPointsファイルの出力。clusteredPointsファイルには各要素がどのクラスタに所属するか記載されている
  • --method ⇒ mapreduceを指定した場合はMapReduceによる計算、sequentialを指定した場合はローカルマシンによる計算を行う。デフォルト値はmapreduce

以下のコマンドを入力し、canopyクラスタリングを実行してみましょう。

mahout canopy --input gihyo-mahout-kmeans-sample.vector --output gihyo-canopy-output --t1 33 --t2 40 --clustering -dmorg.apache.mahout.common.distance.EuclideanDistanceMeasure

今回の入力データは、RFMの3つの値を最大値100で正規化しています。上記の例では、3次元間の距離をおおよそこの1/3程度にしたいと考え、t1値に33を、t2値にはそれより少し大きい値を指定しています。

K-Means同様、Canopyクラスタリングの出力も、以下のようにclusterdumpコマンドで確認することができます。

mahout clusterdump --input gihyo-canopy-output/clusters-0-final --output ~/gihyo-canopy-dump.text

今回の著者の環境では、クラスタは8個生成されました。

ただし、Canopyクラスタリングはクラスタの初期重心がランダムで生成されるため、必ずしも同じ実行結果になるとは限りません。

Canopyクラスタリングの結果を利用してK-menasを実行する

次に、この結果をK-Meansの初期クラスタとして利用してみましょう。

Canopyクラスタリングの結果をK-Meansの初期値とする場合、--clustersパラメータにCanopyクラスタリングの結果を指定します。

mahout kmeans --input gihyo-mahout-kmeans-sample.vector --output gihyo-kmeans-canopy-output --maxIter 50 --clusters gihyo-canopy-output/clusters-0-final --clustering -dmorg.apache.mahout.common.distance.EuclideanDistanceMeasure

これまでと同様、clusterdumpコマンドで生成されたクラスタを確認すると、Canopyクラスタリングで生成されたクラスタ数と同じく、8個のクラスタが生成されたことがわかると思います。

このように、CanopyクラスタリングをK-Meansの前処理として利用することで、クラスタの数ではなく、生成したいクラスタの大きさからK-Meansを用いたクラスタリングを実現することができるのです。

ライブラリ呼び出しでK-MeansとCanopyクラスタリングを実行するには

ライブラリを利用してMahoutのK-MeansとCanopyクラスタリングを実行するには、それぞれ以下のクラスのrunメソッドを利用します。

K-Means
⇒org.apache.mahout.clustering.kmeans.KMeansDriverクラス
Canopyクラスタリング
⇒org.apache.mahout.clustering.canopy.CanopyDriverクラス

これらは、どちらもコマンドライン実行の処理を実装しているクラスでもあります。そのため、これらのクラス内部に記載されているコマンドライン実装が、それぞれのrunメソッドの使い方の参考にもなります。ライブラリ呼び出しを実装する際は、適宜参考にしてみてください。

次回は、消費者の購買予測を取り上げます。お楽しみに!

おすすめ記事

記事・ニュース一覧