简介:这是一份面向高校学生与开发者的随机森林气温预测项目源码,适用于毕业设计、课程设计及机器学习入门实践。项目以Python实现,借助Scikit-learn构建随机森林模型,处理湿度、气压、风速等多变量与气温之间的复杂关系,覆盖数据预处理、特征选择、模型训练与评估等完整流程,帮助读者理解集成学习在时间序列预测中的应用。资源包共19个文件,约4.23MB,以py源码、csv数据集、xml配置、txt说明及zbak备份为主,另含dot决策树可视化文件与zip附赠资料,结构清晰便于按模块查阅。目前已有117人学习下载。通过研读源码与数据文件,读者可掌握随机森林建模思路、参数调整与模型验证方法,并熟悉从设计到测试的软件开发流程,适合作为学习研究用途的参考范例。
1. 从一份气温预测源码包说起:随机森林到底能跑出什么结果
很多同学做毕业设计时,选题定到"基于随机森林的气温预测",第一反应是去搜现成源码,结果下回来一堆跑不起来的压缩包。我手上这份temp-predict就是典型的一线课程设计产物:Python 写的随机森林回归模型,配套temps.csv和temps_extended.csv两份气象数据,外加readdata.py、treeparam.py、predict.py三个核心脚本,还有一个newsbayes.py是附赠的朴素贝叶斯分类示例。它解决的不是"从零教你机器学习",而是给你一套能直接跑通、能改参数、能写进论文实验章节的完整流程。
适合谁用?计算机、数据分析、人工智能方向做课程设计或毕业设计的同学,以及想快速验证随机森林在时序气象数据上表现的开发者。不适合指望它直接产出商用气象服务的人——数据规模、特征工程深度都撑不起生产环境。但作为学习随机森林回归、理解特征重要性、练习 sklearn 调参的载体,这份源码的完整度是够的。
2. 拆开压缩包先看什么:文件结构与数据字段的对应关系
2.1 目录里每个文件到底干什么用
拿到temp-predict这个包,别急着python predict.py。先花五分钟把文件角色理清楚,后面调参和排错会省很多事。根据目录结构和常见课程设计项目的组织方式,各文件职责大致如下:
| 文件/目录 | 作用 | 是否核心 |
|---|---|---|
readdata.py | 读取 CSV、做日期解析和特征列筛选 | 核心,先跑 |
treeparam.py | 随机森林参数配置与模型训练入口 | 核心 |
predict.py | 加载模型、输出预测结果与评估指标 | 核心 |
temps.csv | 基础气温数据,含日期、实际温度、预测温度、天气特征 | 核心数据 |
temps_extended.csv | 扩展版数据,字段更多,适合做特征对比实验 | 辅助 |
tree.dot | 决策树可视化输出文件,可用 Graphviz 渲染 | 辅助 |
newsbayes.py | 朴素贝叶斯新闻分类示例,与气温预测无关 | 附赠 |
stopwords.txt | 朴素贝叶斯用的停用词表 | 附赠 |
data.txt | 原始数据备份或说明性文本 | 参考 |
.idea/、*.iml | PyCharm 工程配置,不影响运行 | 可忽略 |
提示:
.idea和.iml是 IDE 自动生成的,换到 VSCode 或命令行运行时直接无视,不要因为找不到 PyCharm 就以为项目跑不了。
2.2 数据字段与特征工程的对应
temps.csv是这份源码的命脉。常见的气温预测数据集字段包括date、actual(实际温度)、forecast(预报温度)、temp_2(前两天温度)、temp_1(前一天温度)、average(历史平均温度)、friend(朋友预测,属于趣味特征)。随机森林要做的,就是把这些列拆成特征矩阵 X 和目标向量 y。
import pandas as pd # 读取气温数据,parse_dates 把 date 列直接转成 datetime 类型 features = pd.read_csv('temps.csv', parse_dates=['date']) # 构造年份、月份、星期几三个时间特征 features['year'] = features['date'].dt.year features['month'] = features['date'].dt.month features['day'] = features['date'].dt.dayofweek # 查看字段类型和缺失情况 print(features.dtypes) print(features.isnull().sum())这段代码的逻辑是:原始date列对树模型没有直接数值意义,必须拆成可比较的数值特征。dt.dayofweek返回 0 到 6,代表周一到周日,比直接保留字符串日期有用得多。参数上,parse_dates指定列名后 pandas 会自动解析,省去手动pd.to_datetime。如果数据里日期格式不统一,这一步会直接报错,所以先print(features.head())确认格式是稳妥做法。
2.3 特征与标签的拆分逻辑
# 目标列是 actual,其余数值列作为特征 labels = features['actual'] features = features.drop(['actual', 'date'], axis=1) # 独热编码处理天气类别列(如 week 列) features = pd.get_dummies(features) # 转成 numpy 数组,供 sklearn 使用 import numpy as np features = np.array(features) labels = np.array(labels) print('特征矩阵形状:', features.shape) print('标签向量形状:', labels.shape)这里的关键决策是pd.get_dummies。气温数据里常有week这种类别列(比如"星期一""星期二"),树模型不能直接吃字符串,独热编码把它变成 0/1 向量。参数drop_first默认 False,保留全部类别,对随机森林影响不大,因为树不依赖线性假设。features.shape打印出来应该是(样本数, 特征数),如果第二维是 0,说明drop时把特征全删了,检查列名拼写。
3. 随机森林回归的建模流程:从 train_test_split 到特征重要性
3.1 训练集测试集划分与模型初始化
数据理干净之后,进入建模环节。treeparam.py的核心逻辑就是配置随机森林回归器并拟合。常见做法是用train_test_split按 8:2 切分,random_state固定住保证每次结果可复现。
from sklearn.model_selection import train_test_split from sklearn.ensemble import RandomForestRegressor # 固定随机种子,保证实验可复现 X_train, X_test, y_train, y_test = train_test_split( features, labels, test_size=0.2, random_state=42 ) # 初始化随机森林回归器 rf = RandomForestRegressor( n_estimators=1000, # 树的数量 max_depth=None, # 不限制深度 min_samples_split=2, # 节点分裂最小样本数 min_samples_leaf=1, # 叶节点最小样本数 random_state=42, n_jobs=-1 # 用满所有 CPU 核心 ) rf.fit(X_train, y_train) print('训练集得分:', rf.score(X_train, y_train)) print('测试集得分:', rf.score(X_test, y_test))参数说明:n_estimators=1000是课程设计里常见的保守值,树越多方差越小,但训练时间线性增长。max_depth=None让树完全生长,气温数据样本量通常不大,过拟合风险可控。n_jobs=-1在四核机器上能把训练时间压到单核的四分之一左右。score返回的是 R²,越接近 1 越好,但如果训练集 0.98、测试集 0.6,说明过拟合了,得回头调max_depth或加min_samples_leaf。
3.2 预测与误差评估
# 在测试集上预测 predictions = rf.predict(X_test) # 计算绝对误差 errors = abs(predictions - y_test) print('平均绝对误差:', round(np.mean(errors), 2), '度') # 计算平均绝对百分比误差 mape = 100 * np.mean(errors / y_test) accuracy = 100 - mape print('预测准确率:', round(accuracy, 2), '%')abs(predictions - y_test)得到每个样本的绝对误差,取均值就是 MAE。MAPE 把误差归一化到百分比,更适合向答辩老师解释"模型准不准"。注意y_test里如果有 0 或接近 0 的温度值,MAPE 会爆炸,这时候改用 MAE 或 RMSE 更稳妥。我一般会同时打印三个指标,避免单一指标误导。
3.3 特征重要性排序与 tree.dot 可视化
随机森林自带特征重要性输出,这是它比单一决策树更适合写论文的地方——你能明确说"前一天温度贡献了 60% 的预测能力"。
# 获取特征重要性 importances = list(rf.feature_importances_) feature_list = list(features.columns) if hasattr(features, 'columns') else [f'f{i}' for i in range(features.shape[1])] # 排序输出 feature_importances = [(feature, round(importance, 4)) for feature, importance in zip(feature_list, importances)] feature_importances = sorted(feature_importances, key=lambda x: x[1], reverse=True) for pair in feature_importances[:10]: print('特征: {:20} 重要性: {}'.format(*pair))如果前面用了np.array(features),列名会丢失,所以更稳妥的做法是保留 DataFrame 到 fit 之前再转数组,或者单独存一份列名列表。tree.dot文件是export_graphviz的输出,装好 Graphviz 后执行dot -Tpng tree.dot -o tree.png就能得到单棵决策树的结构图,放论文里很直观。
4. 避坑与排查:这份源码跑不起来时先查这五处
4.1 现象:FileNotFoundError: temps.csv→ 原因:工作目录不对 → 解决:切到脚本所在目录
这是最高频的翻车点。PyCharm 默认工作目录是项目根,命令行运行python predict.py时工作目录是你当前终端所在路径。如果 CSV 和脚本不在同一层,就会报找不到文件。解决方式有两种:cd到temp-predict目录再运行,或者在代码里用os.path.dirname(__file__)拼绝对路径。
import os base_dir = os.path.dirname(os.path.abspath(__file__)) csv_path = os.path.join(base_dir, 'temps.csv') features = pd.read_csv(csv_path, parse_dates=['date'])4.2 现象:KeyError: 'actual'→ 原因:列名有空格或大小写不一致 → 解决:先打印列名
CSV 文件从不同系统导出时,列名可能带 BOM 头或首尾空格。features.columns打印出来如果看到'\ufeffactual'或'actual ',就得先清洗:features.columns = features.columns.str.strip()。别凭记忆写列名,先看再写。
4.3 现象:R² 为负数 → 原因:特征里混入了 ID 列或标签泄漏 → 解决:检查 drop 列表
如果features里不小心保留了actual的衍生列,或者把行号当特征,模型在测试集上会表现得比均值预测还差,R² 直接为负。检查features.columns里有没有明显不该出现的列,确认drop(['actual'])执行成功。
4.4 现象:ModuleNotFoundError: No module named 'sklearn'→ 原因:环境没装 scikit-learn → 解决:pip 安装并确认版本
pip install scikit-learn pandas numpy python -c "import sklearn; print(sklearn.__version__)"建议用 0.24 以上版本,老版本RandomForestRegressor的n_jobs参数行为有差异。如果用的是 Anaconda,conda install scikit-learn更省事。
4.5 现象:训练极慢或内存溢出 → 原因:n_estimators过大或数据未降维 → 解决:先小规模验证
n_estimators=1000在几千条数据上通常几十秒内完成,但如果temps_extended.csv有几万行且特征上百,内存会吃紧。先把n_estimators降到 100 跑通流程,确认无误再往上加。n_jobs=-1在部分 Windows 环境下反而变慢,改成n_jobs=1对比一下。
5. 把模型用出论文价值:参数调优与结果呈现的两个技巧
5.1 用 GridSearchCV 做一轮可写进论文的调参
课程设计如果只跑默认参数,答辩时容易被问"你为什么选 1000 棵树"。用GridSearchCV做一轮小网格搜索,把过程写进论文,说服力完全不同。
from sklearn.model_selection import GridSearchCV param_grid = { 'n_estimators': [100, 300, 500], 'max_depth': [None, 10, 20], 'min_samples_leaf': [1, 2, 4] } rf = RandomForestRegressor(random_state=42, n_jobs=-1) grid_search = GridSearchCV( estimator=rf, param_grid=param_grid, cv=5, # 5 折交叉验证 scoring='neg_mean_absolute_error', verbose=1 ) grid_search.fit(X_train, y_train) print('最优参数:', grid_search.best_params_) print('最优得分:', grid_search.best_score_)cv=5表示 5 折交叉验证,把训练集再切五份轮流验证,比单次train_test_split更稳。scoring用负 MAE 是因为 sklearn 的评分函数统一"越大越好",负号只是取反。verbose=1会打印进度,网格大的时候能看出卡在哪一组。跑完把best_params_和best_score_截图放论文,比空口说"调过参"扎实得多。
5.2 预测结果可视化:让误差曲线说话
光有数字不够,画一张"实际温度 vs 预测温度"的折线图,答辩时一眼就能看出模型在哪些区间偏差大。
import matplotlib.pyplot as plt plt.figure(figsize=(12, 6)) plt.plot(y_test.values, label='实际温度', alpha=0.8) plt.plot(predictions, label='预测温度', alpha=0.8) plt.xlabel('测试样本序号') plt.ylabel('温度') plt.title('随机森林气温预测结果对比') plt.legend() plt.tight_layout() plt.savefig('prediction_result.png', dpi=150) plt.show()alpha=0.8让两条线半透明,重叠部分能看出吻合程度。dpi=150保证截图放论文里不糊。如果两条线在高温段分叉明显,说明模型对极端值拟合不足,可以在论文"不足与改进"一节里写"未来可引入梯度提升树或增加历史同期特征"。
5.3 一个我踩过的坑:别在测试集上反复调参
刚做这类项目时,我习惯每次改完参数就rf.score(X_test, y_test)看一眼,调了十几轮后测试集得分很好看,但换一份新数据直接崩。血泪经验是:测试集只能用一次,调参阶段看交叉验证得分,最终模型定下来再跑测试集。从那以后我每次做课程设计都强制走一遍"训练集切分 → 交叉验证调参 → 测试集最终评估"的流程,再也没在答辩时被问倒过。
这份temp-predict源码的价值不在于模型多先进,而在于它把数据读取、特征构造、模型训练、结果评估这条链路完整摆出来了,改一改就能套到其他回归预测题目上。需要参考的同学可以按上面的步骤先跑通readdata.py,再逐步替换成自己的数据。希望帮到你。
本文还有配套的精品资源,点击获取