尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
LSTM与Django集成的空气质量预测系统实战
简介一套基于LSTM深度学习模型与Django Web框架实现的空气质量监测及预测系统源码主要面向计算机相关专业正在准备毕业设计的学生也可用于课程设计、期末大作业等实战场景。资源包共计260个文件体积约7.02MB涵盖Python核心代码、Django后端配置、HTML/CSS/SCSS/JS前端页面、SQLite数据库、CSV格式的空气历史数据以及模型训练生成的pyc等文件直观呈现了从数据采集、时序预测到页面可视化展示的完整项目结构。项目已经过严格调试下载即可运行可节省环境搭建与排错时间并便于针对LSTM网络结构、前端图表展示等做二次开发。已有329人学习下载对需要快速搭建空气质量预测毕业设计的同学来说是一份兼具完整性与可运行性的参考素材。1. 毕设源码包里的 LSTMDjango空气质量预测系统到底要做什么打开一个空气质量监测与预测的毕业设计源码包你最先看到的通常不是模型而是一个 Django 工程目录、几个 h5 权重文件外加一堆 CSV 或 Excel 数据。这套组合在近年课设和毕设里几乎是固定配置LSTM 负责把 AQI、PM2.5 这类时序数据做成“预测”卖点Django 负责把数据管理、历史曲线、未来 48 小时预报整合成一个能演示的 Web 系统。想拿这套源码改成自己的项目建议先别急着python manage.py runserver——你得先搞清楚模型喂进去的是什么形状、预测结果是怎么从 Python 后端以 JSON 形式传到网页上的再动手改才不踩坑。下面按这个顺序把落地细节拆开先用 Keras 训练一个能用的 LSTM 模型再用 Django 把模型包成接口和页面最后讲清楚调参、回测和部署时容易翻车的地方。对 Python 和深度学习流程不熟的新手按步骤能跑通全部流程如果已经有工程经验可以直接翻到滞后处理和免训练推理那两节。2. 先用 Keras 把 LSTM 空气质量预测模型跑通2.1 为什么选 LSTM 而不是 ARIMA时序预测选型的一次实际对比空气质量监测数据是典型的小时粒度时间序列PM2.5 会随早晚高峰波动AQI 白天和夜间差异明显这是周期性和非线性叠加的信号。ARIMA 也能做预测但需要先做平稳性检验、确定差分阶数再对 p、d、q 三个参数做网格搜索遇到周期项还得加 SARIMA整套流程对毕设来说调参成本太高。XGBoost 这类树模型同样能做但它的输入是“人肉构造的特征”你得自己把滞后 1 小时、滞后 24 小时、当日平均、星期几这些字段手工拼出来。LSTM 的优势在于端到端给它一个固定长度的滑动窗口它能自己从序列里找依赖关系不需要显式设计特征。对毕业设计场景这一点不仅省事答辩时也好讲——遗忘门、输入门、输出门这套机制比“我做了 30 个滞后特征”听起来完整得多。选择 LSTM 不等于说它永远最优。如果你的数据只有 200 条日粒度记录LSTM 很容易过拟合这时候 ARIMA 或简单线性回归反而更稳。但空气质量小时级数据动辄几千条LSTM 的基本盘是够的。2.2 数据准备监测字段和缺失值处理项目里的数据通常长这样一张表存监测站逐小时采样值字段含义典型单位date采样时间小时2023-01-01 01:00pm25PM2.5 浓度ug/m³pm10PM10 浓度ug/m³so2二氧化硫ug/m³no2二氧化氮ug/m³co一氧化碳mg/m³o3臭氧ug/m³aqi空气质量指数无量纲用 pandas 读入后先做三件事把 date 列转成 datetime 格式并设为索引按时间排序再处理缺失值。监测设备离线会导致整行或某列空缺。常见处理策略是先ffill()前向填充残留的缺口用前后均值插值不要直接 dropna 删除整行——时间序列删除行等于破坏了连续性回测时会少掉一大段真实样本。import pandas as pd df pd.read_excel(air_quality.xlsx) df[date] pd.to_datetime(df[date]) df df.set_index(date).sort_index() # 缺失值先前向填充再对剩余空值做线性插值 df df.ffill().interpolate(methodlinear) # 检查是否还有空值 print(df.isnull().sum())参数说明interpolate(methodlinear)会按相邻有效值做线性插值配合ffill()能把设备短时离线造成的毛刺降到最低。若某一天整列连续缺失超过 6 小时建议把该段标记出来训练时直接跳过因为插值造出来的数据会误导模型让它在缺失时段学到假的规律。2.3 滑动窗口切分训练样本LSTM 的输入要求是三维张量(样本数, 时间步长, 特征数)。把一条完整的时间序列切成长度为lookback的历史窗口每个窗口对应一个未来值作为标签。这个lookback就是滑动窗口长度也是后面 Django 接口调用时前端必须传过来的历史点数。小时级数据里 24 能覆盖一个完整日周期所以窗口取 24 是常用起点想捕捉周末和工作日差异可以考虑 48 甚至 168一周。窗口越大模型看到的上下文越完整但训练样本数会变少噪声也会累积。import numpy as np def make_dataset(data, lookback24, predict_steps1): X, y [], [] for i in range(len(data) - lookback - predict_steps 1): X.append(data[i:i lookback]) y.append(data[i lookback:i lookback predict_steps]) return np.array(X), np.array(y) # 以 AQI 单列为示例shape 为 (n, 1) aqi_values df[[aqi]].values.astype(float32) X, y make_dataset(aqi_values, lookback24, predict_steps1) print(X.shape, y.shape)逻辑说明循环里data[i:ilookback]取的是连续 24 个历史点data[ilookback]是第 25 个点作为标签。滑动 1 格就生成下一组。predict_steps1代表单步预测如果改成 3标签就是未来 3 个时刻的序列模型输出维度也要同步调整。2.4 模型训练与模型保存模型结构按“两层 LSTM Dropout 全连接”搭这是时间序列预测里最稳妥的标配。第一层return_sequencesTrue是为了把完整序列传给第二层第二层return_sequencesFalse只输出最后时间步的隐状态。units 这里取 64 和 32数据量大时可以相应放大。from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping model Sequential([ LSTM(64, return_sequencesTrue, input_shape(24, 1)), Dropout(0.2), LSTM(32, return_sequencesFalse), Dense(1) ]) model.compile(optimizeradam, lossmse, metrics[mae]) early_stop EarlyStopping(patience10, restore_best_weightsTrue) history model.fit( X_train, y_train, validation_data(X_val, y_val), epochs100, batch_size32, callbacks[early_stop], verbose1 ) model.save(model/best_lstm.h5)参数说明restore_best_weightsTrue会在验证 loss 连续 10 轮不降时回滚到最优轮次的权重这是防止过拟合最直接的手段。batch_size32对几千条样本足够显存吃紧时改 16。模型保存用 Keras 自带的model.save会生成一个 h5 文件后面 Django 加载的就是这个文件不需要再装 GPU 版 TensorFlowCPU 推理完全够用。3. 把模型接入 Django从数据加载到 Web API3.1 Django 项目结构与 app 划分用django-admin初始化一个项目把预测相关代码单独放一个predictapp 里不要把模型加载代码堆在views.py中。这样等你要换模型版本或者做单元测试时改动范围能控制在一个小目录内。django-admin startproject air_monitor cd air_monitor python manage.py startapp predict python manage.py migrate项目内推荐结构air_monitor/ ├── manage.py ├── air_monitor/ │ ├── settings.py │ └── urls.py ├── predict/ │ ├── views.py │ ├── urls.py │ └── services.py ├── model/ │ ├── best_lstm.h5 │ └── scaler.pkl ├── templates/ │ └── index.html └── static/ └── echarts.min.js这里model/目录存放训练产物predict/services.py负责加载模型和封装预测调用views.py只做 HTTP 参数解析和 JSON 响应。分层之后哪怕你换成 XGBoost 或 Prophet也只需要替换services.py内部的实现。3.2 模型加载与预测服务封装模型加载这个动作注定慢首次加载可能要 1 到 2 秒。不能在每次请求里都load_model要放在模块层进程启动后常驻内存。# predict/services.py import numpy as np import joblib from tensorflow.keras.models import load_model class AqiPredictor: def __init__(self, model_pathmodel/best_lstm.h5, scaler_pathmodel/scaler.pkl): self.model load_model(model_path, compileFalse) self.scaler joblib.load(scaler_path) self.lookback 24 def predict_next(self, history): # history: list[float]长度必须等于 lookback arr np.array(history, dtypenp.float32).reshape(-1, 1) # (24, 1) arr_scaled self.scaler.transform(arr) x arr_scaled.reshape(1, self.lookback, 1) pred_scaled self.model.predict(x, verbose0)[0] pred self.scaler.inverse_transform(pred_scaled.reshape(-1, 1)) return float(pred[0, 0]) predictor AqiPredictor()逻辑说明.transform用的是训练时 fit 好的 scaler不能在运行时重新 fit否则归一化分布会不一致。inverse_transform把预测结果还原成真实浓度单位。这里默认输入单特征 AQI如果你想同时用 PM2.5 和 PM10 作为输入特征训练时把输入维度改成对应特征数reshape的最后一维同步调整。3.3 预报查询接口的实现预测接口接收最近 24 个小时的 AQI 历史值返回预测结果。前端页面也可以用同样的接口做未来多步预测每预测一步就丢掉最早的一个点把新预测值拼到序列尾部滚动调用。# predict/views.py import json from django.http import JsonResponse from django.views.decorators.csrf import csrf_exempt from predict.services import predictor csrf_exempt def forecast(request): if request.method ! POST: return JsonResponse({code: 405, msg: 只支持 POST}) body json.loads(request.body) history body.get(history) if not history or len(history) ! predictor.lookback: return JsonResponse({code: 400, msg: fhistory 长度必须为 {predictor.lookback}}) pre predictor.predict_next(history) return JsonResponse({code: 0, predict: pre})# predict/urls.py from django.urls import path from predict.views import forecast urlpatterns [ path(api/forecast, forecast), ]csrf_exempt在这里是因为演示系统通常没有登录态POST 接口放开 CSRF 校验方便前端直接 fetch。参数说明history必须是按时间正序排列的连续数值列表若中间缺数据先用第 2 章里的插值方法补好再传接口本身不做清洗。接口参数类型必填说明historylist[float]是最近 24 个时刻的 AQI 值按时间升序返回 predictfloat-下一个时刻的预测值3.4 前端图表联动展示页面这部分用 ECharts 折线图展示历史曲线和预测点前端请求接口后在图上把预测值画成一个独立的标记点。async function getForecast() { const history window.recentAqi.slice(-24); const resp await fetch(/predict/api/forecast, { method: POST, headers: {Content-Type: application/json}, body: JSON.stringify({history: history}) }); const data await resp.json(); if (data.code 0) { chart.setOption({ series: [{ name: 预测值, data: [...history.map((v, i) [i, v]), [24, data.predict]] }] }); } }这里data.predict是当前时序中第 25 点的预测值图表坐标轴把 24 个历史点映射到 0-23新预测点落在横轴 24 的位置上视觉上表现为历史折线向右延伸了一个点。多步预测要在前端做循环拼接每轮把上一轮预测结果追加到 history 尾部。4. 训练参数怎么调、Django 集成时容易翻车的三个点4.1 MinMaxScaler 归一化和反归一化到底在哪个环节做归一化是整个链路里最容易写错位置的一步。正确顺序是训练前对训练集fit_transform保存 scaler 到 pkl 文件测试集只transform不重新 fitDjango 预测时先transform再进模型输出后inverse_transform还原成真实单位。常见的错误是顺手在测试集上重新 fit或者预测时忘记反归一化返回一个 0 到 1 之间的数前端画图时怎么对都对不上真实 AQI。另一个隐蔽问题如果原始数据里有极端峰值MinMaxScaler 会把绝大多数正常值压到 0 附近模型学起来很吃力。遇到这种情况先做分位数裁剪把 99% 分位以上的值截断再做归一化。from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) # 训练集清洗后 aqi_train df_train[[aqi]].values scaler.fit(aqi_train) joblib.dump(scaler, model/scaler.pkl) # 测试集 aqi_test_scaled scaler.transform(df_test[[aqi]].values)逻辑说明scaler 保存后它内部记录着训练集的最小值和最大值。模型训练和线上推理共用这组参数才能保证输入输出分布一致。4.2 LSTM 训练参数参考表与调参路径模型效果不好时不一定要加层数优先调的是窗口长度和隐层大小。下面这张表是我在类似项目里常用的起点实际以你自己的数据量为准参数推荐范围调参方向lookback24 / 48 / 168数据周期长取大噪声大取小LSTM units32 / 64 / 128数据量每翻 10 倍再考虑增加Dropout0.1 - 0.3训练 loss 低、验证 loss 高时调大batch_size16 / 32 / 64训练震荡时调大显存小调小epochs50 - 200配 EarlyStopping跑满没有意义optimizeradamlr 默认 0.001loss 不降时改 0.0005从默认参数开始训练看训练集和验证集的 loss 曲线两条都高是模型容量不够加大 units训练低验证高是过拟合加大 Dropout 或减小 lookback两条都在 0.1 量级反复震荡多半是归一化没做好或 batch_size 过小。4.3 Django 集成时的版本、编码和路径坑Django 集成 TensorFlow 模型最常见的三个坑按踩中概率排序版本冲突、中文乱码、路径问题。TensorFlow 2.x 对 numpy 版本敏感TensorFlow 2.10 以下配合 numpy 1.24.x 是相对稳妥的组合装成 numpy 2.0 后经常报module numpy has no attribute bool这类错。建议在 requirements.txt 里精确锁版本Django4.2.9 tensorflow-cpu2.10.0 numpy1.24.4 pandas2.0.3 scikit-learn1.3.2 joblib1.3.2CSV 文件读取时请用encodingutf-8-sig这个编码会保留 BOMpandas 读入后第一列列名不会多出\ufeff前缀。Excel 文件里 date 列读进来是 datetime 对象JSON 序列化前必须str()转成字符串否则JsonResponse会报 TypeError。模型路径不建议写相对路径model/best_lstm.h5因为 manage.py 的当前工作目录随启动方式变化用BASE_DIR / model / best_lstm.h5最稳from pathlib import Path BASE_DIR Path(__file__).resolve().parent.parent MODEL_PATH BASE_DIR / model / best_lstm.h55. 用历史回测验证预测效果再决定要不要上双向 LSTM5.1 回测脚本算 RMSE、MAE、R²模型训练完后不要直接上线先把测试集的真实值和预测值并排跑一遍算三个误差指标。回测的意义不只是得到一个数字而是帮你判断模型到底学到了规律还是学会了“复制粘贴”。from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score y_true y_test.reshape(-1) y_pred model.predict(X_test, verbose0).reshape(-1) print(RMSE:, round(float(np.sqrt(mean_squared_error(y_true, y_pred))), 3)) print(MAE :, round(float(mean_absolute_error(y_true, y_pred)), 3)) print(R2 :, round(float(r2_score(y_true, y_pred)), 3))预测滞后性在这个环节会现出原形如果预测曲线和真实曲线形状几乎完全一样只是在时间轴上向右平移了一个点说明模型把“预测”退化成“把上一个时刻抄一遍”。这种情况 RMSE 未必难看但多步预测时会迅速发散。判断方法很简单计算预测值和真实值在 t-1 时刻的相关系数如果显著高于 t 时刻基本可以断定存在滞后。缓解手段包括输入里加入差分特征让模型预测增量而非绝对值或者把标签改成未来 2 小时的值强迫模型看更远的依赖。5.2 演示环境加一个免训练开关毕设演示现场往往没有 TensorFlow 环境或者那台笔记本装不了最新版的依赖。一个实用的妥协方案在系统里加一个环境变量开关当设置为快速模式时后端不加载 h5 模型改用线性外推做降级预测页面演示照常跑只是预测曲线更平缓。# predict/services.py import os import numpy as np class LinearBaselinePredictor: def predict_next(self, history): arr np.array(history, dtypenp.float32) # 用最近两个点的线性趋势外推一步 diff arr[-1] - arr[-2] return float(arr[-1] diff) def get_predictor(): if os.getenv(AIR_FAST_MODE): return LinearBaselinePredictor() return AqiPredictor()用AIR_FAST_MODE1 python manage.py runserver启动时走基线模型正常环境不设置该变量就跑 LSTM。这招在答辩演示、给别人拷代码试运行时特别好用能避免花 20 分钟在线安装 TensorFlow 的尴尬。迁移到云服务器时也可以用类似思路开局先跑基线模式把页面和数据流调通再后台补装深度学习依赖最后切回真实模型。要进一步提升预测上限把第二层 LSTM 换成Bidirectional(LSTM(32))训练时间会翻倍但往往能引入未来时刻的上下文信息对日周期明显的空气质量数据通常有正收益。本文还有配套的精品资源点击获取
RELATED

相关推荐

Claude Code 配 TaoToken:办公 Agent 选型按任务类型对比执行边界

Claude Code 配 TaoToken:办公 Agent 选型按任务类型对比执行边界

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📅 2026/9/17 0:15:36
Linux信号机制:原理、实战与性能优化

Linux信号机制:原理、实战与性能优化

1. Linux信号机制深度解析:从原理到实战信号(Signal)作为Linux系统中进程间通信的重要机制,已经伴随Unix/Linux系统走过了半个世纪。这种软件层次的中断模拟机制,在系统编程中扮演着关键角色——当我在处理一个耗时计算…

📅 2026/9/17 0:10:35
牛顿-拉夫逊法三相潮流计算程序开发与实践

牛顿-拉夫逊法三相潮流计算程序开发与实践

1. 三相潮流计算程序概述在电力系统分析领域,三相潮流计算是最基础也是最重要的计算工具之一。我开发的这个基于牛顿-拉夫逊法的三相潮流计算程序,主要用于解决电力系统稳态运行分析中的各类计算问题。不同于教科书上的简化示例,这个程序针对…

📅 2026/9/17 0:10:35
MORE NEWS

更多资讯

📰

OpenClaw 安全过滤调用内容安全模型报 401?TaoToken 这样给通道地址

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

AI写论文实用指南:工具选择、技巧方法与合规注意事项全解析

在研究生的科研过程中,数据分析是一个至关重要的环节。无论你是在进行实验数据处理、统计分析,还是在进行大规模数据挖掘,选择合适的工具将直接影响到研究的进展和结果。随着技术的不断发展,越来越多高效的数据分析工具问世&#…

📰

配额超限 429?TaoToken 这样改 Codex 的限流重试

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

📰

eVTOL毕设从开题到答辩,我常用的AI工具组合

飞行器设计与工程专业的毕业任务,往往不是“写一篇文章”那么简单。很多同学会遇到一个特别典型的题目:小型电动垂直起降无人机(eVTOL)总体设计与气动性能分析。 你需要提交的成果通常包括: 开题报告:任务需…

📰

拒绝“憋论文”:用书匠策AI把课程论文从“苦役”变成“可控工程”

官网:www.shujiangce.com | 微信 公众号 :书匠策AI 如果你正在带本科生的课程论文,大概率见过这样的场景:学生在选题边缘反复横跳,大纲改了八版还是被导师一句“逻辑不通”打回,格式调到凌晨四点却在提…

📰

Claude Code 并行开 5 个进程,改走 TaoToken 行不行?

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬