FEATURED · 精选文章

灰色神经网络GM(1,1)+BP小样本预测MATLAB实现

发布时间 / 2026/8/31 16:03:52
来源 / 创域科博编辑部
栏目 / 资讯中心
灰色神经网络GM(1,1)+BP小样本预测MATLAB实现 简介本资源面向MATLAB初学者与数据建模实践者聚焦小样本条件下预测精度不足的典型痛点提供灰色神经网络这一融合灰色系统理论与ANN优势的解决方案。资源包共2个文件10.76MB含1个核心MATLAB脚本sss.m实现灰色神经网络建模、训练与预测全流程以及1个配套讲解视频mp4详细演示数据预处理、网络结构设计、Levenberg-Marquardt等优化算法调参及结果可视化全过程。已有138人学习下载内容紧扣邓聚龙灰色系统理论在有限信息场景下的工程落地代码可直接运行、参数注释清晰视频同步展示关键步骤与调试技巧特别适合科研中遇到样本量少、噪声干扰强、传统ANN易过拟合的实际问题。 手头只有十几个数据点却要预测未来三五期的走势这种事我干过不止一次。最开始总想着上LSTM、上Transformer后来发现样本量根本撑不起这么复杂的模型拟合结果好看一泛化就露馅。后来退回组合思路上把灰色预测和BP神经网络搭在一起用效果反而稳定得多。这套方案在MATLAB里实现起来不复杂代码量也不大很适合小样本场景下的趋势预测无论是做课程设计、毕业论文还是日常的数据分析都能直接用。这里说的“灰色神经网络”不是某一个固定模型而是一类思路的统称先用灰色模型通常就是GM(1,1)去捕捉数据的主趋势再用神经网络去修正灰色模型留下的残差最后得到组合预测。为什么这么组合能成因为GM(1,1)擅长用少数据拟合指数趋势但对波动、非线性细节无能为力而BP神经网络恰好擅长做非线性映射两者各管一段配合起来互补性很强。这篇文章我会把原理、MATLAB完整代码、参数选择和踩坑经验都摊开来讲代码可以直接复制去跑换成自己的数据就能用。1. 为什么小样本预测要选灰色神经网络而不是硬上深度学习1.1 小样本数据真正棘手的地方先说说小样本预测为什么难。样本量小尤其是只有10到20个观测点时很多经典方法都不好使。统计模型需要估计的参数数量如果接近样本量方差会非常大预测结果甚至不如一个简单的均值回归机器学习模型又依赖足够的训练样本来学习特征数据太少时几乎必然过拟合。深度学习就更不用说了LSTM、Transformer这类模型更适合长序列、大数据量场景你拿十几个点去训一个循环神经网络得到的通常是“训练误差极小、测试误差极大”的假象。还有一个现实问题小样本数据往往不是“干净”的。实际拿到的数据可能来自实验测量、市场调研、设备运行日志里面带着噪声、缺失和异常值样本量少意味着你很难通过统计手段把噪声“平均”掉。这种情况下模型对单个异常点极度敏感一个点异常就可能把整个预测结果带偏。灰色系统方法就是为这种“贫信息、少数据”的场景设计的。它不要求数据服从正态分布也不需要大样本支撑而是从数据本身挖掘规律用累加生成等操作把隐藏在原始序列里的趋势信息暴露出来这正是小样本场景下它还能站住脚的根本原因。1.2 灰色模型GM(1,1)是怎么用少量数据建模的灰色系统理论里最常用的就是GM(1,1)模型意思是“一阶微分方程、一个变量的灰色模型”。它的核心思想很直接原始序列如果看起来随机性很强先做一次累加生成AGO也就是把前k个数据逐项累加得到一个新序列这个新序列往往会呈现出明显的光滑指数趋势然后对这个累加序列建立一阶微分方程解出方程参数再做累减还原得到原始量纲下的预测值。用数学语言说设原始序列为x(0)一次累加后得到x(1)则x(1)(k) sum(x(0)(1), ..., x(0)(k))。对x(1)建立白化微分方程dx(1)/dt a * x(1) b其中a称为发展系数b称为灰作用量。这两个参数通过最小二乘法从数据里估计出来具体地构造矩阵B和向量Y用公式theta (BB)^(-1)BY即可得到[a, b]。得到参数后时间响应函数可以算出任意时刻的累加预测值再累减还原就能得到原始量纲下的预测结果。为什么n4就能建模因为GM(1,1)本质上是用一个指数曲线去逼近累加序列指数函数只有两个自由参数a和b估计起来需要的样本量天然就少。这也是它和统计回归一个很大的区别回归要估计趋势、季节、周期很多项灰色模型则把问题压缩到两个参数上牺牲了灵活度换来了小样本下的稳定性。1.3 灰色加神经网络结合的到底是哪一步GM(1,1)虽然能建但它的局限也很明显纯指数型数据预测效果好真实数据一旦带有波动、拐点、周期性GM(1,1)的拟合残差就很大直接预测会越偏越远。这时神经网络就派上用场了它不直接预测原始数据而是去学习灰色模型残差里的非线性规律。具体做法就是先用GM(1,1)算出对历史数据的拟合值用真实值减去拟合值得到残差序列再用BP神经网络对残差序列做时间序列预测得到未来各期的残差估计最终预测值等于灰色预测值加神经网络残差预测值。这等于把数据的“主趋势”和“细节波动”分开建模两部分的难度都降低了组合起来反而更准。我在实际比较中也试过直接用神经网络对原始序列做滑动窗口预测但小样本下效果很不稳定同样的代码换一组数据预测曲线经常变形。而GM(1,1)BP残差修正这种结构灰色部分已经兜住了趋势BP只需要修正局部偏差训练压力小得多鲁棒性也强不少。这就是为什么整体方案我更推荐这一种。2. 灰色神经网络建模方案与关键参数设计2.1 整体方案选型GM(1,1)趋势预测 BP残差修正组合模型的具体方案有很多变种常见的有三类第一类是GM(1,1)预测值直接作为BP输入的一部分第二类是BP对累加生成序列建模预测完再累减还原第三类就是本文采用的GM(1,1)预测主趋势、BP修正残差。三类方案里第一类需要把GM预测结果和原数据拼接成特征矩阵结构稍显繁琐第二类本质上是让神经网络重新学一个累加序列的映射如果样本太少网络很难学到稳定的累加规律第三类各模块职责清晰灰色模型负责趋势BP负责残差训练目标和评估指标都很明确出了问题也容易定位所以我建议新手从第三类入手。方案框架可以概括为四个步骤累加生成与GM(1,1)拟合、计算残差、BP滚动预测残差、累减还原并合成最终预测结果。这里有一个关键认知我们并不过分追求让GM(1,1)的拟合绝对误差最小化而是希望它把趋势捕捉干净让残差尽量只留下可被网络学习的局部模式。如果GM拟合本身误差很大且残差看起来完全是白噪声那加BP修正的意义也不大此时应该返回去检查数据质量或者考虑是否适合用灰色模型。2.2 数据预处理与样本构造GM(1,1)对数据有一个硬性要求原始序列必须非负。如果数据里有零值或负值比如利润、温差异常波动序列需要先做整体平移比如全部加一个常数c保证最小值为正预测结束后再减去c。这一步很多人会忘直接在代码里报错或预测出负值注意一下就行。残差序列在输入BP网络之前最好做归一化。MATLAB里通常用mapminmax函数它可以把数据映射到[0,1]区间。为什么要归一化因为残差的数量级可能和网络权值初始化不匹配不归一化会导致网络训练收敛很慢甚至不收敛。这里有两个容易踩的坑一是归一化时只能用训练数据计算统计量不能把验证期数据一起统计进去否则会造成信息泄漏评估结果虚假偏高二是对残差做归一化时要保存好ps结构体预测完必须用同一个ps做反归一化不要重新调用mapminmax否则数值对不上。BP样本构造也是一个关键环节。残差序列本身是一维时间序列想预测未来值就得把监督学习问题构造出来。常用的做法是滑动窗口取前p个残差作为输入第p1个残差作为输出窗口每滑动一步生成一个样本。比如残差序列长度是12p取3就能生成9个训练样本对。窗口大小p直接决定了样本数量和信息量p太小网络能看到的上下文太短p太大生成的样本对数又太少。根据我的经验小样本场景下p取2到4比较合适不要超过5。2.3 网络结构、优化算法与训练参数BP网络结构不需要复杂。小样本残差修正任务里输入层节点数就是窗口大小p输出层节点数是1隐含层只要一层就够了节点数我一般取3到8之间。有人喜欢堆大网络觉得隐含层越多拟合能力越强但在样本量只有十几个甚至几个的时候复杂网络几乎注定过拟合训练误差降到0也没意义预测阶段照样崩。优化算法这里要专门提一下标题里写了“优化算法”其实MATLAB神经网络的训练过程本身就是一个优化问题。fitnet默认的trainlm是Levenberg-Marquardt算法收敛快适合中等样本的回归拟合但对小样本来说它很容易把训练误差压到极小产生严重过拟合。小样本残差修正我更推荐trainbr也就是贝叶斯正则化方法它会在目标函数里加入权值惩罚项自动控制网络权值大小抑制过拟合。trainbr在小样本上的稳定性明显好于trainlm代价是训练时间稍长不过残差修正的网络很小这个时间成本基本可以忽略。还有一个必须养成习惯的操作设置随机种子。MATLAB里用rng(2024)这样的命令固定随机数生成器否则每次运行网络初始权值都不同结果会变来变去。如果你希望自己的实验可复现或者导师要求结果一致这一步不能省。3. MATLAB实操灰色神经网络预测代码逐段拆解3.1 灰色预测函数gm11的实现我不建议每次都把灰模型代码写一遍最好封装成函数。下面的gm11函数是完善后的版本包含了级比检查、参数估计、拟合和预测功能直接复制就能用。function [y_pred, y_hat, a, b] gm11(data, num_pred) % GM(1,1) 灰色预测函数 % 输入 % data 原始序列列向量或行向量均可要求非负 % num_pred 预测未来数据点的个数 % 输出 % y_pred 未来预测值长度为 num_pred % y_hat 对原始数据的拟合值长度与 data 相同 % a, b 灰色发展系数和灰作用量 data data(:); n length(data); if any(data 0) error(GM(1,1)要求原始数据非负请先对数据做平移处理); end % 级比检查判断数据是否适合GM(1,1) lambda data(1:end-1) ./ data(2:end); if min(lambda) exp(-2/(n1)) || max(lambda) exp(2/(n1)) warning(数据级比未落在标准区间内GM(1,1)预测效果可能不理想); end % 1. 一次累加生成 x1 cumsum(data); % 2. 紧邻均值生成序列 z1 z1 0.5 * (x1(2:end) x1(1:end-1)); % 3. 构造B矩阵和Y向量 B [-z1, ones(n-1, 1)]; Y data(2:end); % 4. 最小二乘估计参数 a, b theta (B * B) \ (B * Y); a theta(1); b theta(2); % 5. 时间响应函数求解累加序列预测 % x1_pred长度 n num_pred x1_pred zeros(n num_pred, 1); x1_pred(1) data(1); for k 1 : n num_pred - 1 x1_pred(k1) (data(1) - b/a) * exp(-a * k) b/a; end % 6. 累减还原得到原始量纲预测值 y_diff diff(x1_pred); % 长度为 nnum_pred-1 y_hat [data(1); y_diff(1:n-1)]; % 对原始数据的拟合 y_pred y_diff(n:end); % 未来预测 end这段代码里需要注意几个点。级比检查是很多演示代码里不会写的它用来判断原始数据是否呈现出适合灰色建模的指数规律。检查方法很简单计算相邻两期数据的比值lambda(i) data(i) / data(i1)如果所有lambda都落在区间[exp(-2/(n1)), exp(2/(n1))]内说明数据通过检验GM(1,1)可以使用如果不通过程序会给出警告提醒你结果可能不可靠。我在实际调试中遇到不少次级比超出区间但预测仍可用的案例所以这里只给警告不中断但你要有这个意识。最小二乘估计那一步theta (BB)\ (BY)用的是MATLAB左除运算内部会采用数值稳定的方法求解比直接写inv(B*B)*B*Y要稳。a为负时说明原始序列整体呈增长趋势为正时是下降趋势绝对值越大趋势越强。如果估计出的发展系数绝对值大于0.8说明数据变化过于剧烈GM(1,1)预测可信度会明显降低这时候建议考虑换其他模型。3.2 BP残差修正函数的实现GM(1,1)算出拟合值后残差序列就是真实值减去拟合值。这个残差序列需要用BP网络做滚动预测。下面这个函数把样本构造、归一化、训练、滚动预测都封装好了。function res_pred bp_residual_predict(res_train, num_pred, p, hidden) % BP神经网络残差预测函数 % 输入 % res_train 历史残差序列列向量 % num_pred 需要预测的未来残差个数 % p 滑动窗口长度即用前p个残差预测下一个 % hidden 隐藏层节点数 % 输出 % res_pred 未来残差预测值长度为 num_pred res_train res_train(:); n length(res_train); if n p 1 error(残差序列太短请减小窗口长度p); end % 构造训练样本对输入X为连续p个残差输出Y为下一个残差 X []; Y []; for i 1 : n - p X [X; res_train(i : ip-1)]; Y [Y; res_train(ip)]; end % 归一化注意保存ps结构体用于后续反归一化 [X_norm, psx] mapminmax(X, 0, 1); [Y_norm, psy] mapminmax(Y, 0, 1); % 建立BP网络采用贝叶斯正则化优化算法 net fitnet(hidden, trainbr); net.trainParam.showWindow false; net train(net, X_norm, Y_norm); % 滚动预测未来残差 % 用残差序列最后p个值作为初始输入 last res_train(end-p1 : end); res_pred zeros(num_pred, 1); for i 1 : num_pred last_norm mapminmax(apply, last, psx); pred_norm net(last_norm); pred mapminmax(reverse, pred_norm, psy); res_pred(i) pred; % 把新预测值拼到窗口末尾去掉最前面的旧值继续滚动 last [last(2:end), pred]; end end这个函数里最关键的是滚动预测逻辑。网络训练完成后我们手里只有历史残差序列没有未来的真实残差所以预测第一步之后必须把预测值本身当作已知值拼进输入窗口再预测下一步。这类似时间序列预测里常说的递归多步预测策略。如果预测步数比较多误差会逐步累积这是滚动预测本身的固有特点所以小样本场景下建议预测步数不要太多一般3到10期比较合适。这里还要注意mapminmax的用法。mapminmax默认是按行处理的所以输入X要先转置成“特征维数×样本数”的格式。归一化后得到的psx和psy两个结构体非常重要它们记录了训练数据的最大值、最小值和映射公式预测时用mapminmax(apply, ...)做相同的归一化再用mapminmax(reverse, ...)反归一到原始量纲。如果预测时重新调用mapminmax会得到完全错误的结果。3.3 主程序、结果可视化与指标计算把灰色预测和BP残差修正串起来就构成了完整的小样本预测流程。下面的主程序以一个12期销量数据为例预测未来5期。clear; clc; close all; rng(2024); % 固定随机种子保证结果可复现 % 原始数据某产品连续12期销量 data [112 118 132 129 135 148 150 157 163 172 178 184]; num_pred 5; % 预测未来5期 % 1. 灰色预测部分 [gm_pred, gm_fit, a, b] gm11(data, num_pred); fprintf(发展系数 a %.4f, 灰作用量 b %.4f\n, a, b); % 2. BP残差修正部分 res data - gm_fit; % 历史残差 p 3; % 滑动窗口长度 hidden 5; % 隐含层节点数 res_pred bp_residual_predict(res, num_pred, p, hidden); % 3. 最终组合预测 final_pred gm_pred res_pred; % 4. 绘制结果 figure; plot(1:length(data), data, ko-, LineWidth, 1.5); hold on; plot(1:length(data), gm_fit, b--, LineWidth, 1.2); plot(length(data)1 : length(data)num_pred, final_pred, r*-, LineWidth, 1.8); legend(真实值, 灰色拟合值, 灰神经最终预测, Location, northwest); xlabel(期数); ylabel(销量); grid on; title(GM(1,1)BP残差修正预测结果); % 5. 拟合阶段误差指标 mape_fit mean(abs((data - gm_fit)./data)) * 100; rmse_fit sqrt(mean((data - gm_fit).^2)); fprintf(拟合MAPE %.2f%%\n, mape_fit); fprintf(拟合RMSE %.2f\n, rmse_fit);这段代码跑下来你会看到三条线原始数据折线、灰色拟合曲线、未来预测曲线。灰色部分会先大致描出上升趋势BP残差修正后再把局部的上下波动补回来。如果残差修正有效拟合曲线会比纯灰色模型更贴近真实点预测结果也更符合数据惯性。这里要特别提醒验证方式的问题。很多新手会把全部数据拿去训练然后直接说预测未来5期这样没问题但你无法确认模型效果。严谨的做法是留出后几期数据做验证只用前n-n_test期建模预测后n_test期和真实值对比计算误差。代码和主程序逻辑完全一致只是data传入时截取前段预测长度设为n_test。这种留出验证在论文里是必须的否则评审看不到你的模型泛化能力。我在实际项目里还会用滚动验证方式再测一轮每次从开头到第i期训练预测第i1期然后把真实值加入训练集继续滚动。对十几个样本来说这种做法能最大化利用数据但代码会稍微复杂一些。对于日常课程设计留出后几期验证就足够了。4. 调参经验与常见问题排查4.1 模型打不准时的检查顺序如果你跑出来的预测结果不好先别急着调网络结构我建议按下面的顺序排查。第一步检查数据是否适合灰色模型。数据里有没有零值或负值级比检验是否通过如果原始数据波动非常大比如忽高忽低、没有明显趋势GM(1,1)本身就很难拟合后续残差修正也救不回来。这种情况下先考虑数据变换比如取对数、做平滑、平移看能否让数据更接近指数趋势。第二步检查灰色模型拟合效果。看gm_fit和原始data的拟合曲线是否接近。如果拟合线已经严重偏离数据形态问题出在GM(1,1)本身BP修正再强也只是在错误的基础上打补丁。可以尝试改变GM的建模方式比如用新信息GM(1,1)、新陈代谢GM(1,1)但那是后话。第三步检查残差修正是否真的有效。把纯GM预测和GMBP预测放到同一张图里对比如果加了BP之后预测曲线变化不大要么是隐藏层节点数太少模型没学到规律要么是残差本身就是噪声没有可学习的模式。如果加了BP之后预测值异常大或异常负优先检查归一化和反归一化是否用了同一个ps结构体。第四步检查指标是否真的反映问题。MAPE很高不一定代表结果不可用如果数据本身接近零MAPE会被极小的真实值放大这时候更推荐看RMSE或者平均绝对误差MAE。4.2 灰色模型的前提检验与数据变换GM(1,1)有它的适用边界硬用会出问题。除了非负要求最重要的就是发展系数a的取值。a的绝对值小于0.3时模型适合中长期预测在0.3到0.5之间时适合短期预测中长期预测要谨慎在0.5到0.8之间时短期预测也要很小心大于0.8时不建议用GM(1,1)。这个判断标准在灰色系统理论教材里都有代码里可以加一个判断超过某个阈值直接报错或警告。数据变换这块我经常用的一种做法是对数变换加平移。假设原始数据出现负值先整体加一个c变为正数预测后再减c如果数据跨度很大比如从几十到几万可以先取对数再建模预测完再取指数还原。变换的目的是让数据更接近指数规律让GM(1,1)的主趋势捕捉更准确。还有一个容易忽略的点累加生成会放大数据的整体数量级如果原始数据本身很大比如销售额达到百万级累加后数值更大直接用于参数计算时可能会出现数值稳定性问题。解决方法是先对原始数据做一次归一化或缩放到0到1区间再送入gm11函数最后把预测结果缩放回去。灰色模型的尺度敏感性比神经网络低但该缩放的时候还是要缩放。4.3 小样本下神经网络过拟合的应对小样本训练神经网络过拟合是最大的敌人。我在实际调试中的经验是先用trainbr做贝叶斯正则化如果训练误差和验证误差差距仍然过大再考虑减小隐藏层节点数、增大滑动窗口p或者用早停。早停在MATLAB里可以通过设置net.trainParam.max_fail实现但由于小样本下验证集本身可能只有两三个点早停的可靠性有限我更推荐靠正则化和网络小型化来解决问题。网络结构的起点可以保守一些。输入层是p个节点隐藏层我推荐从5开始效果不好再试着加减。隐藏层节点数大于10时基本可以断定会过拟合尤其你的训练样本对可能只有十几个。输出层永远是1个节点不需要调整。另一个容易被忽视的问题是训练目标。残差修正网络的目标不是“把训练残差降到最小”而是“预测的未来残差能让最终预测更准”。有时候你会发现训练误差很小但最终预测结果反而更差这说明网络把残差里的随机噪声也学进去了。这种时候加入一点权值惩罚、减少训练轮数或者对残差序列做一次平滑都比继续加深网络要有效。数据量实在不够时还有一个简单粗暴的技巧对残差序列做灰色预测或指数平滑作为修正不一定要用神经网络。毕竟残差序列往往已经去掉了主趋势剩下的规律较弱简单方法有时反而更稳定。我遇到过不少案例BP修正和简单移动平均修正的效果差不多但BP需要调参简单移动平均不需要。所以不要为了技术含量硬上神经网络实用优先。结尾从模型原理到MATLAB实现这一套走下来我自己最大的体会是小样本预测永远不要指望某个模型“万能”关键是想清楚让每个模块负责什么。灰色模型管趋势神经网络管残差组合使用能解决很多单一模型的短板但也别忽略数据本身的检验和清洗。最后再提醒一句rng固定种子、mapminmax保存ps结构体、用留出法验证这三个习惯比任何高级调参都重要。项目里直接套用这套代码先把流程跑通再根据数据特征慢慢调整p和hidden效果通常会给你惊喜。本文还有配套的精品资源点击获取
RELATED — 相关阅读

相关资讯

LATEST — 最新资讯

最新发布

TODAY — 本日精选

新闻

WEEKLY — 本周精选

新闻

MONTHLY — 本月精选

新闻