并行化调参时mlr自定义度量无法找到外部函数的问题求助
解决mlr并行调优中自定义度量函数找不到的问题
我来帮你搞定这个并行调优时的函数找不到问题,以及后续用extra.args传递函数时的参数错误。
问题根源分析
- 并行时函数找不到:开启并行会话后,子进程(不管是socket还是multicore模式)不会自动共享主进程全局环境的所有对象,所以
inverse_fun虽然在主进程全局环境中,但子进程没办法找到它。 - extra.args使用错误:你之前创建
makeMeasure时直接调用了transf_measure_fun(extra.args = list(...)),这会导致函数被立即执行,而此时缺少pred等必要参数,所以才会报错"argument 'pred' is missing"。正确做法是把函数对象传给fun参数,而不是函数调用后的结果。
正确的解决方案
步骤1:修正自定义度量的定义
重新定义度量函数和度量对象,确保fun参数传递的是函数本身,并且正确使用extra.args:
# 保持inverse_fun的定义不变 inverse_fun = function(x){x^2} inverse_fun = Vectorize(inverse_fun) inverse_fun = cmpfun(inverse_fun, options=list(suppressUndefined=T)) tuning_criterion = 'rmse' original_measure = getMeasure(tuning_criterion) # 用getMeasure替代eval(parse)更安全、更规范 # 修正度量函数:从extra.args中获取逆函数 transf_measure_fun = function(task, model, pred, feats, extra.args){ # 从extra.args中取出逆函数 inv_fun = extra.args$inv_fun # 转换预测值和真实值 pred$data$truth = inv_fun(pred$data$truth) pred$data$response = inv_fun(pred$data$response) # 调用原始度量的函数 return(original_measure$fun(task, model, pred, feats, extra.args)) } # 创建度量对象:这里fun传函数对象,不要调用它! transf_measure = makeMeasure( id = 'ii', name = 'ccc', properties = original_measure$properties, minimize = original_measure$minimize, best = original_measure$best, worst = original_measure$worst, fun = transf_measure_fun, # 传函数本身,不是调用结果 extra.args = list(inv_fun = inverse_fun) # 在这里设置默认的extra.args,也可以在tuneParams时传递 ) # 保持聚合度量的定义不变 transf_measure = setAggregation(transf_measure, original_measure$aggr) aggregated_measure = list(transf_measure, setAggregation(transf_measure, test.sd), setAggregation(transf_measure, train.mean), setAggregation(transf_measure, train.sd))
步骤2:并行调优时确保参数正确传递
开启并行会话后,tuneParams会自动把度量的extra.args传递给子进程,这样子进程就能获取到inv_fun了。
完整的并行调优代码:
# 初始化并行会话 current_os = Sys.info()[['sysname']] if (current_os == "Windows"){ set.seed(1, "L'Ecuyer-CMRG") parallelStart(mode = "socket", cpus = detectCores(), show.info = F) parallel::clusterSetRNGStream(iseed = 1) } else if (current_os == "Linux"){ set.seed(1, "L'Ecuyer-CMRG") parallelStart(mode = "multicore", cpus = detectCores(), show.info = F) } else { cat('\n\n#### OS not recognized, check parallelization init\n\n') } # 执行调优:此时transf_measure已经携带了extra.args,子进程能获取到inv_fun res = tuneParams(lrn.lm, task = bh.task, resampling = rdesc, par.set = discrete_ps, control = ctrl, measures = transf_measure) # 关闭并行会话 parallelStop()
备选方案:用parallelExport导出函数
如果你不想修改度量的extra.args,也可以在开启并行后,用parallelExport把inverse_fun导出到所有子进程:
# 开启并行后添加这一行 parallelExport("inverse_fun")
这种方式也能让子进程找到inverse_fun,但用extra.args的方式更符合mlr度量的设计规范,耦合性更低。
验证修复
你可以先在非并行模式下验证自定义度量的正确性,再开启并行测试:
# 验证非并行模式 res = tuneParams(lrn.lm, task = bh.task, resampling = rdesc, par.set = discrete_ps, control = ctrl, measures = transf_measure) # 验证并行模式(按上面的代码执行)
这样应该就能解决你遇到的两个问题了。
内容的提问来源于stack exchange,提问作者Alessandro Bitetto
相关产品推荐
相关产品推荐

