如何让R中的backfitting算法函数在多核心/处理器上运行提速
R语言backfitting函数多核心优化方案
先修复原函数已知bug
原函数参数定义为backfit = function(X1, X2, Y, eps),但内部硬编码了Y_RESPONSE3变量,会导致运行报错,需要统一替换为参数Y。
核心优化逻辑
backfitting迭代过程中,两次ffunopare.knn.gcv()调用存在数据依赖(后一次调用依赖前一次调用输出的rx2结果),无法直接并行这两个调用。性能瓶颈全部集中在ffunopare.knn.gcv的K近邻距离计算、GCV交叉验证步骤,并行优化也针对这两个环节实现。
具体实现方案
方案1:并行化GCV交叉验证(改动最小,适配单次拟合场景)
ffunopare.knn.gcv的GCV调参过程默认是串行执行交叉验证折,我们可以用future+furrr工具链把交叉验证步骤分配到多个核心运行:
- 先安装依赖包
install.packages(c("future", "furrr", "bbefkr"))
- 重写适配并行的GCV调用逻辑,替换原函数中
ffunopare.knn.gcv的调用部分:
library(bbefkr) library(future) library(furrr) # 提前启动多核心worker,可根据自己CPU核心数调整workers数值,建议设为物理核心数 plan(multisession, workers = 4) # 并行版knn.gcv,仅修改交叉验证部分的循环逻辑 par_ffunopare.knn.gcv = function(RESPONSES, CURVES, PRED, q, semimetric, nfolds=10) { # 拆分交叉验证折 fold_id = sample(1:nfolds, size = nrow(RESPONSES), replace = T) # 并行计算每个折的验证误差 fold_errors = future_map(1:nfolds, function(fold) { train_idx = fold_id != fold test_idx = fold_id == fold fit = ffunopare.knn(RESPONSES = RESPONSES[train_idx,], CURVES = CURVES[train_idx,], PRED = CURVES[test_idx,], q = q, semimetric = semimetric) mean((RESPONSES[test_idx,] - fit$Estimated.values)^2) }, .options = furrr_options(seed = T)) # 选误差最小的k值拟合全量数据 best_k = which.min(unlist(fold_errors)) final_fit = ffunopare.knn(RESPONSES = RESPONSES, CURVES = CURVES, PRED = PRED, q = q, semimetric = semimetric, k = best_k) return(final_fit) } # 替换原backfit函数中的ffunopare.knn.gcv为par_ffunopare.knn.gcv即可 backfit_par = function(X1, X2, Y, eps){ rx1_storage = list() rx2_storage = list() rx1_init = matrix(1, nrow=1000, ncol=100) rx2_init = rx1_init rx1_storage[[1]] = rx1_init rx2_storage[[1]] = rx2_init i=2 repeat{ a = Y - rx1_storage[[i-1]] a_func_of_x2 = par_ffunopare.knn.gcv(RESPONSES=a, CURVES=X2, PRED=X2,q=4,semimetric="pca") rx2 = a_func_of_x2$Estimated.values b = Y - rx2 b_func_of_x1 = par_ffunopare.knn.gcv(RESPONSES=b, CURVES=X1, PRED=X1, q=4,semimetric="pca") rx1 = b_func_of_x1$Estimated.values rx1_storage[[i]] = rx1 rx2_storage[[i]] = rx2 dmax_rx1 = max(abs(rx1_storage[[i]] - rx1_storage[[i-1]])) dmax_rx2 = max(abs(rx2_storage[[i]] - rx2_storage[[i-1]])) max_dist = max(c(dmax_rx1, dmax_rx2)) print(max_dist) if(max_dist <= eps) break i = i+1 } # 用完关闭多核心进程 plan(sequential) return(list(rx1=rx1_storage[[i]], rx2=rx2_storage[[i]], iterations=i-1)) }
方案2:批量任务并行(适配多组参数/多组数据拟合场景)
如果你需要同时跑多组不同的eps、初始值或者数据集,直接用parallel包的parLapply并行提交多个backfit任务即可,不需要改动原函数逻辑:
library(parallel) # Windows系统用makePSOCKcluster,Linux/macOS可以用mclapply更简单 cl = makeCluster(detectCores(logical = F)) # 导出需要的变量和包到集群节点 clusterEvalQ(cl, library(bbefkr)) clusterExport(cl, c("backfit", "X1", "X2", "Y")) # 比如同时跑5个不同的eps值 eps_list = c(1e-2, 1e-3, 1e-4, 1e-5, 1e-6) results = parLapply(cl, eps_list, function(eps) { backfit(X1, X2, Y, eps) }) stopCluster(cl)
性能提升参考
4核心CPU下,单次拟合的GCV步骤可以提升3-4倍运行速度,批量任务可以线性提升运行效率。
内容的提问来源于stack exchange,提问作者stat_math123
相关产品推荐
相关产品推荐

