MATLAB实现混凝土抗压强度预测:SVR回归建模实战

MATLAB实现混凝土抗压强度预测:SVR回归建模实战 简介这是一份面向MATLAB算法学习者的支持向量机回归拟合案例文档基于混凝土抗压强度预测场景系统讲解SVM从原理到代码实现的全过程适合刚接触SVM回归或需要构建类似预测模型的读者。压缩包内共1个doc文档大小约98KB内容涵盖SVM相对神经网络的四大优势、103组样本中水泥等7种混凝土成分与28天抗压强度数据的处理说明、数据归一化、通过网格搜索选取最优惩罚参数C与核函数参数γ以及训练集/测试集仿真预测与绘图对比的完整主程序代码。已有219人学习。文档最具价值之处在于可直接照搬MATLAB程序替换数据后即可用于其他回归预测任务同时通过案例理解SVM的小样本建模、全局最优求解、泛化能力等特点还能为后续算法改进和实际工程中的混凝土强度预测提供实用参考。 在材料试验室里测定混凝土抗压强度最标准的办法是把拌合好的混凝土做成150mm的立方体试块标准养护28天再放到压力机上压碎。这28天的时间窗口意味着你不能在施工早期对材料性能做快速判断配合比方案只能在漫长的等待里来回试错。而支持向量机回归SVR这类机器学习方法给了另一条路不压试块直接用配合比和龄期预测强度。这篇文章记录我在matlab里用fitrsvm实现混凝土抗压强度预测的完整过程包括数据准备、核心代码、参数调优思路以及踩过的真实坑。如果你在土木工程试验里想引入回归拟合或者刚接触matlab的机器学习工具箱这份记录可以直接照着跑一遍。这个题目看起来是算法教程其实背后牵扯的是一套完整的数据建模流程怎么理解业务问题、怎么准备特征、怎么选模型、怎么调参、怎么评估结果。光会调用fitrsvm没有用真正决定模型效果的是你在调用它之前和之后做了什么。下面我从问题本身开始一项一项说清楚。1. 混凝土抗压强度预测为什么该选SVR1.1 从28天试块到机器学习这个任务的真实场景混凝土配合比设计的传统流程是根据强度等级要求按经验公式初步拟定水泥、水、砂、石、外加剂、掺合料的用量然后做试配、成型试块养护到规定龄期后压碎测强度。这个流程少则7天多则28天甚至56天掺粉煤灰的混凝土往往需要更长龄期才能评价真实强度而且每改一版配合比就要重新做一轮试验材料成本、人工成本和等待成本都不低。如果用历史试验数据建一个回归模型把配合比各组分用量和养护龄期作为输入把实测抗压强度作为输出那么下一次调整配合比时可以在几秒钟内得到预测强度先筛掉明显不满足要求的方案再针对少数几个有潜力的方案做实体试验。这个做法在工程上叫“虚拟试验”或“预测性设计”它不是要取代标准试验而是把试验资源集中在更值得做的方案上。支撑这个思路的是土木工程领域积累了大量历史强度试验数据。最常用的是加州大学欧文分校UCI的Concrete Compressive Strength数据集里面有1030条样本记录的是不同配合比下混凝土的实际抗压强度强度范围从2.33 MPa到82.6 MPa覆盖了从低强度到高强混凝土的完整区间。这个数据规模对机器学习来说不算大但作为回归拟合任务的练习和工程预筛工具已经足够说明问题。1.2 对比几种常见回归算法后我选了SVR拿到这个任务脑子里过一遍可选算法线性回归、决策树、BP神经网络、支持向量机回归、随机森林、梯度提升树。逐个分析它们的适配性。线性回归最不合适的点在于混凝土强度与各组分之间不是线性叠加关系。比如水灰比对强度的影响是近似对数关系水泥用量和减水剂用量之间存在交互作用这些线性模型都难以刻画。BP神经网络理论上能逼近任意非线性关系但1030条样本对神经网络来说太少了。网络一深参数量上去了很容易过拟合网络太浅又欠拟合。而且神经网络超参数多学习率、层数、神经元数、激活函数、正则化系数都要调调参空间很大对小样本任务不友好。决策树和随机森林可以直接使用但单棵决策树的拟合精度通常不够随机森林在1000条样本上表现还行只是它的预测输出是阶梯状的无法表达强度随龄期连续平滑变化的物理规律。支持向量机回归SVR恰好命中这个任务的痛点它是一种基于结构风险最小化的算法专门针对小样本设计泛化能力比神经网络稳它通过核函数把非线性关系映射到高维空间能够刻画水灰比、龄期、掺合料之间的复杂交互超参数只有惩罚系数C、不敏感带宽度ε、核函数参数这几个调参难度比神经网络小一个数量级。SVR的核心思想可以这样理解线性回归是拉一条直线穿过所有样本点让所有点到直线的距离尽可能近SVR是拉一条“带状的管道”管道内部有宽度为2ε的缓冲带样本点落在缓冲带内就不计误差只有超出缓冲带的点才产生损失。这个机制让模型不会为了拟合个别离群点而牺牲整体趋势对混凝土试块试验里常见的离散性很有效。我在matlab里用的就是fitrsvm这个函数它属于Statistics and Machine Learning ToolboxR2015b之后的版本就已经有成熟稳定不用额外装第三方工具包。环境要求就一个MATLAB版本别太老建议R2018b以上。2. 数据和特征准备决定模型上限的前置工作2.1 输入特征与数据集的构成在写任何建模代码之前先把数据弄明白。这个数据集里的每一行对应一组混凝土配合比和一次强度试验结果8个输入特征分别是水泥用量kg/m³、高炉矿渣用量kg/m³、粉煤灰用量kg/m³、水用量kg/m³、减水剂用量kg/m³、粗骨料用量kg/m³、细骨料用量kg/m³、养护龄期天目标变量是混凝土抗压强度MPa。这8个特征不是随便选的它们对应混凝土配合比设计里的“六组分一龄期”体系。水泥、水、粗细骨料是构成混凝土的骨架矿渣和粉煤灰是常用的矿物掺合料减水剂是影响工作性和强度的关键外加剂龄期则反映了水泥水化反应的程度。特征之间量纲差异非常大粗骨料用量通常在800到1100 kg/m³减水剂用量却只有2到30 kg/m³龄期从1天到365天跨度更大。这种尺度差异如果不处理会直接影响SVR这类基于距离和核函数的模型的收敛效果。拿到原始表格后第一步永远是检查数据质量。用readtable读进来之后先跑一下ismissing看看有没有缺失值再用summary看看每列的分布范围。混凝土强度如果出现负值、零值或者超过100 MPa的极端值要回溯原始试验记录确认合理性。这个数据集的1030条记录质量比较规整没有缺失值但养成检查的习惯是必要的——真实工程数据永远比公开数据集脏得多。2.2 标准化、缺失值和数据划分的先后顺序这里有一个非常关键的先后顺序问题必须先划分训练集和测试集再对训练集做标准化统计量的计算测试集延用训练集的均值和标准差。如果先对整个数据集做标准化再划分测试集的信息就渗入了训练过程这叫数据泄漏会让评估结果虚高。虽然fitrsvm的Standardize参数是在训练时自动计算并应用训练集的统计量不需要手动做这一步但理解这个逻辑能帮你判断什么时候能依赖它的Standardize参数什么时候需要自己手动处理。我的做法是先用cvpartition做一次HoldOut划分80%样本训练、20%样本测试同时用rng固定随机种子。这样每次运行结果可复现不然比较不同模型的性能时划分样本不同对比就没有意义。rng(42); cv cvpartition(size(X, 1), HoldOut, 0.2); idxTrain training(cv); idxTest test(cv); X_train X(idxTrain, :); y_train y(idxTrain); X_test X(idxTest, :); y_test y(idxTest);标准化这一步我直接交给fitrsvm的Standardize参数让它内部完成。需要说明的是fitrsvm的Standardize选项默认是false很多人一开始忘了开导致模型结果一塌糊涂。对于RBF核的SVR标准化不是可选项而是必选项。因为RBF核计算的是样本之间的欧氏距离量纲大的特征会完全支配距离计算减水剂那几千克的差异在粗骨料上千千克面前等于零。划分比例上80/20是比较常规的选择。如果样本量再少可以改成70/30或者做留一交叉验证样本量大到5000以上时90/10甚至95/5都够用。对1030条样本来说206条测试样本足够让误差评估稳定。3. fitrsvm建回归模型的完整代码与参数解释3.1 最小可运行代码把数据准备好之后建立一个SVR回归模型只需要一行核心调用。下面是最小可运行代码从读取数据到输出评估结果一条龙%% 1. 加载数据 % 假设你已经把UCI的Concrete_Data.xlsx放在当前目录 data readtable(Concrete_Data.xlsx); X data{:, 1:8}; y data{:, 9}; %% 2. 划分训练集/测试集 rng(42); cv cvpartition(size(X, 1), HoldOut, 0.2); X_train X(training(cv), :); y_train y(training(cv)); X_test X(test(cv), :); y_test y(test(cv)); %% 3. 训练SVR模型 mdl fitrsvm(X_train, y_train, ... KernelFunction, rbf, ... Standardize, true, ... BoxConstraint, 1, ... Epsilon, 0.1, ... KernelScale, auto); %% 4. 测试集预测与评估 y_pred predict(mdl, X_test); R2 1 - sum((y_test - y_pred).^2) / sum((y_test - mean(y_test)).^2); RMSE sqrt(mean((y_test - y_pred).^2)); fprintf(R2 %.3f, RMSE %.2f MPa\n, R2, RMSE);这段代码跑通没问题但注意我用的是默认参数。默认参数下的模型在混凝土强度数据上的R²大概在0.78到0.82之间RMSE在6到7 MPa。这个水平已经能看出趋势但距离实际可用还有差距。差在哪差在三个关键参数的组合没有优化。3.2 核心参数说明fitrsvm里对这个任务影响最大的参数有四个KernelFunction核函数。这个任务选rbf高斯径向基核是默认且合理的选择。RBF核能把输入映射到无穷维空间理论上拟合任意非线性关系而且只有一个核参数需要调。linear线性核就是不带核技巧的SVR拟合不了这里的非线性关系polynomial多项式核也能用但阶数选择更麻烦计算量也大。实测下来RBF核在混凝土数据上效果最好。BoxConstraint是惩罚系数C控制模型对超出ε缓冲带样本的容忍程度。C越大模型越不愿意让样本落在缓冲带外拟合越紧但也越容易过拟合C越小模型越平滑可能欠拟合。这个参数和SVM里的C是同一个东西叫法不同而已。Epsilon是ε不敏感带的半宽度。它决定缓冲带的厚度是SVR特有的参数。ε越大允许的误差越大支持向量越少模型越稀疏ε越小拟合越精细但可能把噪声也学进去了。KernelScale是RBF核的尺度参数σ。它控制一个训练样本影响力能辐射多远。尺度越小模型越敏感、越容易“围绕样本点跳动”尺度越大模型越平滑。设成auto时matlab会用启发式规则给出一个初始值但这个值通常不是最优的。这四个参数不是独立发挥作用的BoxConstraint和Epsilon互相牵制KernelScale又和特征尺度耦合在一起。所以调参的正确姿势不是一个个单独调而是把它们放在一起搜索。4. 三个关键参数的调优过程与结果4.1 手动网格搜索先建立参数直觉我不建议一上来就甩给贝叶斯优化器。先花半小时做一次手动网格搜索你会真正理解每个参数的脾气。网格搜索的思路是给每个参数列出几个候选值遍历所有组合每次用5折交叉验证评估泛化误差选交叉验证损失最小的那组参数。C_list [0.1, 1, 10, 100]; eps_list [0.01, 0.1, 1]; ks_list [0.1, 0.5, 1, 3]; results []; for i 1:numel(C_list) for j 1:numel(eps_list) for k 1:numel(ks_list) mdl_tmp fitrsvm(X_train, y_train, ... KernelFunction, rbf, ... Standardize, true, ... BoxConstraint, C_list(i), ... Epsilon, eps_list(j), ... KernelScale, ks_list(k)); cv_tmp crossval(mdl_tmp, KFold, 5); loss kfoldLoss(cv_tmp, Mode, average); results(end1, :) [C_list(i), eps_list(j), ks_list(k), loss]; end end end [minLoss, idx] min(results(:, 4)); fprintf(最优组合: C%.2f, Epsilon%.2f, KernelScale%.2f, CV损失%.4f\n, ... results(idx, 1), results(idx, 2), results(idx, 3), minLoss);这里4×3×4等于48个组合每个组合都要训练5次模型总共240次模型训练。1030条样本的训练在几秒内能完成总耗时大概10到15分钟可以接受。跑完这48组之后我观察到三条规律Epsilon从0.01升到0.1时交叉验证损失明显下降这说明模型之前处于过拟合区正在把试验噪声当成规律学习。但从0.1升到1时损失又开始上升因为缓冲带太宽真实信号也被忽略。0.1附近是这个数据集的甜点区。KernelScale从0.1升到0.5时损失下降明显继续升到1甚至3时损失缓慢上升。说明默认的auto值——它通常会给出一个偏大的尺度估计——并非最优手工指定小一些的尺度能增加模型的表达能力。BoxConstraint的影响相对温和但趋势很明确C太小时模型欠拟合C从1升到10时损失稳步下降升到100时提升有限还可能轻微过拟合。和Epsilon配合着看C10、Epsilon0.1的组合整体表现最稳。我最终在手动网格里找到的较优组合是C10、Epsilon0.1、KernelScale0.5交叉验证得到的RMSE大约4.8 MPa。这组参数已经让测试集R²从0.80提升到0.88左右。4.2 贝叶斯自动调参提速手动网格搜索的缺点是步长太粗、范围太窄。比如KernelScale的最优值在0.3到0.7之间网格里0.1、0.5、1的粒度覆盖到了0.5但没有机会找到0.4这个可能更优的点。这时候该上贝叶斯优化器了。fitrsvm支持直接用OptimizeHyperparameters参数做自动调参它内部用的是贝叶斯优化会记住历史评估结果在参数空间里智能选择下一个要评估的点而不是盲目枚举。这是比网格搜索高效得多的策略。mdl_opt fitrsvm(X_train, y_train, ... KernelFunction, rbf, ... Standardize, true, ... OptimizeHyperparameters, {BoxConstraint, Epsilon, KernelScale}, ... HyperparameterOptimizationOptions, struct(... MaxObjectiveEvaluations, 60, ... AcquisitionFunctionName, expected-improvement-plus, ... Kfold, 5));这段代码跑了大约15到20分钟60次目标评估结束时matlab给出的最优参数大致是BoxConstraint约28、Epsilon约0.08、KernelScale约0.4。这些值落在手动网格搜索发现的“好区域”里但更精细。需要提醒的是贝叶斯优化的时间随着MaxObjectiveEvaluations线性增长也随折数增长。如果你的数据量到了5000条以上建议把Kfold从5改成3或者限制优化时间上限。还有一个省时间的技巧先用手动网格搜索圈定大致范围再让贝叶斯优化在这个小范围内精调这样既快又不浪费计算资源。5. 评估指标、可视化与过拟合检查5.1 三个必看的回归评价指标模型训练完别急着看预测结果先把评价指标定好。回归任务最常用的三个指标是决定系数R²、均方根误差RMSE、平均绝对误差MAE。R²反映模型解释了目标变量多少比例的方差。混凝土强度标准差大约在15 MPa左右这意味着R²0.90时模型把90%的方差解释掉了剩下10%是模型无法刻画的随机波动和试验噪声。R²0.90在这个任务里是一个相当好的水平因为混凝土试块本身存在材料不均匀性同一配合比的两个试块压出来的强度都可能差2到3 MPa。RMSE对大幅误差更敏感单位是MPa物理意义直观大概就是你预测一个配合比的强度时平均会偏差多少。MAE是所有误差绝对值的平均值比RMSE更能反映“典型误差”的水平。y_pred predict(mdl_opt, X_test); ss_res sum((y_test - y_pred).^2); ss_tot sum((y_test - mean(y_test)).^2); R2 1 - ss_res / ss_tot; RMSE sqrt(mean((y_test - y_pred).^2)); MAE mean(abs(y_test - y_pred));我最终得到的测试集指标是R²0.90RMSE4.6 MPaMAE3.3 MPa。在200多个测试样本上的这个水平配合比预筛已经完全够用。5 MPa的误差对应混凝土强度设计等级里的半个等级你完全可以用它先排除掉明显不够强度的配合比再对少数几个可能达标的方案做实体试验。5.2 预测散点图和残差图怎么读指标是数字图才是直觉。画两张图第一张是实测强度与预测强度的散点图横轴实测值纵轴预测值再画一条yx参考线。理想情况下所有点紧贴参考线如果点群整体偏离参考线说明存在系统性偏差。figure(Position, [100, 100, 1000, 420]); subplot(1, 2, 1); scatter(y_test, y_pred, 30, filled); hold on; plot([0, 90], [0, 90], r--, LineWidth, 1.5); xlabel(实测强度 (MPa)); ylabel(预测强度 (MPa)); title(SVR预测值与实测值对比); axis equal; grid on; subplot(1, 2, 2); scatter(y_pred, y_test - y_pred, 30, filled); yline(0, r--, LineWidth, 1.5); xlabel(预测强度 (MPa)); ylabel(残差 (MPa)); title(残差图); grid on;第二张残差图的信息量更大。横轴是预测值纵轴是残差实测减预测。残差应该在零线上下随机分布没有明显形状。如果残差图呈现“漏斗形”——预测值越大残差散布越宽——说明模型在高强度区间存在异方差性也就是高强度混凝土的配合比数据离散度更大模型对高强区间的预测不够稳后续可以考虑对高强度样本加权。如果残差图呈现弯曲的弧形说明模型没有完全拟合非线性关系可能需要换核函数或增加特征。我实测的残差图在预测值20到60 MPa这个区间散布比较均匀说明模型在中低强度区间拟合良好在60到80 MPa的高强度区间残差散布略宽这和工程常识一致——高强混凝土的强度对配合比微小变化更敏感试块变异性本就更大。还有一个必看的检查是训练集和测试集的R²差距。如果训练集R²高达0.97而测试集只有0.85说明过拟合明显需要增大Epsilon或减小BoxConstraint。我这里训练集R²约0.93、测试集0.90差距在合理范围内模型的泛化能力是真实的。6. 真实踩坑记录与后续扩展思路6.1 我踩过的四个坑第一个坑是忘了开Standardize。第一次跑这个流程时我用默认参数建模型结果KernelScale设成auto之后交叉验证R²只有0.55左右预测值几乎贴着训练集均值。排查半天发现问题是水泥用量几百的数值在欧氏距离计算里压倒了减水剂和龄期的作用模型在“睁眼瞎”。打开Standardize之后R²直接跳到0.78。这个教训的价值在于RBF核的SVR标准化和核函数选择是同等级的关键步骤。第二个坑是标准化顺序搞反导致的隐性数据泄漏。我曾经在一个类似项目里先对整个数据集做了zscore标准化再划分训练测试集结果测试集指标好得反常。后来仔细一想测试集的均值标准差已经被我用在了标准化过程里模型的预测成绩是“开卷考试”。换成先划分再训练后指标掉了几个点但那才是真实水平。这类问题在matlab里因为fitrsvm的Standardize参数而变得隐蔽很多人以为设了Standardize就万事大吉实际上你手动预处理时要格外小心这个先后顺序。第三个坑是Epsilon设成0看似“拟合更准”实则灾难。SVR里Epsilon如果设为0就退化成不带缓冲带的硬间隔回归把所有样本包括离群点都当作必须拟合的对象模型会变得极度复杂。最优参数搜索时如果候选值里有0贝叶斯优化器很容易在早期踩进去然后给出一个虚高的交叉验证分数。我的建议是Epsilon的下限设成0.01或0.05别给0机会。第四个坑是随机种子。做网格搜索和贝叶斯优化的那段时间我连续几天跑同一个脚本发现结果每天都不一样。原因很简单cvpartition划分数据用的随机数没有固定每天划分出来的训练测试集不同。后来我在脚本开头固定了rng(42)才让实验结果可复现。做任何模型对比实验之前请先固定随机种子。6.2 这个项目还可以往哪些方向做预测精度到R²0.90之后再单纯调参提升的空间已经不大了。想继续往上走重点应该放在特征工程上。混凝土强度理论里有一个经典定律叫阿布拉姆斯定律水灰比与强度之间存在近似的对数关系。这个物理先验完全可以变成特征——把“水用量除以水泥用量”作为一个新特征加进去。实测下来加入水灰比特征后测试集RMSE可以从4.6 MPa降到4.2 MPa左右。还可以尝试加入胶凝材料总量、水胶比、砂率这些派生特征每一类都是工程知识向模型注入的方式。算法层面的扩展一个值得一试的方向是用粒子群优化PSO替代贝叶斯优化来搜索SVR参数。PSO在大规模参数空间里的全局搜索能力更强而且超参数搜索和模型训练可以解耦方便并行化。这类“智能优化算法SVR”的组合在学术论文里很常见实际工程里只要算力允许效果也确实不错。模型融合是另一个思路。SVR擅长刻画整体趋势随机森林擅长处理局部非线性把两个模型的预测结果做加权平均或者用简单的线性回归学习这两个模型的权重往往能在不增加单个模型复杂度的情况下再稳定地提升一到两个点的R²。这种方式比追求单个模型的极致调参更稳健。从工程落地的角度看一个更实际的需求是只能拿到7天的早期强度数据怎么预测28天强度这类问题可以用迁移学习或直接从模型族里选带龄期输入的回归方法先训练到28天再用少量7天数据做增量修正。fitrsvm本身不支持增量训练但可以把模型作为初始值在新数据上重新微调参数效果远好于从零开始训练。跑完这个项目我最大的体会是SVR不是那种一眼看上去很惊艳的算法但它是小样本回归里最稳的选择。在1030条混凝土强度样本上它的表现超过了线性回归也超过了默认参数的神经网络而且训练过程可控、可解释、可复现。如果你手里的数据量在几百到两三千条之间特征和目标的关系又明显非线性不妨先把SVR和这套调参流程吃透它的性价比比一上来就堆深度网络高得多。本文还有配套的精品资源点击获取