news 2026/9/24 18:17:10

Python MLP时间序列预测实战:从焦作数据到滚动预测的完整源码解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Python MLP时间序列预测实战:从焦作数据到滚动预测的完整源码解析

简介:这份资源面向计算机、电子信息工程、数学等专业的大学生及算法入门者,提供一套可直接运行的MLP时间序列预测完整方案,适用于课程设计、期末大作业与毕业设计等场景。压缩包共3个文件,包含1个Python源码脚本与2个CSV数据集,整体约46KB,源码与数据配套齐全,无需额外搜集样本即可上手调试。代码基于Anaconda、PyCharm与TensorFlow环境编写,采用参数化编程思路,关键参数可灵活修改,且配有保姆级注释,几乎一行一注释,便于零基础读者理解神经网络建模、训练与预测的完整流程。作者为某大厂资深算法工程师,具备八年Matlab与Python算法仿真经验,擅长智能优化算法、神经网络预测与信号处理等方向。目前已有567人学习下载,读者可借此掌握时间序列预测的代码框架、数据组织方式与调参排错思路,并在此基础上迁移到自己的课题数据中。

1. 从焦作数据到预测曲线:这套 MLP 源码到底能跑出什么

焦作.csv 和焦作全.csv 这两个文件名,第一次看到的时候我以为是某个统计年鉴的导出表,打开才发现是两条完整的时间序列,一条用来训练、一条用来验证。MLP.py 是主程序,MLP.zip 里打包了源码和数据,整个包不大,但结构干净——没有多余的依赖,没有花哨的封装,就是一份能直接跑通的 Python MLP 时间序列预测实现。如果你正在做课程设计、期末大作业或者毕业设计,需要一份带保姆级注释、参数化编程的神经网络预测代码,这套东西的完成度是够的。它不教你从零推导反向传播,但它把数据加载、归一化、滑窗构造、模型搭建、训练、预测、反归一化、误差评估这条链路全部串起来了,而且几乎一行一注释。对于刚接触 python MLP 时间序列预测的人来说,这种注释密度能省掉大量查文档的时间。

2. 拆开 MLP.py:数据流、滑窗构造与参数化设计

2.1 先看数据长什么样,再决定怎么喂给网络

拿到一份时间序列数据,第一件事不是写模型,而是确认它的结构。焦作.csv 和焦作全.csv 大概率是单列或多列的数值型 CSV,常见的时间序列格式无非两种:一种是带时间戳的,比如date,value;另一种是纯数值序列,只有一列或几列观测值。这套代码的通用做法是读取指定列作为预测目标,其余列如果存在,可以作为辅助特征拼进去。

我一般会先用 pandas 快速扫一眼数据的形状、缺失情况和数值范围:

import pandas as pd import numpy as np # 读取数据,header=0 表示第一行是列名,如果数据没有列名就改成 header=None df = pd.read_csv('焦作.csv', header=0, encoding='utf-8') # 查看前五行和基本统计信息,确认数据是否读进来、有没有异常值 print(df.head()) print(df.describe()) # 检查缺失值,时间序列里缺失值不能直接丢,要么插值要么前向填充 print(df.isnull().sum()) # 如果目标列是第 0 列,转成 float32 数组,方便后面做归一化 data = df.iloc[:, 0].values.astype('float32')

这段代码的逻辑很直白:先确认数据能被正确解析,再确认数值范围是否合理。参数上需要注意的是encoding,如果 CSV 里有中文列名或者特殊字符,utf-8 不一定能读进来,可以换成 gbk 试试。另外header参数要根据实际文件调整,很多课程设计的数据集第一行就是数据本身,没有列名,这时候必须设成None,否则第一行会被当成列名吃掉,后面预测结果对不上,这种坑我见过不止一次。

2.2 滑窗构造:时间序列预测的核心预处理

MLP 本身不处理时序依赖,它看到的是一个一个样本。要把时间序列变成监督学习问题,必须做滑窗。假设用过去 10 个时刻的值预测下一个时刻的值,那就要把长度为 N 的序列切成 N-10 个样本,每个样本的输入是连续 10 个值,标签是第 11 个值。

def create_dataset(data, look_back=10): """ data: 归一化后的时间序列数组 look_back: 用过去多少个时刻预测下一个时刻 返回: X shape=(样本数, look_back), Y shape=(样本数, 1) """ X, Y = [], [] for i in range(len(data) - look_back): # 取连续 look_back 个值作为输入特征 X.append(data[i:(i + look_back)]) # 取紧接着的下一个值作为预测目标 Y.append(data[i + look_back]) return np.array(X), np.array(Y)

look_back是这套代码里最需要调的参数之一。设太小,模型看不到足够的上下文,预测会滞后;设太大,样本数减少,训练容易过拟合,而且 MLP 的输入维度会膨胀。常见做法是先试 5、10、20 这几个值,看验证集上的 RMSE 或 MAE 哪个更低。注意create_dataset返回的 X 是二维数组,形状是(样本数, look_back),直接喂给 Keras 的 Dense 层没问题,因为 Dense 层接受二维输入。

2.3 归一化与反归一化:别让量纲毁了训练

时间序列的数值范围可能很大,比如 GDP 数据动辄几万亿,直接扔进网络,梯度会炸。这套代码用的是 MinMax 归一化,把数据压到 [0,1] 区间:

from sklearn.preprocessing import MinMaxScaler scaler = MinMaxScaler(feature_range=(0, 1)) # reshape(-1, 1) 是因为 sklearn 的 scaler 要求二维输入 data_scaled = scaler.fit_transform(data.reshape(-1, 1)).flatten() # 构造滑窗样本 X, Y = create_dataset(data_scaled, look_back=10) # 划分训练集和测试集,时间序列不能打乱顺序 train_size = int(len(X) * 0.8) X_train, X_test = X[:train_size], X[train_size:] Y_train, Y_test = Y[:train_size], Y[train_size:]

这里有个血泪经验:归一化必须用训练集的参数去 transform 测试集,不能对全体数据 fit 之后再切分。虽然这套代码为了简化可能直接对全序列做了 fit_transform,但在实际项目里,正确的做法是先切分再 fit。否则测试集的信息会泄露到训练过程中,验证指标会虚高,上线就翻车。反归一化的时候用scaler.inverse_transform,把预测值还原回原始量纲,才能算真实的误差。

2.4 模型搭建与参数化编程的体现

MLP.py 的模型部分用 TensorFlow/Keras 搭全连接网络,结构不复杂,但参数化做得比较到位。常见的写法是把层数、每层神经元数、学习率、batch_size、epochs 都抽成变量放在文件开头,改的时候不用翻遍整个脚本。

import tensorflow as tf from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout from tensorflow.keras.optimizers import Adam # 参数区:改这里就能控制模型结构和训练过程 HIDDEN_UNITS = [64, 32] # 两个隐藏层,神经元数分别是 64 和 32 DROPOUT_RATE = 0.2 # Dropout 比例,防止过拟合 LEARNING_RATE = 0.001 # Adam 优化器的学习率 BATCH_SIZE = 16 EPOCHS = 100 model = Sequential() # 输入层 + 第一个隐藏层,input_shape 等于 look_back model.add(Dense(HIDDEN_UNITS[0], activation='relu', input_shape=(look_back,))) model.add(Dropout(DROPOUT_RATE)) # 第二个隐藏层 model.add(Dense(HIDDEN_UNITS[1], activation='relu')) model.add(Dropout(DROPOUT_RATE)) # 输出层,回归任务不加激活函数 model.add(Dense(1)) # 编译模型,回归任务用 mse 损失,优化器用 Adam model.compile(optimizer=Adam(learning_rate=LEARNING_RATE), loss='mse') # 训练,validation_split 从训练集里再切一部分做验证 history = model.fit(X_train, Y_train, epochs=EPOCHS, batch_size=BATCH_SIZE, validation_split=0.1, verbose=1)

HIDDEN_UNITS这个列表的设计很实用,想加层就加元素,想减层就删元素。Dropout的加入说明作者考虑到了过拟合问题,时间序列数据量通常不大,不加 Dropout 很容易在训练集上 loss 很低但测试集一塌糊涂。input_shape=(look_back,)必须和滑窗的look_back一致,改了一个忘了改另一个,运行时报维度错误,这是新手最常见的翻车点之一。

2.5 预测、反归一化与误差评估

训练完之后,用测试集做预测,再把预测值反归一化,和真实值对比:

# 预测 train_predict = model.predict(X_train) test_predict = model.predict(X_test) # 反归一化,注意 scaler 是在全体数据上 fit 的,这里直接 inverse_transform train_predict = scaler.inverse_transform(train_predict).flatten() test_predict = scaler.inverse_transform(test_predict).flatten() Y_train_inv = scaler.inverse_transform(Y_train.reshape(-1, 1)).flatten() Y_test_inv = scaler.inverse_transform(Y_test.reshape(-1, 1)).flatten() # 计算 RMSE 和 MAE from sklearn.metrics import mean_squared_error, mean_absolute_error import math train_rmse = math.sqrt(mean_squared_error(Y_train_inv, train_predict)) test_rmse = math.sqrt(mean_squared_error(Y_test_inv, test_predict)) test_mae = mean_absolute_error(Y_test_inv, test_predict) print(f'Train RMSE: {train_rmse:.4f}') print(f'Test RMSE: {test_rmse:.4f}') print(f'Test MAE: {test_mae:.4f}')

RMSE 和 MAE 是最常用的两个回归指标,RMSE 对大误差更敏感,MAE 更稳健。如果测试集 RMSE 远大于训练集 RMSE,说明过拟合了,可以减小网络规模、增大 Dropout、或者增加训练数据。如果两者都很大,说明欠拟合,需要加层、加神经元或者调学习率。这套代码把评估部分也写全了,不用自己再补。

3. 环境配置与运行:从 Anaconda 到 PyCharm 的完整链路

3.1 环境依赖与版本选择

这套代码标注的技术栈是 Anaconda + PyCharm + Python + TensorFlow。TensorFlow 的版本兼容性是第一个要确认的事情。如果你用的是 Windows,TensorFlow 2.x 对 Python 版本有要求,常见组合是 Python 3.8 到 3.10 配 TensorFlow 2.6 到 2.12。Anaconda 的好处是可以用 conda 创建独立环境,避免和系统里的其他包冲突。

# 创建名为 mlp_ts 的虚拟环境,指定 Python 版本 conda create -n mlp_ts python=3.9 # 激活环境 conda activate mlp_ts # 安装依赖,tensorflow 会自动带上 numpy、protobuf 等 pip install tensorflow==2.12.0 pip install pandas scikit-learn matplotlib

版本号不要照抄,根据自己机器上 CUDA 的情况选。如果没有 NVIDIA 显卡,直接装 CPU 版就行,这套 MLP 规模很小,CPU 训练完全够用。matplotlib是用来画预测对比图的,如果代码里有绘图部分,这个包不能少。

3.2 PyCharm 项目配置与运行

在 PyCharm 里新建项目,解释器选刚才创建的 conda 环境。把 MLP.py、焦作.csv、焦作全.csv 放在同一个目录下,直接右键运行 MLP.py。如果报FileNotFoundError,检查 CSV 路径是不是相对路径,PyCharm 默认的工作目录是项目根目录,不是脚本所在目录。可以在代码里用os.path.dirname(os.path.abspath(__file__))拼绝对路径,避免这个问题。

常见做法是在脚本开头加一段路径处理:

import os # 获取当前脚本所在目录,拼接数据文件路径 base_dir = os.path.dirname(os.path.abspath(__file__)) data_path = os.path.join(base_dir, '焦作.csv') df = pd.read_csv(data_path, header=0, encoding='utf-8')

这样不管在 PyCharm 还是命令行运行,路径都不会错。另外,如果 CSV 里有中文,Windows 下用 utf-8 读可能报UnicodeDecodeError,换成gbk或者gb18030试试。

3.3 训练过程监控与结果可视化

训练的时候verbose=1会打印每个 epoch 的 loss 和 val_loss。如果 loss 一直不降,先检查学习率是不是太大,Adam 默认 0.001 一般没问题,但如果数据没归一化,loss 可能直接 NaN。如果 val_loss 先降后升,说明过拟合了,可以提前停止或者加 Dropout。

画预测对比图能直观判断模型有没有学到趋势:

import matplotlib.pyplot as plt plt.figure(figsize=(12, 6)) # 画真实值 plt.plot(Y_test_inv, label='True') # 画预测值 plt.plot(test_predict, label='Predict') plt.legend() plt.title('MLP Time Series Prediction') plt.show()

如果预测曲线明显滞后于真实曲线,说明look_back太小,模型看不到足够的趋势信息。如果预测曲线是一条直线,说明模型没学到东西,检查归一化、学习率和网络结构。

4. 避坑与排查:这套代码最容易翻车的五个地方

4.1 现象:运行报错ValueError: Input 0 of layer dense is incompatible

原因:input_shape和实际输入的维度对不上。最常见的情况是改了look_back但忘了改input_shape,或者create_dataset返回的 X 形状不是二维。

解决:在model.fit之前打印X_train.shape,确认第二维等于look_back。如果 X 是一维的,检查create_datasetnp.array(X)有没有正确堆叠。另外,如果用了多特征输入,input_shape要改成(look_back, n_features),Dense 层前面可能需要加 Flatten。

4.2 现象:预测结果全是同一个值,或者和真实值完全对不上

原因:归一化和反归一化的对象搞混了。比如对全体数据做了fit_transform,但反归一化的时候用错了 scaler,或者对 Y 做了归一化但预测后忘了反归一化。

解决:统一用同一个 scaler 做归一化和反归一化。训练前打印data_scaled的最大最小值,确认在 [0,1] 区间。预测后打印test_predict的前几个值,确认量纲和原始数据一致。如果原始数据是几千几万,预测出来是 0.几,说明忘了反归一化。

4.3 现象:训练 loss 下降但验证 loss 震荡,RMSE 很大

原因:时间序列切分时打乱了顺序,或者validation_split从训练集尾部切的时候恰好切到了分布差异大的片段。

解决:时间序列绝对不能shufflemodel.fit里不要设shuffle=Truevalidation_split默认从尾部切,如果数据有周期性,尾部可能和头部分布不同,可以手动切验证集,确保验证集和训练集分布一致。另外,batch_size太小会导致 loss 震荡,可以适当增大到 32 或 64。

4.4 现象:CSV 读取后数据形状不对,多了一列或少了一列

原因:header参数设置错误,或者 CSV 分隔符不是逗号。有些数据集用分号或制表符分隔,read_csv默认按逗号解析,会把整行读成一列。

解决:先用文本编辑器打开 CSV 看一眼分隔符。如果是分号,加sep=';';如果是制表符,加sep='\t'。如果第一行是数据不是列名,设header=None。读完之后打印df.shapedf.head(),确认列数和预期一致。

4.5 现象:TensorFlow 导入报错或者 GPU 不可用

原因:TensorFlow 版本和 Python 版本不匹配,或者 CUDA/cuDNN 版本不对。Windows 上还可能是缺少 Visual C++ 运行库。

解决:先用pip list看 tensorflow 版本,再去官网查对应的 Python 版本要求。如果没有 GPU,直接卸载 GPU 版装 CPU 版:pip install tensorflow-cpu。导入时如果报DLL load failed,装一下 Microsoft Visual C++ Redistributable。这套 MLP 不需要 GPU,CPU 版省心得多。

5. 进阶技巧:用焦作全.csv 做滚动预测与多步验证

焦作全.csv 的存在说明作者留了一个完整的验证集或者更长的序列。很多人跑完 MLP.py 看到测试集 RMSE 就结束了,但真正能说明模型泛化能力的,是用焦作全.csv 做滚动预测。滚动预测的意思是:用模型预测下一个点,把预测值追加到输入窗口末尾,再预测下一个点,如此循环。这样能模拟真实场景中模型不断接收新数据、不断预测的过程。

def rolling_forecast(model, scaler, last_window, n_steps): """ model: 训练好的模型 scaler: 归一化器 last_window: 初始输入窗口,shape=(look_back,) n_steps: 向前预测多少步 返回: 反归一化后的预测序列 """ predictions = [] current_window = last_window.copy() for _ in range(n_steps): # 当前窗口 reshape 成 (1, look_back) 喂给模型 input_seq = current_window.reshape(1, -1) pred = model.predict(input_seq, verbose=0)[0, 0] predictions.append(pred) # 把预测值追加到窗口末尾,去掉最旧的值 current_window = np.append(current_window[1:], pred) # 反归一化 predictions = scaler.inverse_transform(np.array(predictions).reshape(-1, 1)).flatten() return predictions

这个函数的关键参数是n_steps,也就是向前预测多少步。步数越多,误差累积越严重,这是自回归预测的固有缺陷。我一般会先跑 10 步、20 步、50 步,看 RMSE 随步数增长的曲线。如果 10 步之后误差就爆炸,说明模型学到的模式不够稳定,需要重新调look_back或者网络结构。

另一个技巧是用焦作.csv 训练、焦作全.csv 验证,而不是从同一条序列里切分。这样能真正检验模型在不同时间段上的表现。如果两条序列的统计分布差异大,比如均值、方差不一样,可以先做分布对齐,或者用焦作.csv 的 scaler 去 transform 焦作全.csv,看反归一化后的误差是否可接受。

还有一个容易被忽略的点:MLP 对输入特征的尺度很敏感,如果焦作全.csv 的数值范围超出了训练集的范围,归一化后可能超出 [0,1],模型预测会失真。常见做法是在归一化之前先做异常值处理,把超出训练集范围的数值截断或者用训练集的 min/max 重新缩放。我吃过这个亏,验证集里有个别极端值,归一化后变成 1.2,模型没见过这种输入,预测结果直接飞了。从那以后我每次做时间序列预测,都强制走一遍训练集和验证集的分布对比,确认量纲一致再往下跑。

希望帮到你。

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

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

OpenCV人脸识别课设实战:从LBPH原理到答辩避坑指南

简介:一份研一图像处理期末大作业,基于 OpenCV 实现人脸识别全流程,面向需要完成课程设计或入门计算机视觉的学生,代码可直接运行并二次开发。项目按数据处理、检测、训练、识别划分模块,包含图像预处理、数据集采集脚…

作者头像 李华
网站建设 2026/9/24 18:13:58

Java毕设考勤系统全流程实战:Spring Boot+小程序从表设计到部署避坑

简介:这是一套面向本科毕业设计的小程序上课考勤系统完整源代码,基于Spring Boot与微信小程序开发,适合Java学习者、毕设学生作为项目参考。系统实现了后台管理、小程序端GPS签到、定位打卡、迟到统计等核心考勤功能,设计获得优秀…

作者头像 李华
网站建设 2026/9/24 18:13:10

递增的三元子序列

题目描述 给你一个整数数组 numsnumsnums&#xff0c;判断这个数组中是否存在长度为 3 的递增子序列。 如果存在这样的三元组下标 (i,j,k)(i, j, k)(i,j,k) 且满足 i<j<ki < j < ki<j<k &#xff0c;使得 nums[i]<nums[j]<nums[k]nums[i] < nums[…

作者头像 李华
网站建设 2026/9/24 18:11:49

基于UNet的脑肿瘤分割与生存预测:从2D到3D的完整实践指南

简介&#xff1a;面向医学影像分析与深度学习方向的高校学生、科研工作者&#xff0c;这份毕设资源系统实现了三种脑肿瘤分割算法&#xff0c;并包含生存预测模型和项目报告&#xff0c;主要解决从算法理论到代码落地、从结果评估到论文撰写的完整需求。资源共五十个文件&#…

作者头像 李华
网站建设 2026/9/24 18:11:05

Windows GDI AlphaBlend 像素级半透明绘制实战指南

简介&#xff1a;本资源是一份面向Windows桌面开发初学者与中级程序员的AlphaBlend半透明绘制实战源码包&#xff0c;聚焦图形界面中位图透明叠加这一典型视觉需求。资源完整实现基于GDI的32位带Alpha通道位图混合渲染&#xff0c;涵盖设备上下文配置、BLENDFUNCTION结构体设置…

作者头像 李华
网站建设 2026/9/24 18:11:01

Python交通流预测实战:855个传感器数据清洗与拥堵等级建模

简介&#xff1a;这份资源面向具备一定Python基础、希望入门智能交通与数据挖掘的学习者&#xff0c;围绕道路短时车流量与拥堵状态预测展开。项目基于GCM Corridor真实交通数据&#xff0c;覆盖16座城镇主干道、855个传感器每5分钟采集的拥堵记录&#xff0c;包含日期、方向、…

作者头像 李华