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

第7回DecisionForestを用いて顧客の購買予測を行う

Mahout 0.8からTestForestによる分析結果の出力がよりくわしく出力されるように

前回は理論編として、決定木とRandomForestについて解説しました。今回は実践編として、RandomForestのMahout実装であるDecisionForestを用いて、実際に顧客の購買予測を行ってみます。

解説の前に、2013年7月25日に、Mahoutのバージョン0.8がリリースされたことに触れておきます。バージョン0.8のDecisionForestは、バージョン0.7と比較し、後述するTestForestによる分析結果でκ統計量と信頼度も出力されるように改良されています。細かい不具合への対応やパフォーマンス改善も行われているので、これからMahoutを導入される方はバージョン0.8をお勧めします。

ただし、バージョン0.8が内部で利用しているHadoopはバージョン1.1.2へ変更されており、バージョン0.7が利用していたバージョン0.20とは異なります。バージョン0.7からバージョン0.8へ移行される方はご注意ください。

今回は、これまでに引き続きバージョン0.7の実行例を示しますが、すべての操作はバージョン0.8でも動作します。

サンプルデータをダウンロードする

今回利用するサンプルデータは、CSV形式で記載さており、以下のような構造になっています。

  • 1行が1顧客のデータ
  • 各行はカンマ区切りで各商品の購入有無を記載(Y:ある、N:ない)
顧客Aの商品1の購入有無, 顧客Aの商品2の購入有無, … 顧客Aの商品1000の購入有無
顧客Bの商品1の購入有無, 顧客Bの商品2の購入有無, … 顧客Bの商品1000の購入有無
顧客Cの商品1の購入有無, 顧客Cの商品2の購入有無, … 顧客Cの商品1000の購入有無
(省略)

今回は1000個の商品数を対象とするので、CSVファイルの列数は1000列で構成されています。

また、モデルの構築に利用する学習データと、分類時に利用する分類データで、顧客数はそれぞれ以下のようになっています。

descriptorファイルを作成する

今回利用するDecisionForestは、入力データにCSV形式を利用します。CSV形式は、前々回で利用したARFF形式と異なり、各列の値の型や、分類クラスを表す列の指定を定義できません。そのため、まず最初にこれらを定義するdescriptorファイルを作成する必要があります。

descriptorファイルを作成するには、mahout-core-0.7-job.jar[1]に実装されている、org.apache.mahout.classifier.df.tools.Describeクラスを利用します。

Describeクラスは、以下のようにhadoopコマンドから実行します。

hadoop fs -put gihyo-mahout-df-sample-learning.csv .
hadoop jar $MAHOUT_HOME/mahout-core-0.7-job.jar org.apache.mahout.classifier.df.tools.Describe -p gihyo-mahout-df-sample-learning.csv -f gihyo-mahout-df-sample.info -d 999 C L

Describeクラスのパラメータには、以下があります。

  • --path (-p)⇒入力データ(学習データ)を指定
  • --file (-f)⇒describeファイルの出力先を指定
  • --descriptor (-d)⇒入力データの各列を説明
  • --regression (-r)⇒回帰問題を解く場合はこのオプションを付加

--descriptor (-d)オプションでは、以下の種類の型を利用できます。

  • N⇒数値
  • C⇒カテゴリ(性別など)
  • L⇒ラベル(分類先クラス)
  • I⇒無視(利用しない)

たとえば、入力ファイルの列が5列で、数値、数値、カテゴリ、無視、ラベルの順に並んでいる場合、以下のように表記します。

-d N N C I L

同じ種類が連続する場合は、以下のように、連続する数をその種類の前に記入することで、表記を省略できます。

-d 2 N C I L

今回のサンプルデータでは、1000列のうち、先頭からの999列が「Y」「N」のカテゴリ値、そして最後の1列がラベルになります。そのため、以下のように表記しています。

-d 999 C L

モデルを作成する

descriptorファイルが無事に作成できた後は、descriptorファイルと学習データを利用してモデルを作成します。

モデルの作成は、mahout-examples-0.7-job.jarに実装されている、org.apache.mahout.classifier.df.mapreduce.BuildForestクラスを利用します。BuildForestクラスも、先ほど同様、以下のようにhadoopコマンドから実行します。

hadoop jar $MAHOUT_HOME/mahout-examples-0.7-job.jar org.apache.mahout.classifier.df.mapreduce.BuildForest -d gihyo-mahout-df-sample-learning.csv -ds gihyo-mahout-df-sample.info -sl 32 -t 25 -o gihyo-mahout-df-sample.model

BuildForestクラスのおもなパラメータには、以下があります。

  • --data (-d)⇒入力データ(学習データ)を指定
  • --dataset (-ds)⇒作成したdescriptorファイルを指定
  • --selection (-sl)⇒一つの決定木で利用する説明変数の数(CSVファイルの列数)を指定(ランダム抽出)
  • --partial (-p)⇒指定した場合、学習データのうち、一部のデータのみを利用
  • --nbtrees (-t)⇒作成する決定木の数を指定
  • --output (-o)⇒モデルの出力先

--selection (-sl)の値は、説明変数がn個の場合、√nが推奨値です。よって今回は、--selection (-sl)の値を32で実行しています。

モデルの精度を評価する

無事モデルが生成されたら、次はモデルの精度を評価する必要があります。

本来、モデルの評価は、モデルの作成に利用した学習データとは異なるデータを利用して行うべきです。しかし今回は省略して、同一のデータを利用し、手順のみを示します。

モデルの評価は、mahout-examples-0.7-job.jarに実装されている、org.apache.mahout.classifier.df.mapreduce.TestForestクラスを利用します。

hadoop jar $MAHOUT_HOME/mahout-examples-0.7-job.jar org.apache.mahout.classifier.df.mapreduce.TestForest -i gihyo-mahout-df-sample-learning.csv -ds gihyo-mahout-df-sample.info -m gihyo-mahout-df-sample.model -a -mr -o gihyo-mahout-df-sample-test

TestForestのおもなパラメータには、以下があります。

  • --input (-i)⇒入力データ(テストデータ)を指定
  • --dataset (-ds)⇒作成したdescriptorファイルを指定
  • --model (-m)⇒作成したモデルを指定
  • --output (-o)⇒出力先を指定
  • --analyze (-a)⇒付加した場合、分析結果を出力
  • --mapreduce (-mr)⇒付加した場合、MapReduceで実行

--analyze (-a) オプションを指定した場合、以下のような出力が表示されます[2]⁠。

=======================================================
Summary
-------------------------------------------------------
Correctly Classified Instances          :        989           98.9%
Incorrectly Classified Instances        :         11            1.1%
Total Classified Instances              :       1000

=======================================================
Confusion Matrix
-------------------------------------------------------
a         b         <--Classified as
796       4          |  800        a     = N
7         193        |  200        b     = Y

各欄の出力は、以下を意味します。

Summary
  • Correctly Classified Instances⇒正解数と正解率
  • Incorrectly Classified Instances⇒誤答数と誤答率
  • Total Classified Instances⇒全データ件数
Confusion Matrix
  • 列⇒今回分類されたクラス
  • 行⇒テストデータに記載されたクラス

上記の出力の場合、以下を意味します。

  • ラベルNのクラスに分類されるべき800件のデータのうち、796件が正しくNに分類され、4件が誤ってYに分類された
  • ラベルYのクラスに分類されるべき200件のデータのうち、193件が正しくYに分類され、7件が誤ってNに分類された

モデルを視覚化する

作成したモデルは、以下のようにForestVisualizerクラスを利用することで、決定木をツリー構造でコマンドライン上に出力できます。

hadoop jar $MAHOUT_HOME/mahout-core-0.7-job.jar org.apache.mahout.classifier.df.tools.ForestVisualizer --dataset gihyo-mahout-df-sample.info --model gihyo-mahout-df-sample.model

Tree[1]:
120 = N
|   47 = N
|   |   783 = N
|   |   |   893 = N
|   |   |   |   215 = N
|   |   |   |   |   987 = N
|   |   |   |   |   |   274 = N
|   |   |   |   |   |   |   270 = N
(省略)

ForestVisualizerのおもなパラメータには、以下があります。

  • --dataset (-ds)⇒作成したdescriptorファイルを指定
  • --model (-m)⇒作成したモデルを指定

モデルを用いて分類する

次は分類です。分類も、前述のTestForestクラスを用いて、以下のように実現できます。

hadoop fs -put gihyo-mahout-df-sample-classified.csv .
hadoop jar $MAHOUT_HOME/mahout-examples-0.7-job.jar org.apache.mahout.classifier.df.mapreduce.TestForest -i gihyo-mahout-df-sample-classified.csv -ds gihyo-mahout-df-sample.info -m gihyo-mahout-df-sample.model -mr -o gihyo-mahout-df-sample-classified

分類結果は、先ほどの実行例でも利用した--output (-o)パラメータの指定先以下に保存されます。以下のコマンドで確認してみましょう。

hadoop fs -cat gihyo-mahout-df-sample-classified/gihyo-mahout-df-sample-classified.csv.out

1.0
0.0
1.0
0.0
0.0
(省略)

出力は、各行が分類対象として与えた入力データの1行に対応します。

今回の分類先クラスのラベルはYとNでしたが、上記の出力は0.0と1.0で表現されています。このように、分類先のクラスは、学習データで指定したラベルではなく、ラベルに紐づくコード(double型)で出力されます。今回の例では、Nが0.0、Yが1.0で表現されています。各ラベルとコードの紐付けは、TestForestを--analyze (-a)を付加して場合した場合に結果に含まれる、ConfusionMatrixと同じ順序になります。

descriptorファイルの中身を確認する

Mahoutのバージョンが0.8の場合は、descriptorファイルはJSON形式で作成されているため[3]⁠、以下のように、作成したdescriptorファイルの中身からラベルとコードの紐付けを目視でも確認できます。

hadoop fs -cat gihyo-mahout-df-sample.info

[{"values":["N","Y"],"label":false,"type":"categorical"}, … {"values":["N","Y"],"label":true,"type":"categorical"}]

学習データの各列に対応して、以下の順で情報が記載されています。

  • values⇒値のパターン
  • label⇒ラベルかどうか
  • type⇒descriptorファイル作成時に指定した型

今回利用した学習データは、最後の1列がラベルなので、最後の列のlabelがtrue、それ以外の列はlabelがfalseになっています。valuesの値の順序がコードに対応し、1つ目が0.0、2つ目が1.0……のようになります。

そのため、もともとラベルが数値で表現されている場合、出力された数値はラベルのコードであり、ラベルそのものではないことに注意が必要です。ラベルの数値がラベルのコードと同じ値になる保証はありません。

プログラム内部からDecisionForestを利用するには

プログラム内部からMahoutのDecisionForestを利用する場合、プログラム内部で各ラベルとコードの紐付けが解決できる必要があります。以下のメソッドを使えば、コードからラベル名を取得できます。

org.apache.mahout.classifier.df.data.Dataset.getLabelString(double code)

逆に、ラベルからコードを取得する場合は、以下のメソッドを利用します。

org.apache.mahout.classifier.df.data.Dataset.labelCode(String label)

ラベルの一覧は、以下のメソッドで取得できます。

org.apache.mahout.classifier.df.data.Dataset.labels()

このように、Mahoutを用いることで、RandomForestによる分類モデルの構築、評価、視覚化、そして分類までをひととおり行うことができます。

次回は、ナイーブベイズを利用したアンケート分析を取り上げます。

おすすめ記事

記事・ニュース一覧