PySpark:CrossValidator.avgMetrics与从collectSubModels计算出的平均值之间的不一致

问题描述 投票:0回答:1

我已经将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打印出的各个指标却计算错误。无论哪种方式,两者之间都存在明显的不一致。

python apache-spark pyspark cross-validation
1个回答
0
投票

我已将此问题直接发布到Apache Spark github,并被告知我做错了。

如果有人遇到相同的问题,我会在此处发布答案。

[基本上,我认为我是按照每(k)个交叉验证运行的保留(即验证)部分打印出单个RMSE。实际上,我转而打印出在(每张折叠的)training set部分上计算出的RMSE。

但是,显然,没有简单的方法可以回忆起我试图获取的信息,因为该信息似乎也不会存储。好消息是,交叉验证平均值有意义。

希望这会有所帮助。

© www.soinside.com 2019 - 2024. All rights reserved.