天气预测毕设Python源码包:爬虫、五模型与可视化全流程

发布时间:2026/10/10 11:46:53
天气预测毕设Python源码包:爬虫、五模型与可视化全流程
简介这是一套基于Python的天气预测与天气可视化毕业设计项目面向计算机相关专业学生尤其适合正在完成毕业设计、课程设计或期末大作业以及希望进行项目实战练习的学习者。项目已通过导师指导并获评99分代码完整可运行从全国天气数据爬取、历史数据清洗与探索到气温预测建模和可视化界面展示均有覆盖预测部分包含3层MLP、LSTM、随机森林、线性回归、决策树等多种模型并附带GUI正式版与最简版方便对照学习和测试。资源压缩包共38个文件约12.17MB以.py脚本、.ipynb分析笔记、.h5/.pkl/.joblib训练模型、.csv历史天气数据及.docx设计说明文档为主另含预测效果图和界面截图便于直观理解模型表现。目前已有99人学习下载适合直接运行调试还可配套完成设计报告与答辩演示整体完整度高。1. 天气预测毕设项目一份能跑通全流程的 Python 源码包做天气预测毕业设计最怕的不是模型不收敛而是答辩前几天才发现爬虫、数据、模型、界面四个环节总有一个跑不通。这个基于 Python 的天气预测和天气可视化项目把全国天气爬虫、临沧四年历史气温数据、五套预测模型、Tkinter 桌面程序和可视化图表打包成一整套源码顺着跑一遍就能覆盖“数据采集 → 特征工程 → 模型训练 → 结果展示”的完整链路。线性回归、决策树、随机森林、3 层 MLP、LSTM 五个模型都有独立脚本模型文件、标准化器、CSV 数据、文档手册一应俱全。适合正在做毕业设计、课程设计、期末大作业的同学也适合想系统练 Python 数据分析和深度学习的初学者——哪怕只挑其中一个模型跑通也能撑起一次完整答辩。2. 数据采集与清洗从全国天气爬虫到临沧四年历史数据2.1 爬虫层怎么搭城市编码字典与请求参数我一般会先看数据从哪来因为模型的预测上限由数据质量决定。全国天气信息爬取.py 依赖 all_county_dict.json 和 city_dict_1.json 两个字典文件前者按省维护县域编码后者维护常用城市编码本质都是城市名到天气网站编码的映射。常见做法是拿城市编码拼接 URL 请求天气页面解析出温度、天气现象后落成结构化记录。这个项目把爬虫独立成一个脚本方便你替换成自己城市的编码再跑一遍。import requests import json import time # city_dict_1.json: {城市名: 城市编码} with open(city_dict_1.json, r, encodingutf-8) as f: city_dict json.load(f) headers { User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36, Accept: text/html,application/xhtmlxml } def fetch_weather_by_city(city_name): code city_dict.get(city_name) if not code: raise ValueError(f字典中找不到城市编码: {city_name}) url fhttp://www.weather.com.cn/weather/{code}.shtml resp requests.get(url, headersheaders, timeout10) resp.encoding utf-8 return resp.text if __name__ __main__: html fetch_weather_by_city(临沧) print(html[:300])fetch_weather_by_city 先从字典里取城市编码拼出目标 URL再用 requests.get 带自定义请求头发起访问最后把响应内容按 utf-8 解码返回。先打印前 300 个字符是为了确认页面结构再决定下一步用哪种方式提取温度字段。headers 里的 User-Agent 必须模拟真实浏览器很多天气站点会拦截裸的 Python requeststimeout 设 10 秒是防止某个城市请求卡死拖住整个爬虫resp.encoding 手动指定 utf-8是因为部分站点返回的编码声明与内容不一致靠自动判断容易乱码。拿到 HTML 之后的解析常见做法是正则或 BeautifulSoup 提取温度、天气现象日常的解析逻辑就写在同一个脚本里。需要注意控制请求频率建议每次请求之间 sleep 0.5 到 1 秒别有一次性把几千个城市全打一遍的冲动——毕设选两三个城市做演示完全够用。2.2 临沧历史天气Lingcang202001-202312.csv 的字段与清洗临沧历史天气爬取.py 生成的数据文件 Lingcang202001-202312.csv跨度从 2020 年 1 月到 2023 年 12 月。这个范围选得比较聪明四年覆盖不同季节和年份的温差异常后续做训练集和验证集切分时有足够的连续序列供滞后特征和滚动窗口使用。文件字段不复杂核心就五列。字段示例类型用途date2023-07-15字符串/日期时间轴与特征提取high28℃字符串预测目标最高气温low19℃字符串次目标最低气温weather多云字符串天气现象可转为类别特征wind西南风2级字符串风的信息暂可不参与建模import pandas as pd df pd.read_csv(Lingcang202001-202312.csv, encodingutf-8) print(行列数:, df.shape) print(df.head()) # 统一列名把温度列的 ℃ 去掉并转成数值 df.columns [date, high, low, weather, wind] for col in [high, low]: df[col] pd.to_numeric(df[col].astype(str).str.replace(℃, ), errorscoerce) # 检查缺失值 print(df.isna().sum()) # 删除温度缺失的样本 df df.dropna(subset[high, low]) df.to_csv(Lingcang_clean.csv, indexFalse, encodingutf-8)读取后先打印行列数和前几行确认表头实际情况接着把列名改成 date、high、low、weather、wind方便后续所有代码统一调用。pd.to_numeric 配合 str.replace 去掉温度里的 ℃ 符号errorscoerce 会把无法转换的值变成 NaN最后 dropna 删除这些脏样本并另存一份干净数据。encodingutf-8 是读取时指定Windows 下如果源文件被 Excel 二次编辑过可能需要改成 encodinggbkerrorscoerce 非常实用——它不会因为某一行温度写了“-”或“暂无”就中断程序而是把异常值转成 NaN 留给 dropna 统一处理。数据清洗是毕设里最容易被老师追问的环节。我建议在文档说明里写清楚这三个决定表头为什么统一成英文、缺失值为什么直接删而不是填充、温度为什么转成数值。答辩时能说出这几个理由比堆十个模型更让老师认可。2.3 数据探索建模前先看分布和相关性数据探索.py 做的事情是建模前必走的流程看缺失、看分布、看相关性。很多人拿到 CSV 直接丢进模型结果训练集与测试集分界处预测值断崖最后才发现是数据里有重复日期或者某个月整段缺失。先用代码扫一遍能省下后面大量排查时间。import pandas as pd import matplotlib.pyplot as plt import seaborn as sns df pd.read_csv(Lingcang_clean.csv, encodingutf-8) df[date] pd.to_datetime(df[date]) df[month] df[date].dt.month df[year] df[date].dt.year # 1) 覆盖范围检查 print(时间跨度:, df[date].min(), -, df[date].max()) print(每月样本数:\n, df.groupby([year, month]).size().head()) # 2) 温度分布 plt.figure(figsize(12, 4)) plt.subplot(1, 2, 1) sns.histplot(df[high], bins30, kdeTrue) # 3) 月度箱线图 plt.subplot(1, 2, 2) sns.boxplot(xmonth, yhigh, datadf) plt.tight_layout() plt.savefig(explore_high.png, dpi150)先把 date 转成 datetime 类型提取 month 和 year 作为分组字段。打印时间跨度和每月样本数能一眼看出是否存在整月缺失直方图看最高气温是否近似正态分布箱线图看每个月的温度中位数和异常点哪个季节方差大一目了然。sns.histplot 的 kdeTrue 会在直方图上叠加核密度曲线方便对比分布形态bins30 对四年数据来说密度够细boxplot 的 xmonth 会把 1 到 12 月分别成组异常点用点状标出。这也就是后面特征工程里为什么把 month 单独作为一列的直观依据——月份的周期性在图上非常清楚。3. 五套温度预测模型从线性回归到 LSTM 的选型与实现3.1 特征工程把日期变成模型能学到的数字模型本身不认“2023年5月1日”这种写法第一步是把日期拆成数值特征。这个项目里的特征组合是年、月、日、星期、季节再加前一天最高温、前两天最高温和过去七天平均最高温。季节不是标准库自带的常见做法是用月份整除 3 映射。如果你熟悉 Python 入门阶段的 pandas 操作这些代码可以直接复用。import pandas as pd import numpy as np def build_features(df, targethigh): df df.copy() df[date] pd.to_datetime(df[date]) df[year] df[date].dt.year df[month] df[date].dt.month df[day] df[date].dt.day df[dayofweek] df[date].dt.dayofweek df[season] (df[month] % 12) // 3 # 滞后特征与滚动均值 df[high_lag1] df[target].shift(1) df[high_lag2] df[target].shift(2) df[high_rolling7] df[target].rolling(window7).mean() df[high_diff] df[target].diff() return df.dropna().reset_index(dropTrue) df pd.read_csv(Lingcang_clean.csv, encodingutf-8) df build_features(df) print(df[[date, year, month, season, high_lag1, high_rolling7]].head())shift 把前一天、前两天的温度拿过来作为今天预测的输入rolling 计算近 7 天滑动平均diff 计算温度相较前一天的差值。这三个序列特征对气温这种强惯性数据非常有效因为今天的温度往往接近昨天也接近近一周的平均水平。dropna 会删掉数据开头因 shift 和 rolling 产生的空值行。window7 表示滑动窗口为 7 天如果做月尺度预测可以改成 30天尺度预测 7 天更合适season 用 (month % 12) // 3 计算1 到 3 月是 04 到 6 月是 1正好对应冬春夏秋四个季节。特征工程必须在切分训练集之前完成但滞后特征不能跨训练/测试边界。我一般先整体构造特征再按时间顺序切分这样能避免数据泄露——如果用随机切分模型会偷看到未来的温度测试分数虚高答辩时一复现就露馅。3.2 sklearn 三件套线性回归、决策树与随机森林机器学习气温预测模型.py 里同时训练了线性回归、决策树和随机森林三个模型共用一个特征矩阵。这样做的目的是对比线性回归看基线水平决策树看非线性拟合能力随机森林看集成带来的提升。三个模型分别存成 pkl 文件models 目录下的 linear_regression_model.pkl、decision_tree_model.pkl、random_forest_model.pkl 就是训练产物。from sklearn.linear_model import LinearRegression from sklearn.tree import DecisionTreeRegressor from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error import joblib X df[[year, month, day, dayofweek, season, high_lag1, high_lag2, high_rolling7, high_diff]] y df[high] # 按时间顺序切分不随机打乱 split_idx int(len(df) * 0.8) X_train, X_test X.iloc[:split_idx], X.iloc[split_idx:] y_train, y_test y.iloc[:split_idx], y.iloc[split_idx:] models { linear_regression: LinearRegression(), decision_tree: DecisionTreeRegressor(max_depth8, random_state42), random_forest: RandomForestRegressor(n_estimators200, max_depth10, random_state42, n_jobs-1) } for name, model in models.items(): model.fit(X_train, y_train) pred model.predict(X_test) print(f{name}: MAE {mean_absolute_error(y_test, pred):.2f} ℃) joblib.dump(model, fmodels/{name}_model.pkl)这里刻意用 iloc 按行号切分而不是 train_test_split因为气温序列必须保持时间顺序随机切分会把未来数据混进训练集。三个模型依次训练、评估、序列化随机森林 n_jobs-1 让模型并行利用所有 CPU 核心训练速度明显加快。DecisionTreeRegressor 的 max_depth8 限制树深防止单棵树过拟合RandomForestRegressor 的 n_estimators200 表示 200 棵树max_depth10 做剪枝这两个参数在这个数据规模下够用random_state42 固定随机种子保证复现时结果一致。线性回归不需要额外参数它就是评估下限用的。3.3 3 层 MLP用 Keras 搭一个前馈网络3层MLP气温预测.py 对应 models 目录下的 MPL_temperature_prediction_model.h5——注意文件名里 MPL 是笔误实际是 MLP加载时按文件名读就行。MLP 在 sklearn 三件套基础上加了隐藏层能学特征之间的非线性组合。为什么是 3 层而不是 10 层因为特征只有 9 个数据不到 1400 条层数再多就会把噪声也背下来训练误差很低但验证误差反而升高。import numpy as np import joblib from tensorflow.keras.models import Sequential from tensorflow.keras.layers import Dense, Dropout from tensorflow.keras.callbacks import EarlyStopping from sklearn.preprocessing import StandardScaler X df[[year, month, day, dayofweek, season, high_lag1, high_lag2, high_rolling7, high_diff]].values y df[high].values scaler StandardScaler() X_scaled scaler.fit_transform(X) joblib.dump(scaler, models/mlp_scaler.joblib) split_idx int(len(df) * 0.8) X_train, X_test X_scaled[:split_idx], X_scaled[split_idx:] y_train, y_test y[:split_idx], y[split_idx:] model Sequential([ Dense(64, activationrelu, input_shape(X_train.shape[1],)), Dropout(0.2), Dense(32, activationrelu), Dense(1) ]) model.compile(optimizeradam, lossmse, metrics[mae]) early EarlyStopping(monitorval_loss, patience20, restore_best_weightsTrue) model.fit(X_train, y_train, epochs200, batch_size32, validation_split0.1, callbacks[early], verbose1) model.save(models/MPL_temperature_prediction_model.h5)MLP 与 sklearn 模型最大的差别是输入要标准化。这里用 StandardScaler 对全部特征做 z-score 归一化再把标准化器存成 mlp_scaler.joblib后面 GUI 加载模型做预测时新输入的日期特征也要用同一个 scaler 转换。网络结构是 64-32-1中间插了一个 Dropout 层防止过拟合。Dropout(0.2) 表示每轮训练随机丢弃 20% 的神经元测试时不丢弃这是抑制过拟合的常用手段EarlyStopping 的 patience20 指验证集损失连续 20 轮不下降就提前终止训练restore_best_weightsTrue 会把模型权重回滚到验证集最好的那一步epochs200 只是上限实际通常跑不到。3.4 LSTM把气温序列当时间序列来学LSTM 与前四个模型不一样它不吃“今天特征预测今天温度”这种扁平结构而是吃“前 7 天的温度序列预测第 8 天温度”这种时间窗口。LSTM气温预测.py 里的核心工作是把一维温度序列重排成 (样本数, 时间步数, 特征数) 的三维数组。这个 reshape 是新手最容易出错的地方直接把一维数组喂进去Loss 会一直不下降。import numpy as np from tensorflow.keras.models import Sequential from tensorflow.keras.layers import LSTM, Dense def create_sequences(data, look_back7): X, y [], [] for i in range(len(data) - look_back): X.append(data[i:i look_back]) y.append(data[i look_back]) return np.array(X), np.array(y) values df[high].values.astype(float) look_back 7 X, y create_sequences(values, look_back) split_idx int(len(X) * 0.8) X_train, X_test X[:split_idx], X[split_idx:] y_train, y_test y[:split_idx], y[split_idx:] # 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)) model Sequential([ LSTM(64, activationtanh, input_shape(look_back, 1)), Dense(32, activationrelu), Dense(1) ]) model.compile(optimizeradam, lossmse, metrics[mae]) model.fit(X_train, y_train, epochs100, batch_size32, validation_split0.1, verbose1) model.save(models/LSTM_temperature_prediction_model.h5)create_sequences 用一个滑动窗口把连续温度切成长度为 look_back 的输入块窗口每向右移动一格产生一个样本。reshape 时必须用 X_train.shape[0] 和 X_train.shape[1] 动态获取维度硬编码样本数会在数据长度变化时报错——这是我实际踩过的坑改数据集规模后第一件事就是这里。look_back7 是时间窗口代表用前一周的记忆预测明天LSTM(64) 的 64 是隐藏单元数越大记忆容量越大但越容易过拟合activationtanh 是 LSTM 的标准激活函数不要改成 relu否则梯度不稳定input_shape(look_back, 1) 里的 1 是特征数这里只用最高温单变量做序列预测。4. 可视化与 GUI把预测结果变成能演示的桌面程序4.1 气温可视化.ipynb先用图把四年趋势讲清楚毕业设计里的可视化不只是加分项是必交项。气温可视化.ipynb 用 matplotlib 画的图核心目的不是好看而是让老师 30 秒内看懂数据特征。最常见的三张图是四年最高/最低温叠加折线图、月均气温柱状图、每年温度分布箱线图。这些图做好之后可以直接放进论文和答辩 PPT。import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(Lingcang_clean.csv, encodingutf-8) df[date] pd.to_datetime(df[date]) plt.figure(figsize(14, 5)) plt.plot(df[date], df[high], label最高气温, linewidth0.7, alpha0.8) plt.plot(df[date], df[low], label最低气温, linewidth0.7, alpha0.8) plt.xlabel(日期) plt.ylabel(温度 (℃)) plt.title(临沧 2020-2023 日最高/最低气温变化) plt.legend() plt.grid(alpha0.3) plt.savefig(temp_trend.png, dpi150) plt.show() # 月度平均 df[month] df[date].dt.month monthly df.groupby(month)[[high, low]].mean() monthly.plot(kindbar, figsize(10, 4)) plt.title(各月平均气温) plt.savefig(monthly_mean.png, dpi150)先画全量折线图四年日数据直接用 linewidth0.7 压低线宽避免线条糊成一团然后按月分组算平均气温画柱状图展示季节规律。这两张图已经足够支撑 PPT 里的数据介绍页后面模型的预测对比图可以另画。alpha0.8 控制透明度多条线叠在一起时防止互相遮挡dpi150 是导出分辨率论文插图 150 够用打印的话建议 300groupby(month) 把 1 到 12 月聚合后取均值每个月都有足够样本不会出现某个月只有几天数据导致的抖动。4.2 GUI 最简版与正式版从能跑到能答辩GUI最简版.py 和 GUI正式版.py 都在根目录两者差别很明显。最简版只有一个输入框和一个按钮输入日期调用预训练模型输出温度用来验证整套预测链路是否畅通正式版加了模型下拉选择、日期起止范围、结果表格、预测折线图等于把前面所有模块串成一个可交互的演示系统。功能点GUI 最简版GUI 正式版输入方式单个日期框起始日期 天数模型选择固定模型下拉选择五套模型结果展示文本框数字表格 折线图依赖单模型单脚本全模型池 scaler答辩效果能跑通链路可演示可截图import tkinter as tk from tkinter import ttk, messagebox import joblib import numpy as np class WeatherApp: def __init__(self, root): self.root root root.title(临沧气温预测系统) # 模型清单显示名 - (模型路径, scaler路径) self.model_pool { 随机森林: (models/random_forest_model.pkl, None), 线性回归: (models/linear_regression_model.pkl, None), 决策树: (models/decision_tree_model.pkl, None), 3层MLP: (models/MPL_temperature_prediction_model.h5, models/mlp_scaler.joblib), LSTM: (models/LSTM_temperature_prediction_model.h5, models/lstm_scaler.joblib), } self.loaded_models {} self.create_widgets() def predict_one_day(self, model, scaler, date_str): # 把日期字符串转成特征向量 ...GUI 的核心是把模型路径和 scaler 路径做成字典池按用户选择动态加载。模型首次选定后缓存在 self.loaded_models 里避免每次预测都重新读一次几个小时训练好的网络。predict_one_day 内部会复用 build_features 的逻辑将日期字符串转成特征数组后再预测。model_pool 字典的键是下拉框显示名值为模型文件和标准化器路径的元组None 表示该模型不需要标准化。这个设计让新增模型时只需要改字典不用动界面逻辑。正式版还有一个细节预测过程中要禁用按钮避免用户重复点击导致多个预测线程叠加。4.3 模型加载与预测的串通scaler 必须和模型成对使用这里有个容易被忽略的细节MLP 和 LSTM 模型的输入必须经过对应的 scaler而 sklearn 模型不需要。因为神经网络对输入特征尺度敏感训练时用了标准化特征预测时就必须用同一个 scaler 转换随机森林和决策树是树模型对尺度不敏感训练时没做标准化预测时也不能做。混用的话MLP 的预测值会偏得离谱而且不会报错——纯靠肉眼从图上才能看出方向对、数值全乱。def preprocess_input(scaler, feature_array): if scaler is None: return np.array([feature_array]) scaled scaler.transform([feature_array]) return scaled # 预测单个日期 # 顺序: 年、月、日、星期、季节、lag1、lag2、rolling7、diff features [2023, 7, 15, 5, 1, 28.5, 29.1, 27.8, 0.4] X preprocess_input(scaler, features) temp model.predict(X)[0, 0]preprocess_input 先判断 scaler 是否为 None是就直接把特征数组包装成二维矩阵交给树模型不是则调用 scaler.transform 做标准化。注意这里必须用 transform 而不是 fit_transform因为 scaler 已经用训练集拟合过了再 fit 会覆盖均值和方差。返回结果 model.predict 后取 [0, 0]是因为 Keras 输出是二维张量。feature_array 的顺序必须和训练时完全一致否则输入维度对不上或数值语义错位。MLP 的 scaler 是 mlp_scaler.joblibLSTM 用的是 lstm_scaler.joblib两个网络训练时使用的特征组合不同不能混用。5. 避坑指南天气预测毕设最容易翻车的五个地方这些坑不是凭空想象是项目代码结构里能反推出来的典型事故。每条按“现象 → 原因 → 解决”拆开写方便踩到的时候直接定位。5.1 现象LSTM 预测出来几乎是一条直线整个项目里最阴间的坑模型训练 loss 降了但预测曲线几乎不波动像一条被压平的直线。我见过好几个同学卡在这里甚至开始怀疑数据有问题。原因通常是序列构造写错了比如 create_sequences 的窗口内数据顺序没保持模型学到的是“恒等于最近值”的退化解另一种常见原因是测试集混入了训练样本模型在分界处输出突变。解决方法是把 create_sequences 生成的前几个样本打印出来人工检查确认 X[i] 是 X[i1] 的过去而不是未来切分数据用时间顺序不要用 train_test_split如果 train loss 降得很低但 test loss 纹丝不动优先查窗口边界是不是跨了 train/test。5.2 现象全国天气爬虫跑到一半报 403 或返回空白页爬虫跑到第几十个城市突然 403或者返回的 HTML 里一片空白这是反爬机制在起作用。requests 默认的 User-Agent 会被识别成脚本也有人一秒钟请求几十个城市IP 被临时封禁还有站点要求请求头带上 Referer。解决方法是把 headers 补全至少包含 User-Agent 和 Accept第一次先不加 Cookie两次请求之间加 time.sleep(1)如果单城市能跑通但批量跑全挂把并发降到串行再不行就换数据源直接下载历史 CSV 而不是实时爬。这个脚本本来就是教学演示用的重点在链路完整不在爬取量。5.3 现象加载 h5 模型报 Unknown layer 或无法导入加载 MPL_temperature_prediction_model.h5 或 LSTM_temperature_prediction_model.h5 时Keras 报错 Unknown layer 或者找不到自定义类大概率是训练方和加载方的 TensorFlow 版本不一致。老版本保存的模型在 2.x 下会因层名解析失败而崩溃我遇到过 2.4 和 2.10 之间互导模型直接报错的情况。解决方法是先对照 requirements.txt 统一版本再尝试加载实在不匹配就用 load_model 时传入 custom_objects但更稳的方案是改训练脚本用 model.save_weights 和 model.load_weights只存权重配合模型结构代码加载彻底绕开版本兼容问题。5.4 现象GUI 点击预测后窗口无响应或者直接白屏Tkinter 是单线程 UI 框架把 model.predict 这种 CPU 密集型操作放在主线程里界面会一直等待模型计算看起来就像卡死。实际不是死机是主线程被预测计算占住了窗口重绘事件排队等不到执行。解决方法是把预测放到 threading.Thread 里执行预测完成后再用 queue 把结果送回主线程更新界面或者更简单的预测前先禁用预测按钮并把状态文字改成“预测中”至少让用户知道程序在干活而不是没响应。正式版 GUI 里我一般直接上线程方案最简版可以用按钮禁用方案。5.5 现象models 目录里写的是 MPL 而不是 MLP加载路径对不上models 目录下的 MPL_temperature_prediction_model.h5这个名字是源码包里的原始笔误实际对应的是多层感知机 MLP。如果你按习惯把路径改成 MLP_temperature_prediction_model.h5代码里找不到文件直接报 FileNotFoundError。解决方法是加载路径直接用 models/MPL_temperature_prediction_model.h5不要手痒改名如果坚持要改models 目录、GUI 的 model_pool、3层MLP气温预测.py 三个地方必须同步改漏一个都会崩。这个小坑特别适合写进答辩的“项目说明”里反而能体现你对代码细节熟悉。6. 用 key_predictions 做多步预测回测与未来七天怎么落地key_predictions.py 是整个项目里容易被忽略但很实用的小工具。它的作用是用训练好的模型预测未来 N 天的最高气温并把预测结果按日期打印成表格。前几章的模型都是单步预测也就是用过去 7 天预测明天而 key_predictions 把单步预测拼成多步预测——预测出明天的温度后把它当成新的历史值再去预测后天滚动向前。import joblib import pandas as pd import numpy as np model joblib.load(models/random_forest_model.pkl) df pd.read_csv(Lingcang_clean.csv, encodingutf-8) last_date pd.to_datetime(df[date].iloc[-1]) last_high df[high].iloc[-1] history df[high].iloc[-7:].tolist() predictions [] for i in range(1, 8): future_date last_date pd.Timedelta(daysi) # 用历史窗口构造特征 features [future_date.year, future_date.month, future_date.day, future_date.dayofweek, (future_date.month % 12) // 3, history[-1], history[-2], np.mean(history[-7:]), history[-1] - history[-2]] pred model.predict([features])[0] predictions.append(pred) history.append(pred) # 滚动更新 print(f{future_date.date()}: {pred:.1f} ℃)这个滚动预测的逻辑与 LSTM 的滑窗思想一致只是换成了 sklearn 模型。每次循环打印一个日期和预测温度同时把预测值追加进 history作为下一次预测的输入。注意 features 的顺序与 3.2 中 X 的列顺序保持一致这是最容易漏的地方。验证方法很直接把数据集最后 30 天当作测试集模型只使用 30 天之前的数据训练然后逐日预测这 30 天的最高温与真实值对比算 MAE。如果 MAE 能控制在 1.5℃ 以内这个模型就有实际参考意义。我之前用随机森林在这个数据上回测效果优于线性回归但比 LSTM 的稳定性稍差——所以正式演示时我会同时展示三个模型的回测误差而不是只挑表现最好的那个。从那以后我每次做毕设项目都强制走一遍回测流程不管用什么模型先把最后一段数据切出去当验证集预测完画一张真实值和预测值的对比图。这个动作看起来简单但在答辩时能让你快速回答“你的模型预测准不准”比描述 loss 曲线有说服力得多。希望帮到你。本文还有配套的精品资源点击获取