web-dev-qa-db-ja.com

PySparkでspark.mlからモデルのハイパーパラメータを抽出する方法は?

私はPySparkのドキュメントからいくつかの相互検証コードをいじくり回し、PySparkにどのモデルが選択されたかを教えてもらうようにしています:

from pyspark.ml.classification import LogisticRegression
from pyspark.ml.evaluation import BinaryClassificationEvaluator
from pyspark.mllib.linalg import Vectors
from pyspark.ml.tuning import ParamGridBuilder, CrossValidator

dataset = sqlContext.createDataFrame(
    [(Vectors.dense([0.0]), 0.0),
     (Vectors.dense([0.4]), 1.0),
     (Vectors.dense([0.5]), 0.0),
     (Vectors.dense([0.6]), 1.0),
     (Vectors.dense([1.0]), 1.0)] * 10,
    ["features", "label"])
lr = LogisticRegression()
grid = ParamGridBuilder().addGrid(lr.regParam, [0.1, 0.01, 0.001, 0.0001]).build()
evaluator = BinaryClassificationEvaluator()
cv = CrossValidator(estimator=lr, estimatorParamMaps=grid, evaluator=evaluator)
cvModel = cv.fit(dataset)

これをPySpark Shellで実行すると、線形回帰モデルの係数を取得できますが、相互検証手順で選択されたlr.regParamの値が見つからないようです。何か案は?

In [3]: cvModel.bestModel.coefficients
Out[3]: DenseVector([3.1573])

In [4]: cvModel.bestModel.explainParams()
Out[4]: ''

In [5]: cvModel.bestModel.extractParamMap()
Out[5]: {}

In [15]: cvModel.params
Out[15]: []

In [36]: cvModel.bestModel.params
Out[36]: []
21
Paul

この問題にも遭遇しました。何らかの理由でJavaプロパティを呼び出す必要があることがわかりました。理由はわかりません。

_from pyspark.ml.tuning import TrainValidationSplit, ParamGridBuilder, CrossValidator
from pyspark.ml.regression import LinearRegression
from pyspark.ml.evaluation import RegressionEvaluator

evaluator = RegressionEvaluator(metricName="mae")
lr = LinearRegression()
grid = ParamGridBuilder().addGrid(lr.maxIter, [500]) \
                                .addGrid(lr.regParam, [0]) \
                                .addGrid(lr.elasticNetParam, [1]) \
                                .build()
lr_cv = CrossValidator(estimator=lr, estimatorParamMaps=grid, \
                        evaluator=evaluator, numFolds=3)
lrModel = lr_cv.fit(your_training_set_here)
bestModel = lrModel.bestModel
_

必要なパラメーターを印刷します。

_>>> print 'Best Param (regParam): ', bestModel._Java_obj.getRegParam()
0
>>> print 'Best Param (MaxIter): ', bestModel._Java_obj.getMaxIter()
500
>>> print 'Best Param (elasticNetParam): ', bestModel._Java_obj.getElasticNetParam()
1
_

これは、extractParamMap()などの他のメソッドにも適用されます。彼らはすぐにこれを修正する必要があります。

27
wernerchao

CvModel3Dayがモデル名であると仮定すると、Spark Scala

val params = cvModel3Day.bestModel.asInstanceOf[PipelineModel].stages(2).asInstanceOf[GBTClassificationModel].extractParamMap()

val depth = cvModel3Day.bestModel.asInstanceOf[PipelineModel].stages(2).asInstanceOf[GBTClassificationModel].getMaxDepth

val iter = cvModel3Day.bestModel.asInstanceOf[PipelineModel].stages(2).asInstanceOf[GBTClassificationModel].getMaxIter

val bins = cvModel3Day.bestModel.asInstanceOf[PipelineModel].stages(2).asInstanceOf[GBTClassificationModel].getMaxBins

val features  = cvModel3Day.bestModel.asInstanceOf[PipelineModel].stages(2).asInstanceOf[GBTClassificationModel].getFeaturesCol

val step = cvModel3Day.bestModel.asInstanceOf[PipelineModel].stages(2).asInstanceOf[GBTClassificationModel].getStepSize

val samplingRate  = cvModel3Day.bestModel.asInstanceOf[PipelineModel].stages(2).asInstanceOf[GBTClassificationModel].getSubsamplingRate
4

これはwernerchaoの回答ほど良くないかもしれません(変数にハイパーパラメーターを保存するのは便利ではないため)が、この方法でクロス検証モデルの最良のハイパーパラメーターをすばやく見ることができます:

cvModel.getEstimatorParamMaps()[ np.argmax(cvModel.avgMetrics) ]
2

また、この壁で頭をバウンスしましたが、残念ながらspecificモデルのパラメーターはspecificしか取得できません。ロジスティック回帰の場合、切片と重みにアクセスできますが、悲しいことにregParamを取得できません。これは、次の方法で実行できます。

best_lr = cv.bestModel

#get weigths
best_lr.weights
>>>DenseVector([3.1573])

#or better
best_lr.coefficients
>>>DenseVector([3.1573])

#get intercept
best_lr.intercept
>>>-1.0829958115287153

前に書いたように、各モデルには抽出できるパラメーターがほとんどありません。パイプラインからの関連モデルの全体的な取得(たとえば、クロスバリデーターがパイプライン上で実行される場合のcv.bestModel)は、次の方法で実行できます。

best_pipeline = cv.bestModel
best_pipeline.stages
>>>[Tokenizer_4bc8884ad68b4297fd3c,CountVectorizer_411fbdeb4100c2bfe8ef, PCA_4c538d67e7b8f29ff8d0,LogisticRegression_4db49954edc7033edc76]

各モデルは、単純なリストのインデックス付けによって取得されます

best_lr = best_pipeline.stages[3]

これで上記を適用できます。

2
elkbrs

実際には2つの質問があります。

  • 適合モデルの側面は何ですか(係数や切片など)
  • bestModelの適合に使用したメタパラメーターは何でしたか。

残念ながら、適合推定器(モデル)のpython api)は、推定器のパラメーターへの(簡単な)直接アクセスを許可しないため、後者の質問に答えるのが難しくなります。

ただし、Java api。を使用する回避策があります。完全を期すために、最初に相互検証モデルの完全セットアップを行います。

_%pyspark
from pyspark.ml import Pipeline
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.evaluation import BinaryClassificationEvaluator
from pyspark.ml.tuning import CrossValidator, ParamGridBuilder
logit = LogisticRegression(maxIter=10)
pipeline = Pipeline(stages=[logit])
paramGrid = ParamGridBuilder() \
    .addGrid(logit.regParam, [0, 0.01, 0.05, 0.1, 0.5, 1]) \
    .addGrid(logit.elasticNetParam, [0.0, 0.1, 0.5, 0.8, 1]) \
    .build()
evaluator = BinaryClassificationEvaluator(metricName = 'areaUnderPR')
crossval = CrossValidator(estimator=pipeline,
                          estimatorParamMaps=paramGrid,
                          evaluator=evaluator,
                          numFolds=5)
tuned_model = crossval.fit(train)
model = tuned_model.bestModel
_

getRegParam()のようなメソッドを明示的に参照せずに、Javaオブジェクトのジェネリックメソッドを使用してパラメーター値を取得できます。

_Java_model = model.stages[-1]._Java_obj
{param.name: Java_model.getOrDefault(Java_model.getParam(param.name)) 
    for param in paramGrid[0]}
_

これにより、次の手順が実行されます。

  1. 最適なモデルの最終段階から推定器によって作成された、適合した logit model を取得します:crossval.fit(..).bestModel.stages[-1]
  2. __Java_obj_から内部のJavaオブジェクトを取得
  3. paramGrid(辞書のリスト)から構成済みの名前をすべて取得します。最初の行のみが使用されます。実際のグリッドであると仮定すると、各行には同じキーが含まれます。それ以外の場合は、任意の行で使用されたすべての名前を収集する必要があります。
  4. Javaオブジェクトから対応する _Param<T>_ パラメータ識別子を取得します。
  5. _Param<T>_インスタンスを getOrDefault() 関数に渡し、実際の値を取得します
1
gerben

これは解読に数分かかりましたが、私はそれを理解しました。

from pyspark.ml.tuning import CrossValidator, ParamGridBuilder

    # prenotation: I've built out my model already and I am calling the validator ParamGridBuilder
paramGrid = ParamGridBuilder() \
                          .addGrid(hashingTF.numFeatures, [1000]) \
                          .addGrid(linearSVC.regParam, [0.1, 0.01]) \
                          .addGrid(linearSVC.maxIter, [10, 20, 30]) \
                          .build()
crossval = CrossValidator(estimator=pipeline,\
                          estimatorParamMaps=paramGrid,\
                          evaluator=MulticlassClassificationEvaluator(),\
                          numFolds=2)

cvModel = crossval.fit(train)

prediction = cvModel.transform(test)


bestModel = cvModel.bestModel

    #applicable to your model to pull list of all stages
for x in range(len(bestModel.stages)):
print bestModel.stages[x]


    #get stage feature by calling correct Transformer then .get<parameter>()
print bestModel.stages[3].getNumFeatures()
0
Nelson Sung