ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

麻雀搜索算法改进实战:融合K-means多策略复现与调参

麻雀搜索算法改进实战:融合K-means多策略复现与调参 1. 论文复现先把SSA算法的痛点与改动逻辑讲透一提到群体智能优化算法很多人首先想到的是粒子群PSO、灰狼GWO、鲸鱼WOA这些经典方法但2020年提出的麻雀搜索算法SSA这两年热度一直很高在国内学术圈尤其明显。原因其实很简单SSA的数学形式不复杂角色机制直观代码实现门槛低但它的性能又确实比很多老牌算法在部分测试函数上要能打性价比很高。欧阳城添老师这篇《融合K_means的多策略改进麻雀搜索算法研究》就是SSA改进方向里被讨论得比较多的一篇核心思路是在原版麻雀算法的基础上用K-means聚类把种群分成若干子群再叠加多种策略来修正SSA的先天缺陷。这篇博文就围绕这个标题做一次完整复现把设计动机、核心改动、关键代码和调参经验全部摊开讲。这篇文章适合谁看如果你是正在做论文复现的研究生或者想用SSA做工程优化但发现效果总差一口气的从业者又或者只是对群体智能算法感兴趣、想搞明白聚类是怎么和元启发式算法结合的入门读者都能从中拿走可以直接用的东西。原论文的完整代码没有公开所以我会基于常见实践把策略组合补齐并给出可运行的Python复现框架。你拿到手的不是一句话的介绍而是一套能落地的、能跑出对比曲线的完整思路。先说为什么值得复现这篇。SSA原版有三个让人头疼的毛病初始种群完全随机生成多样性差容易一开始就把搜索引向错误区域发现者快速向当前最优靠拢导致种群早熟收敛多峰函数上一旦陷入局部最优就很难出来算法后期的开发能力参差不齐收敛精度经常达不到要求。这三个毛病几乎是所有群体智能算法的通病但SSA因为发现者机制吃最优值吃得太狠表现尤其明显。欧阳城添这篇的思路就是针对这三大痛点逐一下药用K-means聚类做种群分工用反向学习提升初始解质量用自适应权重平衡全局探索和局部开发再用变异扰动增加跳出局部最优的概率。整体框架非常像一个针对SSA的“定向补丁包”这种逐个痛点对应策略的写法也正是国内期刊论文最常见的结构复现一遍对整个改进方法论的理解都会加深不少。2. 融合K-means与多策略改进的核心设计拆解2.1 原版SSA机制三个角色的分工与边界要理解这篇论文的改动必须先吃透原版SSA。麻雀算法模拟麻雀的觅食和反捕食行为把种群分成三类角色发现者Producer、加入者Scrounger和警戒者Vigilant。发现者负责大范围探索帮整个种群找食物丰富的区域加入者跟着发现者走在发现者附近拾取食物警戒者占少数一旦感知到危险就向安全区域移动。每次迭代所有个体先算适应度并排序然后按角色分别更新位置。具体到公式层面发现者的位置更新是这样当预警值R2小于安全阈值ST时发现者按照指数分布随机步长向全局最优位置靠拢这一步决定了SSA前期有很强的向心性当R2大于等于ST时说明有捕食者出现发现者会随机飞到较远的位置这个机制给了种群一定的跳跃能力。加入者的更新分两路排名前一半的加入者向当前全局最优位置学习后一半则向全局最差位置方向飞开这种两极分化保证了种群不会全都挤在一起。警戒者的更新更简单如果这只麻雀不是最优个体就向全局最优靠拢如果它恰好就是最优个体则飞到随机位置躲避危险。这套机制的问题边界很清楚。发现者的向心性太强迭代中后期几乎所有个体都会被拉到全局最优附近多峰函数上这就是灾难性的过早收敛。而K-means这类聚类方法本质上可以把种群按位置相似度划分成若干个簇每个簇就代表一个搜索区域让算法天然地去同时开发多个山峰。这正是欧阳城添这篇论文把K-means引入SSA的原始动机不改变SSA的基本框架而是在种群层面增加空间上的“区域划分”让不同簇承担不同区域的搜索任务。注意这里需要区分“簇”和SSA原有“角色”的关系。角色是功能分工谁探索、谁跟随、谁警戒簇是空间分工谁负责哪片区域。两套逻辑叠加在一起不影响原算法的角色更新公式但会改变个体的更新方向参考——这是融合类改进最常见的切入点。2.2 为什么选K-means聚类成本与收益的平衡有人可能会问聚类方法那么多DBSCAN、均值漂移、谱聚类都可以为什么偏偏选K-means答案藏在两个字里便宜。群体智能算法每一轮迭代都要更新N个个体如果聚类本身计算量太大那改进带来的性能提升还不够抵消时间开销等于白忙活。K-means的复杂度是O(N×K×d×iter)其中N是种群规模、K是簇数、d是维度、iter是均值迭代次数在N通常只有30到50、K取3到5的配置下单次聚类的耗时基本可以忽略不计。这是工程上的实打实优势。另外K-means的实现路径太成熟了scikit-learn一行代码就能调用自己手写也就二十行对复现者来说门槛极低调试起来也很容易。收益侧K-means能把位置相近的麻雀聚到一起每个簇的聚类中心其实就是这片区域的“代表点”。有了簇中心以后我们可以在策略设计上做文章让簇内的麻雀除了向全局最优学习之外还有一定概率向自己簇的质心方向学习。这样一来即使一个簇内部个体已经收敛到局部区域也不会被全局最优强行拉走——全局最优负责“拉”整体簇中心负责“留”局部形成一种“全局吸引局部保持”的搜索格局。这个设计在Rastrigin、Griewank这类多峰函数上的提升特别明显因为它们最大的陷阱就是很多局部最优会把种群骗过去而K-means的本质就是给种群划出多条搜索路径。当然K-means也不是没有代价。K值选多少需要实验调不同测试函数上最优的K值可能完全不同在高维问题上维度大于50欧氏距离的区分度会下降聚类效果会打折扣。这些都是复现时要心里有数的点后面我会专门谈调参。2.3 多策略组合怎么搭针对三大痛点的定向补丁欧阳城添论文标题里的“多策略改进”具体策略组合因为题目原始信息里没有展开这里按复现工作中最常用、也最容易在实验中看到效果的一套来搭建。我这套组合分了五个层次每个策略对应一个具体痛点。第一层是初始化的精英反向学习。SSA初始种群完全随机可能整体偏离最优区域导致收敛慢。反向学习的思路很直接对每个随机生成的个体X同时生成它的反向解X把X和X放在一起比适应度谁的适应度好谁留下。这样初始种群就天然包含了对搜索空间的“正反双向”覆盖降低了初始阶段就全体跑偏的概率。实现成本极低但提升初始最好解的效果很明显。第二层是K-means聚类分区。在种群初始化完成后以及每迭代一定轮数后对种群做一次K-means聚类更新每个簇的质心。这是整个改进的骨架。第三层是发现者位置更新中的自适应权重。原版发现者更新公式里没有权重系数也就是说不管迭代早期还是晚期发现者向最优靠拢的速度都一样快。改进方案是引入一个随迭代次数从0.9衰减到0.2的权重w前期保持大步长保证探索后期缩小步长精细搜索。这个思路和PSO的惯性权重一脉相承简单有效。第四层是柯西变异扰动。每次迭代对一部分个体以固定概率施加柯西分布的随机扰动。柯西分布和高斯分布相比尾部更重产生的随机数有更高概率出现大幅值也就是说扰动偶尔会很大正好帮助陷入局部最优的个体猛跳出去。变异概率一般取0.1到0.2太高会把收敛过程搅乱。第五层是边界处理改进。原版SSA对飞出边界的个体通常是直接拉到边界上这会造成边界处个体扎堆损失多样性。改进做法是飞出边界后以边界为对称轴做反射映射同时叠加一个小的随机偏移。这个细节很多复现者会忽略但它对收敛精度的影响相当可观。提示如果你复现的时候想替换策略建议遵循“一个策略对应一个痛点”的对应关系。改初始化就认准多样性改位置更新就认准探索开发平衡改变异就认准跳出局部最优。混着加策略容易让实验结果说不清楚到底是谁的功劳。3. 动手复现核心代码框架与关键实现3.1 复现的代码结构长什么样我使用的复现环境是Python 3.9 NumPy 1.24 scikit-learn 1.2完全不需要深度学习框架。工程项目结构按模块拆ssa_repro/ ├── main.py # 主程序跑实验对比 ├── ssa_original.py # 原版SSA实现 ├── ssa_improved.py # 改进版SSA实现融合K-means ├── benchmark.py # 基准测试函数集合 └── utils.py # 公共工具反向学习、越界处理、统计函数麻雀个体的编码方式就是一维数组长度为维度D种群就是N×D的矩阵。适应度函数接收一个个体向量或整个种群矩阵返回对应的适应度数组。这种向量化实现方式比逐个体循环快得多在N30、D30、迭代500次、独立跑30次的实验规模下改进版跑完全部测试也就几分钟完全在可接受范围内。3.2 精英反向学习初始化十行代码提升初始解质量反向学习初始化在原版算法里是一个独立的预处理阶段。代码如下import numpy as np def opposition_based_init(pop_size, dim, lb, ub): # 随机生成一半种群 half pop_size // 2 x np.random.uniform(lb, ub, (half, dim)) # 对每个个体生成反向解 x_opp lb ub - x # 反向映射 cand np.vstack([x, x_opp]) # 若总数量不够补随机个体 if cand.shape[0] pop_size: extra np.random.uniform(lb, ub, (pop_size - cand.shape[0], dim)) cand np.vstack([cand, extra]) return cand这里lb ub - x就是反向解的定义把每个维度上的值映射到搜索空间的对称位置。注意这里的lb和ub可以是数组对应每个维度各自不同的边界处理非对称搜索空间时不会出错。生成候选解之后不要直接当作初始种群而是先算一遍每个候选的适应度按适应度排序取前pop_size个作为真正的初始种群。这一步别省很多人直接把候选全塞进算法反而让更大比例的低质量个体混进来了。3.3 主循环里的K-means嵌入位置K-means作为骨架性策略嵌入点有两个。第一个是初始种群生成后立即做一次聚类得到初始簇划分第二个是每隔一定迭代代数比如每10代重新聚类一次用当前个体位置更新簇中心。第二个嵌入点的代码如下from sklearn.cluster import KMeans def kmeans_partition(population, k, max_iter100): 对当前种群做K-means聚类返回簇标签和簇中心 km KMeans(n_clustersk, initk-means, n_init10, max_itermax_iter, random_state42) labels km.fit_predict(population) centers km.cluster_centers_ return labels, centers把这段逻辑放进主循环的指定位置# 每recluster_gap代重聚类 if iteration % recluster_gap 0: labels, centers kmeans_partition(population, k_value)聚类做完之后怎么把“簇信息”用起来才是关键。我采用的做法是对每一只麻雀个体在原有的发现者或加入者位置更新之后以一定概率再向自己所在簇的质心做一次收敛。具体来说生成一个随机数r如果r小于质心学习概率p_c就执行下面这段局部学习def centroid_learning(individual, label, centers, lb, ub, p_c): # 以概率p_c决定是否向簇质心偏移 if np.random.rand() p_c: center centers[label] # 向质心方向做一次步长收缩的偏移 delta center - individual new_pos individual np.random.rand() * delta # 边界处理 new_pos boundary_handle(new_pos, lb, ub) return new_pos return individual这里np.random.rand() * delta限制了偏移幅度最多到质心位置不会出现跨出搜索空间的剧烈跳跃。质心学习概率p_c我一般取0.3到0.5之间太大容易把所有个体都吸到各自的簇质心附近反而失去了探索能力。3.4 SSA三个角色更新公式的改进写法原版SSA的代码很多博客都写过但改进版需要在公式里嵌入自适应权重和柯西变异。发现者更新改进如下def producer_update(x, pop, dim, lb, ub, iteration, max_iter, ST0.8, R20.7): # 自适应权重从0.9衰减到0.2 w 0.9 - 0.7 * (iteration / max_iter) # 最优个体位置 best_pos pop[np.argmin(fitness[:, 0])] N pop.shape[0] idx np.random.rand() * (N - 1) 1 if R2 ST: # 原版公式 自适应权重 new_x pop[idx] w * np.random.randn() * (best_pos - pop[idx]) else: new_x best_pos np.random.randn() * (ub - lb) / (iteration 1) return boundary_handle(new_x, lb, ub)注意原版的发现者更新使用random.standard_normal乘上L的指数衰减系数实际效果等价于向最优靠拢的随机步长。加上权重w后前期w大、步长大全局搜索充分后期w小、步长收缩局部开发精细。这个改动对Sphere这类单峰函数的收敛精度提升很直接。加入者的更新逻辑基本保持原版但在后一半加入者的更新中我给“向最差位置飞开”的动作加了一个簇感知改进def scrounger_update(pop, fitness, labels, centers, lb, ub): N pop.shape[0] new_pop pop.copy() sorted_idx np.argsort(fitness[:, 0]) # 前一半向最优学习 for i in sorted_idx[:N//2]: r np.random.rand() new_pop[i] pop[i] (np.random.rand() * (pop[0] - pop[i])).clip(0, ub - lb) # 后一半向所在簇质心学习 for i in sorted_idx[N//2:]: center centers[labels[i]] new_pop[i] pop[i] np.random.rand() * (center - pop[i]) return new_pop这个设计比原版“向全局最差飞开”更有方向性原版的做法在后期容易让这些个体在差的区域乱飞几乎无用改成向簇质心靠拢后每个簇里的跟随者都会围绕自己区域的中心做定向搜索K-means的空间分区才能真正发挥作用。警戒者的更新基本沿用原版但我额外加了一个“跳跃检测”如果最优个体连续多代适应度不变化就对警戒者中的最优个体做一次柯西变异def cauchy_mutation(individual, lb, ub, mutation_rate0.2): if np.random.rand() mutation_rate: cauchy_noise np.random.standard_cauchy(sizelen(individual)) * 0.1 mutated individual cauchy_noise return boundary_handle(mutated, lb, ub) return individual柯西分布不生成有限的标准差会产生极端值这也正是它适合做“跳出”的原因。当然变异幅度要用0.1左右的缩放系数控制一下否则容易变成纯随机搜索。4. 实验验证测试函数、对比方案与参数调优记录4.1 测试函数怎么选单峰、多峰、固定维度都要覆盖论文复现的核心实验是对比“原版SSA”和“改进版SSA”的性能差异因此测试函数的选择要能覆盖不同地形特征。我复现时用了四组经典基准函数名称类型特点常见维度Sphere单峰只有一个全局最优适合测收敛精度10/30/50Rastrigin多峰大量规律局部极值适合测跳出能力10/30/50Griewank多峰维度越高越平坦适合测全局探索10/30/50Ackley多峰陡峭沟壑适合测局部开发10/30/50实验设置要保证公平种群规模N30维度D30最大迭代次数MaxIter500独立运行次数Runs30边界为[-100, 100]除Ackley为[-32, 32]。每组实验记录30次运行的平均最优值、标准差和最优值。这个统计口径一定写清楚否则你的复现结果和论文对不上时很可能是实验设置不一致而不是算法实现错了。对比对象不是只有“SSA原版”和“改进版”两组。更合理的做是三组原版SSA、只加K-means的SSA消融A、完整改进版SSA消融B。只为验证K-means单独能带来多少提升以及多策略叠加是否真的优于单策略。这一步是复现类工作的灵魂——论文里可能直接给最终结果但你自己复现时必须拆开验证否则根本学不到东西。4.2 改进版SSA的完整流程顺序完整算法主流程我放在这里你可以直接照着搭# 完整改进版SSA主循环 def improved_ssa(func, dim, lb, ub, pop_size30, max_iter500, k_value4, recluster_gap10, p_c0.4, mutation_rate0.2): # 1. 精英反向学习初始化候选种群 pop opposition_based_init(pop_size, dim, lb, ub) fitness np.array([func(ind) for ind in pop]).reshape(-1, 1) # 2. 初始聚类 labels, centers kmeans_partition(pop, k_value) convergence_curve np.zeros(max_iter) for iteration in range(max_iter): # 定期更新聚类 if iteration 0 and iteration % recluster_gap 0: labels, centers kmeans_partition(pop, k_value) # 发现者更新 for idx in producers_idx: pop[idx] producer_update(...) # 加入者更新含簇质心学习 pop scrounger_update(pop, fitness, labels, centers, lb, ub) # 警戒者更新 柯西变异 for idx in vigilants_idx: pop[idx] cauchy_mutation(pop[idx], lb, ub, mutation_rate) # 边界处理 pop boundary_handle(pop, lb, ub) # 适应度计算 fitness np.array([func(ind) for ind in pop]).reshape(-1, 1) convergence_curve[iteration] np.min(fitness) best_idx np.argmin(fitness) return pop[best_idx], fitness[best_idx], convergence_curve代码里有个细节值得强调每一次位置更新之后都要重新调用boundary_handle否则后续计算适应度时可能出现超出边界的野生值导致适应度评估失真。我在最初复现时就是这样聚类和更新都正常但方差很大排查半天才发现是边界处理漏了。4.3 参数调优的实测记录与建议复现不是跑一遍就完事关键参数需要自己测一遍。我把实测趋势记在这里方便你少走弯路。K值是最重要的。K值太小比如2聚类分区形同虚设两个大簇几乎覆盖整个种群质心向心性忽略不计K值太大比如6到7每个簇平均只有四五个个体质心学习容易过度聚焦导致簇内多样性崩溃。我实测下来K4或K5的效果最稳定组合测试函数上K5略优单峰函数上K4更好。建议在你自己跑的时候从K4起步再往两边各试一个值做对比。重聚类间隔recluster_gap影响的是聚类“新鲜度”。每次迭代都聚类K-means的计算开销会在迭代次数大时明显增加间隔太长比如50代簇中心的引导信息已经严重滞后发挥不了作用。实测中10到20代的间隔表现较好。我最终选了10代因为改进版整体计算量本身不大间隔短一点换来的性能提升是划算的。质心学习概率p_c达到0.5以上时算法容易过度偏好簇中心而丧失向全局最优的收敛动力。我试过p_c0.7结果在Sphere上精度明显下降因为在单峰函数上聚类分区本来就没有意义过高的p_c反而干扰了向全局最优也是唯一最优的收敛过程。这就验证了一个重要认知K-means融合策略的价值是多峰函数导向的针对单峰问题它不会有正向收益甚至可能产生负收益。这不是复现错误而是策略本身的特性。4.4 威尔科克森检验证明结果不是偶然论文里往往会有显著性检验的表格复现时也需要自己做一遍。30次独立运行得到的最优值数组两组之间直接用平均值比大小不够严谨因为算法每次运行都有随机性平均值的差异可能只是噪声。我使用的是Wilcoxon符号秩检验这是论文里最常见的非参数检验方法。from scipy.stats import wilcoxon def compare_algorithms(ssa_results, improved_results): # ssa_results 和 improved_results 是各30次运行的最优值数组 stat, p_value wilcoxon(improved_results, ssa_results) if p_value 0.05: return 显著优于原版 if np.mean(improved_results) np.mean(ssa_results) else 显著劣于 else: return 无显著性差异记住一个容易误用的小坑wilcoxon检验要求两组数据成对且样本量不能太小否则会报错或者返回的p值不可靠。我一般会在跑实验前先固定同一个随机种子序列让原版SA和SSA、改进版SSA在每次运行中面对相同的初始种群生成顺序这样两组结果的差异才能最大程度归结于算法本身的改进而不是随机种子不同被白白消耗掉。这也是复现工作里最容易被忽视的严谨性问题。5. 复现中踩过的坑与完整调优经验5.1 为什么加了K-means之后算法反而变慢了我在复现第一版时遇到过一个尴尬情况加入K-means之后算法收敛速度不仅没提升反而明显变慢了尤其在Sphere这种单峰函数上。原因不复杂K-means聚类把种群划分为多个区域每个区域各自为战丧失了对单一最优点的集中火力。单峰函数上不需要维护种群多样性多样性反而是负担算法花了资源去分开搜索根本不存在多个山峰的区域。这就是改进性算法复现时要面对的本质问题没有万能的改进策略只有适合特定问题的策略。遇到这种情况不用恐慌更不用怀疑论文有问题直接把K-means相关策略在单峰函数上关掉或者把p_c调低一截速度就回来了。另一个拖慢性能的原因是重聚类间隔太小加上K值太大K-means自身的开销叠加。在N50、D50、迭代1000次的大规模实验里每次迭代都做K-means会让总耗时增加30%到50%这部分不是算法性能而是纯计算开销。我建议在大规模实验时用MiniBatchKMeans替代标准KMeansfrom sklearn.cluster import MiniBatchKMeans def kmeans_partition_fast(population, k, batch_size20): km MiniBatchKMeans(n_clustersk, batch_sizebatch_size, n_init3) labels km.fit_predict(population) centers km.cluster_centers_ return labels, centersMiniBatchKMeans在聚类过程中只随机抽取小批量样本参与迭代速度比标准K-means快数倍聚类质量差距在中低维问题上可忽略。5.2 结果不稳定跑多次每次结果都差很多如果改进版算法每次运行的最优值波动特别大首先排查的是柯西变异概率和变异幅度。变异概率太高会使算法随机性过大种群频繁被打乱导致收敛曲线尾部反复震荡。第二个排查点是边界处理。原版SSA的越界策略是把越界个体直接拉到边界上这会导致边界个体堆积并反复成为“伪最优”的信息源。我在复现中改成了边界反弹处理def boundary_handle(x, lb, ub): # 对超出边界的情况做反弹映射并附加随机偏移 idx_high x ub idx_low x lb x[idx_high] 2 * ub[idx_high] - x[idx_high] np.random.uniform(-0.1, 0.1, sizex[idx_high].shape) x[idx_low] 2 * lb[idx_low] - x[idx_low] np.random.uniform(-0.1, 0.1, sizex[idx_low].shape) return np.clip(x, lb, ub)这种处理方式不会让越界个体聚在边界上而是弹回搜索空间内部并带一点扰动有利于维持种群多样性。注意修改完边界处理之后原版SSA的效果也会变化所以对比实验时两版算法要用同一套边界处理方式——不要让你的改进版用了更好的边界策略而原版还用最朴素的截断策略那就不公平了。5.3 复现不出论文的数值先排查实验设置很多人在复现论文时焦虑于“为什么我的最优值和论文表格里的数字对不上”。这里要有个清醒认识群体智能算法的实验结果本身就存在随机性论文里给出的最优值或平均值是在特定的随机种子序列、特定的实验环境操作系统、Python版本、NumPy版本下得到的凡是你换一台机器、换一个Python版本浮点数运算的微小差异都可能传导到最终结果。我自己的经验是只要你的改进版算法在绝大多数测试函数上相对于原版SSA有可重复的显著性优势Wilcoxon检验p0.05且相对提升幅度量级与论文一致比如30%到60%的提升这就是一次成功的复现。追求完全一致的绝对值既没有意义也不可能。还有一个经常被忽略的问题感觉写的代码和公式一样但跑出来结果就是不对。那就要检查每个个体维度的边界范围。很多麻雀算法实现代码写的是ub - lb统一用标量但如果你的某个测试函数每个维度的边界不同比如第一维[-10, 10]第二维[-100, 100]用标量算出来的越界判断就是错的。我建议所有和边界相关的参数都设计成数组格式从初始化到边界处理全部都数组化这样能避免大量细节错误。5.4 消融实验的复现小技巧写论文时老师会要求你证明“每个策略都不是白加的”复现论文时同样需要验证。我自己建议的消融顺序是第一组跑原版SSA第二组只加反向学习初始化第三组加反向学习加K-means第四组是完整改进版。你可以清晰地看出哪些策略贡献最大——在我这套组合里反向学习初始化对前期收敛贡献最大而K-means对Rastrigin这种多峰函数最终精度贡献最大自适应权重主要优化收敛曲线的末段。如果某一策略在某个函数上不产生正向贡献这也很正常说明该策略对这个地形不适用。5.5 最后再分享一个关于复现的认知欧阳城添这篇论文的价值不在于它给出了一套“最好的SSA改进方案”而在于它示范了一种含金量很高的改进思路通过聚类把种群组织起来让群体智能算法从“一群个体围着最优跑”变成“多组个体分区域精细搜索”。这种思路的适用范围远不止SSA——你把K-means换成任何聚类方法把SSA换成粒子群、灰狼、螳螂等算法框架完全可以迁移。我复现完这篇论文后最大的体会是看论文时觉得“不过如此不就是加了个聚类吗”真正动手做一遍才感受到把一个看似简单的想法落地成能稳定提升效果的方案中间隔着无数个边界处理、参数调试和组合坑。这也是为什么我一直坚持逢论文必复现只有自己实打实验证过的算法才敢在工程里用。日常项目中如果有同事拿着一篇改进算法论文让我评价我通常建议他先手动复现一遍跑完消融实验再谈好不好用——这才是最公平的评判方式。
返回列表