把 CNN-Transformer 的超参数交给粒子群:一套带 SHAP 解释的多输出回归流水线
调一个 CNN-Transformer 回归模型,最磨人的不是写网络,是那五个说不清道不明的超参数:卷积核多大、特征图给几张、池化窗口和步长怎么配、注意力头开几个。
组合空间不大,试错成本却不低。每换一组参数就要重训一次,500 轮起步,在 CPU 上跑一下午是常事。
这篇聊一套把这些活儿串起来的实现:外层用群智能算法搜超参数,内层训练 CNN-Transformer,输出端支持多个目标,跑完自动出指标、雷达图、误差图,再用 SHAP 解释每个输入特征到底贡献了多少,最后拿训练好的模型批量预测新样本。
01 研究背景
回归预测里,卷积擅长从相邻输入中提局部模式,自注意力擅长建模远距离依赖。两者拼在一起,对"输入维度不高、但特征之间存在复杂交互"的表格型数据比较友好。
问题出在拼接之后。
卷积核、特征图数量、池化窗口、池化步长决定了展平后的序列长度,序列长度又必须和位置编码、注意力层的通道数对齐。牵一发动全身,手工调参很容易调到维度不匹配,或者训出一个精度平平的网络。
另一头是工程端的诉求:模型不能只会报一个 R²,还要回答"哪些输入在起作用";不能只预测一条样本,还要能批量处理新数据。
这套代码就是围绕这两件事长出来的:自动化搜索超参数,同时把可解释性和批量预测做进同一条流水线。
02 这套代码能做什么
- 多输入多输出回归:输入 5 个特征,同时预测 2 个目标,输入输出个数不写死,改 Excel 就能换任务。
- 超参数自动寻优:卷积核、特征图、池化窗口、池化步长、注意力头数,共 5 个变量交给群智能算法搜索。
- 算法可替换:工具箱内置 22 种群智能算法,配合 9 种混沌映射初始化,共 198 种组合,换算法只改一个函数名。
- 优化前后对比:另有一个固定参数的基线模型,同数据、同训练轮数,直接对比。
- 指标与图表自动产出:RMSE、R²、MAE 写入 Excel,同时生成预测对比图、百分比误差柱状图、指标雷达图、拟合回归图。
- SHAP 可解释分析:输出蜂群图和特征重要性条形图,量化每个输入对预测的边际贡献。
- 新数据批量预测:读入新的输入表,输出预测结果并写回 Excel。
03 技术路线
原始 Excel(前 5 列为输入,后 2 列为输出)
│
▼
归一化到 [0,1] ──► 训练集 / 测试集 8:2 划分
│
▼
┌──────────────── 外层:群智能搜索 ────────────────┐
│ 候选超参数 → 内层训练 CNN-Transformer(500 轮) │
│ → 测试集适应度 → 更新种群 │
│ 迭代 10 轮,种群 5,共 50 次训练 │
└──────────────────────────────────────────────────┘
│
▼
最优超参数 → 重建网络 → 正式训练
│
├──► 训练集/测试集预测 → 反归一化 → 指标与图表
├──► SHAP 归因(测试集,逐特征枚举)
└──► 新输入表 → 批量预测 → 输出 Excel
04 算法步骤
第 1 步:读数据、定任务
xlsread 读入表格,前 5 列作为输入矩阵 X,后 2 列作为输出矩阵 Y,列名从表头自动读取。当前数据共 200 条样本、5 个输入(x1--x5)、2 个输出(y1、y2)。
第 2 步:归一化
用 mapminmax 把输入和输出都压到 0,1。归一化参数被保留下来,预测阶段再反变换回原始量纲,避免训练时的尺度差异影响收敛。
第 3 步:划分数据集
按 8:2 切分,160 条训练、40 条测试。脚本里留了一个开关,可以随机打乱样本,也可以按原始顺序切分。
第 4 步:定义搜索空间
五个待优化变量:
| 变量 | 含义 | 取值范围 | 解码方式 |
|---|---|---|---|
| x(1) | 卷积核长度 | 2--6 | 四舍五入取整 |
| x(2) | 特征图数量指数 | 3--7 | 2^n,即 8--128 |
| x(3) | 最大池化窗口 | 1--4 | 四舍五入取整 |
| x(4) | 最大池化步长 | 1--2 | 向上取整 |
| x(5) | 注意力头数 | 2--6 | 四舍五入取整 |
第 5 步:种群初始化
粒子位置不是纯随机生成,而是先由混沌映射产生 0,1 序列,再线性映射到上下界之间。脚本支持 9 种映射:tent、chebyshev、singer、logistic、sine、circle、立方、Hénon、广义 Logistic,通过一个 label 变量切换。
混沌序列的遍历性比均匀随机更均匀,初始粒子更容易铺满搜索空间。
第 6 步:内层训练与适应度评价
每个粒子解码出一组超参数,按这组参数搭网络、训练、在测试集上算适应度。适应度越小,粒子越优。
这里要提醒一句成本:种群 5、迭代 10,意味着一次寻优要训练 50 个网络,每个 500 轮。在 CPU 上这是一笔实打实的时间开销。
第 7 步:速度与位置更新
按标准粒子群公式更新,惯性权重从 0.9 线性衰减到 0.6,个体认知和社会认知系数都取 2,速度做上下限截断。
第 8 步:最优参数落地与后续分析
搜完之后,把最优粒子解码成实际参数,重建网络正式训练,然后一路走完预测、指标计算、绘图、SHAP 和新数据预测。
05 公式原理
5.1 归一化
x' = (x - x_min) / (x_max - x_min)
把每个特征独立拉伸到 0,1,预测结果再按保存下来的参数逆变换回原量纲。
5.2 卷积与池化
一维卷积核在实际实现里写成 [k, 1] 的二维核,对 [特征数, 1, 1] 的输入做卷积:
H_out = (H_in + 2p - k) / s + 1
池化层同样用 [pool, 1] 的窗口和 [step, 1] 的步长。展平后的序列长度必须和位置编码的输入维度对齐,代码里用三个分支分别处理 pool > step、pool = step = 1 和其他情况:
pool > step : L = fm × (F - step - pool + 2)
pool = step = 1 : L = fm × F
其余情况 : L = fm × (F - step - pool + 1)
其中 F 是输入特征数,fm 是特征图数量。这一步算错,网络就搭不起来。
5.3 位置编码与自注意力
卷积-池化支路的输出展平后,加上位置编码,再进入自注意力层:
Attention(Q, K, V) = softmax(QKᵀ / √d_k) V
第一层注意力加了因果掩码,限制每个位置只能看到自己和之前的位置;第二层不加掩码,做全局聚合。多头注意力的头数也是被搜索的变量,每个头的键通道数取 4 × heads。
5.4 粒子群更新
v_i(t+1) = w·v_i(t) + c₁·r₁·(pbest_i - x_i(t)) + c₂·r₂·(gbest - x_i(t))
x_i(t+1) = x_i(t) + v_i(t+1)
w(t) = 0.9 - t/T × (0.9 - 0.6)
r₁、r₂ 是 0,1 随机数,pbest 是粒子历史最优,gbest 是全局最优,T 是总迭代次数。
5.5 评价指标
RMSE = sqrt( mean( (y - ŷ)² ) )
R² = 1 - ||y - ŷ||² / ||y - ȳ||²
MAE = mean( |y - ŷ| )
三个指标分别写在测试集和训练集上,每个输出单独算一遍,最后写入 精度指标.xlsx 和 第 i 个输出指标.xlsx。
5.6 适应度函数
代码里 fit.m 用一个基于测试集残差的标量作为适应度:
matlab
fitness = sqrt(mean(sum(re - y_test)).^2);
值越小越好。这个写法把多输出残差先求和再取均值,对正负误差有一定抵消,对"整体偏差"敏感、对"个体样本的大误差"不够敏感。如果你的任务更看重极端样本,把它换成测试集 RMSE 或 1 - R² 会更直接。
5.7 SHAP 值
对每个特征 j,枚举其余特征的所有子集 S:
φ_j = Σ_{S ⊆ F\{j}} |S|! (|F| - |S| - 1)! / |F|! × [ f(S ∪ {j}) - f(S) ]
代码里以测试集各特征的均值为基准,把不参与组合的特征替换成均值,再比较"含该特征"与"不含该特征"的预测差。5 个特征意味着 2⁵ = 32 种子集,完全枚举还能接受;特征数上到十几,就得改用采样近似。
06 参数设定
搜索层
| 参数 | 取值 | 说明 |
|---|---|---|
| 种群规模 N | 5 | 越大越稳,训练成本线性上升 |
| 迭代次数 T | 10 | 示例用的小预算 |
| 变量维度 | 5 | 对应 5 个超参数 |
| 惯性权重 | 0.9 → 0.6 | 线性递减 |
| c1 / c2 | 2 / 2 | 个体认知 / 社会认知 |
| 混沌映射 | label = 1(tent) | 1--9 可选 |
| 速度范围 | ±6 | 越界截断 |
训练层
| 参数 | 取值 |
|---|---|
| 优化器 | Adam |
| 最大轮数 | 500 |
| 初始学习率 | 0.01 |
| 学习率策略 | piecewise,200 轮后 ×0.1 |
| 数据打乱 | 每轮打乱 |
| 执行环境 | CPU |
| 位置编码最大长度 | 128 |
| 输出层 | 全连接(输出个数)+ 回归层 |
07 运行环境
- MATLAB R2024b :
selfAttentionLayer、positionEmbeddingLayer、indexing1dLayer这几个层需要较新版本,建议 R2023a 及以上。 - Deep Learning Toolbox :
trainNetwork、trainingOptions、mapminmax都来自这里。 - 操作系统 :Windows 即可,示例统一跑在 CPU 上;机器有独立显卡时,把
ExecutionEnvironment改成'gpu'会快不少。 - 数据文件 :
回归数据.xlsx、新的多输入.xlsx放在脚本同级目录。
脚本里用的是 xlsread 和 xlswrite。新版 MATLAB 会提示这两个函数不推荐,但功能仍然可用,不影响结果。
08 输出结果
一次完整运行会留下这些文件:
| 文件 | 内容 |
|---|---|
精度指标.xlsx |
训练集/测试集下,优化前后各三个指标 |
第1个输出指标.xlsx |
第一个输出的指标明细 |
第2个输出指标.xlsx |
第二个输出的指标明细 |
新的输出.xlsx |
54 条新样本的预测结果,2 行 × 54 列 |
| 图形窗口 | 收敛曲线、预测对比、误差柱状图、雷达图、拟合图、SHAP 图 |
当前目录里保留的一组示例结果:
| 数据集 | 模型 | RMSE | R² | MAE |
|---|---|---|---|---|
| 训练集 | 优化后 | 1.4316 | 0.9758 | 1.1872 |
| 训练集 | 未优化 | 1.4311 | 0.9758 | 1.2198 |
| 测试集 | 优化后 | 1.5997 | 0.9657 | 1.2669 |
| 测试集 | 未优化 | 1.4500 | 0.9718 | 1.1480 |
这组数字值得多说两句。
优化后并没有全面赢过基线。 训练集上两者几乎打平,测试集上优化模型的 RMSE 反而高出 0.15 左右。原因不复杂:基线的默认参数(卷积核 3、特征图 8、池化 2、步长 1、4 个头)本来就落在这组数据的舒适区;而搜索只用了 5 个粒子、10 次迭代,50 次训练撒进 5 维空间里,密度很低。
这不是说自动寻优没用,而是说预算和搜索空间要匹配。 想要稳定收益,种群至少上到 20--30,迭代 30 起,同时把适应度换成对误差更敏感的形式,再多跑几次取均值------单次小预算的搜索结果,偶然性很大。
反过来看,基线在测试集上的 R² 已经到 0.97,说明这组数据的规律相对清晰,模型容量不是瓶颈。这类任务里,调参带来的边际收益本来就有限,把精力放在特征工程和样本质量上,往往更划算。
新数据预测这一路是通的:输入 54 条 × 5 个特征,输出 2 条 × 54 列的预测矩阵,直接写回 Excel。















09 应用场景
这套流水线适合"多输入、多输出、要解释、要批量预测"的回归任务:
- 工业过程:工艺参数(温度、压力、配比、时间等)到多项质量指标的映射。
- 材料与配方:成分、工艺条件到强度、韧性、导电率等多个性能的同步预测。
- 能源与环境:气象、负荷、排放等多源输入,预测多个监测指标。
- 农业与食品:生长环境或加工参数,预测产量、品质等复合目标。
- 实验数据补全:用已有实验记录训练,批量预测新工况,减少试错次数。
只要数据能整理成"前几列输入、后几列输出"的表格,脚本不用大改就能跑。