如何在R的pso/hydroPSO包目标函数中调用迭代等计数参数?
我完全懂这种文档摸不着头脑的感觉!在PSO优化过程中要在目标函数里拿到迭代、评估和重启次数,确实需要点技巧,毕竟pso和hydroPSO的实现逻辑不太一样。下面给你分情况讲清楚,附完整示例代码:
一、使用
pso包的实现方法 pso包本身没有直接把这些计数参数传递给目标函数的机制,我们可以通过自定义环境+追踪函数的方式来实现:
步骤1:创建存储计数的环境
先建一个独立环境来存迭代次数、函数评估次数和重启次数,避免污染全局变量:
library(pso) # 创建专属环境存储计数 pso_metrics <- new.env() pso_metrics$current_iter <- 0 pso_metrics$fn_eval_count <- 0 pso_metrics$restart_count <- 0
步骤2:定义目标函数
在目标函数里自增函数评估次数,并直接读取环境里的迭代和重启次数:
my_pso_obj <- function(x) { # 每调用一次目标函数,就加一次评估计数 pso_metrics$fn_eval_count <- pso_metrics$fn_eval_count + 1 # 读取当前的迭代、重启次数 curr_iter <- pso_metrics$current_iter curr_restart <- pso_metrics$restart_count # 可选:打印查看参数(方便调试) cat(sprintf("[重启%s] 迭代%s | 已评估%s次 | 当前解:%.2f, %.2f\n", curr_restart, curr_iter, pso_metrics$fn_eval_count, x[1], x[2])) # 这里替换成你的目标函数计算逻辑,比如用Rosenbrock测试函数 return(sum(100*(x[-1]-x[-length(x)]^2)^2 + (1-x[-length(x)])^2)) }
步骤3:定义追踪函数更新迭代和重启次数
pso的psoptim允许通过traceFun在每次迭代时执行自定义逻辑,我们用它来更新迭代次数,同时判断重启事件:
my_trace_fun <- function(iter, ...) { # 如果当前迭代回到1,且之前已有迭代记录,说明触发了重启 if (iter == 1 && pso_metrics$current_iter > 0) { pso_metrics$restart_count <- pso_metrics$restart_count + 1 } # 更新当前迭代次数 pso_metrics$current_iter <- iter }
步骤4:运行PSO优化
记得在control里开启trace=1(否则traceFun不会生效),并设置重启参数测试:
# 先重置计数 pso_metrics$current_iter <- 0 pso_metrics$fn_eval_count <- 0 pso_metrics$restart_count <- 0 # 启动优化 pso_result <- psoptim( par = c(0, 0), # 初始解 fn = my_pso_obj, # 自定义目标函数 lower = c(-5, -5), # 参数下界 upper = c(5, 5), # 参数上界 control = list( trace = 1, # 必须开启才能触发traceFun traceFun = my_trace_fun, restart.max = 2, # 最大重启次数 restart.tol = 1e-6 # 重启的误差容忍度 ) )
如果不想用全局环境,也可以用闭包封装计数和函数,更符合编程规范:
# 用闭包封装所有逻辑,避免全局变量污染 create_pso_funs <- function() { counts <- list(iter=0, fn_eval=0, restart=0) # 目标函数 obj_fun <- function(x) { counts$fn_eval <- counts$fn_eval + 1 cat(sprintf("[重启%s] 迭代%s | 已评估%s次\n", counts$restart, counts$iter, counts$fn_eval)) return(sum(100*(x[-1]-x[-length(x)]^2)^2 + (1-x[-length(x)])^2)) } # 追踪函数 trace_fun <- function(iter, ...) { if (iter == 1 && counts$iter > 0) counts$restart <- counts$restart + 1 counts$iter <- iter } return(list(obj=obj_fun, trace=trace_fun)) } # 使用闭包 pso_funs <- create_pso_funs() pso_result <- psoptim( par=c(0,0), fn=pso_funs$obj, lower=c(-5,-5), upper=c(5,5), control=list(trace=1, traceFun=pso_funs$trace, restart.max=2) )
二、使用
hydroPSO包的实现方法 hydroPSO在设计上更贴心,它会自动识别目标函数中的特定参数名,并直接传递对应的值,不需要额外的环境或追踪函数!
直接定义带参数的目标函数
只要在目标函数里加上iter(当前迭代)、nfe(函数评估次数)、restart(重启次数)这几个参数,hydroPSO就会自动把值传进来:
library(hydroPSO) my_hydro_obj <- function(x, iter, nfe, restart) { # 直接用这三个参数,不需要额外处理 cat(sprintf("[重启%s] 迭代%s | 已评估%s次 | 当前解:%.2f, %.2f\n", restart, iter, nfe, x[1], x[2])) # 替换成你的目标函数逻辑 return(sum(100*(x[-1]-x[-length(x)]^2)^2 + (1-x[-length(x)])^2)) }
运行hydroPSO优化
直接调用hydroPSO,设置好参数范围和重启配置即可:
hydro_result <- hydroPSO( fn = my_hydro_obj, lower = c(-5, -5), upper = c(5, 5), init = matrix(c(0,0), ncol=2), # 初始种群 control = list( max.restarts = 2, # 最大重启次数 trace = 1 # 可选:开启控制台追踪 ) )
注意事项
hydroPSO会根据参数名匹配传递值,所以参数名必须严格是iter、nfe、restart,顺序可以放在x之后任意位置。- 如果不需要某个参数,直接从目标函数里去掉就行,不影响其他参数的传递。
内容的提问来源于stack exchange,提问作者Don
相关产品推荐
相关产品推荐

