简介:这份资源面向刚接触机器学习分类任务的Python学习者与数据挖掘入门者,提供一套可直接运行的随机森林算法示例代码,帮助理解sklearn中RandomForestClassifier的完整调用流程。压缩包共2个文件,包含1个py脚本和1个csv数据文件,整体约974B,体量轻巧,便于快速下载与本地调试。数据文件每行由四个特征和一个二分类结果组成,脚本负责读取数据、切分训练集与测试集,并调用随机森林分类器完成训练与测试集验证,覆盖从数据加载到模型评估的核心环节。目前已有1485人学习,说明该示例在入门阶段具有较高的参考价值。读者可借此掌握特征与标签的分离方式、训练测试集划分策略以及分类器基本参数设置,并在此基础上替换自有数据、调整树数量与深度等参数,观察模型表现变化,为后续学习集成学习与调参优化打下基础。
1. 从一份 data.csv 说起:这套 sklearn 随机森林代码到底能跑出什么
很多人第一次接触随机森林,是在教程里看到一段被切得七零八落的代码,跑完只得到一个干巴巴的准确率数字,既不知道数据长什么样,也不知道参数改了会怎样。这份资源走的是另一条路:它把data.csv、main.py和随机森林分类器打包在一起,数据文件里每行是四个特征加一个二分类标签,脚本负责读取、切分训练测试集、用RandomForestClassifier训练并在测试集上验证。换句话说,它给的是一个能从头跑到尾的最小闭环,而不是一段需要你自己补数据、补评估的碎片。
它适合两类人:一类是刚装完 sklearn、想找一个结构完整又不臃肿的示例来打通「读数据—切分—训练—评估」全流程的新手;另一类是手头有类似四特征二分类表格数据、想拿一份干净模板改吧改吧就用的从业者。四个特征、一个二分类结果,这个规模刚好卡在「能讲清楚又不至于被数据清洗淹没」的位置上,随机森林在这种小体量表格数据上的表现也相对稳,不容易因为一两个异常点就崩掉。下面就从这份代码的结构开始,一层层拆到参数和坑。
2. 拆开 main.py:数据流、切分逻辑与 RandomForestClassifier 的调用姿势
2.1 先看清 data.csv 的字段约定
这份资源的核心约束在数据格式上:每行四个特征加一个二分类结果。常见做法是把前四列当特征、最后一列当标签,列名有没有都不影响pandas.read_csv读取,但如果表头是中文或带空格,后续按列名取值就容易翻车。我一般会先确认两件事:标签列是不是在最后一列,以及标签的取值是不是 0/1 或两个固定类别。随机森林分类器对标签的编码不挑,字符串标签它内部也能处理,但如果你后面要算 ROC 曲线或者做概率阈值调整,标签最好提前转成 0/1,省得中途再折腾。
import pandas as pd # 读取数据,假设无表头或表头规范 data = pd.read_csv("data.csv") # 查看前几行,确认特征列与标签列的位置 print(data.head()) print(data.shape) print(data.iloc[:, -1].value_counts()) # 看标签分布这段代码不参与建模,但它是排错的第一步。data.shape告诉你样本量和列数,正常应该是「行数 × 5」。value_counts()看标签是否均衡,如果某一类只占个位数,后面切分训练集时很可能某一折里全是另一类,准确率会虚高得离谱。参数上没什么可调的,重点在观察:列数对不对、标签是不是只有两类、有没有明显的缺失值(data.isnull().sum()可以顺手看一眼)。
2.2 训练测试集切分:random_state 和 stratify 这两个参数别省
切分看着简单,但它是后面所有评估数字可信度的地基。train_test_split默认按 75/25 切,random_state不设的话每次跑出来的训练集都不一样,你调完参数发现准确率涨了,可能只是这次切分运气好。更隐蔽的坑是类别不均衡时不做分层抽样,训练集和测试集的标签比例可能差很多。
from sklearn.model_selection import train_test_split X = data.iloc[:, :-1] # 前四列特征 y = data.iloc[:, -1] # 最后一列标签 X_train, X_test, y_train, y_test = train_test_split( X, y, test_size=0.25, # 测试集占 25% random_state=42, # 固定随机种子,保证可复现 stratify=y # 按标签比例分层抽样 )test_size在小数据集上可以放到 0.3,样本多了再往 0.2 收。random_state设成固定值是为了让「同一份代码、同一份数据」跑出同一个结果,这在排查问题时是后悔药级别的存在。stratify=y在二分类且样本量不大时强烈建议加上,它保证训练集和测试集里两类样本的比例跟原始数据一致,避免评估数字忽高忽低。如果这份数据的标签本身很均衡,stratify 加不加差别不大,但加上没有坏处。
2.3 RandomForestClassifier 的关键参数:n_estimators、max_depth 与 n_jobs
到了建模这一步,随机森林的默认参数其实已经能跑出一个不算难看的基线,但知道每个参数在动什么,才能在你自己的数据上不抓瞎。n_estimators是树的数量,树越多模型越稳但越慢,小数据上 100 到 300 之间通常够用;max_depth控制单棵树的最大深度,不设的话树会一直长到叶子纯净,小数据上极易过拟合;n_jobs设成 -1 让所有 CPU 核心参与训练,特征少的时候感知不明显,特征一多差距就出来了。
from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score, classification_report # 初始化分类器 clf = RandomForestClassifier( n_estimators=100, # 树的数量 max_depth=None, # 不限制深度,小数据可设 5~10 防过拟合 random_state=42, # 与切分保持一致的随机种子 n_jobs=-1 # 使用全部 CPU 核心 ) # 训练 clf.fit(X_train, y_train) # 预测与评估 y_pred = clf.predict(X_test) print("准确率:", accuracy_score(y_test, y_pred)) print(classification_report(y_test, y_pred))fit之后模型就存下来了,predict走的是多数投票逻辑:每棵树给出自己的预测,森林取票数最多的那一类。classification_report比单纯看准确率信息量大得多,它能告诉你每一类的精确率和召回率,二分类里如果某一类召回率特别低,说明模型把这类样本大量漏判了,这时候光看准确率会被骗。random_state在分类器里也要设,否则即使切分固定了,每棵树抽的特征子集和样本子集还是随机的,结果依然不可复现。参数调整的顺序我一般是先动n_estimators看收敛,再动max_depth压过拟合,最后才考虑max_features这类更细的旋钮。
3. 把代码跑起来:环境、依赖与一次完整的验证流程
3.1 环境准备:sklearn 装不上多半是这几个原因
这份代码依赖 pandas 和 scikit-learn,两个库的安装本身不复杂,但新手卡在环境上的比例相当高。常见做法是先用pip list确认当前 Python 环境里有没有这两个包,再决定装不装。如果你用的是 conda 环境,优先走conda install,pip 和 conda 混用有时候会把依赖关系搞乱。Python 版本上,scikit-learn 对 3.8 到 3.12 的支持都比较稳,太老的 3.6 会碰到部分依赖装不上的问题。
# 查看当前环境已安装的包 pip list | grep -i "scikit-learn\|pandas" # 安装或升级 pip install -U scikit-learn pandas # 如果下载慢,可以指定国内镜像源 pip install -U scikit-learn pandas -i https://pypi.tuna.tsinghua.edu.cn/simple装完之后别急着跑main.py,先在命令行里python -c "import sklearn; print(sklearn.__version__)"确认能正常导入。如果报ModuleNotFoundError,八成是 pip 装到了另一个 Python 解释器下,这时候用which python和which pip对一下路径,或者干脆用python -m pip install来保证装到当前解释器里。VS Code 或 PyCharm 里跑代码时,还要确认编辑器选的解释器跟你装包的那个是同一个,这个坑我见过太多次了。
3.2 跑通 main.py 并读懂输出
环境就绪后,把data.csv和main.py放在同一目录下,直接运行即可。脚本会依次完成读取、切分、训练、预测和评估,终端里会打印出准确率和分类报告。第一次跑建议把random_state固定住,这样你看到的数字跟别人看到的能对上,讨论问题时才有共同基准。
# 在脚本所在目录下运行 python main.py输出里最需要盯的是classification_report中每一类的recall和f1-score。如果两类样本量差不多,准确率在 0.8 以上通常说明特征和标签之间确实有信号;如果准确率在 0.5 附近晃,要么是特征跟标签本来就没关系,要么是切分或标签列取错了。我一般会顺手加一行print(clf.feature_importances_),看看四个特征里哪个贡献最大,如果某个特征重要性接近 0,可以考虑把它去掉再跑一遍对比,这也是随机森林比单棵决策树好用的地方——它自带特征重要性,不用额外做特征选择实验。
3.3 用交叉验证替代单次切分
单次训练测试切分的评估结果受随机性影响大,尤其是样本量不大的时候。想更稳地判断模型表现,常见做法是上交叉验证,把数据分成 K 折,轮流拿其中一折做验证,最后取平均。cross_val_score几行就能搞定,代价是训练时间变成 K 倍。
from sklearn.model_selection import cross_val_score # 5 折交叉验证,评估准确率 scores = cross_val_score(clf, X, y, cv=5, scoring="accuracy") print("各折准确率:", scores) print("平均准确率: %.4f" % scores.mean())cv=5是折数,样本量小可以设 3,样本多可以设 10。scoring除了 accuracy 还能换成 f1、roc_auc 等,二分类且类别不均衡时用 f1 或 roc_auc 比 accuracy 更能反映真实水平。交叉验证的结果如果各折之间波动很大,说明数据本身噪声大或者样本量不够,这时候与其继续调参,不如先回去看看数据质量。
4. 避坑与排查:这份代码最容易翻车的五个地方
4.1 标签列不在最后一列,特征里混进了标签
现象:准确率高得离谱,接近 1.0,但换一份数据就崩。原因:data.iloc[:, :-1]默认把最后一列当标签,如果原始数据里标签在中间或者第一列,特征矩阵里就混进了标签本身,模型等于开卷考试。解决:先print(data.columns)或print(data.head())确认列顺序,必要时显式指定特征列和标签列,比如X = data[["f1","f2","f3","f4"]]、y = data["label"],别偷懒用位置切片。
4.2 没设 random_state,结果每次都不一样
现象:同一份代码跑两次,准确率差了七八个百分点,调参调了个寂寞。原因:train_test_split和RandomForestClassifier都有随机性,不固定种子的话每次切分和每棵树的抽样都不同。解决:两处都加上random_state=42(数值随意,固定即可),这样在排查问题时至少能保证变量只有一个。等模型稳定了再考虑用交叉验证来看泛化能力,而不是靠单次随机结果下结论。
4.3 类别不均衡导致准确率虚高
现象:准确率 0.9,但少数类的召回率只有 0.2,模型基本只会预测多数类。原因:数据里两类样本比例悬殊,比如 9:1,模型全猜多数类也能拿 0.9 的准确率。解决:切分时加stratify=y,评估时看classification_report里的召回率和 f1,而不是只盯 accuracy。如果确实不均衡,可以在RandomForestClassifier里设class_weight="balanced",让模型对少数类更敏感。
4.4 特征量纲差异大却没做处理
现象:某个特征数值范围是 0 到 1,另一个是几千到几万,模型表现不如预期。原因:随机森林是基于树的分裂,理论上对量纲不敏感,因为分裂点只看排序,但量纲差异过大时,某些实现里的分裂点搜索效率会下降,极端情况下影响树的质量。解决:常见做法是做标准化或归一化,StandardScaler或MinMaxScaler都行,虽然随机森林不是必须,但做了没坏处,还能让特征重要性对比更直观。注意 scaler 只能在训练集上 fit,再 transform 测试集,否则会数据泄露。
4.5 把测试集拿去调参,评估数字失真
现象:调完参数后测试集准确率很高,上线后一塌糊涂。原因:反复用测试集评估并据此调参,等于让模型间接「看到」了测试集,测试集就不再是未见过的数据。解决:把数据切成训练、验证、测试三份,调参在验证集上做,测试集只在最后用一次。样本量小的时候用交叉验证代替固定验证集,但测试集始终留到最后。这个坑最隐蔽,因为代码跑起来完全正常,只有上线才会暴露。
5. 进阶玩法:特征重要性、概率输出与一份可复用的调参习惯
把基础流程跑通之后,这份代码还能往下挖两层。第一层是特征重要性,随机森林在训练完之后会给出每个特征对分类的贡献度,clf.feature_importances_返回一个跟特征数等长的数组,四个特征就是四个值,加起来为 1。我一般会把它跟特征名对应起来打印,如果某个特征重要性低于 0.05,会试着把它去掉再跑一遍交叉验证,看平均准确率有没有明显下降。没有下降就说明这个特征可以不要,模型更简单,推理也更快。这一步不需要额外装库,几行代码就能完成。
import numpy as np # 特征重要性 importances = clf.feature_importances_ for name, imp in zip(X.columns, importances): print(f"{name}: {imp:.4f}") # 按重要性排序 idx = np.argsort(importances)[::-1] print("特征排序:", [X.columns[i] for i in idx])第二层是概率输出。clf.predict只给类别,clf.predict_proba给的是每个类别的概率,二分类返回两列,第二列通常是正类的概率。有了概率就能自己设阈值,比如默认 0.5 以上判为正类,你可以调到 0.6 来提高精确率、牺牲召回率,具体往哪边偏取决于业务更怕漏判还是更怕误判。这个灵活性在只输出类别的模型上是没有的,也是随机森林在实际项目里比很多黑盒模型好用的原因之一。
# 输出正类概率 proba = clf.predict_proba(X_test)[:, 1] # 自定义阈值 threshold = 0.6 y_pred_custom = (proba >= threshold).astype(int) print("调整阈值后的准确率:", accuracy_score(y_test, y_pred_custom))调参这件事,我的习惯是先用默认参数跑一个基线,记下交叉验证的平均准确率和标准差,然后每次只动一个参数,动完再跑同样的交叉验证,对比平均值的提升是否超过标准差。如果提升幅度小于标准差,那这个参数改了等于没改,不值得保留。n_estimators从 100 加到 300 如果平均准确率只涨了 0.002,而标准差是 0.01,那就没必要为了这点提升多花三倍训练时间。这套习惯让我少做了很多无用功,也避免了对随机波动的过度解读。从那以后我每次拿到一份新的表格数据,都会先固定随机种子、跑一遍交叉验证基线,再决定往哪个方向调。希望这份代码和上面的拆解能帮你省下自己摸索的时间。
本文还有配套的精品资源,点击获取