news 2026/10/3 4:53:31

DGM预测程序:离散灰色模型的小样本预测Python实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
DGM预测程序:离散灰色模型的小样本预测Python实现

简介:这是一份基于离散灰色模型(DGM)的MATLAB预测程序包,面向需要处理小样本、信息不完全时间序列的经济预测、工程数据分析和环境科学等场景。包内为一个DGM.m脚本,采用最小二乘法对一阶离散灰色模型DGM(1,1)的参数进行拟合,并完成从原始数据输入、参数估计、序列预测到精度分析的完整流程,可直接加载数据运行,适合灰色系统理论初学者和实际预测人员使用。压缩包仅含1个m文件,体积754B,轻量便捷,便于对照模型公式逐行理解实现细节。目前已有207人学习下载,兼具理论参考与工具价值。通过该程序,读者可以快速掌握离散灰色模型的建模步骤,并能将预测结果用于趋势判断或与其他预测方法对比验证。

1. DGM预测程序到底是什么:小样本预测里被低估的离散灰色模型

手里只有十几个历史数据点,却要预测未来三到五期的走势,这种场景在设备故障率统计、区域用电量预估、商品短期销量预测里非常常见。统计回归扛不住小样本,神经网络又嫌数据太少,而传统灰色预测GM(1,1)虽然能处理贫信息问题,却在离散数据上存在固有误差。DGM预测程序里的DGM,全称是Discrete Grey Model,即离散灰色预测模型,它把灰色预测的连续微分方程改写成离散差分方程,配合最小二乘法直接求解参数,在小样本、短序列预测场景下比GM(1,1)更稳。这个zip包就是一套完整的预测程序,适合做数据分析、论文实证或工程预估的从业者。

2. 离散灰色模型的数学内核:最小二乘法如何拟合出DGM的两个关键参数

2.1 从GM(1,1)到DGM:为什么连续微分方程在离散数据上会翻车

灰色预测的核心思路是先对原始序列做累加生成,把随机性较强的原始数据变成一条相对平滑的指数增长曲线,再用微分方程去逼近这条曲线。GM(1,1)使用的是一阶线性微分方程,但实际工程数据大多是按天、按周采集的离散值,用连续方程去拟合离散观测,本质上就存在模型误差。这个误差在数据点少、序列波动明显的时候会被放大,导致预测值系统性偏移。

DGM的改进在于,它直接使用离散形式的差分方程来建模。具体来说,设原始非负序列为x⁰(k),经过一次累加生成(AGO)得到x¹(k),DGM(1,1)模型的差分方程形式是:

x¹(k+1) = β₁·x¹(k) + β₂

这里的β₁和β₂就是待求参数,模型从GM(1,1)的微分方程求解变成了一个一阶线性差分方程的参数估计问题。因为形式是线性的,参数求解可以严格地通过最小二乘法完成,不需要做任何近似迭代。这是DGM相对GM(1,1)最本质的改动——不是换了个名字,而是把方程形式从连续域搬到离散域,从源头消除了跳跃误差。

2.2 最小二乘求解的具体构造:B矩阵和Y向量的组装方式

要用最小二乘法求解β₁和β₂,第一步是构造线性方程组。根据差分方程,把k=1到k=n-1的观测值代入,可以得到n-1个方程组成一个线性系统:

Y = B·β

其中β = [β₁, β₂]ᵀ,Y向量由累加序列的下一期值构成,B矩阵的第一列是当期累加值,第二列全为1。最小二乘解的标准公式是:

β = (Bᵀ·B)⁻¹·Bᵀ·Y

这里面有一个工程上常见的坑:Bᵀ·B是一个2×2的矩阵,如果原始序列的数据分布过于集中,矩阵可能接近奇异,求逆时会出现数值不稳定。我在写程序时会先检查Bᵀ·B的条件数,条件数超过10⁶就说明这个序列不适合直接用DGM,需要考虑对数据做平移变换或者改用其他预测方法。

2.3 用Python手写最小二乘求解:不到三十行拿到参数

不依赖任何现成的灰色预测库,用NumPy实现DGM参数求解,这是理解整个模型最直接的方式。下面这段代码我通常会在接手新项目时先跑一遍,确认数据适合DGM再进入完整程序。

import numpy as np def dgm_fit(x0): """ DGM(1,1) 参数估计 x0: 原始非负序列,一维数组,长度 n 返回: beta1, beta2, x1_pred """ x0 = np.asarray(x0, dtype=float) n = len(x0) if n < 4: raise ValueError("序列长度至少需要4个观测值") # 1. 累加生成 AGO x1 = np.cumsum(x0) # 2. 构造 B 矩阵和 Y 向量 # B = [[x1[0], 1], [x1[1], 1], ..., [x1[n-2], 1]] B = np.column_stack((x1[:-1], np.ones(n - 1))) Y = x1[1:].reshape(-1, 1) # 3. 最小二乘法求解: beta = (B^T B)^(-1) B^T Y BtB = B.T @ B cond = np.linalg.cond(BtB) if cond > 1e6: print(f"告警: BtB 条件数过大: {cond:.2e},结果可能不稳定") beta = np.linalg.inv(BtB) @ B.T @ Y beta1, beta2 = beta[0, 0], beta[1, 0] # 4. 回代求累加序列拟合值 # x1_pred[k] = beta1 * x1[k-1] + beta2,递推从 x1[0] 出发 x1_pred = np.zeros(n) x1_pred[0] = x1[0] for k in range(1, n): x1_pred[k] = beta1 * x1_pred[k-1] + beta2 # 5. 累减还原 IAGO 得到原始序列拟合值 x0_pred = np.zeros(n) x0_pred[0] = x0[0] for k in range(1, n): x0_pred[k] = x1_pred[k] - x1_pred[k-1] return beta1, beta2, x0_pred

这段代码的关键点在第15到17行的B矩阵构造上。B的第一列是累加序列的当期值,不是原始序列的值,这是灰色模型和普通回归最大的区别。Y向量对应累加序列的下一期值,因为差分方程描述的是x¹(k+1)和x¹(k)之间的关系。这样组装起来,最小二乘求解才符合DGM的数学定义。

第23行我加了一个条件数检查,这是实际调试中养成的习惯。如果不做检查,当数据序列呈现严重共线性时,求逆结果会剧烈震荡,预测值可能直接发散成巨大的正负数。

2.4 预测公式的递推逻辑:为什么预测未来不需要重新建模

参数求出来之后,预测就变成一个纯粹的递推过程。因为差分方程是x¹(k+1) = β₁·x¹(k) + β₂,给定累加序列的最后一个观测值x¹(n),就可以一路递推到未来的任意期:

x¹(n+1) = β₁·x¹(n) + β₂ x¹(n+2) = β₁·x¹(n+1) + β₂

每一期的累加预测值算出来之后,再通过累减还原IAGO得到原始序列的预测值:x⁰(n+k) = x¹(n+k) - x¹(n+k-1)。这里注意,还原用的是预测出来的相邻两期累加值之差,不是直接用原始值。

实际预测中有一个容易误用的点:有些人会直接用最后一个原始观测值开始递推,这不对。递推的起点永远是累加序列的最后一期值,不是原始序列。我在代码里用x1[-1]取最后一期累加值作为预测起点,这个细节在批量预测任务中尤其重要,一旦起点选错,后面所有预测值都会系统性偏移。

3. DGM.zip的程序架构与交付逻辑:从压缩包到可运行的预测管线

3.1 zip包里的典型文件组织:拿到手先看什么

一个以zip形式交付的预测程序,文件组织通常有固定的套路。我用过的DGM项目包,一般包含数据文件、核心算法模块、调用入口和说明文档四类内容。拿到zip包第一件事不是解压后直接跑,而是先看目录结构,确认程序的入口文件和数据格式。

典型的目录结构长这样:

DGM/ ├── data/ │ └── input_data.csv # 原始序列输入 ├── src/ │ ├── dgm_model.py # 核心模型:参数估计 + 预测 │ ├── data_loader.py # 数据读取与校验 │ └── evaluate.py # 误差评估:残差、相对误差 ├── main.py # 命令行调用入口 ├── requirements.txt └── README.md

交付包里经常会出现一个现象:算法文件齐全,但数据读取模块写死了一个相对路径,换台机器跑就报FileNotFoundError。所以我拿到任何zip程序,第一件事是把整个目录解压到一个纯英文路径下,然后运行入口脚本,先用自带的示例数据确认程序能完整跑通,再替换自己的数据。这个过程一般不会超过五分钟,却可以过滤掉大量环境问题。

3.2 数据加载与校验模块:序列长度、非负性、缺失值的预处理

数据加载这个环节看似简单,却是翻车高发区。DGM模型要求原始序列为非负序列,数据里有负数或者零,累加生成后序列形态会变得很奇怪,最小二乘解出来的参数可能直接让预测值发散。我在数据加载模块里都会加三层校验——先检查序列长度是否大于等于4,再检查是否有负值和缺失值,最后检查是否全为零序列。

import pandas as pd import numpy as np def load_series(csv_path, col_name): """ 读取CSV中的一列作为预测序列,并做合法性校验 返回: 一维numpy数组 """ df = pd.read_csv(csv_path) if col_name not in df.columns: raise KeyError(f"列 {col_name} 不存在,请检查CSV列名") series = df[col_name].dropna().values.astype(float) if len(series) < 4: raise ValueError(f"序列长度 {len(series)} 不足4个观测值") if np.any(series <= 0): neg_count = np.sum(series <= 0) raise ValueError(f"序列中存在 {neg_count} 个非正值,DGM要求数据全部大于0") if np.all(series == series[0]): raise ValueError("序列为常数序列,预测无意义") return series

这个模块的做法是快速失败,任何一条校验不过就直接报错退出,而不是带着脏数据往下走。实际项目里我遇到过把Excel里带有格式的日期列读进来、把文本型数字读出object类型的情况,所以加载后强制astype(float)是一个必须的步骤。还有一个隐藏问题:CSV里如果存在NaN值,dropna之后序列长度可能会悄悄变短,所以我先dropna再判断长度,顺序不能反。

3.3 模型类的Fit和Predict接口:训练与预测分离的设计

完整的预测程序里,模型类通常做成训练和预测分离的接口。fit方法负责参数估计和数据拟合,predict方法负责外推预测。分离的意义在于,实际业务中经常需要先离线拟合好模型,再在服务接口里做实时预测,如果每来一个请求就重新拟合一次,不仅慢,而且预测结果会随着新数据的加入不断漂移。

class DGM: def __init__(self): self.beta1_ = None self.beta2_ = None self.x0_ = None self.x1_ = None def fit(self, x0): self.x0_ = np.asarray(x0, dtype=float) self.x1_ = np.cumsum(self.x0_) self.beta1_, self.beta2_, _ = dgm_fit(self.x0_) return self def predict(self, steps): if self.beta1_ is None: raise RuntimeError("请先调用fit方法拟合模型") # 从累加序列最后一期开始递推 x1_pred = np.zeros(steps) x1_pred[0] = self.x1_[-1] for i in range(1, steps): x1_pred[i] = self.beta1_ * x1_pred[i-1] + self.beta2_ # 累减还原 x0_pred = np.zeros(steps) for i in range(steps): if i == 0: x0_pred[i] = x1_pred[i] - self.x1_[-2] else: x0_pred[i] = x1_pred[i] - x1_pred[i-1] return x0_pred

这里有一个值得注意的设计细节:predict方法返回的是未来steps期的预测值数组,长度等于传参的steps。第一期的还原方式是x1_pred[0]减去累加序列倒数第二期的值,后续每期则是预测累加值之间的差值。如果不小心把第一期写成减去self.x1_[-1],那第一期预测值就变成零了,这是典型的边界错误。

用了这套fit和predict分离的结构,模型可以序列化保存,预测时直接加载参数,响应时间在毫秒级别。对需要集成到报表系统或Web服务里的场景,这种设计几乎是必须的。

4. 用DGM程序做一次完整预测:最小复现命令与四个必调参数

4.1 从解压zip到跑通DGM预测的最小命令集

假设你现在拿到DGM.zip,目标是用自带的示例数据完整跑通一遍流程。我一般会在命令行里依次执行以下命令:

# 解压zip包到纯英文目录 unzip DGM.zip -d /workspace/DGM_project # 进入项目目录 cd /workspace/DGM_project # 安装依赖 pip install -r requirements.txt # 用示例数据跑通全流程 python main.py --input data/input_data.csv --steps 5

如果程序运行无误,控制台会输出模型参数、历史数据拟合值、未来五期预测值和误差指标。这个流程的意义在于验证环境是否完备。很多zip包在作者的机器上能跑,换了环境就报错,常见原因集中在pandas版本不兼容、缺少NumPy低版本接口这两个点上。

4.2 四个必调参数:输入路径、预测步数、数据列名、输出格式

DGM预测程序里真正决定预测质量的参数不是模型内部参数,而是调用入口处的参数。我整理了四个每次运行都必须确认的参数:

参数作用我的建议值
input输入CSV路径相对路径易出错,建议用绝对路径
steps预测步数不超过历史数据长度的一半
column要预测的列名和CSV表头严格一致,注意大小写和空格
output结果输出路径建议每次运行生成带时间戳的新文件

预测步数这个参数最需要谨慎。DGM是短期预测模型,它的递推结构决定了预测误差会随着步数增加不断累积。我把经验值定在不超过历史数据长度的一半,这是经过多组实验对比得出的保守边界。用20个历史数据点去预测未来十几期,结果基本不具备参考价值,这不是代码问题,是模型本身的适用范围决定的。

4.3 一个完整的调用示例:用实际数据演示参数组合

下面这段代码展示了一个完整的预测调用过程,包括参数准备、模型训练、预测、结果输出和误差打印。我把每个步骤都加了注释,方便直接复制修改。

import pandas as pd from src.dgm_model import DGM from src.evaluate import mean_absolute_percentage_error # 1. 加载数据 df = pd.read_csv("data/sales_weekly.csv") x0 = df["sales"].values.astype(float) # 2. 切分训练集和测试集:最后3期留作验证 train = x0[:-3] test = x0[-3:] # 3. 拟合DGM模型 model = DGM() model.fit(train) # 4. 预测3期(对应测试集长度) pred = model.predict(steps=3) # 5. 计算误差指标 mape = mean_absolute_percentage_error(test, pred) print(f"MAPE: {mape:.2f}%") # 6. 输出预测结果 future = model.predict(steps=5) print("未来5期预测值:", future)

这段代码的工程意义在于它把模型验证和实际预测分成两个阶段。先用最后几期历史数据做回测,看看模型的预测误差在什么水平,再决定要不要信任未来五期的预测结果。很多新手直接跳过回测就预测未来,等到预测值明显不合理时才回头找原因,那时候已经晚了。

参数方面,我一般会用时间序列的自然周期来设置预测步数。周度数据预测四周、月度数据预测三个月,这个节奏比较接近业务的决策周期。

4.4 结果解读:残差序列和相对误差应该怎么看

程序输出的结果通常包含三块:历史数据的拟合值、未来预测值、误差评估指标。拟合值用于检查模型对历史数据的还原程度,预测值用于业务决策,误差指标用于判断结果可靠性。

我最常看的指标是平均绝对百分比误差MAPE,它把所有期的相对误差取了平均,直观程度最高。MAPE小于5%说明模型对历史数据的拟合非常好,预测相对可信;5%到15%可以接受;超过20%就要怀疑数据是否适合用DGM。还有一个值得关注的信号是残差的分布——如果残差呈现明显的正负交替振荡,说明模型对数据的动态捕捉不足,预测结果要谨慎使用。

5. DGM落地避坑指南:5个让预测结果翻车的隐藏问题

5.1 原始序列存在负值或零值:累加生成后模型参数全面失真

现象:程序没有报错,但预测值忽正忽负,或者出现巨大的负值,MAPE超过100%。

原因:DGM的累加生成要求原始数据为非负序列。当序列里有零或负数时,累加序列的单调性被破坏,最小二乘解出的β₁可能小于1甚至为负,差分方程的递推过程随即发散。这是DGM模型本身的数学前提,不是程序的缺陷。

解决:在数据加载层强制校验非负性。如果业务数据确实存在负值,一个常见处理是对整个序列做平移变换,把所有值加上一个常数使其变为正数。注意平移操作要在预测完成后反向还原,且平移量不能太大,否则会扭曲序列的相对波动结构。

5.2 BᵀB矩阵接近奇异:数据分布过于集中导致参数估计震荡

现象:程序能跑通,但每次修改训练集末尾一个数据点,预测结果就会剧烈变化,参数不稳定。

原因:最小二乘求解时BᵀB的条件数过大。DGM中B的两列分别是累加序列和常数1,当原始序列非常平缓时,累加序列近似一条斜率很小的直线,两列之间的共线性极强,矩阵求逆时微小扰动被放大。

解决:程序里添加条件数检查,条件数超过10⁶就拒绝输出结果。更激进的做法是对累加序列做标准化变换后再建模,但这会改变参数的业务解释性,我一般只在学术研究中这么做,工程上宁可直接放弃DGM改用其他模型。

5.3 zip解压后路径带中文:程序能启动但读不到数据文件

现象:在Windows上解压到带中文的目录,运行main.py时报FileNotFoundError,或者pandas读取CSV时报编码错误。

原因:很多预测程序在写文件路径时使用了硬编码的字符串拼接,遇到中文路径时,Windows下的默认编码GBK和代码内部使用的UTF-8对不上,路径解析失败。

解决:把整个项目目录解压到纯英文路径,例如D:\workspace\DGM_project。如果只能在中文路径下运行,那么需要修改代码内的文件读取方式,用pathlib.Path来处理路径而不是字符串拼接。这个问题在交付的zip包里极常见,也是我拿到任何zip程序第一件事就是解压到英文路径的原因。

5.4 预测步数设置过长:递推误差按指数速度累积

现象:预测前两期还像样,从第四期开始预测值要么暴跌到零附近,要么暴涨到不合理的数量级。

原因:DGM的递推公式是线性差分方程,参数β₁决定了递推过程的稳定性。当β₁大于1时,预测值会按β₁的幂次增长,步数越多发散越严重。这本质上是灰色预测模型的固有局限——它是为短期预测设计的。

解决:严格控制预测步数,不超过历史数据长度的三分之一或一半。如果业务确实需要更长周期的预测,应该使用滚动预测策略,每得到一个新观测值就重新拟合模型更新参数,而不是一次性递推太远。

5.5 交付的zip包内代码和依赖版本不一致:换环境就跑不起来

现象:在作者机器上能跑的代码,到自己的环境里一运行就报各种依赖缺失或语法错误,比如pandas的fillna行为差异、NumPy的某些接口在新版本被移除。

原因:zip交付的程序通常只在开发者的Python环境里验证过,requirements.txt里的版本号可能写的是上限范围,或者根本没锁版本。Python生态里版本变动导致的兼容性问题,在离线交付场景中非常突出。

解决:拿到zip后先创建独立的虚拟环境安装依赖,不要直接往系统Python环境里装。如果运行报错,优先检查pandas、NumPy的版本,一般把主要依赖固定到和作者README中声明的一致就能解决。

6. 让DGM结果经得起质疑:残差检验、后验差检验与滚动验证

DGM预测程序的最后一步不是把预测值交出去就完事,而是要做模型检验。常用的是后验差检验法,核心是两个指标:后验差比值C和小误差概率P。C是残差标准差除以原始序列标准差,P是残差与均值偏差小于0.6745倍原始标准差的频次占比。模型质量分四级,C小于0.35且P大于0.95为一级,C小于0.5且P大于0.8为二级,这两个区间内的模型预测结果可以直接用于决策;C大于0.65则模型不可用。

实际检验中我习惯把后验差检验做成一个独立的脚本:

def posterior_check(x0, x0_pred): n = len(x0) e = x0 - x0_pred se = np.sqrt(np.sum(e**2) / n) sx = np.std(x0, ddof=1) C = se / sx tolerance = 0.6745 * sx p = np.mean(np.abs(e - np.mean(e)) < tolerance) return C, p

检验结果只是一部分,真正让预测结果信服的是滚动验证。我常用的做法是:取前80%的数据拟合模型,对后20%的每一期做单步预测,逐步滚动,把每期预测值和实际值放在一起画对比图,计算平均误差。这套方法配合后验差检验,基本能把模型质量的底细摸清楚。做完检验我也就敢把预测结论写进报告里了——如果C值在0.35以内,说明模型对数据的解释能力足够,预测结果可以作为业务参考;如果超过0.5,我会直接建议换ARIMA或简单指数平滑做对比,DGM不是万能的,但它的检验逻辑可以帮你在第一时间判断该不该信这套结果。检验完把预测值和置信带一起输出,是让业务方接受灰色模型预测结论最容易的方式,这也算是这些年做预测项目攒下来的一个习惯。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 4:52:12

CODESYS安装配置完全指南:从下载到跑通第一个软PLC程序

1. 为什么要写这篇教程&#xff1a;CODESYS到底解决了什么问题第一次接触CODESYS的人&#xff0c;多半是听同行提起“这个软件能做软PLC”“写逻辑跟玩一样”。我当时入坑&#xff0c;是因为接手一个项目&#xff0c;客户指定要用支持IEC 61131-3标准的控制器&#xff0c;而且最…

作者头像 李华
网站建设 2026/10/3 4:51:21

AI投资火热但落地成熟仅1%:企业AI落地的五道坎与四步法

1. AI投资到底为什么“飙”起来了1.1 先说一个让人既兴奋又焦虑的数字过去这一年&#xff0c;不管你在哪个行业&#xff0c;只要打开科技新闻或者参加两场行业峰会&#xff0c;基本都会被同一个词刷屏&#xff1a;AI投资。一级市场里大模型创业公司动辄数亿美金的融资轮&#x…

作者头像 李华
网站建设 2026/10/3 4:51:20

MT5回测设置实战:如何让回测结果贴近实盘?

做MT5量化交易的朋友&#xff0c;应该都遇到过这种场景&#xff1a;策略在回测里跑出来一条漂亮的资金曲线&#xff0c;年化收益高得吓人&#xff0c;回撤也看着很稳&#xff0c;可一旦挂到实盘&#xff0c;要么利润大幅缩水&#xff0c;要么连续亏损让你怀疑人生。这不是策略本…

作者头像 李华
网站建设 2026/10/3 4:51:13

UE4类型系统构建全解析:从UHT到UClass的反射原理

好问题&#xff0c;这正是国内UE4教程里极少被讲透的一块硬骨头。我不打算重复官方文档里的类图&#xff0c;也不打算贴那种“UObject继承自UStruct&#xff0c;UStruct继承自UField”的老生常谈。我想做的&#xff0c;是带你沿着一个类从你敲下UCLASS()宏的那一刻开始&#xf…

作者头像 李华
网站建设 2026/10/3 4:51:13

DeepSeek Harness 安装配置与编程实战:从环境搭建到插件部署全流程

1. 拆解 DeepSeek Harness&#xff1a;它到底解决什么问题第一次看到 DeepSeek Harness 这个名字&#xff0c;很多人会误以为它是某个新出的编程语言或者框架。其实把它理解成一个“模型能力调度与任务编排的中间层”更准确。它的核心作用&#xff0c;是把大模型能力、本地文件…

作者头像 李华
网站建设 2026/10/3 4:50:41

Windows 11开始菜单效率优化:OpenShell安装配置与避坑指南

如果你用过一阵子 Windows 11&#xff0c;多半会有这样的体会&#xff1a;开始菜单的“推荐”区域总是塞满各种用不上的文件&#xff0c;右侧电源、设置、文件管理器的图标排得规规矩矩&#xff0c;但真到了要打开控制面板、运行、网络连接这些高频入口时&#xff0c;却要逐级点…

作者头像 李华