Spark MLlibのHyperParameterチューニングのやり方
Spark MLlibでHyperParameterチューニングを行う方法のメモです。
以下の公式ガイドに書いてあるとおりなのですが、
spark-shellで、順に動かしながら見ていこうと思います。
ML Tuning: model selection and hyperparameter tuning | spark.apache.org
https://spark.apache.org/docs/latest/ml-tuning.html
基本的な用語
まず、基本的な用語を整理しておきます。
- Estimator … チューニング対象のモデルの学習器
- Evaluator … 評価指標の計算器
- ParameterGrid … HyperParameterの候補値セット
Estimatorは、チューニング対象モデルの学習器。
fitメソッドでModelを返却するクラスです。
ML Pipelineを対象にしても問題ありません。
このクラスに与えるHyperParameterの最適値を探すことが目的になります。
例: LogisticRegression, LinearRegression DecisionTreeClassifier など
Evaluatorは、評価指標の計算機です。
evaluateメソッドに予測結果を与えると、指標の値を返却するクラスです。
分類・回帰など、予測モデルにあわせたEvaluatorを利用します。
評価指標は、AUC(ROC曲線下部面積)などになります。
例: RegressionEvaluator, BinaryClassificationEvaluator, MulticlassClassificationEvaluator
ParameterGridは、Estimatorに与えるHyperParameterの候補値セットです。
HyperParameterが複数ある場合は、それらの組み合わせ分の検証を実施します。
例えば、パラメータAに2候補、パラメータBに3候補であれば、2*3で6パターン検証を実施。
これらの情報を、
TrainValidationSplitまたはCrossValidatorに与えて、
実行すると最適なHyperParameterを探すことが出来ます。
TrainValidationSplitはテストデータを教師・検証データに分割して評価を実施。
CrossValidatorは、テストデータをk分割して、それぞれを検証データとしてk回評価を実施。
spark-shellで実行してみる
それでは実際に、HyperParameterを実行してみます。
sample_libsvm_data.txt
をテストデータとして、
LogisticRegressionを対象に、TrainValidationSplitで評価します。
テストデータの読み込み
spark-shellを起動します。
$ spark-shell
テストデータを読み込みます。
scala> val testdata = spark.read.
format("libsvm").load("file://※sparkのインストール先※/data/mllib/sample_libsvm_data.txt")
scala> testdata.show()
+-----+--------------------+
|label| features|
+-----+--------------------+
| 0.0|(692,[127,128,129...|
| 1.0|(692,[158,159,160...|
| 1.0|(692,[124,125,126...|
| 1.0|(692,[152,153,154...|
※以降省略※
ロジスティック回帰のEstimator準備
ロジスティック回帰のEstimatorを準備します。
scala> import org.apache.spark.ml.classification.LogisticRegression
scala> val lr = new LogisticRegression().setMaxIter(10)
ParameterGridの作成
ParameterGridを作成します。
ここでは、
regParamの候補値を、1.0, 0.5, 0.3, 0.1
elasticNetParamの候補値を、1.0, 0.5, 0.1
と置いています。
scala> import org.apache.spark.ml.tuning.ParamGridBuilder
scala> val paramGrid = new ParamGridBuilder().
addGrid(lr.regParam, Array(1.0, 0.5, 0.3, 0.1)).
addGrid(lr.elasticNetParam, Array(1.0, 0.5, 0.1)).
build()
以下のように、4*3=12の検証パターンが出来ていることを確認出来ます。
scala> paramGrid.length
res1: Int = 12
BinaryClassificationEvaluatorの準備
2値分類問題の評価指標を利用するので、
BinaryClassificationEvaluatorを準備します。
scala> import org.apache.spark.ml.evaluation.BinaryClassificationEvaluator
scala> val bce = new BinaryClassificationEvaluator
評価の実行
Estimator, Evaluator, ParameterGridが準備出来たので、評価を実行します
scala> import org.apache.spark.ml.tuning.TrainValidationSplit
scala> val tv = new TrainValidationSplit().
setEstimator(lr).
setEvaluator(bce).
setEstimatorParamMaps(paramGrid)
scala> val tvModel = tv.fit(testdata)
ParameterGrid分のパターンが実行されるので、しばらく時間がかかります。
結果の確認
評価結果モデルのbestModelに、
評価指標が最大となったケースの予測モデルが入っています。
scala> tvModel.bestModel
res2: org.apache.spark.ml.Model[_] = LogisticRegressionModel: uid = logreg_2cb4113ea1e3, numClasses = 2, numFeatures = 692
bestModelのパラメータは以下のようにして確認出来ます。
scala> tvModel.bestModel.extractParamMap()
res3: org.apache.spark.ml.param.ParamMap =
{
logreg_2cb4113ea1e3-aggregationDepth: 2,
logreg_2cb4113ea1e3-elasticNetParam: 1.0,
logreg_2cb4113ea1e3-family: auto,
logreg_2cb4113ea1e3-featuresCol: features,
logreg_2cb4113ea1e3-fitIntercept: true,
logreg_2cb4113ea1e3-labelCol: label,
logreg_2cb4113ea1e3-maxIter: 10,
logreg_2cb4113ea1e3-predictionCol: prediction,
logreg_2cb4113ea1e3-probabilityCol: probability,
logreg_2cb4113ea1e3-rawPredictionCol: rawPrediction,
logreg_2cb4113ea1e3-regParam: 0.3,
logreg_2cb4113ea1e3-standardization: true,
logreg_2cb4113ea1e3-threshold: 0.5,
logreg_2cb4113ea1e3-tol: 1.0E-6
}
全ての検証パターン(候補値と評価指標の組)を知りたい場合は、
以下のように確認出来ます。
scala> tvModel.validationMetrics.zip(paramGrid).foreach(println)
(0.5,{
logreg_2cb4113ea1e3-elasticNetParam: 1.0,
logreg_2cb4113ea1e3-regParam: 1.0
})
(0.5,{
logreg_2cb4113ea1e3-elasticNetParam: 1.0,
logreg_2cb4113ea1e3-regParam: 0.5
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 1.0,
logreg_2cb4113ea1e3-regParam: 0.3
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 1.0,
logreg_2cb4113ea1e3-regParam: 0.1
})
(0.5,{
logreg_2cb4113ea1e3-elasticNetParam: 0.5,
logreg_2cb4113ea1e3-regParam: 1.0
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 0.5,
logreg_2cb4113ea1e3-regParam: 0.5
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 0.5,
logreg_2cb4113ea1e3-regParam: 0.3
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 0.5,
logreg_2cb4113ea1e3-regParam: 0.1
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 0.1,
logreg_2cb4113ea1e3-regParam: 1.0
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 0.1,
logreg_2cb4113ea1e3-regParam: 0.5
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 0.1,
logreg_2cb4113ea1e3-regParam: 0.3
})
(1.0,{
logreg_2cb4113ea1e3-elasticNetParam: 0.1,
logreg_2cb4113ea1e3-regParam: 0.1
})
ここまでの手順で、
Spark MLlibのHyperParameterチューニングの基本的な流れが確認出来ました。
その他のこと
MLPipelineを利用すると、
EstimatorをまたぐHyperParamterの組み合わせを評価することが出来ます。
また、評価指標をカスタマイズしたい場合は、org.apache.spark.ml.evaluation.Evaluator
をextendsして、
evaluateメソッドを実装します。
以上。