简介:这份CEC2013测试集面向进化计算、群体智能与连续优化方向的研究生及算法工程师,用于在统一基准上复现、比较和验证优化算法的收敛精度、稳定性与鲁棒性。压缩包共32个文件,以28个txt输入数据与说明文本为主体,辅以2个m脚本、1个mexw64编译组件和1个cpp源文件,整体约3.18MB,同时覆盖MATLAB快速实验与C++底层实现两条路径。每个问题实例附带维度、搜索范围、目标函数及评价标准等输入信息,包内还提供主程序入口与粒子群算法示例,读者可据此搭建单目标优化实验,观察不同方法在多模态、非凸及约束场景下的表现,并参照说明文档理解数据组织、参数配置与结果评价方式。已有390人学习,适合需要标准测试平台、核对算法实现或补充对比实验的读者,也可作为课程设计与论文实验的基础素材。
1. cec2013测试集和那份 input 目录:你复现的其实是同一份常数
很多人第一次接触 cec2013测试集,是从别人论文里的收敛曲线开始的:自己写的优化器在自造的 sphere、rastrigin 上收敛得干干净净,换到 cec2013 之后曲线整体抬高,或者干脆原地不动。真正出问题的地方常常不在算法,而在 input 目录里那几个文本文件——平移向量读错了一行,旋转矩阵按列主序塞进去了,或者只对齐了一半。这个测试集一共 28 个函数,前 5 个单峰、中间 15 个多峰、最后 8 个组合,全部做过平移,大部分还叠加了旋转,input 里装的就是这些让它“变难”的常数。下面按“这 28 个函数各自检验什么能力 → input 里的文件怎么读对 → 评估器怎么写才能和公开结果对上”的顺序讲,适合正在做连续优化算法对比、又不想在数据搬运上出错的人。
2. 28 个函数的分组逻辑:单峰、多峰、组合各自检验什么能力
先把“考卷”拆开看。cec2013测试集的编号不是随便排的,f1 到 f5 是单峰组,f6 到 f20 是多峰组,f21 到 f28 是组合组。三组的设计意图完全不同:单峰组看的是收敛速度和精度上限,多峰组看的是跳出局部最优的能力,组合组看的是算法在“地形性质突变”时会不会直接失效。你把一个只在单峰组表现好的算法拿去跑组合组,曲线通常会出现明显的台阶——在某个子函数覆盖的区域内还能往下走,一跨进另一个子函数的势力范围就卡住。这不是调参能救的,是算法结构的问题。
2.1 三组函数的具体分工与典型卡点
下表是我自己做基线对比时贴在显示器旁边的版本,右侧一列写的是“这个函数最常让哪类算法暴露问题”,比单纯记名字有用得多。
| 编号 | 函数(简名) | 组别 | 典型卡点 |
|---|---|---|---|
| f1 | Sphere(平移) | 单峰 | 基准线,收不动说明步长或种群初始化有问题 |
| f2 | 高条件数椭圆 | 单峰 | 病态,各维曲率差 1e6 量级,各维等步长直接失效 |
| f3 | Bent Cigar | 单峰 | 单一方向极陡,其余方向极平,方向信息丢失 |
| f4 | Discus | 单峰 | 一维极扁,容易在扁平方向上震荡 |
| f5 | Different Powers | 单峰 | 幂次随维度递增,靠前的维度主导适应度 |
| f6 | Rosenbrock(旋转) | 多峰 | 窄弯曲谷,旋转后谷道不再平行于坐标轴 |
| f7 | Schaffers F7(旋转) | 多峰 | 大面积局部极值,旋转放大方向误导 |
| f8 | Ackley(旋转) | 多峰 | 外围近乎平坦,中心一个深陷阱 |
| f9 | Weierstrass(旋转) | 多峰 | 分形结构,局部极值极密 |
| f10 | Griewank(旋转) | 多峰 | 大范围缓坡叠加高频扰动 |
| f11 | Rastrigin | 多峰 | 规则多峰,未旋转,是最“善良”的多峰函数 |
| f12 | Rastrigin(旋转) | 多峰 | 同上但打乱方向,逐维独立更新的策略立刻退化 |
| f13 | 非连续 Rastrigin(旋转) | 多峰 | 额外做非连续化,相邻点适应度跳变 |
| f14 | Schwefel | 多峰 | 全局最优远离次优,且靠近搜索域边界 |
| f15 | Schwefel(旋转) | 多峰 | 同上,旋转后边界效应更明显 |
| f16 | Katsuura(旋转) | 多峰 | 高频振荡,精度难以下探 |
| f17 | Lunacek Bi-Rastrigin | 多峰 | 双峰谷结构,两个盆地宽度不同 |
| f18 | Lunacek Bi-Rastrigin(旋转) | 多峰 | 同上,旋转后盆地方向不可分 |
| f19 | Griewank+Rosenbrock 展开 | 多峰 | 两种地形拼接,尺度不匹配 |
| f20 | Scaffer F6 展开 | 多峰 | 相邻维度强耦合,可分性彻底丧失 |
| f21–f28 | 组合函数 1–8 | 组合 | 子函数地形、尺度、最优值三者同时切换 |
f21 到 f28 这八个是真正区分算法水平的地方。每个组合函数由若干个子函数按距离加权拼起来,权重随当前解离各子函数中心的远近变化,所以同一个算法在搜索前期和后期面对的等效地形是不一样的。很多算法在 f11 上能压到 1e-8 以下,到 f25 就停在 1e2 附近不动,差距全在这里。
2.2 为什么非要加平移和旋转:让算法没法靠“地形记忆”占便宜
如果不做平移,最优解固定在原点,任何带“向原点收缩”倾向的更新公式都会白拿一份便宜——交叉算子、边界处理、甚至初始化分布,都可能无意中把种群往原点推,跑出来的曲线好看,但和真实搜索能力没关系。cec2013测试集的做法是把每个函数的最优点挪到一个随机位置上,这个偏移量就在 input 目录的 shift 文件里,每个函数一行(组合函数是每个子函数一行)。你只要偏移量没读对,哪怕算法完全正确,曲线也会整体平移一个常数,做对比时看起来“差一点点”,实际是两个不同的函数。
旋转的影响更隐蔽。以 Rastrigin 为例,未旋转时各个维度是可分离的,一个只会做逐维更新的局部搜索就能一个维度一个维度地慢慢啃下去;乘以一个正交矩阵之后,各维之间被线性耦合,逐维更新每次都会破坏上一步的进展。这就是 f11 和 f12 名字几乎一样、难度差一大截的原因。旋转矩阵同样是 input 目录里给好的常数,不用你自己生成——这一点很关键,很多人图省事自己 np.random.randn 生成一个再正交化,跑出来的数永远对不上公开结果。
2.3 三条硬约定:搜索范围、最优值、误差口径
第一,搜索范围。整套函数的定义域统一是每个维度 [-100, 100],初始化也常用这个区间。写成代码就是上下界各是一个长度为 D、元素全为 -100 和 100 的向量,别写成 [-5.12, 5.12]——那是另一个测试集的习惯,混用之后 f1 的收敛精度会突然变得“很好”,其实是搜索空间小了四个数量级。
第二,最优值。官方给每个函数一个已知的全局最优值,f1 是 -1400,往后每项加 100,到 f28 是 1300。这份常数在常见实现里以 f_bias 数组的形式出现,长度正好 28:
import numpy as np # 官方约定的全局最优值序列,f1 -> -1400,逐项 +100,f28 -> 1300 FBias = np.arange(-1400, 1400, 100, dtype=np.float64) assert FBias.size == 28, "长度必须是 28,对不上说明区间端点写错了" print(FBias[0], FBias[27]) # -1400.0 1300.0这段代码的意义不是“算出来”,而是把它当成一条断言:任何时候你怀疑自己读错了版本,先打印首尾两项。首项不是 -1400,说明你手上这份实现的偏差约定和常见版本不一样,后面所有误差值都会差一个常数。
第三,误差口径。做对比时报告的不是 f(x) 的绝对值,而是 e = f(x_best) - f*。这个减法是区分“算法好”和“常数搬对了”的唯一手段。同时把 1e-8 当作“收敛到”的阈值,低于它按 0 记,否则在均值里会被一两个极小的数拉得毫无意义。
注意:f* 只在报告阶段减一次。评估器内部该加的 bias 加了,外层再减一次,误差会凭空偏移两倍,而且偏移量随函数编号递增,特别像“算法在后面的函数上性能下降”。
3. 把 input 里的 shift 数据和旋转矩阵读对:解析、形状与正交性校验
拿到发布包之后先别急着跑算法,花二十分钟把 input 目录弄清楚,后面能省掉几天。这个目录里通常有三类文件:一份平移向量(shift_data.txt 之类)、按维度分的旋转矩阵(M_D10.txt / M_D30.txt / M_D50.txt 这种命名)、以及组合函数专用的一组分量矩阵(M1_D30.txt 到 M8_D30.txt)。不同来源的包命名会有一点出入,所以第一步不是抄代码,是列出文件清单、把“文件名 → 用途 → 维度”这三列写到一张纸上,再动手。
3.1 文件清单与各自的用途对照
| 文件 | 内容 | 用在哪 | 读错的后果 |
|---|---|---|---|
| shift 文件 | 每个函数(组合函数是每个子函数)一个 D 维偏移向量 | 全部 28 个函数 | 曲线整体平移,f1 的最优值对不上 |
| M_D{D}.txt | 一个 D×D 正交矩阵 | 带旋转的单峰、多峰函数 | 各维耦合方式错误,多峰函数难度失真 |
| M{i}_D{D}.txt | 组合函数第 i 个分量的正交矩阵 | f21–f28 | 组合函数权重区域错位,出现假台阶 |
这张表里最容易忽略的是第三行。组合函数的每个子函数有自己独立的旋转矩阵,一共 8 组,它们的下标和函数编号不是一一对应的——f21 可能用到 M1 到 M5,f22 用到 M1 到 M3,具体对应关系写在实现的参数区里。凭直觉按“f21 用 M1”去猜,跑出来的 f21 曲线会很平滑很好看,因为它其实退化成了一个普通多峰函数。
3.2 用 numpy 读平移向量,并按函数号建索引
import numpy as np from pathlib import Path def load_shift(path: Path, dim: int) -> np.ndarray: """把 shift 文件读成 (行数, dim) 的二维数组。""" raw = np.loadtxt(path, dtype=np.float64) if raw.ndim == 1: # 有些包把整份数据写成一长条,按维度切开即可 raw = raw.reshape(-1, dim) if raw.shape[1] != dim: raise ValueError(f"列数 {raw.shape[1]} 与 dim={dim} 不一致,多半拿错了维度的文件") print(f"shift 共 {raw.shape[0]} 行,每行 {raw.shape[1]} 维") return raw SHIFT = load_shift(Path("./input_data/shift_data.txt"), dim=30) o1 = SHIFT[0] # f1 的平移向量函数本身很短,真正要盯的是最后那行打印。行数和你实现里用到的最大行号必须能对上:如果实现里 f28 的最后一个子函数读到第 52 行,而文件只有 28 行,那就说明你读到的不是同一份文件,或者这份包把组合函数的子偏移量单独放在了别的地方。
“行号 → 函数号”的映射在官方实现里是硬编码的,不是 1 到 28 顺排就完事,组合函数一个函数会占连续多行。我的做法是直接把实现里那段 case 分支抄成一张 Python 字典,抄完立刻用行数校验一次,绝不按顺序猜。这一步没有任何技巧,就是老老实实对一遍,对完之后所有函数共用同一份数据,后面再也不用碰它。
3.3 读旋转矩阵并做正交性自检
def load_matrix(path: Path, dim: int) -> np.ndarray: M = np.loadtxt(path, dtype=np.float64) if M.ndim == 1: M = M.reshape(dim, dim) # 单行写完的情况 if M.shape != (dim, dim): raise ValueError(f"矩阵形状 {M.shape} 不是 ({dim},{dim})") return M def orthogonality_error(M: np.ndarray) -> float: D = M.shape[0] return float(np.abs(M.T @ M - np.eye(D)).max()) M = load_matrix(Path("./input_data/M_D30.txt"), dim=30) print("正交性偏差:", orthogonality_error(M)) # 期望 < 1e-8正交性偏差这个数是最好用的“体检指标”。旋转矩阵的定义要求 MᵀM = I,正常情况下偏差在 1e-12 到 1e-15 量级;如果你打印出来是 0.3、1.7 这种数,只有两种可能:文件读的时候形状判断错了,或者行列顺序反了(把本该按行读的内容按列拼进去)。第二种最坑,因为一个矩阵和它的转置都满足“元素个数正确”,只是正交性偏差变大;如果这个矩阵恰好是对称的,你甚至查不出来——所以别只信正交性,顺手打印前两行前两列,和文本文件的开头肉眼对一下。
另一个常见情况是:正交性合格,但每个函数的曲线依然偏。这时候问题不在矩阵本身,而在“用了哪个矩阵”。带旋转的函数共用一个 M_D{D}.txt,组合函数用各自的分量矩阵,这两类千万别串。我一般会把每个函数用到的矩阵文件名记进一张表,跑冒烟测试时顺手打印一次,确认没有函数拿错。
3.4 在 f1 上对齐最优值:一行代码确认搬运没走样
所有校验里最直接的一条:f1 是带平移的 Sphere,把平移向量本身喂进去,适应度必须等于它的理论最优值。
def check_f1(evaluator, shift_row, f_star): x = shift_row.copy() # 最优点就在平移向量的位置 val = evaluator(x, fid=1) print(f"f1(x*) = {val:.6f},期望 {f_star:.6f},偏差 {val - f_star:.3e}") return abs(val - f_star) < 1e-8 check_f1(my_cec13, SHIFT[0], FBias[0]) # 期望偏差接近 0如果这一行打印出来的偏差是 100 的整数倍,说明 bias 加的次数不对;如果是几万、几十万量级,说明平移向量或者旋转矩阵没有真正生效;如果是 NaN,检查一下有没有把整型数组直接送进幂运算。这个检查做完再做下一步,比跑完一整套实验再回头查要省事得多,血泪经验。
4. 写出能跑的评估器:f1–f20 的骨架和 f21–f28 的权重归一化
评估器不需要写得多漂亮,但必须满足两个条件:一是同一个函数只加载一次数据,二是 28 个函数的调用签名完全一致,这样跑批脚本不用写分支。我一般的做法是包一个类,构造时把 shift 和各类矩阵全部读进内存,__call__(x, fid)只做计算。
4.1 统一的调用签名与数据缓存
class CEC13: def __init__(self, dim, input_dir, fbias=None): self.D = dim self.dir = Path(input_dir) self.shift = load_shift(self.dir / "shift_data.txt", dim) self.M = load_matrix(self.dir / f"M_D{dim}.txt", dim) self.fbias = FBias if fbias is None else np.asarray(fbias, dtype=np.float64) self._cfg_cache = {} def __call__(self, x, fid): x = np.asarray(x, dtype=np.float64).ravel() if x.size != self.D: raise ValueError(f"输入维度 {x.size} 与评估器维度 {self.D} 不符") # 先裁剪到搜索域,和常见实现保持一致 x = np.clip(x, -100.0, 100.0) if fid <= 20: raw = self._basic(x, fid) else: raw = self._composition(x, fid) return raw + self.fbias[fid - 1]参数说明:dim决定读哪一份旋转矩阵,10、30、50 之间不能混;fbias允许外部覆盖,方便你在验证别的实现版本时临时替换;np.clip这一步很多人省掉,但 cec2013 的基线结果基本都是在裁剪后的点上算的,你如果不裁剪,越界点的适应度会大得离谱,最终均值被拉偏。裁剪放在__call__里做,算法内部就不用重复处理边界了。
4.2 f1–f20:先移再转,最后套基本函数
基本函数这部分没有捷径,就是老老实实按公式抄。下面给出前五个的写法,其余多峰函数的公式在文献里都能查到,结构完全一样。
def _sphere(z): return float(z @ z) def _elliptic(z): D = z.size a = 1e6 idx = np.arange(D) / (D - 1) return float(np.sum((a ** idx) * z ** 2)) def _bent_cigar(z): return float(z[0] ** 2 + 1e6 * (z[1:] @ z[1:])) def _discus(z): return float(1e6 * z[0] ** 2 + z[1:] @ z[1:]) def _different_powers(z): D = z.size idx = np.arange(D) / (D - 1) return float(np.sum(np.abs(z) ** (2 + 10 * idx))) BASE = {1: _sphere, 2: _elliptic, 3: _bent_cigar, 4: _discus, 5: _different_powers} ROTATED = {2, 3, 4, 6, 7, 8, 9, 10, 12, 13, 15, 16, 17, 18, 19, 20} def _basic(self, x, fid): z = x - self.shift[fid - 1] if fid in ROTATED: z = self.M @ z # 先平移后旋转,顺序不能反 return BASE[fid](z)三个细节值得单独说。第一,平移和旋转的顺序是“先减偏移、再乘矩阵”,写成 M @ (x - o);反过来写成 M @ x - o,等价于把最优点挪到了别的地方,曲线会整体偏一个常数,而且这个常数与 x 相关,看起来像收敛速度问题。第二,ROTATED这张集合要和你手上实现里的宏定义逐个对一遍,光靠函数名字猜容易漏掉几个——f11 和 f14 是不带旋转的,组合函数另算。第三,Elliptic 和 Different Powers 里那个idx一定要用浮点除,整除会让幂次全变成 0 或 1,函数退化成加权平方和。
矩阵乘法的方向也值得确认一次。z 是长度 D 的列向量,写成M @ z;如果你把它当成行向量写z @ M,实际算的是 Mᵀz。对于一个正交矩阵,这不改变函数的取值范围,但会改变最优解的位置,最终结果和公开数据对不上,而且因为数值量级看起来“正常”,很难被发现。
4.3 组合函数的三件套:sigma、lambda 与权重的归一化
f21 到 f28 的通用形式是:若干个基本函数,每个先减去自己的中心、除以自己的尺度 lambda、乘上自己的旋转矩阵,然后按距离给权重,加权求和,最后加各自的偏置。
def _composition(self, x, fid): cfg = self._cfg_cache.get(fid) if cfg is None: cfg = load_comp_config(self.dir, fid, self.D) # n, sigma, lambda, bias, 子函数表 self._cfg_cache[fid] = cfg n = cfg["n"] d2 = np.empty(n) w = np.empty(n) for i in range(n): diff = x - cfg["shift"][i] d2[i] = float(diff @ diff) w[i] = np.exp(-d2[i] / (2.0 * self.D * cfg["sigma"][i] ** 2)) / np.sqrt(d2[i] + 1e-300) w = w / w.sum() # 归一化,别省 total = 0.0 for i in range(n): zi = (x - cfg["shift"][i]) / cfg["lambda"][i] zi = cfg["M"][i] @ zi total += w[i] * (cfg["f"][i](zi) + cfg["bias"][i]) return total逻辑说明:第一层循环算距离平方和同时算权重,权重公式里的1/sqrt(d²)是距离反比项,指数项负责让远处的子函数快速失去影响力,两者相乘再归一化,保证权重和为 1。+1e-300是为了防止 x 恰好落在某个中心上导致除零,这个分支在实际搜索中出现的概率不低,尤其是平移向量被当成初始解的时候。
参数说明:sigma控制每个子函数的影响半径,一般在几十的量级;lambda控制尺度,取值跨度很大,有的子函数是 1,有的是 1e-6 量级,抄错一个数量级,那个子函数就会在求和里彻底消失;bias是每个子函数的偏置,用来把不同子函数的最小值拉到同一水平线上。这三组参数在 D=10、30、50 下各有一套,必须按维度分支取,跨维度混用不会有任何报错,只会让结果看起来“差一点”。
4.4 冒烟测试:28 个函数各打一次分
写完之后先别优化算法,拿一个固定点把 28 个函数全跑一遍,看两件事:有没有 NaN,量级是否合理。
ev = CEC13(dim=30, input_dir="./input_data") rng = np.random.default_rng(2024) x = rng.uniform(-100, 100, size=30) for fid in range(1, 29): v = ev(x, fid) print(f"f{fid:02d} value={v: .6e} error={v - ev.fbias[fid-1]: .6e}")单峰组的误差量级通常是 1e2 到 1e6,多峰组在 1e1 到 1e2,组合组则在 1e2 上下浮动。如果某个函数的误差打印成 0 或者负数,几乎可以肯定是 bias 加错了;如果组合函数的误差比单峰组还小,检查一下权重是不是忘了归一化——没归一化时权重和可能远小于 1,函数值被整体压扁。
5. cec2013 复现最容易出错的地方:从曲线对不上到越界异常
这一节记录的是我在不同机器、不同语言实现之间来回搬这套测试集时真正踩过的坑。每一条都按“现象 → 原因 → 解决”写,看到类似现象可以直接对号入座。
5.1 现象:收敛曲线整体平移一个常数,形状却完全一样
原因几乎总是 f* 用错。常见的三种写法:把 f* 当成 0(也就是直接报告 f(x));评估器内部已经加过 bias,外面又减了一次;或者把 f1 的 -1400 套到了所有函数上。三种情况的共同点是曲线形状不变、只在纵轴上平移,所以看起来像“算法稍差一点”,很容易被忽略。
解决方式是把 f* 变成一条断言而不是一个记忆。构造评估器时校验fbias.size == 28,冒烟测试时打印首尾两项,跑之前用 3.4 那个 f1 检查确认命中理论最优值。我现在的习惯是每个实验脚本开头都跑一次这个检查,两秒钟的事,能挡掉后面几个小时的困惑。
5.2 现象:f1 结果正常,f2 之后全部异常,但正交性检查是通过的
这是旋转矩阵的行列顺序反了。文本文件按行写、按列读,或者 numpy 里整形时order参数没对上,都会得到 Mᵀ。由于 Mᵀ 同样是正交矩阵,orthogonality_error打印出来照样是 1e-15,看不出任何问题。但函数实际算的是 Mᵀ(x-o),各维的耦合方向全变了。
解决方式有两个。一是打印矩阵的前两行,和文本文件开头肉眼对照,这一步最笨也最可靠。二是用函数值反查:取一个随机点 x,分别用 M 和 Mᵀ 算一次 f2,如果两个值差得很远,说明这个矩阵不是对称的,那么行列顺序就一定会影响结果,此时以文件原始排版为准。对于对称矩阵,两种读法等价,也就不用纠结了。
5.3 现象:f21–f28 的收敛曲线呈台阶状,某个区间怎么也下不去
多半是组合函数的权重没归一化,或者 sigma 用了别的维度那一套。没归一化时,权重和随位置变化,等效于给整个函数乘了一个变系数,地形被扭曲成一块一块的;sigma 用错维度时,各子函数的影响半径整体放大或缩小,某些子函数在自己的中心附近反而拿不到主导权重。
解决方式是先在每个子函数的中心点上单独测一次。把 x 设成第 i 个子函数的中心,此时权重应该几乎全部落在第 i 项上,函数值应当接近bias[i] + f_i(0)。如果测出来偏差很大,先查 sigma,再查 lambda,最后查子函数的 shift 是不是也用错了行。这个逐项验证的过程比较枯燥,但能把问题锁死在一个子函数上。
5.4 现象:换个维度重跑,结果和文献里的数差一个数量级
cec2013测试集在 D=10、30、50 下各有一整套常数,包括平移向量、旋转矩阵和组合函数参数。常见错误是 D 改了但只换了旋转矩阵,shift 文件还是原来那份——有些包里的 shift 文件是按维度分开放的,有些是同一份文件里按列切,还有的干脆每个维度一个文件。文件名看着像,内容不是。
解决方式是把维度写进所有文件的加载路径,并且在构造时做一次形状强校验:列数必须等于 D,行数必须能覆盖到最大行号,组合函数的参数块必须能取到D对应的分支。这三个校验任何一条不过就直接抛异常,别让它静默跑完。静默跑完是最贵的错误,你要花两天才能发现两天的结果都是废的。
5.5 现象:越界之后最优值一路漂移,最终结果比不裁剪还好
算法不做边界处理,或者越界点直接返回一个大常数。前者会让解跑到搜索域外,而域外的函数值有可能反而更小(尤其是平移后的函数,最优点不一定在域中心);后者会让种群在边界外“撞墙”,实际有效的搜索次数被浪费。cec2013 的基线结果基本都是在裁剪到 [-100, 100] 之后算的,你的实现不裁剪,两边就不是同一个问题。
解决方式是在评估器入口统一裁剪,而不是在每个算法里各写一遍。这样所有算法共享同一套边界语义,对比才公平。同时把裁剪前后的目标值差异记进日志,如果某个算法有大量个体被裁剪,说明它的步长控制有问题,这个信号比最终均值有用得多。
提示:这五条里,5.1 和 5.2 占了我遇到问题的七成以上。跑任何新实现之前,先做 f1 的最优值检查和旋转矩阵的肉眼对照,剩下的问题排查成本会低一个量级。
6. 拿它做对比实验:预算、重复次数和一份能交差的记录方式
评估器对了之后,真正的活是设计实验。cec2013测试集的常见预算口径是 1e4 × D 这个量级,也就是 30 维跑 3e
本文还有配套的精品资源,点击获取