web-dev-qa-db-fra.com

Comment extraire les hyper-paramètres de modèle de spark.ml dans PySpark?

Je bricole quelques codes de validation croisée de la documentation PySpark et j'essaie de faire en sorte que PySpark me dise quel modèle a été sélectionné:

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)

En exécutant ceci dans PySpark Shell, je peux obtenir les coefficients du modèle de régression linéaire, mais je n'arrive pas à trouver la valeur de lr.regParam sélectionnée par la procédure de validation croisée. Des idées?

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]: []
15
Paul

Couru dans ce problème aussi. J'ai découvert que vous devez appeler la propriété Java pour une raison quelconque, je ne sais pas pourquoi. Alors faites ceci:

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

Imprimer les paramètres que vous voulez:

>>> 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

Ceci s'applique également à d'autres méthodes telles que extractParamMap(). Ils devraient résoudre ce problème bientôt.

21
wernerchao

En supposant que cvModel3Day correspond à vos noms de modèle, les paramètres peuvent être extraits comme indiqué ci-dessous dans 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
2
Ashish Markanday

J'ai aussi fait rebondir ma tête sur ce mur. Malheureusement, vous ne pouvez obtenir que les paramètres spécifiques pour les modèles spécifiques. Heureusement pour la régression logistique, vous pouvez accéder à l'interception et aux poids, malheureusement, vous ne pouvez pas récupérer le regParam . Cela peut être fait de la manière suivante:

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

Comme je l'ai écrit précédemment, chaque modèle ne contient que peu de paramètres pouvant être extraits ..__ De manière générale, l'obtention des modèles pertinents à partir d'un pipeline (par exemple, cv.bestModel lorsque le validateur croisé s'exécute sur un pipeline) peut s'effectuer avec:

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

Chaque modèle est obtenu par simple indexation par liste

best_lr = best_pipeline.stages[3]

Maintenant, ce qui précède peut être appliqué.

2
elkbrs

En réalité, il y a deux questions: 

  • quels sont les aspects du modèle ajusté (comme les coefficients et les interceptions)
  • quels étaient les méta-paramètres avec lesquels la bestModel a été ajustée.

Malheureusement, l'api python des estimateurs ajustés (les modèles) ne permet pas un accès direct (facile) aux paramètres de l'estimateur, ce qui rend difficile la réponse à cette dernière question.

Toutefois, il existe une solution de contournement utilisant l’API Java. Pour être complet, tout d’abord une configuration complète d’un modèle validé de manière croisée

%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

On pourrait ensuite utiliser les méthodes génériques sur l'objet Java pour obtenir les valeurs de paramètre, sans se référer explicitement à des méthodes telles que getRegParam():

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

Cela exécute les étapes suivantes:

  1. Obtenez le modèle ajusté logit tel que créé par l'estimateur à partir de la dernière étape du meilleur modèle: crossval.fit(..).bestModel.stages[-1]
  2. Obtenir l'objet Java interne à partir de _Java_obj
  3. Obtenez tous les noms configurés à partir de la paramGrid (qui est une liste de dictionnaires). Seule la première ligne est utilisée, en supposant qu’il s’agit d’une grille réelle, car chaque ligne contient les mêmes clés. Sinon, vous devez collecter tous les noms jamais utilisés dans une ligne.
  4. Obtenez l'identifiant de paramètre Param<T> correspondant à partir de l'objet Java.
  5. Passez l'instance Param<T> à la fonction getOrDefault() pour obtenir la valeur réelle 
1
gerben

Cela peut ne pas être aussi bon que la réponse wernerchao (car il n'est pas pratique de stocker des hyperparamètres dans des variables), mais vous pouvez rapidement consulter les meilleurs hyper-paramètres d'un modèle de validation croisée de cette façon:

cvModel.getEstimatorParamMaps()[ np.argmax(cvModel.avgMetrics) ]
1
Arius

Cela a pris quelques minutes pour déchiffrer, mais je l'ai compris.

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