资讯动态

Python实现灰色预测GM(1,1)模型:原理、代码与实战应用

发布时间:2026/8/27 11:43:08 来源:尧图企业网站定制
1. 项目概述从“黑箱”到“灰箱”的预测艺术在数学建模和数据分析的实战中我们常常会遇到一个经典难题手头的数据量少得可怜样本信息模糊不清传统的统计模型因为需要大样本和特定分布而束手无策。这时候一个听起来有点“玄学”但实则非常“硬核”的工具就该登场了——灰色预测模型。它不追求数据的“全貌”而是擅长从少量、不完全的信息中挖掘内在规律实现对未来趋势的预测特别适合处理“小样本、贫信息”的不确定性问题。简单来说它把研究对象从一个信息完全未知的“黑箱”变成了一个部分信息已知的“灰箱”这正是“灰色系统理论”的核心思想。对于经常使用Python进行数学建模、算法竞赛如国赛、美赛、亚太杯或者处理经济、社会、工程领域预测问题的朋友来说掌握灰色预测模型的原理和代码实现无疑是工具箱里的一把利器。它不像深度学习那样需要海量数据和强大算力也不像传统时间序列分析那样对数据平稳性有严苛要求。它的魅力在于“四两拨千斤”用简洁的数学形式和清晰的实现逻辑解决看似复杂的预测问题。本文将从一个实践者的角度手把手拆解灰色预测模型尤其是最核心的GM(1,1)模型的Python实现不仅给你能“抄作业”的代码更会深入每个参数、每行代码背后的“为什么”并分享我在多次建模实战中积累的调参技巧和避坑指南。2. 灰色预测模型核心思想与适用场景辨析在深入代码之前我们必须先搞清楚灰色预测模型到底在解决什么问题以及它最适合的应用场景是什么。盲目套用模型是建模大忌。2.1 “灰色”的本质信息的不完全性灰色系统理论由邓聚龙教授提出其核心是承认并处理信息的不完全性。与“白色系统”信息完全明确和“黑色系统”信息完全未知不同“灰色系统”内部信息部分已知、部分未知。灰色预测模型就是针对这种灰色系统进行预测的方法。它通过生成处理技术如累加生成来弱化原始数据的随机性挖掘其潜在规律然后建立微分方程模型进行预测最后再通过累减生成还原得到预测值。2.2 GM(1,1)模型一阶单变量的经典之作我们最常接触的也是本文重点讲解的是GM(1,1)模型。这个名字拆解开来就是G (Grey): 灰色M (Model): 模型第一个1: 表示一阶微分方程第二个1: 表示单个变量所以GM(1,1)本质上是一个基于一阶微分方程的单变量灰色预测模型。它的基本假设是尽管原始数据序列可能看起来杂乱无章但经过一次累加生成1-AGO后形成的新序列具有近似指数增长的规律可以用一个一阶线性微分方程来拟合。2.3 适用场景与禁忌根据我的经验GM(1,1)模型在以下场景中表现优异数据量极少通常只需要4个以上的数据点就能建模这在历史数据匮乏的场景如新产品初期销量预测、新兴社会现象分析下是巨大优势。趋势单调数据呈现出明显的增长或下降趋势且没有剧烈的、周期性的波动。例如某种资源的中长期消耗量、处于成长期的产品累计用户数、某些慢性病的发病率年度变化等。短期预测灰色预测更适合中短期预测。对于长期预测由于模型是基于指数趋势的外推误差会逐渐累积放大需要谨慎使用并建议结合其他模型进行对比。而以下场景则需要警惕或避免直接使用GM(1,1)数据波动剧烈如果原始数据上下震荡没有稳定趋势累加生成后也难以形成光滑的指数曲线模型拟合效果会很差。具有明显季节性/周期性例如月度销售额、电力负荷日数据等。纯GM(1,1)无法捕捉周期特征必须与季节调整模型结合如灰色-马尔可夫模型、季节调整的GM(1,1)等。长期预测需求如前所述长期外推风险高。数据包含异常值异常值会严重扭曲累加序列的趋势导致发展系数a和灰色作用量b的估计严重偏离必须进行数据预处理如平滑处理或剔除异常值。实操心得在拿到数据后不要急着写代码。先画图用matplotlib把原始数据的时间序列图画出来直观判断其趋势性、波动性和周期性。这是选择模型的第一步也是最重要的一步能节省大量无效编码和调试时间。3. GM(1,1)模型的数学原理与手动演算理解数学原理是写出正确代码、并能进行调试和优化的基础。我们用一个最简单的例子来手动推导一遍这样你看代码时就会觉得每一行都理所当然了。假设我们有原始数据序列X0 [x0(1), x0(2), x0(3), x0(4)] [2.874, 3.278, 3.337, 3.390]步骤1一次累加生成1-AGO这是灰色预测的“灵魂操作”目的是弱化随机性凸显趋势。X1(k) sum_{i1}^{k} X0(i)计算得到X1 [2.874, 2.8743.2786.152, 6.1523.3379.489, 9.4893.39012.879]即X1 [2.874, 6.152, 9.489, 12.879]。你可以看到X1序列比X0平滑得多增长趋势更接近指数曲线。步骤2生成紧邻均值序列Z1为了构建微分方程我们需要X1的“背景值”。通常使用紧邻均值生成Z1(k) 0.5 * [X1(k) X1(k-1)], for k 2, 3, 4... 计算得到Z1 [-, 0.5*(2.8746.152)4.513, 0.5*(6.1529.489)7.8205, 0.5*(9.48912.879)11.184]即Z1 [-, 4.513, 7.8205, 11.184]。注意Z1的长度比X1少1。步骤3建立GM(1,1)的微分方程及其白化形式灰色系统理论的核心微分方程是dX1/dt a * X1 b这个方程被称为GM(1,1)模型的白化方程或影子方程。其中a称为发展系数反映X1也即累加序列的增长势头。a为负表示增长a为正表示衰减。其绝对值大小影响预测曲线的陡峭程度。b称为灰色作用量可以理解为系统发展的内生驱动力量。我们的目标就是根据已知的X0原始序列来估计参数a和b。步骤4利用最小二乘法求解参数a, b将微分方程离散化可以得到近似方程X0(k) a * Z1(k) b for k 2, 3, 4... (因为X0(k) X1(k) - X1(k-1))这构成了一个线性方程组。写成矩阵形式Y B * [a, b]^T。 其中Y [X0(2), X0(3), X0(4)]^T [3.278, 3.337, 3.390]^TB [[-Z1(2), 1], [-Z1(3), 1], [-Z1(4), 1]] [[-4.513, 1], [-7.8205, 1], [-11.184, 1]]利用最小二乘法求解参数向量[a, b]^T (B^T * B)^(-1) * B^T * Y通过计算这里略去具体矩阵运算我们可以得到a和b的估计值。假设我们算得a ≈ -0.0372,b ≈ 3.0653。a为负表明序列是增长趋势这与我们观察一致。步骤5求解时间响应式预测模型解白化方程dX1/dt a * X1 b 得到其时间响应式即累加序列的预测公式X1_hat(k1) (X0(1) - b/a) * exp(-a*k) b/a 其中k从0开始计数。将X0(1)2.874,a-0.0372,b3.0653代入就得到了X1的预测模型。步骤6累减还原得到原始序列预测值因为我们预测的是累加序列X1而我们需要的是原始序列X0的预测值。所以要进行累减还原IAGOX0_hat(k1) X1_hat(k1) - X1_hat(k) 其中定义X1_hat(0) 0。 对于第一个值通常令X0_hat(1) X0(1)即用原始第一个数据作为拟合起点。通过以上六步我们就完成了从原始数据到建立预测模型的全过程。手动算一遍虽然繁琐但能让你透彻理解后续代码中每一个数组运算的意义。4. Python代码实现从零构建GM(1,1)预测类理解了原理我们开始用Python将其实现。我将构建一个完整的、面向对象的GM11类它封装了建模、预测、评估和可视化的全部功能。这样的设计便于复用和集成到更大的项目中。4.1 类结构与初始化import numpy as np import pandas as pd import matplotlib.pyplot as plt from typing import Union, Optional, Tuple class GM11: 一阶单变量灰色预测模型 GM(1,1) 的Python实现类。 功能包括模型拟合、预测、精度检验与可视化。 def __init__(self, data: Union[list, np.ndarray, pd.Series]): 初始化GM11模型。 参数 data : 原始非负数据序列。建议长度大于4。 self.original_data np.array(data, dtypenp.float64).flatten() self.n len(self.original_data) if self.n 4: raise ValueError(数据量过少灰色预测至少需要4个数据点以获得稳定参数。) if np.any(self.original_data 0): # 注意理论上GM(1,1)要求非负实际中对于负值或零值需要做平移或变换处理 print(警告原始数据包含负值可能影响模型精度。考虑进行非负化处理。) # 初始化模型参数和结果存储 self.a None # 发展系数 self.b None # 灰色作用量 self.accumulated_data None # 一次累加序列 (1-AGO) self.z_data None # 紧邻均值序列 self.fitted_values None # 原始序列的拟合值 self.predicted_values None # 原始序列的预测值包含未来 self.relative_errors None # 相对误差序列 def _accumulate(self) - np.ndarray: 一次累加生成 (1-AGO) return np.cumsum(self.original_data) def _generate_background(self, accumulated_data: np.ndarray) - np.ndarray: 生成紧邻均值序列 (背景值) # 使用系数0.5这是最常用的。也有研究使用可变权重。 return 0.5 * (accumulated_data[:-1] accumulated_data[1:])代码解析与注意np.array(data, dtypenp.float64).flatten()确保输入被转换为一维浮点数数组兼容列表、NumPy数组和Pandas Series。数据量检查 (self.n 4)这是经验性约束。理论上3个点就能解出a和b但结果极不稳定容易过拟合或失真。4个点是实践中的安全起点。负值警告经典GM(1,1)要求数据非负。如果数据为负常见的处理方法是给所有数据加上一个常数平移变换使最小值为一个正数如1预测后再减回去。本类中仅给出警告更健壮的实现应内置数据预处理模块。np.cumsum()NumPy的累加函数高效实现1-AGO。背景值生成accumulated_data[:-1]取前n-1个元素accumulated_data[1:]取后n-1个元素相加后乘以0.5完美生成Z1序列。这是模型精度的一个关键点有些改进模型会尝试优化这个生成系数。4.2 核心拟合方法参数求解与拟合def fit(self) - GM11: 拟合GM(1,1)模型计算发展系数a和灰色作用量b。 返回 self : 返回实例自身支持链式调用。 # 1. 累加生成 self.accumulated_data self._accumulate() # 2. 生成背景值 self.z_data self._generate_background(self.accumulated_data) # 3. 构造矩阵B和向量Y # B [[-z1, 1], [-z2, 1], ..., [-z_{n-1}, 1]] B np.column_stack((-self.z_data, np.ones_like(self.z_data))) # Y [x0(2), x0(3), ..., x0(n)]^T Y self.original_data[1:].reshape(-1, 1) # 4. 最小二乘法求解参数 [a, b]^T (B^T B)^{-1} B^T Y # 使用np.linalg.pinv求伪逆比直接求逆更数值稳定 BTB_inv np.linalg.pinv(B.T B) params BTB_inv B.T Y self.a, self.b params.flatten() # 解压参数 # 5. 计算拟合值 self.fitted_values self._predict_fitted() # 6. 计算拟合误差 self._calculate_errors() return self def _predict_fitted(self) - np.ndarray: 计算原始序列的拟合值对历史数据的回代预测 n self.n fitted np.zeros(n) fitted[0] self.original_data[0] # 第一个拟合值等于原始值 # 时间响应式: X1_hat(k1) (X0(1) - b/a) * exp(-a*k) b/a # 注意这里的k是累加序列的序号从0开始。 # 我们先计算累加序列的拟合值 X1_fitted X1_fitted np.zeros(n) X1_fitted[0] self.accumulated_data[0] # 也可以直接用original_data[0] for k in range(1, n): # k 对应公式中的 k-1因为公式中k从0开始而我们的索引从1开始 X1_fitted[k] (self.original_data[0] - self.b / self.a) * np.exp(-self.a * (k-1)) self.b / self.a # 累减还原得到原始序列拟合值: X0_hat(k) X1_hat(k) - X1_hat(k-1) for k in range(1, n): fitted[k] X1_fitted[k] - X1_fitted[k-1] return fitted def _calculate_errors(self): 计算相对误差百分比 self.relative_errors np.abs((self.original_data - self.fitted_values) / self.original_data) * 100代码解析与注意np.column_stack用于构建矩阵B将-self.z_data和全1列并排堆叠。np.linalg.pinv使用伪逆Moore-Penrose逆而不是np.linalg.inv直接求逆。这是因为在数据量少或背景值序列特殊时B.T B可能接近奇异矩阵直接求逆会导致数值不稳定甚至报错。伪逆提供了更稳健的求解方式是工程实践中的推荐做法。时间响应式的循环实现这里用for循环是为了清晰展示公式。实际上np.exp操作可以向量化效率更高。在后续优化部分我们会提到。误差计算使用相对误差百分比这比绝对误差更能反映拟合精度尤其是在数据量级较大的时候。4.3 预测与结果展示方法def predict(self, steps: int 1) - np.ndarray: 进行未来预测。 参数 steps : 预测步数。 返回 predicted : 未来steps步的预测值原始序列尺度。 if self.a is None or self.b is None: raise ValueError(模型尚未拟合请先调用 fit() 方法。) n self.n total_length n steps self.predicted_values np.zeros(total_length) self.predicted_values[:n] self.fitted_values # 前n个是拟合值 # 计算累加序列的预测值 # X1_hat(k) for k n, n1, ..., nsteps-1 X1_pred np.zeros(total_length) # 先计算历史部分的累加拟合值与_fitted_predict中逻辑一致可复用 X1_pred[0] self.original_data[0] for k in range(1, n): X1_pred[k] (self.original_data[0] - self.b / self.a) * np.exp(-self.a * (k-1)) self.b / self.a # 预测未来部分的累加值 for k in range(n, total_length): # 注意公式中的指数项是 (k-1)这里k是数组索引从0开始。 X1_pred[k] (self.original_data[0] - self.b / self.a) * np.exp(-self.a * (k-1)) self.b / self.a # 累减还原得到原始序列预测值 for k in range(1, total_length): self.predicted_values[k] X1_pred[k] - X1_pred[k-1] # 第一个值保持为原始数据 self.predicted_values[0] self.original_data[0] # 返回未来steps步的预测值 return self.predicted_values[-steps:] def summary(self): 打印模型摘要信息 if self.a is None: print(模型未拟合。) return print(*50) print(GM(1,1) 模型摘要) print(*50) print(f原始数据序列长度: {self.n}) print(f发展系数 (a): {self.a:.6f}) print(f灰色作用量 (b): {self.b:.6f}) print(f预测模型方程: dX1/dt ({self.a:.4f})*X1 {self.b:.4f}) print(\n拟合精度:) print(f 平均相对误差: {np.mean(self.relative_errors):.2f}%) print(f 最大相对误差: {np.max(self.relative_errors):.2f}%) if hasattr(self, c): print(f 后验差比值 (C): {self.c:.4f}) print(f 小误差概率 (P): {self.p:.4f}) print(*50) def plot(self, future_steps: int 0, figsize(10, 6)): 绘制原始数据、拟合曲线及预测曲线。 参数 future_steps : 要展示的未来预测步数。 figsize : 图形尺寸。 fig, ax plt.subplots(figsizefigsize) x_history np.arange(self.n) ax.plot(x_history, self.original_data, bo-, label原始数据, markersize8, linewidth2) ax.plot(x_history, self.fitted_values, rs--, label拟合数据, markersize6, linewidth1.5) if future_steps 0: future_values self.predict(future_steps) x_future np.arange(self.n, self.n future_steps) ax.plot(x_future, future_values, g^--, labelf预测数据 ({future_steps}步), markersize8, linewidth1.5) # 在拟合与预测连接处画一条竖虚线 ax.axvline(xself.n - 0.5, colorgray, linestyle:, alpha0.7) ax.set_xlabel(时间序列 / 步数, fontsize12) ax.set_ylabel(数值, fontsize12) ax.set_title(GM(1,1) 模型拟合与预测效果图, fontsize14, fontweightbold) ax.legend(locbest) ax.grid(True, linestyle--, alpha0.6) plt.tight_layout() plt.show()代码解析与注意predict方法它首先检查模型是否已拟合然后根据时间响应式外推累加序列最后累减得到原始序列的预测值。返回的是未来steps步的预测值同时将完整序列历史拟合未来预测存储在self.predicted_values中。summary方法提供模型关键参数的概览这是模型诊断的第一步。从发展系数a的符号和大小可以初步判断趋势和增长速率。plot方法可视化是评估模型效果最直观的方式。图中清晰区分了原始数据点、模型拟合线和未来预测线并用灰色虚线标出了历史与未来的分界一目了然。5. 模型检验不仅仅是看误差百分比拟合完模型算出预测值工作只完成了一半。我们必须对模型的精度和可靠性进行严格的检验。在数学建模比赛中模型检验部分是拿分的关键。灰色预测模型常用的检验方法有以下几种5.1 残差检验相对误差分析这是我们上面代码中已经实现的。计算每个历史点的相对误差epsilon(k) |X0(k) - X0_hat(k)| / X0(k) * 100%通常平均相对误差小于5%可以认为模型精度较高小于10%基本合格大于20%则模型精度不佳需要检查数据或模型假设。5.2 后验差检验一个更综合的指标后验差检验比单纯看相对误差更全面它同时考虑了原始数据和残差的波动性。步骤1计算原始序列的均值与方差X0_bar mean(X0)S1^2 variance(X0)步骤2计算残差序列的均值与方差残差e(k) X0(k) - X0_hat(k)e_bar mean(e)(理论上一个好的拟合其残差均值应接近0)S2^2 variance(e)步骤3计算后验差比值 C 和小误差概率 P后验差比值 CC S2 / S1C越小越好表明残差波动远小于原始数据波动模型预测值与原数据差异小。等级参考C 0.35优秀0.35 C 0.5合格0.5 C 0.65勉强合格C 0.65不合格。小误差概率 PP P(|e(k) - e_bar| 0.6745 * S1)计算残差与残差均值之差的绝对值小于0.6745 * S1的比例。P越大越好表明残差分布较为集中。等级参考P 0.95优秀0.80 P 0.95合格0.70 P 0.80勉强合格P 0.70不合格。Python实现后验差检验我们可以在GM11类中添加一个方法def posteriori_test(self): 进行后验差检验计算后验差比值C和小误差概率P。 结果存储在实例属性中。 residuals self.original_data - self.fitted_values mean_original np.mean(self.original_data) std_original np.std(self.original_data, ddof1) # 样本标准差 mean_residual np.mean(residuals) std_residual np.std(residuals, ddof1) # 后验差比值 C self.c std_residual / std_original if std_original ! 0 else np.inf # 小误差概率 P threshold 0.6745 * std_original count_small_error np.sum(np.abs(residuals - mean_residual) threshold) self.p count_small_error / self.n # 精度等级判断 grade 未知 if self.c 0.35 and self.p 0.95: grade 优秀 (Good) elif self.c 0.5 and self.p 0.80: grade 合格 (Qualified) elif self.c 0.65 and self.p 0.70: grade 勉强合格 (Barely Qualified) else: grade 不合格 (Unqualified) self.posteriori_grade grade return self.c, self.p, grade在summary方法中可以加入对C和P值的显示。5.3 关联度检验可选关联度检验是灰色系统理论中衡量模型曲线与原始数据曲线几何形状相似度的方法。关联度越大说明模型曲线形状与原始序列变化趋势越接近。计算稍复杂在一般建模中残差和后验差检验已足够。若需实现其核心是计算关联系数gamma(k) (min_min rho * max_max) / (delta(k) rho * max_max)其中delta(k)是第k点残差的绝对值min_min和max_max分别是两级最小差和最大差rho是分辨系数通常取0.5。最后关联度r为所有关联系数的均值。实操心得在比赛或报告中务必进行模型检验。不要只展示预测曲线图。将相对误差、后验差比值C、小误差概率P做成表格呈现并给出精度等级这是模型可信度的直接证明。如果检验不合格需要回到第二步重新审视数据的适用性或考虑使用改进的灰色模型。6. 实战案例城市用电量预测让我们用一个完整的例子串起从数据加载、模型拟合、检验到预测的全过程。假设我们有某城市2018-2023年的年度用电量数据单位亿千瓦时。# 示例城市年度用电量预测 if __name__ __main__: # 1. 准备数据 years np.arange(2018, 2024) # 2018-2023 electricity_consumption np.array([125, 136, 148, 162, 178, 195]) # 模拟数据 print(原始数据:) for yr, val in zip(years, electricity_consumption): print(f {yr}: {val} 亿千瓦时) # 2. 初始化并拟合模型 print(\n 开始拟合GM(1,1)模型...) model GM11(electricity_consumption) model.fit() # 3. 模型摘要 model.summary() # 4. 后验差检验 c, p, grade model.posteriori_test() print(f\n后验差检验:) print(f 后验差比值 C {c:.4f}) print(f 小误差概率 P {p:.4f}) print(f 模型精度等级: {grade}) # 5. 预测未来3年2024-2026的用电量 future_years 3 predictions model.predict(stepsfuture_years) print(f\n 预测未来 {future_years} 年用电量:) for i, yr in enumerate(range(2024, 2024future_years)): print(f {yr}: {predictions[i]:.2f} 亿千瓦时) # 6. 可视化 model.plot(future_stepsfuture_years) # 7. 详细误差分析可选 print(\n 详细拟合误差分析:) error_df pd.DataFrame({ 年份: years, 原始值: model.original_data, 拟合值: model.fitted_values, 绝对误差: np.abs(model.original_data - model.fitted_values), 相对误差(%): model.relative_errors }) print(error_df.round(4))运行这段代码你将得到完整的建模输出包括模型参数、检验指标、预测值和可视化图表。通过这个案例你可以清晰地看到模型如何捕捉用电量的增长趋势并对未来做出预测。7. 高级话题与改进模型探讨基础的GM(1,1)模型虽然强大但也有其局限性。在实际应用中我们常常需要对其进行改进以适应更复杂的情况。7.1 数据预处理应对非负序列与波动平移变换当原始数据有负数或零时令Y0(k) X0(k) C其中C为常数使得新序列Y0全部为正。预测结果Y0_hat再减去C即可。选择C的常见方法是C |min(X0)| 1确保最小值变为1。对数变换或开方变换当数据波动较大时可以先进行平滑变换弱化随机波动再对变换后的数据建模预测后再反变换回来。例如取对数Y0(k) ln(X0(k))要求X0(k) 0。7.2 背景值优化经典GM(1,1)使用固定权重0.5生成背景值Z1(k) 0.5*(X1(k)X1(k-1))。研究表明这不一定是最优的。可以考虑引入可变权重p即Z1(k) p*X1(k) (1-p)*X1(k-1)并通过优化算法如最小化平均相对误差来求解最优的p。这被称为优化背景值的GM(1,1)模型通常能提升拟合精度。7.3 模型优化离散GM(1,1)与分数阶累加离散GM(1,1)模型 (DGM(1,1))直接针对离散的累加序列建立差分方程而不是从连续微分方程离散化而来。其形式为X1(k1) beta1 * X1(k) beta2。求解参数后其预测公式直接是离散递推形式有时比传统GM(1,1)更稳定。分数阶累加GM(1,1)模型传统模型使用一阶累加1-AGO。分数阶累加r-AGO, 0r1可以更灵活地调节数据的记忆性和平滑度。通过寻找最优的阶数r可以使累加后的序列更符合指数规律从而提高预测精度。这通常需要结合智能优化算法如粒子群算法PSO、遗传算法GA来求解最优r。7.4 组合模型灰色-马尔可夫模型GM(1,1)擅长刻画趋势但对随机波动敏感。马尔可夫链擅长描述状态转移的概率特性。将两者结合先用GM(1,1)预测趋势值再用马尔可夫链对预测残差实际值与趋势值的偏差进行状态划分和概率预测对趋势预测结果进行修正。这种方法特别适用于数据有一定波动性但整体有趋势的场景能有效提高预测精度。避坑指南不要迷信“高级”模型。对于很多问题经典的GM(1,1)已经足够好用且可解释性强。在选择改进模型前先问自己我的数据问题是什么是波动大还是有周期性还是数据点太少针对具体问题选择改进方向否则会增加不必要的复杂性甚至可能过拟合。8. 在数学建模竞赛中的应用技巧与代码优化如果你正在准备数学建模比赛如国赛、美赛、亚太杯灰色预测模型是一个快速出结果的“法宝”。以下是一些实战技巧快速上手将本文的GM11类代码保存为一个单独的gm11.py文件。在比赛中需要时直接导入使用可以节省大量从头编写和调试的时间。结果可视化务必绘制精美的对比图。使用matplotlib调整线条颜色、标记样式、添加图例和网格。一张清晰的“历史拟合未来预测”图是论文中的亮点。模型对比不要只用一个模型。在论文中可以同时运行GM(1,1)、线性回归、指数平滑等简单模型将预测结果和误差指标如MAPE, RMSE做成对比表格。说明在“小样本、贫信息”条件下灰色预测模型的优越性。敏感性分析可以尝试略微改变输入数据如增加/减少一个早期数据点观察预测结果的变化以此说明模型的稳定性或对早期数据的依赖性。代码优化上述教学代码为了清晰使用了循环。在实际比赛中为了效率和代码简洁应尽量向量化。向量化优化示例替换类中的部分方法def _predict_fitted_vectorized(self) - np.ndarray: 向量化计算拟合值 n self.n k_seq np.arange(0, n) # [0, 1, 2, ..., n-1] # 计算累加序列拟合值 X1_hat X1_fitted (self.original_data[0] - self.b / self.a) * np.exp(-self.a * k_seq) self.b / self.a # 累减还原 fitted np.zeros(n) fitted[0] self.original_data[0] fitted[1:] X1_fitted[1:] - X1_fitted[:-1] return fitted def predict_vectorized(self, steps: int 1) - np.ndarray: 向量化预测 if self.a is None or self.b is None: raise ValueError(模型尚未拟合。) n self.n total_length n steps k_seq_total np.arange(0, total_length) # 计算完整历史未来的累加序列预测值 X1_pred_total (self.original_data[0] - self.b / self.a) * np.exp(-self.a * k_seq_total) self.b / self.a # 累减还原 predicted_total np.zeros(total_length) predicted_total[0] self.original_data[0] predicted_total[1:] X1_pred_total[1:] - X1_pred_total[:-1] self.predicted_values predicted_total return predicted_total[-steps:]向量化代码利用NumPy的广播机制消除了显式循环运行速度更快代码也更简洁。灰色预测模型是一个强大而灵活的工具箱的入口。从经典的GM(1,1)出发理解了其“累加找规律微分建模型”的核心思想后你可以根据具体问题向数据预处理、背景值优化、分数阶累加、组合模型等方向深入探索。记住没有万能的模型只有最适合数据的模型。在动手编码前花时间分析你的数据特征是成功建模的第一步。希望这份超详细的指南和即拿即用的代码能成为你在数据预测道路上的得力助手。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价