☰
矿山监测数据清洗与沉降预测:从脏数据到GM(1,1)、BP和LSTM的实战方案
2026/9/26 9:40:28 网站建设 项目流程

简介:一份针对2025年五一数学建模竞赛B题“矿山监测数据高效处理与建模优化”的完整参赛方案资源,涵盖论文正文与配套代码,适合具备一定数学建模基础、关注矿山数据处理及智能监测的科研人员和工程师学习参考。作者作为参赛作品,质量稳定,可作保底二等奖水平范例。资源以PDF格式为主,压缩包内含1个PDF文件,整体大小约3.23MB,便于直接查阅与打印。内容围绕五个子问题展开:用BP神经网络完成数据拟合与变换,借助主成分分析实现167:1高压缩比降维并控制还原误差,利用卡尔曼滤波进行时序去噪,再通过贝叶斯优化与早停机制调整网络超参数,最终整合模型使测试集R²达到0.9870。除完整建模思路和Python求解代码外,还包含误差分布检验、五折交叉验证、复杂度分析等细节,有助于读者理解从数据预处理到模型评估的全流程。目前已有306人学习下载,是一份兼具专业性和实践性的竞赛参考资源。

1. 2025年五一赛B题矿山数据处理:一道把脏数据变成预测模型的实战题

2025年五一赛B题把一堆真实的矿山监测数据摆在参赛者面前:地表沉降点的高程记录、裂缝宽度、降雨量、时间戳,中间还混着缺测、异常波动和单位不一致。这道题的核心根本不是模型炫技,而是你能不能把一堆“脏数据”处理成一条可信的沉降预测曲线,再把全过程写成一篇让评审挑不出毛病的完整论文。和很多人的直觉相反,这道题拉开差距的地方,恰恰是论文前 60% 的数据清洗与特征构造,而不是最后那 30 行 LSTM 代码。适合两类人:一是第一次参加数模、想完整走通“数据→模型→论文”全流程的新手;二是已经会用 sklearn 但总在数据组织上翻车的工科生。往下读,我把这套矿山数据处理方案按可复现的标准拆给你。

2. 矿山监测数据清洗:三个必做步骤与可复现的 pandas 脚本

B 题的数据不会像教材里那样干净。真实矿山监测表里,一个测点可能隔几天才有人去读一次数,下雨天设备失灵导致连续缺测,甚至同一个字段在不同 sheet 里单位都不一样。所以拿到数据的第一件事不是建模,是先建数据字典,再清洗缺失值,最后做异常值检测。顺序不能反——单位没统一就插值,插出来全是错的。

2.1 数据字典先行:先搞清楚每个字段在说什么

我处理这类赛题的第一步,是打开 Excel 把所有 sheet 的列名、单位、采样频率抄成一张字典。矿山沉降数据常见的字段有测点编号、观测日期、高程、裂缝宽度、降雨量、岩性代码等。其中最容易出问题的是单位:高程有的表用米、有的用毫米,裂缝宽度基本是毫米,而降雨量可能是日累计值也可能是 7 天累计值。

建议在第一轮就把字段统一成如下约定:测点编号用字符串,日期用 datetime,高程统一转成毫米,降雨量统一成日累计值。单位不统一会让后面的模型预测结果直接偏一个数量级,而且这种错误在论文里很难自查。我的习惯是写一个字段字典放在脚本里,谁的字段对不上就报错,而不是靠肉眼一个个看。数据字典不写成文档,写成 Python 字典或表格,跑一遍就能发现表头是否缺失。

2.2 缺失值清洗:同一测点内做线性插值,端点也要补

B 题的数据文件里,缺测通常表现为空单元格或“NA”字符串。直接删行是最省事的做法,但删掉之后测点的时间序列就不再等间隔,后面灰色模型和 LSTM 都会出问题。常见做法是按测点分组,在组内对高程做线性插值,首尾缺测用 limit_direction="both" 补上。

import pandas as pd import numpy as np # 读取原始监测数据,sheet_name 按实际文件调整 df = pd.read_excel("mine_monitor.xlsx", sheet_name="raw") # 统一列名,原始表可能是“点号/日期/高程(m)”之类的中文表头 df.columns = ["point_id", "obs_date", "elevation_m", "crack_width_mm", "rainfall_mm"] # 高程统一转成毫米,这是最容易被忽略的一步 df["elevation_mm"] = df["elevation_m"] * 1000.0 # 按测点和时间排序,保证插值方向是从早到晚 df = df.sort_values(["point_id", "obs_date"], ascending=[True, True]) # 同一测点内线性插值,首尾缺测也补齐 df["elevation_mm"] = ( df.groupby("point_id")["elevation_mm"] .transform(lambda s: s.interpolate(method="linear", limit_direction="both")) )

这里的关键是 transform 与 groupby 的组合。transform 会把插值后的结果按原索引返回,保证行数不变;如果你用 apply 对每个组操作,某些版本会返回拼接后的新索引,后面按行合并时就乱了。limit_direction="both" 意味着如果某个测点前 3 天数据缺失,会按后面第一个有效值反向补出缺失值。对沉降监测这种缓慢变化的物理量来说,线性插值已经够用,不需要拉格朗日插值,后者反而会在缺测段两端出现明显过冲。

2.3 异常值检测:3σ 和 IQR 一起用,别单看一种

矿山监测数据里的异常值来源很多:测量员误录、传感器瞬时跳变、基准点被扰动。我的经验是单用 3σ 在沉降序列上会漏掉缓慢漂移的局部异常,单用 IQR 又容易把真实的地表突变当成异常。比较稳的做法是两种方法都算,同时判定为异常才处理,少误杀真实沉降信号。

# 三倍标准差法,适合接近正态分布的序列 mean_v = df["elevation_mm"].mean() std_v = df["elevation_mm"].std() df["flag_3sigma"] = (np.abs(df["elevation_mm"] - mean_v) > 3 * std_v) # IQR 法,对偏态分布更稳健 q1 = df["elevation_mm"].quantile(0.25) q3 = df["elevation_mm"].quantile(0.75) iqr = q3 - q1 df["flag_iqr"] = (df["elevation_mm"] < q1 - 1.5 * iqr) | (df["elevation_mm"] > q3 + 1.5 * iqr) # 两种方法都认为异常的点,置空后再插值 df.loc[df["flag_3sigma"] & df["flag_iqr"], "elevation_mm"] = np.nan df["elevation_mm"] = ( df.groupby("point_id")["elevation_mm"] .transform(lambda s: s.interpolate(method="linear", limit_direction="both")) )

把 3σ 和 IQR 的判定结果分别存成布尔列,而不是直接删行,这是为了让论文里能放一张“异常值识别数量”的统计表。评审老师喜欢看你处理了多少个异常点、依据是什么。另外要注意,异常值替换后再插值,会让原本的异常值变成一条平滑过渡的曲线,这符合沉降缓慢变化的物理规律。

2.4 时间对齐:让每个测点都变成等间隔序列

灰色预测 GM(1,1) 要求原始序列等间隔,LSTM 虽然理论上能处理不等间隔,但实际训练时也最好按固定频率采样。矿山监测常见的问题是雨天没法测、节假日停工,导致时间戳参差不齐。我一般按天做重采样,同一测点同一天有多条记录就取均值,没有记录的天数自动补成 NaN。

# 日期转成 pandas 时间戳 df["obs_date"] = pd.to_datetime(df["obs_date"]) # 按测点重采样到日频率,缺失日期自动补 NaN df_daily = ( df.groupby("point_id") .resample("D", on="obs_date")["elevation_mm"] .mean() .reset_index() )

重采样之后的高程列里会引入大量 NaN,所以这段代码必须放在缺失值插值之后执行,否则重采样产生的空档没人补。如果你的测点有两三个月的连续空档,线性插值会把中间的一大段补成一条直线,这时建议把空档超过 15 天的测点直接剔除,不参与建模,并在论文里写明剔除标准。这一步对后续 GM(1,1) 的级比检验影响很大,空档补出来的等间隔序列会让级比假性通过。

3. 沉降预测模型选型:GM(1,1)、BP 与 LSTM 的取舍和关键参数

数据洗干净之后,B 题的重头戏是建立沉降预测模型。绝大多数参赛队伍会在灰色模型、BP 神经网络和 LSTM 之间三选一或做组合。我的建议是都做一遍,然后用测试集误差决定最终用谁。三个模型不是替代关系,而是分别适合小样本、多特征、长时间序列三种场景。

3.1 小样本场景为什么先考虑 GM(1,1)

矿山沉降监测点往往只有二三十期数据,这种规模下深度学习模型就是黑匣子,反而灰色模型 GM(1,1) 只需要少量等间隔数据就能建立指数增长模型。GM(1,1) 的核心假设是原始序列经过一次累加后近似满足指数规律。开始建模前必须做级比检验,级比落在可容区间内才算数据合格。

def gm_ratio_check(series): """GM(1,1)级比检验:要求相邻两个观测值之比落入可容区间""" n = len(series) lb = np.exp(-2.0 / (n + 1)) ub = np.exp(2.0 / (n + 1)) series = np.asarray(series, dtype=float) ratios = series[:-1] / series[1:] # 前一项比后一项 ok = ratios[(ratios > lb) & (ratios < ub)] print(f"级比落在区间内的比例: {len(ok) / len(ratios):.2%}") return ratios

注意这里用的是 series[:-1] / series[1:],也就是前一个观测值除以后一个。很多代码写成 series[1:] / series[:-1],区间上下界一样所以判断结果不变,但如果你在论文里展示级比序列,方向错了会显得很不专业。如果级比检验通过率低于 70%,说明序列波动太大,可以考虑先对数据做平移变换或直接换 BP。GM(1,1) 的建模过程不长,核心是求发展系数和灰作用量,用最小二乘法解两个参数,最后累减还原得到预测值。

3.2 BP 神经网络:把裂缝和降雨量一起作为输入特征

GM(1,1) 只能输入一条历史沉降序列,而矿山沉降明显受降雨和裂缝发育影响,这时候 BP 神经网络可以把降雨量、裂缝宽度、历史高程一起作为输入特征。我用 sklearn 的 MLPRegressor 就能跑通最小示例,没必要一开始就上深度学习框架。

from sklearn.neural_network import MLPRegressor from sklearn.model_selection import train_test_split from sklearn.preprocessing import StandardScaler # 特征:降雨量、裂缝宽度;目标:高程值 X = df[["rainfall_mm", "crack_width_mm"]].values y = df["elevation_mm"].values # 时间序列切分不能用随机洗牌,必须按时间顺序切 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.2, shuffle=False, random_state=42 ) scaler = StandardScaler() X_train = scaler.fit_transform(X_train) X_test = scaler.transform(X_test) model = MLPRegressor( hidden_layer_sizes=(32, 16), activation="relu", solver="adam", max_iter=800, random_state=42, ) model.fit(X_train, y_train) print(f"测试集 R2: {model.score(X_test, y_test):.4f}")

shuffle=False 是最容易踩的坑。数模题里的监测数据是时间序列,如果按随机切分,模型相当于用未来数据去预测过去,测试集误差会异常好看,论文一旦被评审追问数据泄漏问题就会很被动。MLPRegressor 的 hidden_layer_sizes 我习惯先试 (32,16),层数深反而容易在小样本上过拟合。max_iter 不要按默认 200,小数据量 800 轮才容易收敛。

3.3 LSTM 滚动预测:归一化、时间步切分和还原预测结果

LSTM 比 BP 强在能直接学习序列的先后依赖关系,适合测点历史数据超过 60 期的场景。B 题如果给了长时间序列,LSTM 是拉开差距的地方。常见做法是用滑窗把序列切成 (时间步, 特征数) 的输入块,每个块预测下一期的高程。

import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense from sklearn.preprocessing import MinMaxScaler def make_windows(s, n_steps=10): """把一维序列切成滑窗样本,前 n_steps 期预测下一期""" X, y = [], [] for i in range(n_steps, len(s)): X.append(s[i - n_steps:i]) y.append(s[i]) return np.array(X), np.array(y) series = df["elevation_mm"].values.reshape(-1, 1) scaler = MinMaxScaler(feature_range=(0, 1)) scaled = scaler.fit_transform(series).flatten() X, y = make_windows(scaled, n_steps=10) split = int(len(X) * 0.8) X_train, X_test = X[:split], X[split:] y_train, y_test = y[:split], y[split:] model = Sequential() model.add(LSTM(64, activation="tanh", input_shape=(10, 1))) model.add(Dense(1)) model.compile(optimizer="adam", loss="mse") # 训练时也保持顺序,validation_split 自动取尾部 10% model.fit( X_train, y_train, epochs=120, batch_size=16, validation_split=0.1, shuffle=False, verbose=1, ) # 预测结果要还原回原始量纲 y_pred = model.predict(X_test) y_pred_original = scaler.inverse_transform(y_pred)

n_steps 取多少直接影响模型表现。我一般取序列长度的十分之一左右,比如 100 期数据取 10~15 步。太小模型看不到趋势,太大训练样本数量骤减。归一化必须用 MinMaxScaler 把数据压到 [0,1],LSTM 的 tanh 激活函数在这个区间才敏感。最后一步 inverse_transform 如果漏了,预测曲线会全部落在 0 到 1 之间,和原始高程完全对不上,这是新手最常翻车的点。

3.4 三个模型的误差对比:用同一份测试集说话

B 题论文里一定要放一张误差对比表,这是决定模型选择说服力的关键。建议用均方根误差 RMSE 和平均绝对百分比误差 MAPE 两个指标,分别衡量绝对误差和相对误差。RMSE 对极大偏差更敏感,MAPE 则看整体百分比水平。

模型RMSE (mm)MAPE (%)适用条件主要局限
GM(1,1)通常最低通常最低序列短、趋势单一无法引入外部特征
BP中等中等特征多、样本充足容易过拟合
LSTM取决于数据量取决于数据量长序列、强时序依赖小样本表现不稳定

实际比赛中,GM(1,1) 短序列测试集误差常常优于 LSTM,别被深度学习名气带偏。如果三个模型都跑完了,最终论文可以主打一个效果最好的,另外两个放入灵敏度分析或对比章节。

4. 把求解结果写成完整论文:摘要、图表和灵敏度分析的组织顺序

很多队伍模型做得不错,论文却因为结构混乱被压分。B 题这种“数据处理题”,评审最看重的是你能不能讲清楚“数据怎么处理的 → 为什么选这个模型 → 结果可不可信”。论文结构我建议按摘要、问题重述、数据处理、模型建立、模型求解、灵敏度分析、模型评价的顺序写。

4.1 摘要怎么写:把“处理方法 + 模型名 + 关键结果”压缩成一段

摘要的第一句话直接说清楚研究目标,比如“针对矿山地表沉降预测问题,基于 120 期监测数据建立了组合预测模型”。不要用“本文深入探讨了”这种空话开头。中间部分要像电梯演讲一样,把数据清洗方法(缺失值插值、异常值剔除)、核心模型(GM(1,1)、BP、LSTM)、关键指标(测试集 RMSE 是多少毫米)全部放进 300 字以内。

摘要里的数字要让评审一眼看到结果。例如“LSTM 模型在测试集上的 RMSE 为 0.82mm,较 GM(1,1) 降低 31%”,比写“预测精度较高”有力得多。如果三个模型都做了,摘要里只写最终选用的那个模型及其误差,其他模型放到正文灵敏度分析里。

4.2 模型假设与符号说明:去掉废话,留下评审会查的部分

模型假设不是凑字数。矿山数据处理题里最常被忽略的假设是:监测点之间的数据相互独立,同一测点相邻期高程变化可近似线性,降雨量对沉降的影响存在滞后效应。写出这些假设的主要目的,是让后面的建模步骤在数学上站得住脚。

符号表用三列表格:符号、含义、单位。常见误区是把所有符号堆在一行里解释,评审看不了五分钟就会烦躁。像 S(t) 表示 t 期累计沉降量,R(t) 表示第 t 日降雨量,这类核心符号要放在最前面。符号表之前先写一句“若无特殊说明,本文所有高程数据均已统一为毫米单位”,把单位问题彻底封死。

4.3 图表规范:沉降曲线、误差对比和灵敏度分析

B 题论文的数据处理部分至少要放三类型的图。第一类是原始数据与清洗后数据的对比折线图,横轴时间、纵轴高程,清洗前后的曲线叠加在同一坐标系里,这样能直观展示插值和异常值替换的效果。第二类是模型预测值与真实值的散点拟合图,对角线越集中说明预测越准。第三类是误差柱状图,把 GM(1,1)、BP、LSTM 的 RMSE 画在一起。

灵敏度分析是拿奖的关键,也是很多队伍完全忽略的部分。常见做法是把 LSTM 的时间步 n_steps 从 5 调整到 20,观察 RMSE 如何变化,并解释“当时间步小于 10 时模型记忆不足,大于 15 时训练样本减少导致误差上升”。也可以对降雨量特征做扰动,比如把降雨量增大 10%,看预测沉降是否同步增大。这部分不需要重跑全部模型,只需要说明参数变化对结果的影响方向,就能让论文显得完整。

论文章节必写内容常见丢分点
数据处理清洗前后数据量、异常值数量只放代码不放统计结果
模型建立模型数学表达式、参数含义只贴代码不写公式
模型求解测试集误差、预测曲线测试集切分方式不清
灵敏度分析参数变化对误差的影响完全没有此节

5. 矿山数据处理的避坑清单:5 条翻车记录与排查方法

这几条坑是我反复在类似赛题里见到的,也踩过其中两三个。每一条都按现象、原因、解决来写,方便你对号入座。

5.1 单位不统一让模型输出量级错乱

现象:GM(1,1) 预测出来的沉降量是几百,而原始数据看起来只有零点几。原因:高程原始数据是米,裂缝宽度是毫米,建模时直接混用没有统一单位。解决:在数据清洗第一步就打印每个字段的 dtype 和数值范围,高程全部乘以 1000 转成毫米。如果发现一个测点的高程波动在 0.001 级别而另一个在 0.5 级别,大概率是单位混了。

5.2 级比检验没过还硬用 GM(1,1)

现象:GM(1,1) 训练集拟合得很好,测试集预测值却一路发散到天文数字。原因:原始序列波动太大,不符合灰色模型指数增长假设,级比检验根本没有通过。解决:把级比检验的通过率像打印日志一样写进脚本,通过率低于 70% 就直接转 BP 或 LSTM,不要为了凑灰色模型篇幅硬套。

5.3 缺测数据插值引发边界漂移

现象:某个测点中间断了 20 天,插值后曲线出现一段完美直线,后续预测也明显偏离。原因:线性插值把长空档补成了任意直线,这段“数据”不是真实观测。解决:对每个测点计算最大连续缺失天数,超过阈值(我一般取 15 天)就剔除该测点,并在论文里写明剔除标准。

5.4 LSTM 验证集出现“水平线”预测

现象:LSTM 训练损失下降正常,但验证集预测结果是一条几乎水平的线,预测值全落在训练集均值附近。原因:大概率是时间步 n_steps 设置过大,导致训练样本过少;也可能是数据归一化后序列波动太小,模型学会了直接输出上一个时间步的值。解决:把 n_steps 降到序列长度的 1/10,同时检查 MinMaxScaler 之后的数据范围,如果最大值最小值差距不到 0.1,说明输入特征失效了。

5.5 论文查重率高不是因为模型,是因为数据处理套话

现象:论文模型部分全是自己写的,查重率还高达 40%。原因:问题重述和模型假设大量复制了教材里的标准表述,比如“灰色系统理论是研究少数据、贫信息不确定性问题的新方法”。解决:把套话改写和自己数据相关的描述,比如“本赛题提供的 18 个测点中,有 3 个测点连续缺失天数超过 12 天,因此采用线性插值后剔除了其中 2 个”。用自己的数据事实替代通用定义,既降重又显得扎实。

6. 把赛题代码整理成一个可复用的数据处理框架:参数、落盘与二次验证

最后做一个小工程化收尾。比赛提交的代码往往是一堆 notebook,评审根本跑不起来。我会把所有参数集中到一个 config.py 里,让每个模型文件都从配置文件读参数,这样调整 n_steps 或 epochs 不用满文件找数字。

# config.py RAW_PATH = "data/mine_monitor.xlsx" TARGET_COL = "elevation_mm" MODEL_NAME = "lstm" # 可选: gm / bp / lstm N_STEPS = 10 TEST_RATIO = 0.2 EPOCHS = 120 BATCH_SIZE = 16

训练完所有模型后,把每个模型在测试集上的预测结果统一落盘成 result.csv,格式为测点编号、观测日期、真实高程、预测高程、模型名称。这一步的价值在于做二次验证:你可以按测点画残差图,看哪些测点预测误差系统性偏大,再回到特征工程里找原因。比如某个测点残差始终为正,说明该测点还有未被建模的地下水影响。

最后的验证习惯是:每个模型都必须用同一个测试集切分别在训练后再跑一遍,而不是只看训练集拟合。把测试集预测结果和真实值画在一张图里,如果预测曲线明显滞后于真实曲线,说明 n_steps 太小或模型复杂度过高。几年下来我养成的习惯是,宁可少跑一个模型,也不能省掉这个可视化验证。数据清洗、建模、落盘、验证,四个环节跑通一遍,B 题的完整论文才有底气提交。希望帮到你。

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询