この記事は、Machine Learning Advent Calendar 2016 8日目の記事です。
spark mlの分類器をお試しする上でのTipsを4つご紹介します!
1. お試しに便利なlibsvmデータセット
機械学習を試す上で、データセットを用意する必要があります。
よく知られているものとして、
UCI http://archive.ics.uci.edu/ml/index.html
kaggle https://www.kaggle.com/datasets
MovieLens http://grouplens.org/datasets/movielens/
などを利用することが多いと思います。
ただ、クラス数、特徴量の数、データ量など、適切なものを探すのは大変です。
LIBSVMでは、UCI等で取得できる分類や回帰に利用できるデータが、それぞれのクラス数、特徴量数、データ量、ダウンロード元が一目でわかるようにまとまっています。
また、データはすべてlibsvm形式で格納されています。
libsvmはsparseなデータに対する分類や回帰を行うために適したデータレイアウトのテキストフォーマットのデータソースです。
実際のデータレイアウトはこんな感じです。
label index1:value1 index2:value2 ・・・
また、sparkのdataFrameとしてロードすると、このような形で見えます。
// sample 1
import org.apache.spark.sql.SparkSession
val spark = SparkSession.builder.appName("VectorIndexerSample").getOrCreate()
spark.read.format("libsvm").load("/path/to/data").head(1)
//出力結果
//Array[label,(num features,[index1,index2,・・・],[value1,value2,・・・])]
このように、inputFormatによって、Sparkのmllib/mlを使用する際の入力データの形式であるSparseMarixの形でロードされるため、データをロードした後の前処理に苦労しなくて良いという利点があります。
なお、libsvmは、データセット以外にも様々なライブラリでlibsvmフォーマットを利用するためのパッケージなども置いてあります。
2. 前処理
データを分類器の学習に適した形にするための特徴量変換のAPIが幾つかあります。
注意が必要な点として、分類器を使う上で、特徴量を連続値として扱うかを、制御する必要があります。
mllibでは、分類器の学習時に与えるパラメータとして、カテゴリ値として利用する特徴量をcategoricalFeaturesInfo: Map[Int, Int]の形式で与えるようになっていました。
ただし、このやり方の場合、カテゴリ値として使用する特徴量を手動で与えるので、特徴量の数が膨大な場合は実装を行う上で少し面倒です。
そこで、spark.mlでは、VectorIndexer
というAPIで自動的にスキーマのmetadataとしてカテゴリ値か連続値かを判別するためのattributeを付与するようになっています。VectorIndexer
では、カーディナリティがmaxCategoriesで与えた数値以下である特徴量はカテゴリ値、カーディナリティがmaxCategoriesで与えた数値よりも大きい場合は連続値としてattributeを付与します。
以下サンプルコードです。
// sample 2
import org.apache.spark.sql.SparkSession
org.apache.spark.ml.feature.VectorIndexer
val spark = SparkSession.builder.appName("VectorIndexerSample").getOrCreate()
val data = spark.read.format("libsvm").load("/path/to/data")
val featureIndexer = new VectorIndexer()
.setInputCol("features")
.setOutputCol("indexedfeatures")
.setMaxCategories(20)
.fit(data)
val featureIndexed = featureIndexer.transform(data)
3. 多クラス分類への対応
sparkのmllib/mlの分類器は、多クラス分類に対応しているものと、2クラス分類のみに対応しているものがあります。
以下、対応を表にしてみました。
mllib | spark.ml | |
---|---|---|
DecisionTree | 2クラス/多クラス | 2クラス/多クラス |
RandomForest | 2クラス/多クラス | 2クラス/多クラス |
GBT(勾配ブースティングツリー) | 2クラスのみ | 2クラスのみ |
LogisticRegression | 2クラス/多クラス | 2クラスのみ |
NaiveBayes | 2クラス/多クラス | 2クラス/多クラス |
SVM | 2クラスのみ | 実装なし |
4.分類木のメモリ設定
決定木、ランダムフォレスト、GBTといった分類木で、特に深い木の学習を行う際など、パフォーマンスが気になる場合は、
maxMemoryInMB
の設定を行うと良いです。
Sparkでは、分類木の学習において、このmaxMemoryInMB
で指定したメモリサイズで扱える数のツリーノードの計算を並列化します。このmaxMemoryInMB
は、初期値が256MBと小さいため、クラスタのexecutorメモリのサイズに合わせてチューニングを行うことで、並列度を上げて、学習のイテレーション数を減らすことができます。
以下、決定木の学習におけるサンプルコードです。
(sample 2 の続き)
// sample 3
import org.apache.spark.ml.classification.DecisionTreeClassifier
//maxMemoryInMBの値を1024MBに設定
val model = new DecisionTreeClassifier()
.setLabelCol("label")
.setFeaturesCol("indexedFeatures")
.setMaxMemoryInMB(1024)
.fit(featureIndexed)
本当は最後に各分類器のパフォーマンスやAccuracyを比較したかったのですが、
時間が取れなかったのでまたの機会に。。。