Random Forests(Partial Implementation)を試す

ここの話を試す.まず,テストデータをダウンロードする.

$ wget http://nsl.cs.unb.ca/NSL-KDD/KDDTrain+.arff
$ wget http://nsl.cs.unb.ca/NSL-KDD/KDDTest+.arff

ダウンロードしたら,それらのファイルで@で始まる行を削除する.そしたら,それらのファイルをhdfsに上げる.

$ hadoop fs -mkdir mahout
$ hadoop fs -mkdir mahout/nslkdd
$ hadoop fs -put KDDTrain+.arff mahout/nslkdd
$ hadoop fs -put KDDTest+.arff mahout/nslkdd

次に説明ファイルを作る.

$ hadoop jar $MAHOUT_HOME/core/target/mahout-core-0.5-SNAPSHOT-job.jar org.apache.mahout.df.tools.Describe -p mahout/nslkdd/KDDTrain+.arff -f mahout/nslkdd/KDDTrain+.info -d N 3 C 2 N C 4 N C 8 N 2 C 19 N L

できたら,モデルを作るために以下を実行.

$ hadoop jar $MAHOUT_HOME/examples/target/mahout-examples-0.5-SNAPSHOT-job.jar org.apache.mahout.df.mapreduce.BuildForest -Dmapred.max.split.size=1874231 -oob -d mahout/nslkdd/KDDTrain+.arff -ds mahout/nslkdd/KDDTrain+.info -sl 5 -p -t 100 -o nsl-forest

モデルができたら,テスト例でテストを流す.

$ hadoop jar $MAHOUT_HOME/examples/target/mahout-examples-0.5-SNAPSHOT-job.jar org.apache.mahout.df.mapreduce.TestForest -i mahout/nslkdd/KDDTest+.arff -ds mahout/nslkdd/KDDTrain+.info -m nsl-forest -a -mr -o predictions

という感じで,一通り完了.

がしかし,今日時点のtrunkはバグっていました….数日待てば修正されそうな気もするけど,とりあえず,試したかったので,以下の修正で回避しました.

Index: core/src/main/java/org/apache/mahout/df/mapreduce/Classifier.java
===================================================================
--- core/src/main/java/org/apache/mahout/df/mapreduce/Classifier.java	(リビジョン 1090534)
+++ core/src/main/java/org/apache/mahout/df/mapreduce/Classifier.java	(作業コピー)
@@ -158,6 +158,8 @@
// read all the output
for (Path path : outfiles) {
+log.info("path = {}", path.toString());
+if(path.getName().startsWith("part-m-")){
FSDataOutputStream ofile = null;
try {
for (Pair<LongWritable,Text> record : new SequenceFileIterable<LongWritable,Text>(path, true, conf)) {
@@ -179,7 +181,7 @@
}
} finally {
ofile.close();
-      }
+      }}
}
}
Index: core/src/main/java/org/apache/mahout/df/mapreduce/partial/PartialBuilder.java
===================================================================
--- core/src/main/java/org/apache/mahout/df/mapreduce/partial/PartialBuilder.java	(リビジョン 1090534)
+++ core/src/main/java/org/apache/mahout/df/mapreduce/partial/PartialBuilder.java	(作業コピー)
@@ -170,6 +170,7 @@
// read all the outputs
int index = 0;
for (Path path : outfiles) {
+if(path.getName().startsWith("part-m-")){
for (Pair<TreeID,MapredOutput> record : new SequenceFileIterable<TreeID, MapredOutput>(path, conf)) {
TreeID key = record.getFirst();
MapredOutput value = record.getSecond();
@@ -181,7 +182,7 @@
}
processOutput(firstIds, key, value, callback);
index++;
-      }
+      }}
}
// make sure we got all the keys/values
Index: core/src/main/java/org/apache/mahout/df/mapreduce/partial/Step0Job.java
===================================================================
--- core/src/main/java/org/apache/mahout/df/mapreduce/partial/Step0Job.java	(リビジョン 1090534)
+++ core/src/main/java/org/apache/mahout/df/mapreduce/partial/Step0Job.java	(作業コピー)
@@ -139,10 +139,12 @@
// read all the outputs
for (Path path : outfiles) {
+log.info("path = {}", path.toString());
+if(path.getName().startsWith("part-m-")){
for (Pair<IntWritable,Step0Output> record : new SequenceFileIterable<IntWritable,Step0Output>(path, conf)) {
keys.add(record.getFirst().get());
values.add(record.getSecond());
-      }
+      }}
}
return processOutput(keys, values);

という感じで,Random Forestsを試してみたものの,今やりたいことがこのままでは適用できない感じだから,このRandom Forestsを手直しするか,自前でCARTを実装するか,迷い中….どうしようかな.

Random Forestsを試す

ここに書いてあるけど、Random Forestsを試してみる。Hadoop環境が構築済みで、Mahoutをチェックアウトしてビルド(mvn install -DskipTestsをやっておく)してあることが前提で進めると、まず、テストデータを取得してHDFSに入れる。

$ wget http://archive.ics.uci.edu/ml/machine-learning-databases/glass/glass.data
$ hadoop fs -mkdir mahout
$ hadoop fs -mkdir mahout/glass
$ hadoop fs -put glass.data mahout/glass

そんで、説明ファイルを作る。

$ hadoop jar $MAHOUT_HOME/core/target/mahout-core-0.5-SNAPSHOT-job.jar org.apache.mahout.df.tools.Describe -p mahout/glass/glass.data -f mahout/glass/glass.info -d I 9 N L

以下のようにやれば中身を確認できる。

$ hadoop fs -cat mahout/glass/glass.info

Iが無視、Nが数値、Cがカテゴリ文字列、Lがラベル(目的変数)な感じ。N N N N みたいに書くのは面倒だから、4 N みたいな省略も可能。そんで、実行。

$ hadoop jar $MAHOUT_HOME/examples/target/mahout-examples-0.5-SNAPSHOT-job.jar org.apache.mahout.df.BreimanExample -d mahout/glass/glass.data -ds mahout/glass/glass.info -i 10 -t 100
...
11/04/09 07:29:09 INFO df.BreimanExample: ********************************************
11/04/09 07:29:09 INFO df.BreimanExample: Selection error : 0.2857142857142857
11/04/09 07:29:09 INFO df.BreimanExample: Single Input error : 0.3
11/04/09 07:29:09 INFO df.BreimanExample: One Tree error : 0.41356654135338333
11/04/09 07:29:09 INFO df.BreimanExample: Mean Random Input Time : 0h 0m 0s 280
11/04/09 07:29:09 INFO df.BreimanExample: Mean Single Input Time : 0h 0m 0s 95
11/04/09 07:29:09 INFO df.BreimanExample: Mean Random Input Num Nodes : 6722
11/04/09 07:29:09 INFO df.BreimanExample: Mean Single Input Num Nodes : 11326

という感じで実行結果が表示される。

Mahoutで自分のjarを作る

Mahoutは最新コードを利用したい場合、チェックアウトして、ビルドして、bin/mahoutを実行して、何かを実行することになります。いろいろとやっていくと自分のコードも実行したくなると思いますが、まぁ、単純にexamplesのsrc/main/javaに自分のパッケージを切って、追加する方法もあるかと思います。でも、やっぱり自分のjarを別プロジェクトにして置きたいな~、というときの方法です。Mahout的に推奨のやり方がどこかにあるのか、わからないけど…。ひとまず、私はこんな感じでやっていますということで。(もっと良いやり方があれば、それに切り替えると思うけど)

まず、自分のプロジェクトを作ります。Mavenが前提で話を進めます。

mvn archetype:generate

maven-archetype-quickstartを選んで、適当なプロジェクト名をつけます。今回はhogegroupのhogeprojectにでもしておきます。プロジェクトができたら、pom.xmlを編集しておきます。

:
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.6</source>
<target>1.6</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-core</artifactId>
<version>0.5-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-math</artifactId>
<version>0.5-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.apache.mahout</groupId>
<artifactId>mahout-utils</artifactId>
<version>0.5-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.7</version>
<scope>test</scope>
</dependency>
</dependencies>
:

という感じで良いでしょう。あとは、適当にhogeprojectでsrc/main/java以下に自分のMapReduce的なコードを書きます。できたら、mvn installでビルドしておきます。

次に、チェックアウトしてMahoutのコードに戻ります。2つほどファイルを変更します。まず、examples/pom.xmlを追加します。

:
<dependency>
<groupId>hogegroup</groupId>
<artifactId>hogeproject</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
:

次に、examples/src/main/assembly/job.xmlを編集します。

:
<excludes>
<exclude>org.apache.hadoop:hadoop-core</exclude>
<exclude>org.apache.mahout:mahout-core</exclude>
<exclude>org.apache.mahout:mahout-utils</exclude>
<exclude>org.apache.mahout:mahout-examples</exclude>
<exclude>hogegroup:hogeproject</exclude>
</excludes>
</dependencySet>
<dependencySet>
<unpack>true</unpack>
<scope>runtime</scope>
<outputDirectory>/</outputDirectory>
<includes>
<include>org.apache.mahout:mahout-core</include>
<include>org.apache.mahout:mahout-utils</include>
<include>org.apache.mahout:mahout-examples</include>
<include>hogegroup:hogeproject</include>
</includes>
:

というようにexcludeとincludeに追加します。できたら、Mahoutをビルドします。

mvn clean
mvn install

を実行してください。これにより、examples/target/mahout-examples-*-job.jarにhogeprojectが展開されて組み込まれます。あとは、普通にMahoutの実行と同じで

bin/mahout hogegroup.HogeDriver

みたいな感じで実行できます。

Mahoutは一度ビルドしておけば良いので、2回目からはhogeprojectでmvn installして、Mahoutのexamplesディレクトリでmvn clean;mvn packageをすれば、jarが更新されます。そんな感じで遊んでみてください。