Apache PigのUDFで機械学習するアレを作りました

N番煎じ感が強いが、SIGMOD 2012で発表されたTwitterの論文のアレを作ってみた。

https://github.com/y-tag/java-pig-MyUDFs

アレとはApache Pigのユーザー定義関数(UDF)を使って分類問題を扱うというもの。オリジナルの論文は以下。

Large-Scale Machine Learning at Twitter

この実装でも、元の論文と同じようにstorage functionで学習を行うようにしている。現状では、簡単なオンライン二値分類、オンラインマルチクラス分類、オンライン線形回帰、バッチ二値分類などがある。

ビルドの際に依存するライブラリはmavenを使えば自動的に取って来てくれる。

$ mvn build

あとはPig LatinからUDFを呼び出して用いる。

学習のサンプルは以下。

register target/pig_udfs-0.0.1.jar;
define Random myorg.pig.evaluation.MyRandom('$RANDOMSEED');
training = load '$TRAINFILE' using myorg.pig.storage.SVMLightLoader() as (label: int, features: map[]);
training = foreach training generate label, features, Random() as random;
training = order training by random parallel $PARTITIONS;
training = foreach training generate label, features;
store training into '$MODELDIR' using myorg.pig.storage.FeaturesPABuilder('$FEATUREBIT', '$FEATURECONVERT', 'PA2', '1.0');

ちなみに'$'から始まる大文字($TRAINFILEや$MODELDIRなど)は、Pig Latinの変数みたいなものである。この例では、学習ファイルはSVMLightのフォーマットで記述されており、ラベルは+1か-1である。学習データを読み込んだ後、順序をシャッフルしてデータを分割し、複数($PARTITIONS)の学習器に渡している。学習器としてはオンライン二値分類のPA2を用い、正規化パラメータCは1.0である。$FEATUREBITは素性のパラメータ空間を表現する値であり、例えばこの値に20を指定すると、2の20乗個のパラメータを扱うことができる。

予測のサンプルは以下。テストファイルを読み出して予測を行い、予測と正解がどれだけマッチしていたかを表示するものである。

register target/pig_udfs-0.0.1.jar;
define Classify myorg.pig.evaluation.ClassifyWithBinaryOnlineClassifier('$MODELDIR');
test = load '$TESTFILE' using myorg.pig.storage.SVMLightLoader() as (label: int, features: map[]);
predict = foreach test generate label, (Classify(features) > 0 ? 1 : -1) as prediction;
results = foreach predict generate (label == prediction ? 1 : 0) as matching;
cnt = group results by matching;
cnt = foreach cnt generate group, COUNT(results);
dump cnt;

以前作成したモデルパラメータを用いて学習を再開することも可能なので、以下のようにすれば、オンライン学習器で同じデータを複数パスで学習することもできる。

store training into '$MODELDIR' using myorg.pig.storage.FeaturesPABuilder('$PREVMODELDIR');

これらの例では、あらかじめSVMLightフォーマットで作成しておいた学習データやテストデータを用いているが、実際にはTSV形式などのデータを読み込んだ後で、Pig上で素性を作って学習する流れになると思う。SVMLightフォーマットのように、素性のキーが数字であれば$FEATURECONVERTに'PARSING'を用い、キーが文字列の場'HASHING'を用いる。

以下の場所にいくつかのサンプルスクリプトが置いてある。オンライン二値分類器以外にも、オンラインマルチクラス分類器、オンライン線形回帰、バッチ二値分類器のスクリプトがある。
https://github.com/y-tag/java-pig-MyUDFs/tree/master/src/main/scripts

サンプルスクリプトで用いた、SVMLightフォーマットのデータは、以下の場所から取得できる。
LIBSVM Data

オリジナルの論文に書かれていたロジスティック回帰+SGDを行う場合は、少し面倒くさい。FeaturesSVMSGDBuilderの損失関数として'LOG'を指定して学習し、予測値を'Sigmoid'というUDFで0から1の範囲の数に変換する必要がある。

今回、SVM+SGDだけでなくPerceptronやPassiveAggressiveを実装したのはSGDの学習率の設定を間違えると精度が大きく下がると思ったからである。学習率に比べれば、正規化パラメータの設定は比較的簡単で、とりあえずデフォルト値でもそれなりに動くと思う。論文と同じく、機械学習の詳細を知らなくても、Hadoop上にあるデータをとりあえず分類してみるか、くらいの気持ちでさくっと使えるとうれしいツールを考えていたので、あまり設定が面倒でないようにしたいと考えていた。とはいえ、ここらへんは思い込みによるもので、実際はそんなに難しくないのかもしれない。

いろいろと機能を追加することはできるが、オリジナルの論文に書かれているように、誰も使わないのに高機能なツールを作っても仕方ないので、これくらいにしておく。