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]: []
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.
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
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é.
En réalité, il y a deux questions:
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:
crossval.fit(..).bestModel.stages[-1]
_Java_obj
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.Param<T>
correspondant à partir de l'objet Java.Param<T>
à la fonction getOrDefault()
pour obtenir la valeur réelle 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) ]
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()