MATLAB实现GM(1,1)灰色预测:小样本数据建模与趋势分析实战 📅 发布时间:2026/8/29 9:51:58 👁 浏览次数: 1. 项目概述从“小数据”到“趋势洞察”的灰色预测在数据分析与预测领域我们常常面临一个尴尬的局面手头的数据量太少传统的统计模型如回归分析、时间序列ARIMA对样本量有较高要求难以施展拳脚同时数据本身又带有明显的随机性和不确定性呈现出一种“部分信息已知部分信息未知”的“灰色”状态。比如一个初创公司只有过去五年的年度营收数据想预测下一年的趋势或者一个地区只有寥寥几年的某种传染病发病率记录需要评估未来风险。这时候一个诞生于上世纪80年代、专为应对“贫信息”不确定性系统而设计的模型——灰色预测模型GM(1,1)就成为了我们手中的利器。GM(1,1)是灰色系统理论中最核心、应用最广泛的预测模型。这里的“GM”是Grey Model的缩写“(1,1)”则代表模型是1阶方程只包含1个变量。它的核心思想非常巧妙不是直接对原始杂乱无章的数据序列进行拟合而是通过一次累加生成操作1-AGO将原始数据转化为具有明显指数增长规律的新序列。这个新序列的规律性更强更容易用微分方程来描述。我们求解这个微分方程得到新序列的预测值再通过累减生成I-AGO还原就得到了原始序列的预测值。整个过程相当于把“灰”色的、看不清规律的数据通过数学变换“白”化挖掘出其内在的规律。为什么在MATLAB里实现它因为灰色预测的计算过程涉及矩阵运算、微分方程求解和累加累减手动计算繁琐且易错。MATLAB强大的矩阵计算和符号数学工具箱能让这些步骤变得清晰、高效。你只需要编写一个几十行的脚本就能完成从数据导入、模型构建、精度检验到预测绘图的完整流程。这对于数学建模竞赛、科研分析或者商业决策中的快速趋势研判来说效率提升不是一点半点。接下来我将以一个具体的例题为线索手把手带你拆解GM(1,1)的每一个数学细节并用MATLAB代码将其实现同时分享我在多次使用中积累的实战经验和避坑指南。2. GM(1,1)模型的核心原理与数学拆解要真正用好一个模型不能只停留在调用函数。理解其背后的数学机理才能知道它的适用边界并在结果出现偏差时知道从哪里排查。GM(1,1)的推导过程是其灵魂所在。2.1 数据预处理累加生成1-AGO假设我们有一个原始非负数据序列X⁽⁰⁾ [x⁽⁰⁾(1), x⁽⁰⁾(2), ..., x⁽⁰⁾(n)]上标(0)表示原始序列。这个序列可能波动很大看不出明显趋势。累加生成1-AGO是第一步也是关键一步。它生成一个新序列X⁽¹⁾其中每个元素是原始序列从第一个到当前元素的累加和x⁽¹⁾(k) Σ_{i1}^{k} x⁽⁰⁾(i), k1,2,...,n例如原始序列为[2, 3, 4, 5]那么1-AGO序列就是[2, 235, 2349, 234514]。注意为什么累加后规律会变明显这其实是一种平滑处理。原始数据的随机波动在累加过程中被部分抵消而数据的长期趋势增长或衰减被放大和凸显出来。通常经过1-AGO处理后的序列会呈现出近似指数增长的形态这为后续用微分方程建模奠定了基础。2.2 构建灰色微分方程我们对生成的新序列X⁽¹⁾建立一阶常微分方程这就是GM(1,1)模型的白化方程或影子方程dx⁽¹⁾/dt a*x⁽¹⁾ u其中a称为发展系数反映了序列X⁽¹⁾的发展态势u称为灰色作用量可以理解为系统内的内生驱动项。然而我们拥有的是离散数据点而非连续函数。因此需要用离散形式来近似这个微分方程。这里引入了背景值z⁽¹⁾(k)通常取为紧邻均值的生成z⁽¹⁾(k) 0.5 * [x⁽¹⁾(k) x⁽¹⁾(k-1)], k2,3,...,n用差分代替微分用背景值代替x⁽¹⁾得到GM(1,1)的基本形式灰色微分方程x⁽⁰⁾(k) a*z⁽¹⁾(k) u, k2,3,...,n注意这里x⁽⁰⁾(k)恰好等于x⁽¹⁾(k) - x⁽¹⁾(k-1)即累减生成。2.3 参数估计与时间响应式将k2,3,...,n分别代入灰色微分方程我们可以得到一个线性方程组。用矩阵形式表示为Y B * [a, u]ᵀ其中Y [x⁽⁰⁾(2), x⁽⁰⁾(3), ..., x⁽⁰⁾(n)]ᵀB [[-z⁽¹⁾(2), 1]; [-z⁽¹⁾(3), 1]; ...; [-z⁽¹⁾(n), 1]]这是一个典型的超定方程组方程数多于未知数我们用最小二乘法来求解参数a和u[a, u]ᵀ (Bᵀ * B)⁻¹ * Bᵀ * Y这一步在MATLAB里就是一行代码P (B*B) \ (B*Y);P(1)是aP(2)是u。求出a和u后代入白化方程dx⁽¹⁾/dt a*x⁽¹⁾ u并利用初始条件x⁽¹⁾(1) x⁽⁰⁾(1)求解这个微分方程得到X⁽¹⁾序列的时间响应式预测模型x̂⁽¹⁾(k1) [x⁽⁰⁾(1) - u/a] * exp(-a*k) u/a, k0,1,2,...这个x̂⁽¹⁾就是我们累加序列的预测值。2.4 数据还原累减生成I-AGO最后我们需要将预测的累加序列x̂⁽¹⁾还原为原始序列的预测值x̂⁽⁰⁾。这个过程是累加生成的逆运算称为累减生成I-AGOx̂⁽⁰⁾(k1) x̂⁽¹⁾(k1) - x̂⁽¹⁾(k), k1,2,...特别地x̂⁽⁰⁾(1) x⁽⁰⁾(1)。至此我们完成了从原始数据到预测值的完整数学闭环。可以看到整个模型的核心参数只有两个a和u结构简洁这正是它适用于“小样本”预测的优势所在。3. 实战例题城市年度用电量预测理论总是抽象的我们用一个具体的例子来贯穿始终。假设某城市2018年至2022年的年度用电量单位亿千瓦时数据如下年份20182019202020212022用电量125143162185210我们的任务是建立GM(1,1)模型预测该城市2023年和2024年的用电量并对模型精度进行评估。3.1 手算推导与模型建立首先定义原始序列X⁽⁰⁾ [125, 143, 162, 185, 210]步骤1进行一次累加生成1-AGOx⁽¹⁾(1) 125x⁽¹⁾(2) 125 143 268x⁽¹⁾(3) 268 162 430x⁽¹⁾(4) 430 185 615x⁽¹⁾(5) 615 210 825得到X⁽¹⁾ [125, 268, 430, 615, 825]步骤2计算背景值z⁽¹⁾(k)z⁽¹⁾(2) 0.5*(125268) 196.5z⁽¹⁾(3) 0.5*(268430) 349z⁽¹⁾(4) 0.5*(430615) 522.5z⁽¹⁾(5) 0.5*(615825) 720步骤3构造矩阵B和向量YY [x⁽⁰⁾(2), x⁽⁰⁾(3), x⁽⁰⁾(4), x⁽⁰⁾(5)]ᵀ [143, 162, 185, 210]ᵀB [[-z⁽¹⁾(2), 1]; [-z⁽¹⁾(3), 1]; [-z⁽¹⁾(4), 1]; [-z⁽¹⁾(5), 1]] [[-196.5, 1]; [-349, 1]; [-522.5, 1]; [-720, 1]]步骤4最小二乘法估计参数a, u计算Bᵀ*B和Bᵀ*Y然后求解。Bᵀ*B [[196.5²349²522.5²720², -(196.5349522.5720)]; [-(196.5349522.5720), 4]] ≈ [[995029.5, -1788]; [-1788, 4]]Bᵀ*Y [-(196.5*143349*162522.5*185720*210), 143162185210]ᵀ ≈ [-322445, 700]ᵀ解方程组(Bᵀ*B) * [a, u]ᵀ Bᵀ*Y得到a ≈ -0.1248u ≈ 117.9766步骤5确定时间响应式将a, u和x⁽⁰⁾(1)125代入公式x̂⁽¹⁾(k1) [125 - 117.9766/(-0.1248)] * exp(0.1248*k) 117.9766/(-0.1248)化简得x̂⁽¹⁾(k1) ≈ 1070.33 * exp(0.1248*k) - 945.33步骤6计算拟合值及还原令k0,1,2,3,4x̂⁽¹⁾(1) 1070.33*exp(0) - 945.33 125.00(与初始值一致)x̂⁽¹⁾(2) 1070.33*exp(0.1248*1) - 945.33 ≈ 268.18x̂⁽¹⁾(3) 1070.33*exp(0.1248*2) - 945.33 ≈ 430.65x̂⁽¹⁾(4) 1070.33*exp(0.1248*3) - 945.33 ≈ 614.73x̂⁽¹⁾(5) 1070.33*exp(0.1248*4) - 945.33 ≈ 822.98累减还原得到原始序列拟合值x̂⁽⁰⁾(1) 125.00x̂⁽⁰⁾(2) x̂⁽¹⁾(2) - x̂⁽¹⁾(1) 268.18 - 125.00 143.18x̂⁽⁰⁾(3) 430.65 - 268.18 162.47x̂⁽⁰⁾(4) 614.73 - 430.65 184.08x̂⁽⁰⁾(5) 822.98 - 614.73 208.25步骤7预测2023和2024年用电量预测是向前外推。对于2023年对应k5x̂⁽¹⁾(6) 1070.33*exp(0.1248*5) - 945.33 ≈ 1057.52x̂⁽⁰⁾(6) x̂⁽¹⁾(6) - x̂⁽¹⁾(5) 1057.52 - 822.98 234.54(亿千瓦时) 对于2024年对应k6x̂⁽¹⁾(7) 1070.33*exp(0.1248*6) - 945.33 ≈ 1321.94x̂⁽⁰⁾(7) x̂⁽¹⁾(7) - x̂⁽¹⁾(6) 1321.94 - 1057.52 264.42(亿千瓦时)手算过程虽然能加深理解但效率低且易错。接下来我们看看如何用MATLAB优雅地完成这一切。4. MATLAB实现从脚本编写到可视化分析在MATLAB中实现GM(1,1)我们将过程模块化编写一个清晰、可复用的函数。这里我分享一个我常用的、包含完整检验和绘图功能的实现版本。4.1 核心函数编写我们将主要步骤封装在一个名为GM11的函数中。这个函数输入原始数据序列和需要预测的步数输出拟合值、预测值、模型参数以及精度指标。function [fit, forecast, a, u, C, P] GM11(original_data, forecast_step) % GM(1,1)灰色预测模型 % 输入 % original_data: 原始数据行向量例如 [125, 143, 162, 185, 210] % forecast_step: 需要向前预测的步数例如 2 % 输出 % fit: 对原始数据的拟合值 % forecast: 预测值长度为forecast_step的向量 % a: 发展系数 % u: 灰色作用量 % C: 后验差比值 % P: 小误差概率 n length(original_data); X0 original_data(:); % 确保为行向量 % 1. 累加生成(1-AGO) X1 cumsum(X0); % 2. 构造数据矩阵B和Y Z (X1(1:end-1) X1(2:end)) / 2; % 背景值序列 Y X0(2:end); B [-Z, ones(n-1, 1)]; % 3. 最小二乘法估计参数 a, u P (B * B) \ (B * Y); % 核心计算 a P(1); u P(2); % 4. 计算时间响应式及拟合值 % 时间响应式: X1_hat(k1) (X0(1)-u/a)*exp(-a*k) u/a k 0:nforecast_step-1; X1_hat (X0(1) - u/a) * exp(-a * k) u/a; % 5. 累减还原(I-AGO)得到原始序列拟合和预测值 X0_hat [X0(1), diff(X1_hat)]; % diff计算后向差分 fit X0_hat(1:n); % 拟合部分 forecast X0_hat(n1:end); % 预测部分 % 6. 精度检验 % 计算残差和相对误差 residual X0 - fit; epsilon abs(residual ./ X0); % 相对误差绝对值 % 计算原始数据均值、方差 X0_mean mean(X0); S1 std(X0); % 原始序列标准差 % 计算残差均值、方差 residual_mean mean(residual); S2 std(residual); % 残差标准差 % 后验差比值C C S2 / S1; % 小误差概率P % 计算小误差概率|残差-残差均值| 0.6745*S1 的比例 P sum(abs(residual - residual_mean) 0.6745 * S1) / n; % 7. 打印关键结果 fprintf(发展系数 a %.4f\n, a); fprintf(灰色作用量 u %.4f\n, u); fprintf(后验差比值 C %.4f\n, C); fprintf(小误差概率 P %.4f\n, P); fprintf(模型精度等级判断); if (P 0.95 C 0.35) fprintf(好 (一级)\n); elseif (P 0.80 C 0.50) fprintf(合格 (二级)\n); elseif (P 0.70 C 0.65) fprintf(勉强合格 (三级)\n); else fprintf(不合格 (四级)\n); end end4.2 主程序调用与结果可视化有了核心函数主程序就非常简洁了。我们调用函数并绘制对比图让结果一目了然。% 主程序城市用电量预测 clear; clc; close all; % 1. 输入数据 year 2018:2022; power_consumption [125, 143, 162, 185, 210]; % 单位亿千瓦时 forecast_years 2; % 预测未来2年 % 2. 调用GM(1,1)模型 [fit_vals, forecast_vals, a, u, C, P] GM11(power_consumption, forecast_years); % 3. 输出预测结果 fprintf(\n 预测结果 \n); for i 1:forecast_years fprintf(预测年份 %d 用电量: %.2f 亿千瓦时\n, year(end)i, forecast_vals(i)); end % 4. 可视化拟合与预测效果图 figure(Position, [100, 100, 900, 500]); % 绘制原始数据点 plot(year, power_consumption, bo-, LineWidth, 2, MarkerSize, 10, MarkerFaceColor, b); hold on; % 绘制拟合曲线包括历史拟合 plot(year, fit_vals, rs--, LineWidth, 1.5, MarkerSize, 8, MarkerFaceColor, r); % 绘制预测部分 future_year [year(end), year(end)1:year(end)forecast_years]; future_data [power_consumption(end), forecast_vals]; plot(future_year, future_data, g^--, LineWidth, 1.5, MarkerSize, 8, MarkerFaceColor, g); % 图例和标签 legend(原始数据, GM(1,1)拟合值, 预测值, Location, best); xlabel(年份); ylabel(用电量 (亿千瓦时)); title(基于GM(1,1)模型的城市用电量预测); grid on; hold off; % 5. 可视化残差分析图 figure(Position, [100, 100, 900, 400]); subplot(1,2,1); bar(year, power_consumption - fit_vals); xlabel(年份); ylabel(残差 (实际-拟合)); title(拟合残差图); grid on; subplot(1,2,2); relative_error abs((power_consumption - fit_vals) ./ power_consumption) * 100; bar(year, relative_error); xlabel(年份); ylabel(相对误差 (%)); title(相对误差百分比图); grid on; sgtitle(模型精度分析);运行这段代码你会在命令窗口看到计算出的参数a≈-0.1248,u≈117.9766以及预测结果2023年约234.54亿千瓦时2024年约264.42亿千瓦时。同时会生成两张图第一张展示历史数据的拟合情况和未来趋势的预测第二张是残差和相对误差分析图直观展示模型的拟合精度。实操心得在编写MATLAB函数时我强烈建议将精度检验后验差C和小误差概率P集成进去。很多初学者只关心预测值忽略了模型本身的可靠性评估。这两个指标是判断你的GM(1,1)模型能否用于实际预测的“体检报告”。如果C值过大或P值过小说明模型精度不够预测结果参考价值有限需要回头检查数据或考虑其他模型。5. 模型检验、优化与常见问题排查模型建好了预测值也出来了但这远不是终点。一个负责任的建模者必须对模型进行严格的检验并知道在什么情况下需要优化以及如何排查常见问题。5.1 精度检验不只是看误差GM(1,1)模型常用的精度检验方法是后验差检验法主要看两个指标后验差比值 CC S2 / S1其中S1是原始序列的标准差S2是残差序列的标准差。C值越小说明预测误差的波动相对于原始数据的波动越小模型精度越高。小误差概率 PP P{|e(k)-ē| 0.6745S1}即残差与残差均值之差的绝对值小于0.6745倍原始序列标准差的概率。P值越大说明模型预测值与实际值偏差较小的概率越高。精度等级通常划分为四级一级好P 0.95 且 C 0.35二级合格P 0.80 且 C 0.50三级勉强合格P 0.70 且 C 0.65四级不合格P ≤ 0.70 或 C ≥ 0.65在我们用电量的例子中计算出的C和P值如果满足一级或二级标准那么预测结果才具有较高的可信度。如果落在三级或四级我们必须谨慎对待预测值并考虑以下优化策略。5.2 模型优化与适用性探讨GM(1,1)模型有其固有的特点和局限性了解这些才能正确使用和优化它。1. 数据预处理优化级比检验在建模前应计算原始序列的级比σ(k) x⁽⁰⁾(k-1) / x⁽⁰⁾(k)。一个适合GM(1,1)建模的序列其所有级比σ(k)应落在可容覆盖区间(exp(-2/(n1)), exp(2/(n1)))内。对于5个数据点区间约为(0.7165, 1.3956)。如果级比超出此范围说明序列可能不适合直接使用原始GM(1,1)需要进行平移变换所有数据加一个常数c或对数变换等预处理使级比落入可容区间内。示例若某序列为[2.5, 3.8, 6.0, 9.5]级比可能超出范围。我们可以尝试给每个数据加1变为[3.5, 4.8, 7.0, 10.5]再检验级比。2. 背景值构造优化经典GM(1,1)用紧邻均值生成背景值z⁽¹⁾(k)0.5*(x⁽¹⁾(k)x⁽¹⁾(k-1))。研究表明这并非最优。可以引入权重系数λ构造z⁽¹⁾(k)λ*x⁽¹⁾(k) (1-λ)*x⁽¹⁾(k-1)并通过优化算法如最小化平均相对误差求解最优λ。通常最优λ在0.3到0.5之间不一定等于0.5。在MATLAB中实现这一点可以将参数估计部分改为一个关于λ的优化问题。3. 模型适用场景与局限适用短期预测通常预测步数不超过数据量的1/2、指数增长或衰减趋势明显的小样本数据通常n≥4即可、数据序列无剧烈震荡。不适用/慎用长期预测误差会累积放大、数据呈现周期性或随机波动、序列中有异常值或突变点、数据量极大此时更复杂的模型可能更优。5.3 常见问题与MATLAB调试技巧在实际编码和运行中你可能会遇到以下问题问题1MATLAB报错“矩阵接近奇异或缩放错误”。原因这通常发生在构造的B矩阵B*B行列式接近零导致求逆(B*B)^(-1)数值不稳定。可能因为数据序列变化太平缓或背景值Z序列过于接近。解决检查原始数据是否差异过小。可以尝试对数据乘以一个缩放因子如1000预测后再除回来不影响相对关系。在计算参数时使用MATLAB更稳定的求解方法P pinv(B*B) * (B*Y);或P lsqminnorm(B*B, B*Y);它们能更好地处理病态矩阵。问题2预测值出现负数但实际物理量不可能为负如人口、销量。原因GM(1,1)的时间响应式是指数形式当发展系数a0时模型是衰减的长期预测可能趋向于-u/a若-u/a为负则预测值可能为负。解决适用性判断首先检查原始数据是否呈下降趋势。如果数据本身在增长a0理论上预测值不会为负。如果为负可能是短期预测中的计算误差或模型已不适用。非负处理如果从业务上确定预测值不能为负可以对最终预测结果进行截断forecast max(forecast, 0);。但这是一种事后补救说明模型可能已偏离实际情况。考虑其他模型对于非负序列可考虑使用灰色Verhulst模型适用于S型饱和序列或其他约束预测模型。问题3拟合效果很好但预测结果明显偏离常识如增长过快。原因GM(1,1)对指数增长趋势外推非常“激进”。如果发展系数a的绝对值较大负得多exp(-a*k)增长极快会导致预测值爆炸式增长。解决审视预测步数GM(1,1)主要用于短期预测。将预测步数forecast_step限制在较小范围如1-3步。结合业务逻辑任何数学模型都只是工具必须与领域知识结合。如果预测值高得离谱需要用人脑判断其合理性并可能需要对结果进行平滑或设定上限。使用滚动预测不一次性预测多步而是用预测出的下一步值加入到历史数据中重新建模预测再下一步。这种方法能部分吸收新信息但计算量增大。问题4如何将模型用于新数据或批量预测将上面的GM11函数保存为.m文件。对于新的数据序列只需在主脚本中修改original_data变量重新运行即可。对于需要批量处理多个序列的情况例如预测多个城市的指标可以写一个循环将每个序列作为GM11函数的输入并将输出结果如预测值、精度等级保存到结构体或元胞数组中。我个人在多次数学建模竞赛中使用GM(1,1)的体会是它是一把锋利的“手术刀”在“小数据、趋势明”的场景下非常有效。但它不是“万能锤”不能解决所有预测问题。最关键的一步永远是建模前的数据分析画图观察趋势、计算级比判断适用性。如果原始数据序列的折线图看起来大致像一条指数曲线那么GM(1,1)很可能给你一个惊喜。反之如果数据上下跳动毫无规律那么强行使用GM(1,1)无异于刻舟求剑。最后永远用后验差检验给模型上个“保险”并用你的专业常识去审视每一个预测结果。