基于变量相关图与残差学习的 GCN 多变量回归方法------混凝土 28 天抗压强度预测
103 组配合比,7 个输入变量,1 个输出。测试集 RMSE 2.46 MPa、R² 0.860。同一份数据上的岭回归是 2.45 MPa、R² 0.862。这篇把代码、公式,还有这个「没赢」的结果一起摊开讲。
一、研究背景:为什么要用图神经网络算混凝土
混凝土配合比设计有个绕不过去的成本:七种配料称好、搅拌、成型,然后等 28 天才能压出抗压强度。
一轮试配 28 天,一个配合比要调几轮,工期就这么过去了。
能不能直接用配料算出强度?这是回归问题:输入 7 个配比变量,输出 28 天抗压强度(MPa)。
常规做法是经验公式加线性回归,或者 SVR、随机森林、BP 网络。它们有一个共同的隐含假设------7 个变量是 7 个互不相干的数字,排成一行喂进模型就行。
但配合比不是这样。水多了,粗骨料占比自然就被压下去;水泥和粉煤灰在胶凝材料里此消彼长。数据里 水与粗骨料的相关系数是 −0.63,水泥与粗骨料是 −0.43。这些关系在普通回归里只能靠共线性被动吸收。
图神经网络给了一个不同的视角:把每个配料变量看成图上的一个节点,变量之间的相关性看成边,让特征沿着边聚合之后再去做预测。
这篇文章做的就是这件事,并且老实回答一个问题------在这张只有 7 个节点的图上,GCN 到底比线性模型强多少。
数据来自 Yeh (2007) 的 Concrete Slump Test 数据集子集(UCI Machine Learning Repository,DOI: 10.24432/C5FG7D),共 103 条样本,输出范围 17.19--58.53 MPa,均值 36.04 MPa。
二、任务定义与数据
| 项目 | 内容 |
|---|---|
| 输入 | 水泥、炉渣、粉煤灰、水、减水剂、粗骨料、细骨料,共 7 维 |
| 输出 | 28 天抗压强度(MPa),连续值 |
| 样本量 | 103 条,无缺失、无异常剔除 |
| 数据划分 | 训练 62 / 验证 16 / 测试 25,随机种子 42 |
| 评估指标 | RMSE、MAE、R²、平均偏差、最大绝对误差 |
三个实验约束从头贯穿到尾,也是这份代码最值得看的地方:
标准化参数、图结构、模型系数,全部只在 62 条训练样本上估计。 测试集一次都没有参与拟合,包括构图这一步。很多论文里的数据泄漏就出在这里------先用全量数据算相关系数构图,再划分训练测试,等于提前把测试集的变量关系告诉了模型。
三、技术路线
整条链路是串行的,前一环的输出就是后一环的输入:
原始表 → 固定种子划分 62/16/25 → 训练集标准化 → 训练集 Pearson 构图 → 岭回归基线 → GCN 学残差 → 验证集早停 → 原始 MPa 单位评估 + 出图
注意这里 GCN 不是直接预测强度,而是在岭回归预测的基础上学剩余误差。
原因很实际:103 条样本训练一个纯端到端的 GCN,参数一多就过拟合;而岭回归在强共线的配料数据上本来就是个很难打的基线。让 GCN 只负责线性模型啃不动的非线性残差,是这类小样本场景里更稳的结构。
四、算法步骤
Step 1 · 标准化
用训练集的均值和标准差缩放输入和输出,测试集沿用训练集的参数。目标值的标准化是为了让优化过程稳定,最后再反变换回 MPa。
Step 2 · 构图
对训练集计算 7×7 的 Pearson 相关系数矩阵,取绝对值作为边权。每个节点只保留相关性最强的 2 个邻居,然后取邻居并集,得到一张无向加权图。最后加上自环,并做对称归一化。
训练集上最终得到 7 个节点、9 条边。
Step 3 · 岭回归基线
配料变量高度共线,普通最小二乘的系数会很不稳。这里对标准化后的系数加 L2 惩罚,截距不罚。惩罚系数从 {0.1, 0.3, 1, 3, 10, 30} 里选,用验证集 MSE 挑,最后选中 λ = 0.1。
Step 4 · 残差 GCN
两层图卷积:第一层把 1 维输入映射到 12 个通道,第二层 12 → 8。每一层都是「先沿图聚合邻居,再做通道变换」。
聚合完,把 7 个节点 × 8 通道展平成 56 维向量,保留节点身份,再接一个 16 维的隐藏层读出,最后输出一个标量残差。
Step 5 · 训练与早停
全批量 Adam,650 轮上限,验证集 MSE 连续 85 轮不下降就停。取验证误差最小那一轮的权重作为最终模型。
五、公式原理
(1)标准化
zi=xi−μiσi,t=y−yˉsyz_{i}=\frac{x_{i}-\mu_{i}}{\sigma_{i}},\qquad t=\frac{y-\bar{y}}{s_{y}}zi=σixi−μi,t=syy−yˉ
μ、σ、ȳ、s_y 全部来自训练集。
(2)变量相关性
rij=1n−1∑k=1nzkizkjr_{ij}=\frac{1}{n-1}\sum_{k=1}^{n}z_{ki}z_{kj}rij=n−11k=1∑nzkizkj
只在训练集上算,得到对称矩阵 RRR。
(3)构图
节点 jjj 保留 ∣rj∣|\boldsymbol{r}_j|∣rj∣ 最大的 k=2k=2k=2 个邻居,边权取相关系数绝对值,然后对称化:
Aij=∣rij∣⋅1j∈Nk(i) or i∈Nk(j),A←max(A,A⊤)A_{ij}=\left|r_{ij}\right|\cdot\mathbb{1}\leftj\\in\\mathcal{N}_{k}(i)\\ \\text{or}\\ i\\in\\mathcal{N}_{k}(j)\\right,\qquad A\leftarrow\max(A,A^{\top})Aij=∣rij∣⋅1j∈Nk(i) or i∈Nk(j),A←max(A,A⊤)
(4)对称归一化传播矩阵
D~ii=1+∑jAij,S=D~−1/2(A+I)D~−1/2\tilde{D}{ii}=1+\sum{j}A_{ij},\qquad S=\tilde{D}^{-1/2}\left(A+I\right)\tilde{D}^{-1/2}D~ii=1+j∑Aij,S=D~−1/2(A+I)D~−1/2
自环保证节点聚合时保留自身信息,两侧除以度数的平方根,避免高连接节点在传播中数值爆炸。
(5)图卷积层
H(1)=tanh(S x W1+b1)\boldsymbol{H}^{(1)}=\tanh\left(S\,\boldsymbol{x}\,\boldsymbol{W}{1}+\boldsymbol{b}{1}\right)H(1)=tanh(SxW1+b1)
H(2)=tanh(S H(1)W2+b2)\boldsymbol{H}^{(2)}=\tanh\left(S\,\boldsymbol{H}^{(1)}\boldsymbol{W}{2}+\boldsymbol{b}{2}\right)H(2)=tanh(SH(1)W2+b2)
其中 x∈R7×1\boldsymbol{x}\in\mathbb{R}^{7\times 1}x∈R7×1 是单个样本的标准化配料向量,W1∈R1×12\boldsymbol{W}{1}\in\mathbb{R}^{1\times 12}W1∈R1×12,W2∈R12×8\boldsymbol{W}{2}\in\mathbb{R}^{12\times 8}W2∈R12×8。SSS 左乘,就是把每个节点的特征换成「它自己和邻居的加权和」。
(6)读出层
h=tanh(vec(H(2))W3+b3),r^=hW4+b4\boldsymbol{h}=\tanh\left(\mathrm{vec}\left(\boldsymbol{H}^{(2)}\right)\boldsymbol{W}{3}+\boldsymbol{b}{3}\right),\qquad \hat{r}=\boldsymbol{h}\boldsymbol{W}{4}+b{4}h=tanh(vec(H(2))W3+b3),r^=hW4+b4
(7)残差融合
y^=yˉ+sy(z⊤β+b+r^)\hat{y}=\bar{y}+s_{y}\left(\boldsymbol{z}^{\top}\boldsymbol{\beta}+b+\hat{r}\right)y^=yˉ+sy(z⊤β+b+r^)
r^\hat{r}r^ 是 GCN 输出的标准化残差,直接加在岭回归的线性预测上。
(8)岭回归目标
β^=argminβ ∥t−X~β∥22+λ∥β1:∥22\hat{\boldsymbol{\beta}}=\arg\min_{\boldsymbol{\beta}}\ \left\|\boldsymbol{t}-\tilde{\boldsymbol{X}}\boldsymbol{\beta}\right\|{2}^{2}+\lambda\left\|\boldsymbol{\beta}{1:}\right\|_{2}^{2}β^=argβmin t−X~β 22+λ∥β1:∥22
截距 β0\beta_{0}β0 不参与惩罚,实现上就是把惩罚矩阵的首个对角元设成 0。
(9)损失与指标
L=1n∑i=1n(r^i−ri)2,RMSE=1n∑(y^−y)2,R2=1−∑(y^−y)2∑(y−yˉ)2L=\frac{1}{n}\sum_{i=1}^{n}\left(\hat{r}{i}-r{i}\right)^{2},\qquad \mathrm{RMSE}=\sqrt{\frac{1}{n}\sum\left(\hat{y}-y\right)^{2}},\qquad R^{2}=1-\frac{\sum\left(\hat{y}-y\right)^{2}}{\sum\left(y-\bar{y}\right)^{2}}L=n1i=1∑n(r^i−ri)2,RMSE=n1∑(y^−y)2 ,R2=1−∑(y−yˉ)2∑(y^−y)2
残差约定为「预测减实测」,所以偏差为正表示整体高估。
六、参数设定
| 参数 | 取值 | 说明 |
|---|---|---|
| neighbors | 2 | 每个节点保留的最强邻居数 |
| channels | 12, 8 | 两层图卷积的通道数 |
| hidden | 16 | 读出层隐藏单元 |
| maxEpochs | 650 | 训练轮次上限 |
| patience | 85 | 早停耐心值 |
| learnRate | 0.004 | Adam 学习率 |
| l2 | 0.003 | 权重衰减系数 |
| ridgeCandidates | 0.1 / 0.3 / 1 / 3 / 10 / 30 | 验证集选出 0.1 |
| splitSeed | 42 | 划分随机种子 |
这些数不是拍脑袋来的,是照着 103 条样本的规模配的:通道数压到 12 和 8,隐藏层 16,整个网络的参数量一千出头。样本再少一点就得继续往下砍。
七、实验结果

图 1 训练收敛与早停(左上)、GCN 与岭回归的 RMSE 对比(右上)、随机划分测试集的逐样本预测(下)
三组数据集的完整指标:
| 集合 | 模型 | n | RMSE | MAE | R² | 偏差 | 最大绝对误差 |
|---|---|---|---|---|---|---|---|
| 训练 | GCN | 62 | 2.60 | 1.99 | 0.904 | −0.02 | 7.81 |
| 训练 | 岭回归 | 62 | 2.60 | 2.00 | 0.904 | 0.00 | 7.85 |
| 验证 | GCN | 16 | 2.74 | 1.99 | 0.851 | +0.47 | 6.52 |
| 验证 | 岭回归 | 16 | 2.75 | 1.99 | 0.850 | +0.49 | 6.56 |
| 测试 | GCN | 25 | 2.46 | 1.80 | 0.860 | −0.40 | 7.81 |
| 测试 | 岭回归 | 25 | 2.45 | 1.79 | 0.862 | −0.38 | 7.74 |
(单位:MPa)
测试集上,GCN 的 RMSE 比岭回归高 0.014 MPa,R² 低 0.0016。这个差距小于任何一次重新划分数据集带来的波动,两者的预测能力实际上完全重合。
再看图 1 左上角那条曲线。验证集 RMSE 最低点出现在第 2 轮,之后一路抬升,第 87 轮触发早停。第 1 轮训练 MSE 是 6.7506 MPa²,而岭回归本身的训练 MSE 是 6.7495 MPa²------差 0.0011。
换句话说,GCN 分支从头到尾几乎没往模型里加进新东西。

图 2 训练集变量图:7 个节点、9 条边,线宽对应相关系数绝对值
图 2 中间这张图值得单独看。最强的边是水--粗骨料(−0.63),其次是水泥--粗骨料(−0.43)和水泥--粉煤灰(−0.41)。蓝色为负相关,橙色为正相关,全图只有炉渣--减水剂这一条正相关边。
右侧是归一化后的传播矩阵 SSS,对角线上的自环权重从 0.41(粗骨料)到 0.66(减水剂)不等,正好反映了各节点在图里的「中心程度」。

图 3 测试集逐样本残差、残差分布、绝对误差累积分布与逐变量置换敏感性
测试集 25 条样本,残差大多落在 ±4 MPa 内。最差的是 8 号样本:实测 36.59 MPa,预测 28.78 MPa,差了 −7.81 MPa。这类样本通常是配合比落在训练集覆盖范围之外的边缘点。
右下角的置换敏感性很有信息量。把一个变量打乱、其余不变,看 RMSE 涨多少,重复 40 次取平均:
粉煤灰 ≈6.4 MPa > 水泥 ≈5.6 MPa > 水 ≈3.2 MPa > 粗骨料 ≈2.9 MPa > 细骨料 ≈0.5 MPa > 炉渣 ≈0.3 MPa > 减水剂 <0.1 MPa
粉煤灰排第一,而不是水泥。这和「水泥用量决定强度」的直觉不一致------掺合料在这个数据集里承担了相当大的解释份额。

图 4 训练集、验证集与测试集的实测--预测散点,右下为测试集 Bland--Altman 一致性分析
三组散点都贴着 45° 线,训练集 R² 0.904,验证集 0.851,测试集 0.860。验证集略低属于正常波动,16 条样本本来就不稳。
右下角的一致性图里,绝大多数散点落在均值 ±1.96 倍标准差带内,说明模型没有明显的系统性偏倚。
八、这个结果说明了什么
第一,GCN 在这份数据上没有赢过岭回归,而且不是差一点。
两者在测试集上的 RMSE 只差 0.014 MPa,最优轮次是第 2 轮,残差分支的贡献接近于零。这不是训练不充分------曲线跑了 87 轮,验证误差只在往上走。
第二,原因在题目本身,不在代码。
图里只有 7 个节点、9 条边,直径只有 3。两层图卷积的感受野是两跳,几乎每个节点两跳之内就能看到大半张图。所谓「沿边聚合邻居特征」,实际退化成了对 7 个变量做一次带权平均。
而配料变量之间的强共线关系,岭回归已经通过 L2 惩罚压进了系数里。同一份信息,两种模型只能各拿一次。
第三,图结构真正起作用的前提,是图本身携带了线性模型拿不到的信息。
比如变量数上到几十个、存在明确的物理传导路径(多站点、多批次、多工况),或者样本量足够撑起参数量。在这类场景里,邻域聚合才可能带来增量。
这篇实验的价值,可能恰恰在于它没赢。如果一开始就把 GCN 套上去、只报一个好看的 R²,读者没法判断到底是图结构起了作用,还是模型在自我安慰。
九、运行环境与复现
| 项目 | 要求 |
|---|---|
| 软件 | MATLAB R2020b 及以上 |
前向传播和反向传播都是手写的,没有调用 trainNetwork 或 dlnetwork。这样做的目的是让每一步梯度的来路都能看清楚:
gcnForward.m:构图后的两次图卷积 + 读出层trainGCN.m:构图、岭回归基线、Adam 更新、早停predictGCN.m:训练好的权重反变换回 MPaplotGCNResults.m:六组诊断图统一导出regressionMetrics.m:RMSE / MAE / R² / 偏差 / 最大误差
主脚本 main.m 顶部的 shuffleAll 改成 false,就会切换到「后采集批次留出」实验:前 78 条按随机顺序分训练和验证,第 79--103 条整批作为测试集。这是时间外推场景更严格的检验方式。
十、应用场景
配合比初筛。 输入七种配料,一次前向传播就能给出强度估计,把试配轮次压到最后一轮验证。设计阶段能快速排除明显不达标的组合。
质量波动预警。 把实测配料作为输入,预测值和实验室实测值持续比对,偏差扩大通常意味着原材料批次变了。
小样本多变量回归的教学样例。 手写两层 GCN 的前向和反向,7 个节点、9 条边,图能直接画在纸上,比在 Cora 数据集上跑现成模型更容易讲清「消息传递」到底在做什么。
方法迁移。 换成任何「多个耦合变量 → 一个连续目标」的小样本场景都成立:合金成分预测性能、发酵工艺参数预测产率、配煤指标预测发热量。把构图规则和残差结构换掉,主体框架不用动。
数据引用:Yeh, I-C. (2007). Concrete Slump Test. UCI Machine Learning Repository. https://doi.org/10.24432/C5FG7D
说明:本文所有指标均在原始单位 MPa 下计算,测试集未参与任何拟合环节。