简介:这是一套面向计算机相关专业学生与项目实战学习者的机器学习天气预测完整项目包,适用于期末大作业、毕业设计及课程实践场景,难度适中,已通过导师评审并获98分。资源共38个文件,压缩包约12.17MB,包含10个py脚本、4个ipynb笔记本、10张png图表及jpg截图,另有pkl、joblib、h5等模型文件与csv、json数据集,配套docx说明手册和txt依赖清单,覆盖从数据爬取、模型训练到可视化展示的完整链路。项目围绕气温预测展开,提供线性回归、决策树、随机森林、三层MLP及LSTM等多种算法实现,并附带GUI正式版与最简版界面,方便对比不同模型效果。目前已有115人学习下载,源码均经本地编译调试可运行,读者可据此快速复现实验、理解特征工程与模型评估流程,也可作为二次开发与论文写作的参考模板。
1. 从一份天气预测源码说起:机器学习到底在预测什么
很多同学做期末大作业时,第一反应是去搜「Python机器学习天气预测可视化源码全套」,拿到压缩包解压一看,目录里躺着data.csv、train.py、app.py和一份 Word 文档,却不知道从哪一行开始读。我当年也是这样,代码能跑通,但被老师追问「你的特征为什么选这几个」「模型评估为什么用这个指标」时直接卡壳。这篇笔记就按一线做项目的顺序,把这份典型大作业拆开讲清楚:数据从哪来、特征怎么造、模型怎么选、可视化怎么画、文档怎么写才经得起答辩。适合正在做机器学习期末大作业、想真正搞懂而不是只交差的人,也适合刚入门想找一个完整小项目练手的同学。
天气预测这个题目看着简单,其实是个标准的监督学习回归问题:用历史气象观测数据(温度、湿度、气压、风速、风向等)作为特征,去预测未来的温度或是否降雨。它不像图像分类那样需要 GPU,一台普通笔记本就能跑完,但麻雀虽小五脏俱全——数据清洗、特征工程、模型对比、超参调优、可视化、结果解释,一个都不少。这也是为什么它常年霸占「机器学习期末大作业」选题榜。下面我按「数据 → 特征 → 模型 → 可视化 → 避坑 → 进阶」的顺序,把每个环节的可复现细节讲透。
2. 数据获取与清洗:天气预测源码的第一道坎
2.1 数据从哪来,以及为什么不能直接用
常见的天气数据集来源有三类:一是公开气象数据平台导出的 CSV,字段通常是日期、最高温、最低温、天气状况、风向、风力;二是自己用爬虫抓的逐小时数据;三是老师直接发的data.csv。不管哪种,拿到手第一件事不是急着read_csv然后fit,而是先看数据长什么样。我一般会先跑一段探查代码,把行数、列名、缺失值、重复值、各列类型一次性看清楚。
import pandas as pd import numpy as np # 读取数据,注意编码,中文数据常见 gbk df = pd.read_csv("weather.csv", encoding="utf-8") # 基础探查:形状、列名、类型、缺失情况 print("数据形状:", df.shape) print("列名:", df.columns.tolist()) print("各列类型:\n", df.dtypes) print("缺失值统计:\n", df.isnull().sum()) print("重复行数:", df.duplicated().sum()) # 看前几行,确认字段含义 print(df.head())这段代码的逻辑是先建立对数据的整体认知,再决定清洗策略。shape告诉你样本量够不够,几百条和几万条对应的建模思路完全不同;isnull().sum()定位缺失集中在哪几列;duplicated()排查是否有重复录入。参数上,encoding一定要和文件实际编码一致,中文 CSV 用utf-8读出来乱码时换成gbk或gb18030,这是新手最常翻车的地方。
2.2 缺失值、异常值和日期字段的处理
天气数据里缺失值很常见,尤其是湿度、气压这类传感器字段。处理方式取决于缺失比例:缺失低于 5% 可以直接删行;5% 到 30% 之间用均值、中位数或前后值填充;超过 30% 的列建议直接放弃,硬填只会引入噪声。异常值方面,温度出现 -100℃ 或 200℃ 这种明显是录入错误,用分位数或物理常识范围过滤。
# 1. 日期字段转成 datetime,并拆出年、月、日、星期 df["date"] = pd.to_datetime(df["date"], errors="coerce") df["month"] = df["date"].dt.month df["day"] = df["date"].dt.day df["weekday"] = df["date"].dt.weekday # 2. 数值列缺失用中位数填充(比均值更抗异常值) num_cols = ["temp_max", "temp_min", "humidity", "pressure", "wind"] for col in num_cols: if col in df.columns: df[col] = df[col].fillna(df[col].median()) # 3. 用物理范围过滤异常温度 df = df[(df["temp_max"] > -50) & (df["temp_max"] < 60)] # 4. 删除日期解析失败的行 df = df.dropna(subset=["date"]).reset_index(drop=True) print("清洗后形状:", df.shape)逻辑说明:日期拆解是天气预测的关键特征工程,因为气温有强烈的季节性和周内规律,模型拿到「月份」和「星期」比拿到一个原始日期字符串有用得多。errors="coerce"让无法解析的日期变成 NaT 而不是直接报错,方便后续统一删除。中位数填充比均值稳健,遇到极端值不会被带偏。物理范围过滤是最后一道保险,把明显不可能的温度剔除。参数上,温度上下限按你所在地区调整,热带和寒带范围不一样。
提示:清洗完一定要把处理后的数据另存一份,比如
weather_clean.csv,后续建模都基于它,避免每次重复清洗,也方便复现。
3. 特征工程与模型选型:让天气预测真正跑起来
3.1 特征怎么造,比选什么模型更重要
很多人一上来就纠结用线性回归还是随机森林,其实在天气预测这种小数据集上,特征质量对结果的影响远大于模型选择。除了原始的温度、湿度、气压,我一般会补几类衍生特征:滞后特征(昨天、前天的温度)、滑动窗口统计(近 7 天平均温度)、季节编码(月份的正弦余弦变换)。滞后特征尤其重要,因为今天的气温和昨天强相关。
# 按日期排序,保证滞后特征方向正确 df = df.sort_values("date").reset_index(drop=True) # 滞后特征:前 1 天、前 2 天的最高温 df["temp_max_lag1"] = df["temp_max"].shift(1) df["temp_max_lag2"] = df["temp_max"].shift(2) # 滑动窗口:近 7 天最高温均值 df["temp_max_roll7"] = df["temp_max"].rolling(window=7).mean() # 月份周期编码,避免 12 月和 1 月被当成距离很远 df["month_sin"] = np.sin(2 * np.pi * df["month"] / 12) df["month_cos"] = np.cos(2 * np.pi * df["month"] / 12) # 滞后和滑动会产生 NaN,删掉开头几行 df = df.dropna().reset_index(drop=True) print("特征工程后形状:", df.shape)逻辑说明:shift(1)把前一天的值挪到当前行,构造出「用历史预测未来」的结构,这是时间序列类任务的核心。rolling(7).mean()平滑掉单日波动,反映趋势。月份的正弦余弦编码解决了一个经典问题——如果直接用 1 到 12 的数字,模型会认为 12 月和 1 月相差 11,实际上它们相邻,周期编码让首尾在特征空间里也接近。参数上,滞后阶数和窗口大小要试,一般从 1、2、7 开始,数据量大可以加到 14、30。
3.2 模型对比:从线性回归到集成模型
天气预测常用的模型有线性回归、决策树、随机森林、梯度提升(如 XGBoost、LightGBM)。我的建议是至少跑三个做对比,这样答辩时能说清楚为什么选最终那个。线性回归作为基线,随机森林作为主力,梯度提升作为冲刺。评估指标用 MAE(平均绝对误差)和 RMSE(均方根误差),MAE 直观,RMSE 对大误差更敏感。
from sklearn.model_selection import train_test_split from sklearn.linear_model import LinearRegression from sklearn.ensemble import RandomForestRegressor from sklearn.metrics import mean_absolute_error, mean_squared_error # 特征列和目标列 feature_cols = ["temp_min", "humidity", "pressure", "wind", "temp_max_lag1", "temp_max_lag2", "temp_max_roll7", "month_sin", "month_cos"] X = df[feature_cols] y = df["temp_max"] # 时间序列不能随机打乱,按顺序切分 split = int(len(df) * 0.8) X_train, X_test = X[:split], X[split:] y_train, y_test = y[:split], y[split:] models = { "线性回归": LinearRegression(), "随机森林": RandomForestRegressor(n_estimators=200, random_state=42) } for name, model in models.items(): model.fit(X_train, y_train) pred = model.predict(X_test) mae = mean_absolute_error(y_test, pred) rmse = np.sqrt(mean_squared_error(y_test, pred)) print(f"{name} -> MAE: {mae:.2f}, RMSE: {rmse:.2f}")逻辑说明:这里最关键的一点是不能随机打乱再切分。天气数据有时间顺序,随机切分会让「未来」的数据混进训练集,造成数据泄漏,测试分数虚高,答辩时被问到就露馅。按 80/20 顺序切分才符合真实预测场景。随机森林的n_estimators是树的数量,200 是常用起点,加到 500 通常还能小幅提升但更慢;random_state固定后结果可复现,写文档时务必固定。跑完对比,如果随机森林明显优于线性回归,说明特征和目标之间存在非线性关系,这个结论本身就是文档里值得写的一段。
注意:如果 MAE 大得离谱(比如十几度),先别怀疑模型,回头检查特征里有没有把目标列本身混进去,这是最常见的泄漏来源。
4. 可视化与文档:天气预测大作业的加分项
4.1 用 Matplotlib 和 Seaborn 画出能讲故事的图
可视化不是把数据随便画出来就行,而是要服务于「让老师一眼看懂你发现了什么」。我一般会准备四张图:温度时间序列折线图、真实值 vs 预测值对比图、特征重要性条形图、相关性热力图。前两张证明模型有效,后两张证明你理解数据。
import matplotlib.pyplot as plt import seaborn as sns plt.rcParams["font.sans-serif"] = ["SimHei"] # 中文显示 plt.rcParams["axes.unicode_minus"] = False # 图1:真实值 vs 预测值 plt.figure(figsize=(12, 5)) plt.plot(y_test.values, label="真实值", color="steelblue") plt.plot(pred, label="预测值", color="orange", alpha=0.8) plt.title("天气预测:真实值 vs 预测值") plt.xlabel("样本序号") plt.ylabel("最高温 (℃)") plt.legend() plt.tight_layout() plt.savefig("pred_vs_true.png", dpi=150) # 图2:随机森林特征重要性 rf = models["随机森林"] importance = pd.Series(rf.feature_importances_, index=feature_cols) importance.sort_values().plot(kind="barh", figsize=(8, 5), color="teal") plt.title("特征重要性排序") plt.tight_layout() plt.savefig("feature_importance.png", dpi=150)逻辑说明:中文显示是 Matplotlib 的老大难,font.sans-serif设成SimHei并关掉负号乱码,这两行几乎是国内做可视化的标配。dpi=150保证导出图片清晰,直接放进 Word 文档不糊。特征重要性图能帮你回答「哪个特征最重要」这个高频答辩问题,通常滞后特征和最低温会排前面。参数上,alpha控制透明度,两条线重叠时方便看清;figsize按文档排版调整,宽图适合时间序列。
4.2 文档说明怎么写才经得起追问
源码配套的文档说明,很多人写成流水账「第一步安装 Python,第二步运行脚本」,这种文档拿不到高分。好的文档应该包含:问题定义、数据来源与字段说明、特征工程理由、模型对比表格、评估结果、结论与不足。尤其是「不足」这一节,主动写出模型在极端天气下预测偏差大、数据量有限等局限,反而显得你思考深入。
| 文档章节 | 必须写清的内容 | 常见扣分点 |
|---|---|---|
| 问题定义 | 预测目标、输入输出 | 只写「预测天气」太笼统 |
| 数据说明 | 来源、字段含义、样本量 | 不写字段单位 |
| 特征工程 | 每个新特征的理由 | 只列代码不解释 |
| 模型对比 | 至少两个模型的指标表 | 只跑一个模型 |
| 结论与不足 | 发现 + 局限 | 只报喜不报忧 |
提示:文档里的指标数字要和代码跑出来的一致,答辩前重新跑一遍截图,避免改了代码忘了更新文档。
5. 天气预测项目避坑:五条血泪经验
5.1 数据泄漏:测试分数高得反常
现象:测试集 MAE 只有 0.5℃,好到不真实。原因:特征里混入了目标列本身,或者随机打乱切分让未来数据进了训练集。解决:检查feature_cols是否包含目标列,时间序列一律按顺序切分,切分前先排序。
5.2 中文乱码:图表和 CSV 双双翻车
现象:Matplotlib 图里中文变成方框,CSV 读出来是乱码。原因:字体未设置、文件编码不匹配。解决:绘图前设SimHei并关负号,读 CSV 时utf-8不行就换gbk,写文件时显式指定encoding="utf-8-sig"让 Excel 也能正常打开。
5.3 滞后特征方向错:模型学到未来
现象:模型表现异常好但实际预测一塌糊涂。原因:数据没按日期排序就做shift,滞后特征取到了错误方向的值。解决:做任何shift、rolling之前先sort_values("date"),并reset_index。
5.4 缺失值填成 0:温度凭空多出零度
现象:填充后数据分布出现异常尖峰。原因:对温度、湿度这类连续量用 0 填充,0 在物理上无意义。解决:连续量用中位数或前后值填充,只有计数类字段才考虑填 0。
5.5 环境依赖缺失:换台电脑就跑不起来
现象:自己电脑能跑,同学或老师电脑报ModuleNotFoundError。原因:没记录依赖版本。解决:用pip freeze > requirements.txt导出,文档里写清 Python 版本和关键库版本,比如 scikit-learn、pandas、matplotlib。
6. 进阶技巧:把天气预测做成可展示的小系统
如果基础版本已经跑通,想让大作业更出彩,可以往两个方向走。一是加一个简单的可视化界面,用 Streamlit 或 Gradio 把预测功能包成网页,输入今天的温湿度就能看到预测结果,答辩时现场演示效果拉满。二是做超参调优,用GridSearchCV或Optuna自动搜索随机森林的最优参数,把调优前后的指标对比写进文档。
from sklearn.model_selection import GridSearchCV # 随机森林超参搜索 param_grid = { "n_estimators": [100, 200, 300], "max_depth": [None, 10, 20], "min_samples_split": [2, 5] } grid = GridSearchCV( RandomForestRegressor(random_state=42), param_grid, cv=3, scoring="neg_mean_absolute_error", n_jobs=-1 ) grid.fit(X_train, y_train) print("最优参数:", grid.best_params_) print("最优 MAE:", -grid.best_score_)逻辑说明:cv=3表示三折交叉验证,时间序列严格来说应该用TimeSeriesSplit,但作为大作业用普通 K 折也能接受,写文档时说明即可。scoring用负 MAE 是因为 sklearn 的评分约定「越大越好」,所以取负。n_jobs=-1用满所有 CPU 核心加速搜索。参数网格别设太大,否则跑一晚上都出不来,先粗搜再细搜。
验证模型是否真的可用,我习惯留出最后 30 天做「盲测」:训练时完全不碰这段数据,最后预测一次,看误差是否和测试集接近。如果盲测误差突然变大,说明模型对近期数据不适应,可能需要加更多近期特征或缩短训练窗口。这个习惯是我被答辩老师追问「你怎么知道上线后准不准」之后养成的,从那以后每次做预测类项目都会留一段盲测数据。希望帮到你。
本文还有配套的精品资源,点击获取