news 2026/10/10 1:15:05

机器学习天气预测作业全流程:数据清洗到随机森林调参实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
机器学习天气预测作业全流程:数据清洗到随机森林调参实战

简介:这是一个面向机器学习课程大作业的天气预测项目压缩包,适合需要完成类似选题的高校学生或想实践回归/时序预测的初学者。包体大小约603KB,文件清单暂未随页面提供,但根据项目定位,应包含数据集、Python代码、实验报告等材料。内容围绕完整建模流程:数据预处理(缺失值、异常值、归一化)、特征工程(滑动平均、滞后值)、常见模型(线性回归、决策树、随机森林、LSTM/GRU)的选择对比,以及训练集测试集划分、MSE/RMSE等评估指标和超参数调整方法,还延伸到模型解释与部署更新。已有7000余人学习下载,对系统掌握机器学习应用于气象预测、提升数据处理与模型构建能力具有直接参考价值。

1. 机器学习大作业-预测天气.zip:拿到手先别急,先把它当成一条需要跑通的流水线

第一次看到「机器学习大作业-预测天气.zip」这个名字,多数人以为是一个现成的答案包,解压就能交出作业。实际拆开你会发现,它更像一台需要你自己拧紧螺丝的小机器:里面有天气数据集、数据清洗脚本、模型训练代码,也可能还带一份写得含含糊糊的说明。你真正要做的事情不是「读代码」,而是顺着数据到预测结果的流程,把每一步在本地跑通,并让最后的评估指标说得过去。这篇文章就按我实际做这类作业的顺序,从拆包、洗数据、建基线模型,到调参、避坑、再把模型导出给下一个人复现,给你一条能照着走的路线。

2. 解压之后先看目录和数据:不要一上来就训练模型

2.1 用 tree 和文件列表搞清楚 zip 里装了什么

这类「机器学习大作业-预测天气.zip」的内部结构通常不复杂,常见的是data/、src/、model/三个目录,再加一个README。我拿到压缩包的第一件事是在终端里用一条 tree 命令看全貌,而不是急着用 IDE 打开。下面这条命令在 Linux 或 macOS 上可以直接用,Windows 上可以换成tar -tf或者直接用资源管理器。

tree -L 2 -h .

-L 2表示只展开两层目录,避免被模型文件刷屏;-h会显示文件大小,能帮你很快判断train.csv是不是一个空壳。如果tree没有安装,可以用find . -type f | sed 's#^./##' | sort看到相对路径。

在 macOS 和 Windows 上解压 zip 时遇到「failed to copy spatial iop zip」这类报错,一般不是压缩包本身坏了,而是解压目标目录权限不足或者磁盘满了。优先清理磁盘空间并换到用户目录重试,比换解压工具更有效。

看清楚结构之后,再读README。如果 README 只写了「直接用 run.py」,你就要自己去看run.py加载的是哪个 CSV、列名是什么。很多作业包里的 README 是抄来的,和实际代码对不上,这个过程等于在帮你自己复盘数据流。

2.2 数据清洗:缺失值、异常值和机器学习的噪声数据到底怎么处理

天气预测数据集里最典型的问题有三个:日期列是字符串、温度列有空值、湿度列偶尔出现离谱的 999。这三类分别对应解析、填充和剔除,处理秩序错了,后续模型全会被带偏。

import pandas as pd df = pd.read_csv("data/weather.csv", parse_dates=["date"]) df = df.dropna(subset=["temperature"]) # 预测目标列不能有缺失 df = df[(df["humidity"] >= 0) & (df["humidity"] <= 100)] # 剔除噪声数据 df["date"] = pd.to_datetime(df["date"], errors="coerce") df = df[df["date"].notna()] df = df.sort_values("date") print(df.isna().sum())

这里先把temperature里有缺失的行删掉,因为它是模型要学的目标,悬空多少会影响损失计算。humidity过滤到 0 到 100 之间,是天气传感器最常见的数据范围,超过这个区间的记录多半是设备错误,不是真实天气。

parse_dates参数把日期列转成datetime类型,后面做时间序列切分时可以直接按时间排序,不会出现字符串比较的玄学错误。噪声数据不一定要全删,但回归任务里一个 999 的湿度会把梯度方向带偏,所以这种明显越界的值,我一般会直接过滤掉,而不是做均值填充。

2.3 特征工程:把日期拆成星期和月份,比盲目堆历史窗口更稳

天气预测作业里很多同学喜欢把过去七天的温度全部做成特征,这个思路没错,但如果你的数据集只有几十条记录,特征维度会比样本量还高,过拟合风险直接拉满。更稳的第一步是先从日期里拆出周期信息,再配合一到两阶滞后特征。

df["month"] = df["date"].dt.month df["dayofweek"] = df["date"].dt.dayofweek df["is_weekend"] = (df["dayofweek"] >= 5).astype(int) for lag in [1, 2, 3]: df[f"temp_lag_{lag}"] = df["temperature"].shift(lag) df = df.dropna()

shift(lag)表示取前一天、前两天、前三天的温度作为特征,注意做完滞后之后前几行会出现 NaN,所以最后要dropna()。月份和星期是为了让模型能区分季节性和周内效应——同一个 20 度的气温,在 1 月和 7 月对明天的天气含义完全不同。

我在实际作业里会把temp_lag_1单独保留,temp_lag_2和temp_lag_3合并成一个「过去三天平均温度」,这样既保留了近期趋势,又不会让滞后特征之间高度共线。特征不是越多越好,尤其对线性模型来说,多了反而让系数解释变困难。

3. 跑通一条基线:线性回归告诉你「这个作业的下限在哪」

3.1 问题建模:把它当成回归任务,而不是分类

天气预测有两种常见建模方式:预测明天的具体温度(回归),或者预测明天是否下雨(分类)。这两者的评估方式完全不同,看作业标题没写明是哪种时,我会先打开标签列看一眼。如果标签是连续浮点数,就按回归走;如果是 0/1,就按分类走。

回归模型的第一选择永远是线性回归。它训练快、可解释性强,而且能帮你快速发现特征和标签之间是否存在线性关系。如果线性回归的 R2 都不到 0.3,说明你手里的特征确实预测不了目标,后面换随机森林也大概率白搭——这种情况要先回头检查数据量是否太少,或者特征和标签的时间错位。

from sklearn.linear_model import LinearRegression from sklearn.metrics import mean_absolute_error, r2_score feature_cols = ["month", "temp_lag_1", "temp_lag_2"] X = df[feature_cols] y = df["temperature"] model = LinearRegression() model.fit(X, y) y_pred = model.predict(X) print("MAE:", mean_absolute_error(y, y_pred)) print("R2:", r2_score(y, y_pred))

这是最简单的训练闭环,直接拿全量数据训练再在同样数据上评估,得到的指标会异常好看,但没用。你只能把它当作「模型能不能学进去」的探针,真正意义上的预测能力,得靠第 4 章里的时间序列切分来检验。

3.2 训练集和测试集不是 random_split,是时间顺序切分

天气数据是时间序列,不能像淘宝用户数据那样用train_test_split(test_size=0.2, random_state=42)随机切。随机切会发生在训练集里偷偷出现测试集日期的「特征泄漏」,模型记住的不再是天气规律,而是数据顺序本身。

常见的做法是前 80% 作为训练,后 20% 作为测试,而且切分前保证数据已经按日期升序排好。

split_idx = int(len(df) * 0.8) train = df.iloc[:split_idx] test = df.iloc[split_idx:] model.fit(train[feature_cols], train["temperature"]) y_pred = model.predict(test[feature_cols])

这段代码里iloc[:split_idx]取前 80% 行,iloc[split_idx:]取后 20%。注意这里不能用train_test_split,否则训练集里可能混进测试集时间的滞后特征。时间序列切分是这类天气预测作业里最容易偷懒也最容易翻车的地方,很多同学以为模型分数上不去是算法问题,实际是切分本身就错了。

3.3 评估指标的读法:MAE 和 R2 各说各话

mean_absolute_error是平均每个样本预测值和真实值差多少度,单位是「摄氏度」,这个最直观。R2 则看整体方差解释程度,0.5 表示模型解释了 50% 的变化。对天气预测来说,MAE 在 2 度以内就算不错,R2 过 0.6 已经能应付作业答辩。

我还习惯把预测结果打印成时间序列图或者一个连续 14 天的对照表,只盯着一个数值指标容易看不到模型在哪些时间段犯错。比如冷空气刚来的那天温度突然跳水,滞后特征完全来不及反应,这种错误在指标上看起来只有一个离群点,但在图上非常刺眼。

4. 换模型与做检测:为什么随机森林和滚动预测能帮你多拿十分

4.1 从线性回归到随机森林:非线性关系的选择理由

线性回归只能学到「特征每增加一个单位,预测值固定改变多少」。实际天气系统不是这样,比如湿度到 90% 后温度骤降,这个变化在 80% 时并不明显。随机森林能根据特征取值区间自动寻找分裂点,相当于给每个特征划出「分段函数」,对非线性关系的拟合天生更好。

from sklearn.ensemble import RandomForestRegressor rf = RandomForestRegressor( n_estimators=200, max_depth=6, min_samples_leaf=3, random_state=42 ) rf.fit(train[feature_cols], train["temperature"]) y_pred = rf.predict(test[feature_cols])

n_estimators是树的数量,200 在这个小规模数据集上足够;max_depth=6和min_samples_leaf=3用来防止过拟合,因为树一旦长得太深,会把训练集里个别噪声温度记录背下来。天气数据里经常有沙尘暴、寒潮这种极端事件,树模型忘得比较快,限制深度反而是福。

在作业里把随机森林和线性回归结果同时打印,就能跟老师解释「我为非线性关系做了一个额外尝试」,这种加分的代价几乎为零。

4.2 特征重要性:别再问哪个是黑匣子,输出排序就知道了

随机森林一个比线性回归更好的地方,是自带feature_importances_属性。它告诉你模型主要靠哪些特征做分裂、哪些特征投入产出比不高。调试时可以拿这个结果来把特征列表精简掉一大半。

importance = pd.DataFrame({ "feature": feature_cols, "importance": rf.feature_importances_ }).sort_values("importance", ascending=False) print(importance)

作业报告里放一张特征重要性排序,能直接说明你「为什么最终留下三列特征」。如果month排在最前,说明季节性规律在这个数据集里占主导;如果temp_lag_1排在最前,说明近期温度记忆更关键。注意这个重要性不是因果,只是一个模型内部的排序依据。

4.3 机器学习检测:滚动预测比一次性切分更真实

一次性切分只有一组训练集和一组测试集,换一次split_idx结果就变,模型的稳定性看不到。实际天气预测作业里,我更常用滚动预测:每次只预测下一天,然后把它追加到训练集,再预测再追加。这个过程模拟了真实上线时「今天只知道昨天」的状态。

import numpy as np history = list(train["temperature"]) for i in range(len(test)): m = LinearRegression() m.fit(feature_cols, history) # 简化示意,实际要用对应特征

想要完整跑通滚动预测,要把特征历史也一起拼进去,代码会变长。但在这里我只提两点:每次迭代的history要包含最近三天的特征值,LinearRegression.fit的输入必须是矩阵而不是列表。滚动预测的评估指标会比一次性切分差一些,因为它逼着模型在每一轮都只依赖过去的信息,这个「自然的差」反而更接近真实。

5. 机器学习大作业避坑:压缩包、路径、日期和特征泄漏的 4 条血泪经验

5.1 现象:解压报错failed to copy spatial iop zip

原因:磁盘空间不够,或者解压路径带中文导致权限异常。我遇到过明明显示还有 10GB 空间,解压到共享盘时报错,改到本地用户目录立刻成功。解决:先清理临时文件,把压缩包复制到~/Downloads下重新解压,再用unzip -o覆盖旧目录。

5.2 现象:代码一跑就报FileNotFoundError: temp_data.csv

原因:训练脚本里写的是相对路径data/temp_data.csv,但你从压缩包的src/目录启动程序,工作目录变成了src/,上一层data/就找不到了。解决:在脚本开头固定工作目录为项目根目录,不要依赖「当前在哪个文件夹」。

import os BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DATA_PATH = os.path.join(BASE_DIR, "..", "data", "weather.csv")

5.3 现象:随机森林训练集 R2 0.99,测试集 R2 只有 0.2

原因:特征里出现了「未来信息」,最常见的是前一天的温度直接参与预测当天的温度,或者切分时混用了train_test_split。解决:先检查特征列是否包含用shift(-1)或sort_index弄出来的前向窗口,再检查切分代码——要是没有按时间排序,赶紧回头用iloc重写。

5.4 现象:Windows 上读取 CSV 时中文列名乱码

原因:作业包里的 CSV 用 UTF-8 编码,Windows 默认记事本保存成 GBK,pandas 默认以 UTF-8 解析时就会乱。解决:读取时显式指定编码格式。

df = pd.read_csv("data/weather.csv", encoding="utf-8")

如果依然报错,改成encoding="gbk"再试一次。判断依据很简单:列名是中文乱码就用 GBK,英文但内容乱码就先试utf-8-sig。这个技巧在数据落地到 Excel 或再导出 CSV 时反复出现。

6. 把这次作业变成看得见的成果:网格搜索与模型导出

网格搜索虽好,但不要总想着调出「完美参数」。天气预测的样本量通常只有几千条,参数窗口太大只会让结果像抽奖。我会固定随机森林的两个上限,然后只对n_estimators和max_depth做一次GridSearchCV,用滚动预测的 MAE 来比较。

from sklearn.model_selection import GridSearchCV param_grid = { "n_estimators": [100, 200], "max_depth": [4, 6, 8], } gs = GridSearchCV(RandomForestRegressor(random_state=42), param_grid, cv=3) gs.fit(train[feature_cols], train["temperature"]) print(gs.best_params_)

这里的cv=3是内部交叉验证折数,不要和气象数据按时间切分混淆。网格搜索只能帮你缩小候选范围,真正可以对外说的评估指标,还是在测试集上的滚动预测结果。我见过很多同学把GridSearchCV的分数当成最终指标,这在数据有时间顺序的天气预测里是不严谨的。

模型调完之后,记得用joblib导出一个.pkl文件,方便写报告时让老师直接跑预测。同时把特征列顺序存成一个 JSON,下一个人加载模型时才知道predict该传哪个顺序的数组。

import joblib joblib.dump(rf, "model/weather_rf.pkl") feature_order = feature_cols

导出这个小文件的价值,往往在最终交付时被放大。它逼着你把自己的模型流程变成长长的「从 CSVD 到pkl再到预测结果」的完整闭环,也让后来的人省去重新摸索特征拼接的时间。最后想跟你分享一个小习惯:我每次做完这类天气预测作业,都会把随机森林的预测结果和真实温度画在同一张图里,保存成prediction_vs_actual.png。图的右下角如果出现一长串偏差,通常不是模型坏了,而是时间切分那一列又没对齐。每次翻到这个图,我就能快速定位是特征还是窗口的问题。希望这条思路能帮你少熬一次夜,也帮你在拿到下一个「预测 XX.zip」时,从第一步起就站在能跑通的那条路上。

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

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

2024电工杯B题微电网储能优化配置:建模主线与求解代码避坑指南

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

作者头像 李华
网站建设 2026/10/10 1:13:38

PCA9422+ATmega1280构建硬件级电源管理闭环系统

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

作者头像 李华
网站建设 2026/10/10 1:13:29

海湾消防主机图形显示器4.0:从报警定位到平面图联动的实战指南

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

作者头像 李华
网站建设 2026/10/10 1:12:36

PCA9422 PMIC与PIC18F46K20 MCU的电源管理实战解析

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

作者头像 李华
网站建设 2026/10/10 1:11:36

SpringBoot+Vue校友社交系统:从部署到二次开发全攻略

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

作者头像 李华