简介一套完整的月亮数据预测项目代码与文档包面向机器学习初学者与数据科学从业者演示如何使用决策树和随机森林算法解决二分类预测问题覆盖从数据预处理、特征选择到模型训练与评估的完整流程。压缩包共13个文件包含Python源码、编译缓存、项目配置文件以及配套说明文档整体大小约1.23MB结构清晰便于对照学习。项目中两个Python脚本分别实现决策树与随机森林算法配合数据流水线脚本可直接运行并观察不同算法在月亮数据集上的表现。说明文档对两类算法的建模思路、参数含义及实验结果做了梳理适合在课程作业或项目实践中参考。目前已有874人学习下载值得对机器学习分类算法感兴趣的读者深入探究。1. 月亮数据预测决策树和随机森林哪个更适合这份二分类任务拿到这份「月亮数据预测(决策树和随机森林算法)」源码包时我第一反应是它戳中了机器学习入门最典型的那个场景用同一个数据集对比单棵决策树和一片随机森林的泛化能力。月亮数据是二维特征生成的非线性二分类样本形状像两弯交错的新月边界模糊、存在噪声用来研究过拟合和集成学习再合适不过。包里不仅有可直接运行的 Python 脚本还附带完整的课程设计文档适合刚学完分类算法想动手复现的初学者也适合需要快速搭一个算法对比 Demo 的从业者。读完这篇笔记你能搞明白代码里每一步在做什么、参数怎么调、哪些地方容易翻车以及如何把这个小项目扩展成自己的实验。2. 算法选型与数据形态为什么月亮数据集是检验过拟合的经典样本2.1 月亮数据的生成逻辑与二分类边界特点所谓「月亮数据」通常指sklearn.datasets.make_moons生成的数据集。它生成两组半月形样本点每组各属于一个类别二维特征X[:,0]和X[:,1]构成非线性可分边界。这个数据集在机器学习课程里经常被拿来当作「复杂边界 噪声」的玩具数据因为它简单到只用两个特征就能可视化但难到线性模型比如逻辑回归很难划出一条直线把两类完美分开。我一般会这样理解它数据点分布就像两弯背对背的月牙在月牙尖端附近两类样本离得很近同时生成时还加入了noise参数控制重叠程度。默认noise0.0时两类完全可分但实际使用时经常设置noise0.1~0.3让部分样本纠缠在一起。这模拟了真实数据里无法避免的噪声和特征重叠而决策树在这种数据上特别容易表现出「训练集完美、测试集拉胯」的过拟合现象。这份源码包里的moon.py应该就负责数据生成和基础建模。按常见做法代码会先调用make_moons生成样本然后切分训练测试集分别交给决策树和随机森林训练。重点在于月亮数据本身不是真实天文数据它只是检验分类器对非线性边界拟合能力的标准测试床理解了这一点后续看代码就不会被「月亮」两个字带偏。2.2 决策树和随机森林的原理差异从单棵树到集成投票决策树的核心是递归划分特征空间。每个内部节点根据某个特征和阈值把样本分成左右子树划分准则常见的有信息增益基于熵和基尼不纯度Gini。月亮数据只有两个特征决策树会尝试在任意特征维度上寻找切割点用一堆平行于坐标轴的线段去逼近弯月边界。如果树的深度不受限制它能把训练样本的每个点都包进去形成锯齿状的决策区域这就是过拟合的根源。随机森林则是「装袋 随机特征选择」的集成。它对训练样本做有放回抽样每棵树用一部分样本训练同时每次分裂时随机挑选部分特征作为候选分裂属性。月亮数据只有两个特征所以特征随机性主要体现在「每棵树可能只用其中一个特征」或「候选特征数设为1」。最终预测由所有树投票决定这样单棵树的过拟合倾向被平均掉整体方差降低泛化能力更强。在对比实验中决策树相当于一个「激进的学生」擅长记住细节随机森林像「委员会」通过群体决策消减个体偏差。但注意随机森林不是永远更好如果基学习器太弱或数据噪声过大集成也可能整体偏移。所以在运行这份代码时我建议同时打印训练集和测试集准确率别只看测试集分数。2.3 项目代码结构与关键文件职责解压这份资源后你会看到以下主要文件PyCharm 项目配置文件.idea等可以忽略不影响运行文件职责moon.py主脚本负责数据生成、训练、评估通常同时调用两个分类器decision_tree_classifier.py单独封装的决策树分类器类可能包含训练和预测方法random_forest_classifier.py封装随机森林分类器通常基于sklearn.ensemble月亮数据预测(决策树和随机森林算法).doc课程设计说明文档包含原理和实验结论lib目录可能存放第三方库的本地备份或自编工具模块从文件命名可以看出这份资源刻意把两个算法拆成独立模块方便你单独调试和扩展。moon.py像总控制器导入两个分类器并跑通流程。而.idea下的workspace.xml只是编辑器的窗口布局跟算法无关删掉也不影响运行。我实际阅读代码时会先看moon.py里的train_test_split是否固定了random_state。如果没固定每次运行结果都不一样那后面对比准确率就没有意义。好在这类作业代码通常都会设置random_state42这类固定种子这也是后续排错时最优先检查的地方。3. 搭建和运行从解压到拿到第一个预测结果3.1 环境依赖与版本建议这份源码基于 Python 和sklearn实现运行前需要准备numpy、pandas可能用到、scikit-learn、matplotlib可视化决策边界时。我用的是 Python 3.9 和 scikit-learn 1.2 的组合能直接跑通。如果你用的是较新版本比如 1.4需要注意sklearn.tree和sklearn.ensemble的 API 没有大改动基本兼容。建议在项目根目录创建一个虚拟环境避免依赖冲突。安装命令如下python -m venv moon_env source moon_env/bin/activate # Windows 下用 moon_env\Scripts\activate pip install scikit-learn matplotlib numpy pandas安装后可以在 Python 里验证版本import sklearn print(sklearn.__version__) # 确认版本号1.2 或以上均可逻辑说明虚拟环境把依赖隔离在单独目录防止其他项目的包干扰。pip install后面列出的包是运行这份代码的最小集合如果代码里用了graphviz可视化树还需要额外安装python-graphviz和系统级 Graphviz但核心预测不需要它。参数说明numpy和pandas虽然sklearn本身也依赖但显式安装能避免版本不匹配。3.2 运行 moon.py训练决策树并输出准确率假设moon.py的代码写成下面这种常见结构这份资源里大概率就是这样你可以直接在终端执行python moon.py如果一切正常你会看到类似输出决策树准确率: 0.86 随机森林准确率: 0.94但为了让运行过程更可控我建议你打开moon.py看一眼核心代码。典型的实现如下# moon.py 核心逻辑 from sklearn.datasets import make_moons from sklearn.model_selection import train_test_split from sklearn.tree import DecisionTreeClassifier from sklearn.ensemble import RandomForestClassifier from sklearn.metrics import accuracy_score # 生成月亮数据噪声设为0.2样本数1000 X, y make_moons(n_samples1000, noise0.2, random_state42) # 训练/测试 7:3 划分固定随机种子 X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.3, random_state42 ) # 决策树限制最大深度为5防止过拟合 dt DecisionTreeClassifier(max_depth5, random_state42) dt.fit(X_train, y_train) y_pred_dt dt.predict(X_test) print(决策树准确率:, accuracy_score(y_test, y_pred_dt)) # 随机森林100棵树每棵树最大深度5 rf RandomForestClassifier(n_estimators100, max_depth5, random_state42) rf.fit(X_train, y_train) y_pred_rf rf.predict(X_test) print(随机森林准确率:, accuracy_score(y_test, y_pred_rf))逻辑说明先make_moons生成数据再用train_test_split划分。决策树和随机森林都设了max_depth5这是为了公平对比——两边复杂度相同差异主要来自集成策略。最后用准确率衡量预测结果。参数说明n_samples1000表示样本量越大越好训练但耗时noise0.2控制类别重叠程度值越大越难分test_size0.3是 70% 训练、30% 测试max_depth5限制树深是抑制过拟合最直接的旋钮n_estimators100表示随机森林里树的棵数越大越稳定但计算越慢。3.3 运行 random_forest_classifier.py对比集成效果random_forest_classifier.py通常是单独封装的随机森林类要么继承sklearn的RandomForestClassifier要么自己实现一个简化版。如果它依赖moon.py里的数据你就需要先跑moon.py或者在命令行里导入该模块验证python -c from random_forest_classifier import RandomForestClassifierModel; print(RandomForestClassifierModel())这里要注意这个文件本身可能没有if __name__ __main__入口所以直接python random_forest_classifier.py可能不会打印任何东西。更稳妥的方式是在moon.py里调用它或者自己写一个临时脚本# 临时脚本 compare.py用于单独评估随机森林 import sys sys.path.append(.) from random_forest_classifier import RandomForestClassifierModel # 假设该类有 train 和 predict 方法 model RandomForestClassifierModel(n_estimators200, max_depth8) # 具体调用方式需要看文件里的方法名 print(model.params)逻辑说明通过sys.path.append把当前目录加入模块搜索路径然后导入该类。实际使用时你最好打开random_forest_classifier.py看看它的__init__方法接受哪些参数比如n_estimators、max_depth、criterion等再决定如何实例化。参数说明n_estimators200相比默认 100 会更稳定但训练时间翻倍max_depth8允许每棵树更深适合噪声更小的数据。月亮数据noise0.2时深度 5 和 8 的差异不会太大但如果噪声降到 0.1深树可能表现更好。3.4 参数说明max_depth、n_estimators 等如何调在对比两个算法时调参是必须的否则不公平。通常我会按下面顺序调整固定随机种子random_state42保证可复现。调整max_depth从 1 到 15 逐步增加观察训练集和测试集准确率。决策树深度超过一定值后训练准确率接近 1.0但测试准确率下降出现「剪刀差」就是过拟合开始。调整n_estimators随机森林从 10 到 200每次增加 10 或 50观察测试准确率是否收敛。通常 50 棵树左右就能稳定100 以上提升有限。调整min_samples_split控制节点继续分裂所需的最小样本数默认 2。调大它能让树更「笨」减少过拟合。月亮数据 1000 个样本设为 5 或 10 是合理的。调整criterion决策树用gini还是entropy。在小数据集上两者差异不大但entropy更吃计算量月亮数据只有两个特征怎么选全看习惯。我一般会写一个循环脚本批量跑不同深度# 测试不同 max_depth 下决策树和随机森林的表现 depths [1, 2, 3, 4, 5, 6, 8, 10, 15] for d in depths: dt DecisionTreeClassifier(max_depthd, random_state42) dt.fit(X_train, y_train) train_acc accuracy_score(y_train, dt.predict(X_train)) test_acc accuracy_score(y_test, dt.predict(X_test)) print(f深度{d:2d} 决策树 训练:{train_acc:.3f} 测试:{test_acc:.3f}) for d in depths: rf RandomForestClassifier(n_estimators100, max_depthd, random_state42) rf.fit(X_train, y_train) train_acc accuracy_score(y_train, rf.predict(X_train)) test_acc accuracy_score(y_test, rf.predict(X_test)) print(f深度{d:2d} 随机森林 训练:{train_acc:.3f} 测试:{test_acc:.3f})逻辑说明通过遍历不同深度可以直观看到曲线变化。决策树在深度增加时训练准确率快速上升而随机森林由于每棵树只在随机抽样的子集上训练训练准确率上升更平缓测试准确率也更稳。参数说明depths列表里的值是给max_depth用的你可以根据实际运行结果缩小范围。输出里的train_acc和test_acc会帮你判断模型是否过拟合两者差距大的时候就需要限制深度或增加正则项。4. 决策树实现细节划分准则与剪枝策略4.1 特征选择准则信息增益与基尼不纯度在这个项目中的取舍决策树在每个节点要选一个特征和阈值做划分。sklearn的DecisionTreeClassifier默认用gini不纯度公式是1 - sum(p_i^2)其中p_i是当前节点第i类样本占比。基尼不纯度越小说明节点越纯。另一种是entropy即信息熵公式是-sum(p_i * log(p_i))。两者在二分类问题上非常相似但基尼不纯度计算更快所以工程上默认用它。月亮数据只有两个特征X[:,0]和X[:,1]决策树每次分裂只能选择一个特征的一个值作为阈值相当于画一条垂直于坐标轴的直线。为了逼近弧形的分类边界树必须多次分裂形成阶梯状区域。你可以在代码里改criterionentropy感受一下准确率是否变化。根据我的经验对于这个数据集两者差异通常在 1% 以内所以不必纠结关键是控制深度。如果你想看每一层的划分过程可以用dt.tree_对象但不太直观。更好的办法是调用sklearn.tree.export_text打印树结构。4.2 剪枝参数max_depth、min_samples_split 对月亮数据的影响剪枝是决策树防过拟合的核心手段。max_depth直接限制树的最大深度比如设为 3树最多做 3 次分裂叶子最多 8 个。min_samples_split则要求节点至少有这么多样本才能继续分裂比如设为 10节点只有 8 个样本时就自动变成叶子。还有min_samples_leaf规定叶子最少样本数避免叶子太小而「记忆」单个异常点。在月亮数据上你会发现一个现象不剪枝时max_depthNone决策树在训练集上准确率接近 1.0但在测试集上可能只有 0.85 左右。随机森林不剪枝时也过拟合但程度轻很多。这是因为每棵树只用了约 63% 的样本有放回抽样而且最终决策是多数投票单棵树的偏差被稀释。你可以这样尝试把DecisionTreeClassifier的max_depth分别设为 2、3、5、10同时输出训练和测试准确率。深度 2 时测试准确率可能只有 0.83深度 5 时能到 0.90深度 10 时反而降到 0.88。这就是过拟合的典型曲线先升后降。4.3 可视化树结构用 graphviz 导出规则树是白盒模型能看到具体规则这是决策树最大的卖点。使用export_graphviz可以把树导出为图片或文本from sklearn.tree import export_graphviz, export_text # 文本形式适合快速查看 print(export_text(dt, feature_names[x1, x2])) # 图形形式需要 graphviz export_graphviz( dt, out_filemoon_tree.dot, feature_names[x1, x2], class_names[0, 1], filledTrue, roundedTrue )逻辑说明export_text直接打印规则文本比如x1 0.123分支。export_graphviz生成.dot文件之后可以用 Graphviz 的dot -Tpng moon_tree.dot -o moon_tree.png转成图片也可以直接在 Jupyter 里display。参数说明feature_names给特征命名便于读图class_names给类别起名filledTrue让节点根据类别上色更直观。对于随机森林你可以挑其中一棵树rf.estimators_[0]来做同样可视化但要记住它只代表整个森林的一票。5. 避坑月亮数据预测中常见的五个坑5.1 数据划分与随机种子相关坑坑一不固定 random_state每次运行准确率都不一样现象同一个moon.py连续跑两次决策树准确率一次 0.87一次 0.90随机森林也波动 2% 左右。原因train_test_split和make_moons内部都用了随机数没固定种子时训练集和测试集每次都不同模型自然不稳定。解决在make_moons、train_test_split、DecisionTreeClassifier、RandomForestClassifier里全部显式指定random_state42或者用同一个数值。这样至少保证同一环境下结果可复现。坑二数据划分时没有分层抽样导致训练集里某一类样本过少现象测试准确率比训练准确率高不少但看起来不自然。原因月亮数据的两个类别本身数量均衡但如果test_size很小且没分层随机划分可能让测试集里恰好都是容易分类的样本。解决在train_test_split里加上stratifyy保证划分前后类别比例一致。这个小动作对平衡数据可能提升不大但能消除一个额外变量。5.2 模型评估与过拟合相关坑坑三只看测试集准确率忽略了训练集与测试集的差距现象决策树训练准确率 1.0测试准确率 0.85你还在网上问为什么随机森林只比决策树高 3%。原因未剪枝的决策树记住了训练样本的噪声测试时泛化失败。解决打印两个准确率计算差值。差值大于 5% 就说明过拟合严重需要限制max_depth、增大min_samples_split或者直接改用随机森林。坑四随机森林的n_estimators设得过大或过小现象设n_estimators10时准确率忽高忽低设n_estimators1000时训练时间明显变长准确率却只提升 0.2%。原因树太少集成效果不稳定树太多边际收益递减。解决以 50 为步长从 50 试到 300找到准确率收敛点。月亮数据 1000 个样本100 棵通常足够。坑五误把feature_importances_当线性系数解释错结论现象打印rf.feature_importances_得到[0.73, 0.27]于是得出结论「第一个特征比第二个重要三倍」。原因对于非线性边界特征重要性只能说明分裂时带来的不纯度下降总量不能说明特征方向和影响符号。解决画决策边界图或部分依赖图看每个特征在不同取值下预测概率如何变化。不要对这个二维数据过度解读原始重要性分数。6. 进一步实验用交叉验证和特征重要性做模型体检跑通基础代码之后我建议你别停在准确率数字上而是做一轮交叉验证让「决策树 vs 随机森林」的结论更靠谱。交叉验证能缓解单次划分的运气成分把数据切成 5 份轮流用 4 份训练、1 份测试最终取平均准确率。代码很简单from sklearn.model_selection import cross_val_score # 用 5 折交叉验证比较两个模型 dt_scores cross_val_score(dt, X, y, cv5, scoringaccuracy) rf_scores cross_val_score(rf, X, y, cv5, scoringaccuracy) print(决策树交叉验证准确率: %.3f ± %.3f % (dt_scores.mean(), dt_scores.std())) print(随机森林交叉验证准确率: %.3f ± %.3f % (rf_scores.mean(), rf_scores.std()))逻辑说明cross_val_score会自己划分数据并重复训练 5 次返回 5 个准确率。平均值代表模型整体的泛化能力标准差代表稳定性。你会发现随机森林的均值比决策树高而且标准差通常更小这就是集成的价值。参数说明cv5表示 5 折你也可以设为 10但数据量只有 1000 时 5 折和 10 折差别不大scoringaccuracy对二分类问题就是正确率也可以换成f1或roc_auc来关注更多细节。接着还可以画决策边界图直观对比两者的分类区域import matplotlib.pyplot as plt import numpy as np def plot_decision_boundary(model, X, y, title): x_min, x_max X[:, 0].min() - 0.5, X[:, 0].max() 0.5 y_min, y_max X[:, 1].min() - 0.5, X[:, 1].max() 0.5 xx, yy np.meshgrid( np.linspace(x_min, x_max, 200), np.linspace(y_min, y_max, 200) ) Z model.predict(np.c_[xx.ravel(), yy.ravel()]) Z Z.reshape(xx.shape) plt.contourf(xx, yy, Z, alpha0.3, cmapcoolwarm) plt.scatter(X[y0, 0], X[y0, 1], cblue, label0) plt.scatter(X[y1, 0], X[y1, 1], cred, label1) plt.title(title) plt.legend() plt.show() plot_decision_boundary(dt, X_train, y_train, Decision Tree) plot_decision_boundary(rf, X_train, y_train, Random Forest)逻辑说明先用np.meshgrid生成网格点让每个网格点都经过模型预测得到类别再画等高线填色。这个图能告诉你决策树边界是锯齿状、随机森林边界更平滑。月亮数据的图上你会看到决策树为了贴合训练样本边界上长出许多「小尖角」而随机森林的边界更靠近真实的弯月形状。最后打印随机森林里某一棵树的feature_importances_和整个森林的对比你会发现单棵树的特征重要性波动很大森林平均后才稳定。这也是集成学习「降低方差」的直观证据。从那以后我每次跑这类对比实验都会强制走一遍「固定种子 → 交叉验证 → 画边界 → 看特征重要性」的流程不然总感觉结果只是一次抽签。希望帮到你下载后照着这份笔记跑一遍你会对这些算法有更深的手感。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?