我已经将CrossValidator
对象与线性回归管线和可供选择的超参数网格结合使用。更具体地说,我对9个不同设置(由两个超参数的组合产生的结果)进行了5倍交叉验证(每个参数取3个值),并通过设置collectSubModels
跟踪all 45个结果模型标记为True
:
...
lr = LinearRegression(featuresCol="features", labelCol="label")
pipeline = Pipeline(stages=indexers + [encoder] + [assembler] + [lr])
param_grid = ParamGridBuilder()\
.addGrid(lr.regParam, [0.0, 0.05, 0.1]) \
.addGrid(lr.elasticNetParam, [0.0, 0.5, 1.0])\
.build()
cross_val = CrossValidator(estimator=pipeline,
estimatorParamMaps=param_grid,
evaluator=RegressionEvaluator(metricName="rmse"),
numFolds=5,
collectSubModels=True
)
# Run cross-validation, and choose the best set of parameters
cv_model = cross_val.fit(train)
return cv_model
一切似乎运行顺利,除了以下事实:当我打印出每种型号的性能(即RMSE)(即每折9个型号)时,我尝试“手动”计算每种型号的平均值折叠后,得到的9个平均值不与我使用avgMetrics
的内部CrossValidator
属性时得到的值完全匹配。仅举一个例子,以下是我使用两个超参数的第一个组合(即都设置为0)获得的5个RMSE值:
*************** Fold #1 ***************
--- Model #1 out of 9 ---
Parameters: lambda=[0.000]; alpha=[0.000]
RMSE: 149354.656
*************** Fold #2 ***************
--- Model #1 out of 9 ---
Parameters: lambda=[0.000]; alpha=[0.000]
RMSE: 146038.521
*************** Fold #3 ***************
--- Model #1 out of 9 ---
Parameters: lambda=[0.000]; alpha=[0.000]
RMSE: 148739.919
*************** Fold #4 ***************
--- Model #1 out of 9 ---
Parameters: lambda=[0.000]; alpha=[0.000]
RMSE: 146816.473
*************** Fold #5 ***************
--- Model #1 out of 9 ---
Parameters: lambda=[0.000]; alpha=[0.000]
RMSE: 149868.621
如您所见,RMSE的所有值都低于150,000。我的期望是,如果我将上述值取平均值,那么我会得到avgMetrics
列表的第一个元素(实际上,它应该包含跨折叠计算出的每个超参数组合的交叉验证平均值)。相反,如果我正在运行cv_model.avgMetrics
,这就是我得到的:
[150091.7372030353, 150091.7372030353, 150091.7372030353, 150091.7345116686, 150093.66131828527, 150090.52769066638, 150091.7338301999, 150090.52716106002, 150091.59829053417]
预期有9个元素,但看起来都不正确!实际上,尽管我的45个模型(不仅是上面列出的5个模型)都没有达到这些数字,但所有这些模型都超过了15万。
看来avgMetrics
的填充方式错误。我知道早在2016年就有一个问题,该值错误地包含了交叉验证指标的总和而不是平均值,但显然是fixed。
[我也尝试检查current implementation对象的_fit
方法的CrossValidator
,并且-尽管我没有花太多时间在此上-显然,一切都很好:
for i in range(nFolds):
validateLB = i * h
validateUB = (i + 1) * h
condition = (df[randCol] >= validateLB) & (df[randCol] < validateUB)
validation = df.filter(condition).cache()
train = df.filter(~condition).cache()
tasks = _parallelFitTasks(est, train, eva, validation, epm, collectSubModelsParam)
for j, metric, subModel in pool.imap_unordered(lambda f: f(), tasks):
metrics[j] += (metric / nFolds)
if collectSubModelsParam:
subModels[i][j] = subModel
其他人也遇到过同样的问题吗?
EDIT:我盲目地认为问题(如果有)在avgMetrics
属性上;但是,可能这些平均值实际上是正确的,而我上面通过调用每个子模型上的.summary.rootMeanSquaredError
打印出的各个指标却计算错误。无论哪种方式,两者之间都存在明显的不一致。
我已将此问题直接发布到Apache Spark github,并被告知我做错了。
如果有人遇到相同的问题,我会在此处发布答案。
[基本上,我认为我是按照每(k)个交叉验证运行的保留(即验证)部分打印出单个RMSE。实际上,我转而打印出在(每张折叠的)training set部分上计算出的RMSE。
但是,显然,没有简单的方法可以回忆起我试图获取的信息,因为该信息似乎也不会存储。好消息是,交叉验证平均值有意义。
希望这会有所帮助。