
简介基于LSTM的气温预测及可视化源码包是面向Python开发者和计算机专业学生的毕设/课设参考项目用于解决气温时间序列建模与预测问题。压缩包共16个文件包含8个Python脚本、4个pyc编译缓存、2个xlsx气温数据文件及2个Markdown说明文档整体仅721KB轻量易读已有280人学习适合需要完整可运行项目的开发者。实现上覆盖数据爬取bs4、预处理、模型构建、训练评估与可视化使用Keras搭建LSTM网络借助过去5天特征预测下一天气温。项目采用北京、上海、广州、郑州四城历史气温数据进行验证每个样本的特征维度为1输入按时间序列格式整理核心代码注释清晰。附有详细项目说明、配置脚本与README文档便于理解从爬虫抓取到模型评估的完整流程是课程设计或毕业设计的高质量参考也能辅助入门者快速上手时间序列预测。1. 从爬虫数据到 LSTM 输入气温预测项目到底在解决什么问题气温预测这种问题第一反应是查天气预报但作为深度学习练手项目它真正的难点不在“预测准”而在把网页上半结构化数据变成能喂给循环神经网络的张量。这个基于 LSTM 的气温预测项目抓取了中国天气网北京、上海、广州、郑州四个城市 2011 年以来的逐日气温用 Keras 搭建单层 LSTM以过去 5 天的温度预测第 6 天的温度。它把数据爬取、清洗、时序转换、模型训练、结果可视化串成了一条完整链路适合正在做 Python 课程设计、毕业设计或者刚接触 LSTM 时间序列预测的开发者。更值得琢磨的是它的数据组织方式每个时间点特征维度为 1但 LSTM 要求输入是三维的这中间怎么转换是多数新手卡住的地方。2. 天气数据爬取与预处理bs4 解析与时间序列构造2.1 项目结构与模块职责解压源码包后核心文件分工清晰weather_spider.py负责从中国天气网抓取历史气温data.py和dataset.py负责读取、清洗和把普通数值列表转换成 LSTM 需要的监督学习格式model.py定义 LSTM 网络结构train.py执行训练并保存权重predict.py加载模型做预测demo.py调用 matplotlib 完成可视化。config.py统一管理城市列表、抓取 URL、时间步长、批大小等参数改一个文件就能切换到其他城市。这种按职责拆分的写法本身就有参考价值。课程设计常用的做法是把所有代码堆在一个main.py里但一旦要调整模型结构或换数据源牵一发动全身。这里把爬虫、数据处理、模型、训练、预测分成独立模块每个模块都能单独运行验证调试体验好很多。2.2 用 bs4 抓取中国天气网历史数据爬虫部分使用了requests获取页面再用bs4的BeautifulSoup解析 HTML 表格。中国天气网的天气详情页会把逐日温度放在ul classt clearfix或者table中不同年份的 URL 规则不同所以config.py里需要维护一个 URL 模板。import requests from bs4 import BeautifulSoup import time def fetch_weather(city_code, year): url fhttp://www.weather.com.cn/weather/{city_code}/{year}.shtml headers {User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)} resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 soup BeautifulSoup(resp.text, html.parser) # 解析每个日期的最高温度中国天气网常见结构为 li 列表 days soup.select(ul.t li) result [] for item in days: date item.h1.get_text(stripTrue) high item.p.span.get_text(stripTrue) # 最高温单位℃ low item.p.i.get_text(stripTrue) # 最低温 result.append((date, float(high), float(low))) return result这段代码的核心是选择器匹配。ul.t li取决于目标页面结构不同浏览器 F12 看到的 class 名可能不一样所以第一次运行失败时不要急着改超时先打印soup.title确认页面是否被反爬拦截。time.sleep(0.5)建议加在循环请求里避免高频访问被服务端拒绝。2.3 构造 LSTM 监督学习样本抓下来的是一串连续日期和温度LSTM 不能直接吃这种裸序列。它需要的是「用前 5 天预测第 6 天」的样本对。这一步在dataset.py中完成把原始数组转换成有监督的(X, y)结构。import numpy as np def create_sequence(data, lookback5): X, y [], [] for i in range(len(data) - lookback): X.append(data[i:i lookback]) y.append(data[i lookback]) return np.array(X), np.array(y) # 示例data 是连续 100 天的最高温列表 temps [f[1] for f in weather_list] # 提取最高温 X, y create_sequence(temps, lookback5) print(X shape:, X.shape) # (95, 5) print(y shape:, y.shape) # (95,)这里是第一个容易出错的地方create_sequence输出的X形状是(样本数, 5)但 Keras 的 LSTM 层要求输入是(样本数, 时间步长, 特征数)所以后面必须做X X.reshape((X.shape[0], X.shape[1], 1))。项目描述里的(batch_size, dimension)就是这个意思其中dimension等于时间步长 5。特征数 1 代表每个时刻只用一个温度值如果之后想加入湿度、降水作为附加特征只需要把特征数改成 2 或更多并把temps换成多列矩阵。2.4 数据归一化与训练集划分气温序列数值范围较大直接输入 LSTM 会导致梯度不稳定。常见做法是用MinMaxScaler把数据压缩到 0 到 1 区间训练完成后再反归一化回真实温度。归一化必须在划分训练集之前进行否则测试集的信息会泄漏到训练集里导致评估结果虚高。from sklearn.preprocessing import MinMaxScaler scaler MinMaxScaler(feature_range(0, 1)) temps np.array(temps).reshape(-1, 1) scaled scaler.fit_transform(temps) X, y create_sequence(scaled.flatten(), lookback5) split int(len(X) * 0.8) X_train, X_test X[:split], X[split:] y_train, y_test y[:split], y[split:] # 转换为 LSTM 三维输入 X_train X_train.reshape((X_train.shape[0], X_train.shape[1], 1)) X_test X_test.reshape((X_test.shape[0], X_test.shape[1], 1))fit_transform是在全部数据上计算的这在实际项目中讨论很多。严格来说应该先用训练集fit再用同一 scaler 去transform测试集。如果数据分布稳定先全局归一化问题不大但为了毕设答辩时不被人追问建议改成scaler.fit(X_train_raw)再分别 transform。3. 基于 Keras 的 LSTM 模型构建与训练参数调整3.1 单层 LSTM 还是堆叠 LSTM气温预测是一维单变量时间序列数据量通常只有几千天没有必要堆叠多层 LSTM。项目采用的是单层 LSTM Dense 输出层这也是此类小规模时序任务最稳妥的结构。层数越多参数量越大越容易过拟合训练时间也越长。对于课程设计单层 LSTM 把超参数调明白比强行加层更有说服力。model.py中的代码如下from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense, Dropout from tensorflow.keras.optimizers import Adam def build_model(lookback5, n_features1): model Sequential() model.add(LSTM(units64, input_shape(lookback, n_features), return_sequencesFalse)) model.add(Dropout(0.2)) model.add(Dense(units32, activationrelu)) model.add(Dense(units1)) model.compile(optimizerAdam(learning_rate0.001), lossmean_squared_error, metrics[mae]) return model关键参数在input_shape(lookback, n_features)这里lookback5是时间步长n_features1是特征维度。return_sequencesFalse表示只返回最后一个时间步的输出因为我们要预测的是未来一个点而不是输出一个序列。Dropout 层防止过拟合在训练数据只有几千条时尤其重要。如果发现训练损失下降慢可以把learning_rate从 0.001 提高到 0.005 试试。3.2 训练流程与早停机制训练时不能只看 loss 曲线还要关注验证集误差。常见做法是预留 20% 数据做验证并配合EarlyStopping在验证损失不再下降时自动停止保存最佳权重。train.py中体现为from tensorflow.keras.callbacks import EarlyStopping, ModelCheckpoint early_stop EarlyStopping(monitorval_loss, patience15, restore_best_weightsTrue) checkpoint ModelCheckpoint(best_model.h5, monitorval_loss, save_best_onlyTrue) history model.fit(X_train, y_train, validation_split0.2, epochs100, batch_size32, callbacks[early_stop, checkpoint], verbose1)patience15表示连续 15 个 epoch 验证损失没有改善就停止。batch_size32对于这个数据量是合适的如果数据特别少可以降到 16。训练过程中可以打印每个 epoch 的loss和val_loss观察两者差距如果训练 loss 很低但 val_loss 很高说明过拟合需要增加 Dropout 比例或减少units。3.3 超参数配置表项目把常用参数集中在config.py方便实验对比。实际调参时可以参考这张表参数推荐值调整方向lookback 时间步长5本项目默认温度连续性短增大到 7/10 可捕获周周期LSTM units64数据量少用 32数据丰富用 128Dropout0.2过拟合时加到 0.3-0.5batch_size32小数据集用 16太大梯度不稳定learning_rate0.001不收敛时试 0.0005收敛太慢试 0.005epochs100早停生效后实际运行可能 40-60 就停其中lookback最值得实验。气温变化有昼夜和天气系统周期5 天只是一个直观选择。你可以改成 7 天、10 天对比测试集 MAE用实验数据说话这在答辩时是加分项。4. 预测结果可视化与误差评估4.1 反归一化还原温度模型输出的是归一化后的 0~1 区间数值画图前必须还原成真实摄氏温度。predict.py里有一段关键代码from tensorflow.keras.models import load_model model load_model(best_model.h5) pred_scaled model.predict(X_test) # 反归一化缩放器保存的是整个序列的 min/max pred scaler.inverse_transform(pred_scaled) y_test_origin scaler.inverse_transform(y_test.reshape(-1, 1))注意scaler.inverse_transform的输入形状必须是(样本数, 1)如果直接传一维数组会报错。这里y_test在构造时已经是二维但pred_scaled是二维的所以先 reshape 再反变换。保存权重时不保存 scaler所以最好在训练结束后把 scaler 对象用pickle或joblib存下来预测脚本才能正确还原。4.2 绘制预测值与真实值对比图可视化是项目中直接肉眼可评估的部分。demo.py使用 matplotlib 绘制两条曲线并计算评估指标import matplotlib.pyplot as plt from sklearn.metrics import mean_absolute_error, mean_squared_error import numpy as np mae mean_absolute_error(y_test_origin, pred) rmse np.sqrt(mean_squared_error(y_test_origin, pred)) print(fMAE: {mae:.2f} °C, RMSE: {rmse:.2f} °C) plt.figure(figsize(12, 5)) plt.plot(y_test_origin, labelTrue Temperature, linewidth1.5) plt.plot(pred, labelLSTM Predicted, linewidth1.5) plt.legend() plt.xlabel(Days) plt.ylabel(Temperature (°C)) plt.title(LSTM Temperature Prediction) plt.grid(alpha0.3) plt.show()MAE能直接反映平均误差几摄氏度RMSE对极端误差更敏感。如果 MAE 在 1~2°C 以内对于逐日最高温预测已经是不错的结果超过 3°C 就要检查是不是测试集里包含跨季数据模型没有捕捉到季节突变。可视化时不要只画测试集可以把训练集尾部连上测试集一起画直观看出预测曲线相对真实值的滞后程度。4.3 常见误差场景排查预测曲线如果整体比真实值滞后一天这通常不是模型问题而是时间序列预测的固有现象。因为模型总是基于过去 5 天做一步预测遇到气温骤升骤降时反应会慢半拍。排查顺序是先看训练集划分是否乱序再看归一化是否在整个序列上做。另一个高频问题是预测值几乎平直只有小幅波动这说明模型学到了温度均值但没学到波动细节对策是增大 LSTM units或者把 lookback 缩短到 3 天试试。5. 进阶技巧多步预测、模块级验证与踩坑记录5.1 从单步预测扩展到多步预测项目默认只预测下一天如果要做未来 3 天或 7 天预测有两种常见方案。递推法是把预测结果当作真实值继续输入模型循环预测多步直接法是把输出层改为多个神经元比如Dense(3)同时输出未来三天。递推法实现简单但误差会累积直接法对数据量要求更高。# 递推法示例预测未来 3 天 current_seq X_test[-1] # 形状 (5, 1) steps 3 out [] for _ in range(steps): p model.predict(current_seq.reshape(1, 5, 1), verbose0) out.append(p[0, 0]) current_seq np.append(current_seq[1:], p.reshape(1, 1), axis0)这段代码演示了如何把 LSTM 的输出重新拼回输入序列。注意append后要保证current_seq始终是(5, 1)形状。递推法的优点是模型不用重新训练缺点是第二步开始就用预测值代替真实值误差会逐层放大。对这个项目来说做 3 天递推可以接受做 7 天时预测曲线容易趋平。5.2 用前向验证检验模型稳定性课程设计答辩时老师常问“你的模型在没见过的年份上表现如何”。前向验证是不错的验证手段用 2011-2018 年训练2019 年测试再换 2011-2019 训练2020 年测试看指标是否稳定。这个项目的爬虫脚本支持按年份抓取所以做这个实验成本很低。# 伪代码按年份滑动验证 for test_year in [2019, 2020, 2021]: train_years range(2011, test_year) train_df fetch_and_merge(train_years) # 自定义合并函数 test_df fetch_weather(city_code, str(test_year)) # 重新训练并评估这种验证方式能暴露模型对极端年份的适应能力。如果某一年夏季特别热或冬季特别冷LSTM 的误差会比其他年份大这是正常现象但提前知道这个边界比答辩时被问住要好得多。5.3 实际运行中的典型坑项目跑通后有几个高频问题值得记录。第一weather_spider.py抓取时遇到p classtemspan-2℃/span/p这样的结构get_text()会返回带℃和换行的字符串需要replace(℃, ).strip()再转 float。第二Keras 版本差异旧项目用from keras.models import Sequential新环境可能报错统一改为from tensorflow.keras即可。第三如果predict.py加载模型时报Unknown metric: mae这是因为自定义指标未注册把compile里的metrics[mae]改成字符串mean_absolute_error能兼容更多版本。把这些经验写进项目说明里比堆叠功能更能体现工程能力。本文还有配套的精品资源点击获取