ML中的一个重要任务是模型选择,或使用数据找到给定任务的最佳模型或参数。 这也叫调音。 可以针对个体估算器(如Logistic回归)或包括多个算法,特征化和其他步骤的 整个管道完成调整。 用户可以一次调整整个流水线,而不是单独调整管道中的每个元素。
MLlib支持使用CrossValidator和TrainValidationSplit等工具进行模型选择。 这些工具需要以下项目:
Estimator:算法或管道调整
Set of ParamMaps:可供选择的参数,有时称为“参数网格”进行搜索
Evaluator:衡量拟合模型对延伸测试数据有多好的度量
在高层次上,这些模型选择工具的工作如下:
他们将输入数据分成单独的训练和测试数据集。
对于每个(训练,测试)对,遍历一组ParamMaps:
对于每个ParamMap,它们使用这些参数适合Estimator,获得拟合的Model,并使用Evaluator评估Model的性能。
选择由最佳性能参数组合生成的模型。
评估者可以是回归问题的回归估值器,二进制数据的BinaryClassificationEvaluator或多类问题的MulticlassClassificationEvaluator。 用于选择最佳ParamMap的默认度量可以被这些评估器中的每一个的setMetricName方法覆盖。为了帮助构建参数网格,用户可以使用ParamGridBuilder实用程序。
交叉验证
CrossValidator首先将数据集分成一组折叠,这些折叠用作单独的训练和测试数据集。 例如,k = 3倍,CrossValidator将生成3个(训练,测试)数据集对,每个数据集使用2/3的数据进行训练,1/3进行测试。 为了评估一个特定的ParamMap,CrossValidator通过在3个不同的(训练,测试)数据集对上拟合Estimator来计算3个模型的平均评估度量。
在确定最佳ParamMap之后,CrossValidator最终使用最好的ParamMap和整个数据集重新拟合Estimator。
示例:通过交叉验证进行模型选择
以下示例演示如何使用CrossValidator从参数网格中进行选择。
请注意,通过参数网格的交叉验证是昂贵的。 例如,在下面的示例中,参数网格具有3个值,用于hashingTF.numFeatures,2个值用于lr.regParam,CrossValidator使用2个折叠。 这被乘以(3×2)×2 = 12个不同的模型被训练。 在现实的设置中,尝试更多参数并使用更多的折叠(k = 3,k = 10是常见的)是常见的。 换句话说,使用CrossValidator可能非常昂贵。 然而,它也是一种成熟的方法,用于选择比启发式手动调谐更具统计学意义的参数。
from pyspark.ml import Pipeline from pyspark.ml.classification import LogisticRegression from pyspark.ml.evaluation import BinaryClassificationEvaluator from pyspark.ml.feature import HashingTF, Tokenizer from pyspark.ml.tuning import CrossValidator, ParamGridBuilder # Prepare training documents, which are labeled. training = spark.createDataFrame([ (0, "a b c d e spark", 1.0), (1, "b d", 0.0), (2, "spark f g h", 1.0), (3, "hadoop mapreduce", 0.0), (4, "b spark who", 1.0), (5, "g d a y", 0.0), (6, "spark fly", 1.0), (7, "was mapreduce", 0.0), (8, "e spark program", 1.0), (9, "a e c l", 0.0), (10, "spark compile", 1.0), (11, "hadoop software", 0.0) ], ["id", "text", "label"]) # Configure an ML pipeline, which consists of tree stages: tokenizer, hashingTF, and lr. tokenizer = Tokenizer(inputCol="text", outputCol="words") hashingTF = HashingTF(inputCol=tokenizer.getOutputCol(), outputCol="features") lr = LogisticRegression(maxIter=10) pipeline = Pipeline(stages=[tokenizer, hashingTF, lr]) # We now treat the Pipeline as an Estimator, wrapping it in a CrossValidator instance. # This will allow us to jointly choose parameters for all Pipeline stages. # A CrossValidator requires an Estimator, a set of Estimator ParamMaps, and an Evaluator. # We use a ParamGridBuilder to construct a grid of parameters to search over. # With 3 values for hashingTF.numFeatures and 2 values for lr.regParam, # this grid will have 3 x 2 = 6 parameter settings for CrossValidator to choose from. paramGrid = ParamGridBuilder() \ .addGrid(hashingTF.numFeatures, [10, 100, 1000]) \ .addGrid(lr.regParam, [0.1, 0.01]) \ .build() crossval = CrossValidator(estimator=pipeline, estimatorParamMaps=paramGrid, evaluator=BinaryClassificationEvaluator(), numFolds=2) # use 3+ folds in practice # Run cross-validation, and choose the best set of parameters. cvModel = crossval.fit(training) # Prepare test documents, which are unlabeled. test = spark.createDataFrame([ (4, "spark i j k"), (5, "l m n"), (6, "mapreduce spark"), (7, "apache hadoop") ], ["id", "text"]) # Make predictions on test documents. cvModel uses the best model found (lrModel). prediction = cvModel.transform(test) selected = prediction.select("id", "text", "probability", "prediction") for row in selected.collect(): print(row)Train-Validation Split
除了CrossValidator Spark,还提供了用于超参数调整的TrainValidationSplit。 TrainValidationSplit仅对参数的每个组合进行一次评估,而在CrossValidator的情况下,则不是k次。 因此,它较便宜,但在训练数据集不够大时不会产生可靠的结果。
与CrossValidator不同,TrainValidationSplit创建一个(训练,测试)数据集对。 它使用trainRatio参数将数据集分成这两个部分。 例如,trainRatio = 0.75,TrainValidationSplit将生成训练和测试数据集对,其中75%的数据用于训练,25%用于验证。
Example: model selection via train validation split
from pyspark.ml.evaluation import RegressionEvaluator from pyspark.ml.regression import LinearRegression from pyspark.ml.tuning import ParamGridBuilder, TrainValidationSplit # Prepare training and test data. data = spark.read.format("libsvm")\ .load("data/mllib/sample_linear_regression_data.txt") train, test = data.randomSplit([0.9, 0.1], seed=12345) lr = LinearRegression(maxIter=10) # We use a ParamGridBuilder to construct a grid of parameters to search over. # TrainValidationSplit will try all combinations of values and determine best model using # the evaluator. paramGrid = ParamGridBuilder()\ .addGrid(lr.regParam, [0.1, 0.01]) \ .addGrid(lr.fitIntercept, [False, True])\ .addGrid(lr.elasticNetParam, [0.0, 0.5, 1.0])\ .build() # In this case the estimator is simply the linear regression. # A TrainValidationSplit requires an Estimator, a set of Estimator ParamMaps, and an Evaluator. tvs = TrainValidationSplit(estimator=lr, estimatorParamMaps=paramGrid, evaluator=RegressionEvaluator(), # 80% of the data will be used for training, 20% for validation. trainRatio=0.8) # Run TrainValidationSplit, and choose the best set of parameters. model = tvs.fit(train) # Make predictions on test data. model is the model with combination of parameters # that performed best. model.transform(test)\ .select("features", "label", "prediction")\ .show()