尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
决策树实战:从沉船数据集到sklearn分类模型
1. 从一个沉船数据集说起为什么我选决策树作为机器学习入门的第一把钥匙很多人学机器学习第一个卡住的地方不是数学而是“我到底该从哪个算法下手”。线性回归太数学神经网络太黑盒聚类又不好评估。我带过不少刚入门的朋友几乎每次都会推荐他们从决策树开始。原因很简单决策树的逻辑和人做判断的方式几乎一模一样。你不需要理解梯度、不需要调学习率、不需要担心特征缩放只要会写 if-else你就能理解它在干什么。这次拿来做实战的数据集是一个经典的二分类问题——根据乘客的若干属性预测他是否在沉船事故中幸存。这个数据集规模不大几百到上千条记录特征包括舱位等级、性别、年龄、同行亲属数量、票价、登船港口等。它最大的好处是特征类型混合有数值、有类别、有缺失值非常贴近真实业务场景。你把这套流程跑通一遍以后遇到类似的分类问题基本就是换数据、调参数的事。这篇文章我会把整个流程拆开讲从决策树的核心原理到 sklearn 的 API 怎么用再到数据预处理、特征工程、模型训练、可视化、调参、过拟合排查最后附上完整可运行的代码。不管你是刚接触机器学习的学生还是想快速把决策树落地到业务里的开发者都能直接抄作业。提示本文代码基于 Python 3.8、scikit-learn 1.0 编写数据集使用公开的沉船乘客数据集已做匿名化处理你可以直接替换成自己的业务数据。2. 决策树到底在做什么用“猜人物”游戏理解核心原理2.1 一棵树就是一连串的 if-else先别管信息熵、基尼系数这些术语。你想象一个游戏我心里想一个人你问我问题我只能回答“是”或“否”你要用最少的问题猜出这个人是谁。你肯定会先问“是男的吗”再问“是活人吗”再问“是从事技术工作的吗”——每一步都在把可能性空间劈成两半。决策树干的就是这件事。在沉船预测这个场景里树可能是这样长的先看性别如果是女性大概率幸存如果是男性继续往下判断。再看年龄如果是男性且年龄小于 10 岁幸存概率也不低如果是成年男性继续看舱位等级。再看舱位等级头等舱的成年男性幸存率比三等舱高一些。每一个内部节点都是一个特征判断每一条分支是一次选择叶子节点就是最终预测结果。整棵树就是一套自动生成的、针对当前数据最优的 if-else 规则集。2.2 为什么选决策树而不是逻辑回归或 SVM我对比过同一个数据集上不同算法的表现。逻辑回归需要做特征缩放、处理共线性SVM 调核函数能把人调崩溃而决策树不需要归一化因为它是基于特征阈值切分的数值大小不影响切分点选择。天然处理混合类型类别特征可以用独热编码也可以直接用序数编码树都能处理。可解释性极强你可以把树画出来直接给业务方看“为什么这个人被预测为幸存”。对缺失值有一定容忍度sklearn 的决策树本身不直接处理缺失值但你可以用简单填充树对填充带来的噪声不像线性模型那么敏感。当然它也有缺点单棵树容易过拟合对数据扰动敏感。所以后面我会讲怎么用剪枝、限制深度、集成方法来缓解。2.3 信息熵、基尼系数树是怎么选特征的决策树在每个节点都要选一个“最好的特征”来切分。什么叫最好就是切完之后子节点的“纯度”提升最大。衡量纯度的指标有两个常用选择信息熵熵越低纯度越高。信息增益 父节点熵 - 子节点熵的加权平均。基尼系数从数据集中随机抽两个样本类别不一致的概率。基尼系数越低纯度越高。sklearn 默认用基尼系数因为计算更快没有对数运算。实际效果上两者差别通常不大。你可以通过criterion参数切换。实操心得如果你的特征有很多类别取值信息增益会偏向取值多的特征比如用户 ID这时候基尼系数或者信息增益率更稳妥。sklearn 没有直接提供信息增益率但你可以通过限制max_features来缓解。3. 数据准备沉船数据集的特征工程与预处理3.1 数据集长什么样这个数据集通常包含以下字段字段名含义类型PassengerId乘客编号数值无意义Survived是否幸存0/1类别标签Pclass舱位等级1/2/3序数Name姓名文本Sex性别类别Age年龄数值有缺失SibSp同行兄弟姐妹/配偶数数值Parch同行父母/子女数数值Ticket票号文本Fare票价数值Cabin客舱号文本大量缺失Embarked登船港口类别少量缺失原始数据里PassengerId、Name、Ticket、Cabin这几个字段要么无意义要么缺失太多要么需要复杂提取。入门阶段我建议先丢掉把精力放在核心特征上。3.2 缺失值处理年龄和登船港口怎么补Age缺失大约 20%Embarked缺失只有 2 条。处理策略Embarked直接填众数出现最多的港口。Age可以用中位数填充但更合理的做法是按性别和舱位分组后填组内中位数。因为不同性别、不同舱位的年龄分布差异很大。我试过两种方式分组填充在验证集上准确率能高 1-2 个百分点。代码大概是这样data[Age] data.groupby([Sex, Pclass])[Age].transform( lambda x: x.fillna(x.median()) )注意填充值一定要基于训练集计算再应用到验证集和测试集。如果先把所有数据合并再填充会造成数据泄露验证分数虚高。3.3 类别特征编码性别、登船港口怎么变成数字决策树在 sklearn 里要求输入全是数值。所以Sex男/女映射为 0/1。Embarked三个港口用独热编码One-Hot或者直接映射为 0/1/2。对于树模型序数编码通常够用因为树可以按阈值切分。但独热编码更安全不会引入虚假的顺序关系。我一般对低基数类别特征用独热编码高基数比如超过 10 个类别才考虑序数编码或目标编码。3.4 特征衍生家庭规模与是否独自一人SibSp和Parch单独看信息量有限但合并成“家庭规模”就很有用data[FamilySize] data[SibSp] data[Parch] 1 data[IsAlone] (data[FamilySize] 1).astype(int)这个衍生特征在沉船数据集上非常重要。因为历史上家庭一起行动会影响幸存率——独自一人可能更灵活但大家庭可能互相帮助。树模型能自动发现这些非线性关系。4. sklearn 决策树实战从训练到可视化的完整流程4.1 划分训练集与测试集from sklearn.model_selection import train_test_split X data.drop(Survived, axis1) y data[Survived] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy )stratifyy保证训练集和测试集的标签比例一致。对于不平衡数据幸存者大约占 38%这个参数很重要。4.2 训练第一棵决策树from sklearn.tree import DecisionTreeClassifier clf DecisionTreeClassifier(random_state42) clf.fit(X_train, y_train) train_score clf.score(X_train, y_train) test_score clf.score(X_test, y_test) print(f训练集准确率: {train_score:.4f}) print(f测试集准确率: {test_score:.4f})如果你直接跑大概率会看到训练集准确率接近 100%测试集只有 75%-80%。这就是典型的过拟合。树长得太深把训练数据里的噪声都学进去了。4.3 可视化决策树把 if-else 画出来from sklearn.tree import export_text, plot_tree import matplotlib.pyplot as plt # 文本形式 print(export_text(clf, feature_nameslist(X.columns))) # 图形形式 plt.figure(figsize(20, 10)) plot_tree(clf, feature_nameslist(X.columns), class_names[Not Survived, Survived], filledTrue, roundedTrue, max_depth3) plt.show()文本形式适合快速查看规则图形形式适合做汇报。我一般会限制max_depth3来画图否则节点太多看不清。4.4 关键参数解析剪枝与复杂度控制sklearn 的DecisionTreeClassifier有几个核心参数参数作用推荐范围max_depth树的最大深度3-10min_samples_split节点再分的最小样本数2-20min_samples_leaf叶子节点的最小样本数1-10max_features每次切分考虑的最大特征数None / sqrt / log2ccp_alpha代价复杂度剪枝参数0-0.1我通常先用max_depth和min_samples_leaf做粗调再用ccp_alpha做精细剪枝。ccp_alpha是后剪枝从完整的树开始逐步剪掉贡献最小的分支。实操心得min_samples_leaf比min_samples_split更有效。因为限制叶子节点样本数能直接防止树生成只有一两个样本的“孤叶”这些叶子几乎都是噪声。5. 调参与评估让模型从“能用”到“好用”5.1 网格搜索找最优参数from sklearn.model_selection import GridSearchCV param_grid { max_depth: [3, 5, 7, 10, None], min_samples_leaf: [1, 3, 5, 10], min_samples_split: [2, 5, 10], criterion: [gini, entropy] } grid GridSearchCV( DecisionTreeClassifier(random_state42), param_grid, cv5, scoringaccuracy, n_jobs-1 ) grid.fit(X_train, y_train) print(f最佳参数: {grid.best_params_}) print(f最佳交叉验证分数: {grid.best_score_:.4f})5 折交叉验证比单次划分更可靠。n_jobs-1用满所有 CPU 核心速度能快好几倍。5.2 评估指标准确率不够还要看召回率和 F1沉船预测是一个二分类问题准确率只告诉你“猜对了多少”但不告诉你“幸存者有没有被漏掉”。如果业务目标是尽量找出幸存者那召回率更重要。from sklearn.metrics import classification_report, confusion_matrix y_pred grid.predict(X_test) print(confusion_matrix(y_test, y_pred)) print(classification_report(y_test, y_pred))classification_report会输出精确率、召回率、F1 分数。我一般会重点关注少数类幸存者的召回率。5.3 过拟合排查学习曲线与验证曲线from sklearn.model_selection import learning_curve, validation_curve import numpy as np train_sizes, train_scores, val_scores learning_curve( DecisionTreeClassifier(max_depth5, random_state42), X_train, y_train, cv5, train_sizesnp.linspace(0.1, 1.0, 10), scoringaccuracy ) train_mean np.mean(train_scores, axis1) val_mean np.mean(val_scores, axis1) plt.plot(train_sizes, train_mean, label训练集) plt.plot(train_sizes, val_mean, label验证集) plt.xlabel(训练样本数) plt.ylabel(准确率) plt.legend() plt.show()如果训练集和验证集曲线中间有很大空隙说明过拟合如果两条曲线都很低且接近说明欠拟合。根据曲线形态决定是加数据、减深度还是加特征。6. 常见问题与排查技巧实录6.1 准确率卡在 78% 上不去怎么办这是沉船数据集上单棵决策树的典型瓶颈。我试过以下手段按性价比排序增加衍生特征家庭规模、是否独自一人、名字中的称呼Mr/Mrs/Miss提取婚姻状态。称呼提取对幸存率预测帮助很大。调整类别编码把 Pclass 和 Embarked 做独热编码而不是序数编码。限制树深度从 None 降到 5-7测试集准确率通常能提升 2-3 个百分点。换集成模型随机森林或梯度提升树准确率能到 82%-85%。但那是下一步的事先把单棵树吃透。6.2 树太深、节点太多怎么剪除了前面说的max_depth和min_samples_leaf还可以用ccp_alpha做后剪枝path clf.cost_complexity_pruning_path(X_train, y_train) ccp_alphas path.ccp_alphas clfs [] for alpha in ccp_alphas: clf DecisionTreeClassifier(random_state42, ccp_alphaalpha) clf.fit(X_train, y_train) clfs.append(clf) # 画图找最佳 alpha train_scores [c.score(X_train, y_train) for c in clfs] test_scores [c.score(X_test, y_test) for c in clfs] plt.plot(ccp_alphas, train_scores, markero, label训练集) plt.plot(ccp_alphas, test_scores, markero, label测试集) plt.xlabel(ccp_alpha) plt.ylabel(准确率) plt.legend() plt.show()选择测试集准确率最高且训练集不过高的那个 alpha。6.3 特征重要性怎么看哪些特征最有用import pandas as pd importance pd.DataFrame({ feature: X.columns, importance: clf.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance)在沉船数据集上通常Sex、Pclass、Fare、Age、FamilySize排在前列。如果某个特征重要性接近 0可以考虑删掉简化模型。注意特征重要性是基于训练集计算的如果特征之间有强共线性重要性会被分散。比如SibSp和FamilySize高度相关两者的重要性都会偏低。6.4 常见报错与解决方法报错信息原因解决方法ValueError: could not convert string to float特征里有字符串做编码确保所有列是数值ValueError: Input contains NaN有缺失值填充缺失值准确率极低 60%标签编码错误或数据泄露检查标签映射确认训练测试划分正确树可视化中文乱码matplotlib 字体问题设置plt.rcParams[font.sans-serif] [SimHei]7. 完整代码与数据集说明7.1 完整可运行代码import pandas as pd import numpy as np import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split, GridSearchCV from sklearn.tree import DecisionTreeClassifier, plot_tree, export_text from sklearn.metrics import classification_report, confusion_matrix # 1. 加载数据 data pd.read_csv(titanic.csv) # 2. 特征选择与衍生 data data[[Survived, Pclass, Sex, Age, SibSp, Parch, Fare, Embarked]] data[FamilySize] data[SibSp] data[Parch] 1 data[IsAlone] (data[FamilySize] 1).astype(int) # 3. 缺失值处理 data[Age] data.groupby([Sex, Pclass])[Age].transform( lambda x: x.fillna(x.median()) ) data[Embarked] data[Embarked].fillna(data[Embarked].mode()[0]) data[Fare] data[Fare].fillna(data[Fare].median()) # 4. 编码 data[Sex] data[Sex].map({male: 0, female: 1}) data pd.get_dummies(data, columns[Embarked], drop_firstTrue) # 5. 划分 X data.drop(Survived, axis1) y data[Survived] X_train, X_test, y_train, y_test train_test_split( X, y, test_size0.2, random_state42, stratifyy ) # 6. 网格搜索 param_grid { max_depth: [3, 5, 7, 10, None], min_samples_leaf: [1, 3, 5, 10], criterion: [gini, entropy] } grid GridSearchCV( DecisionTreeClassifier(random_state42), param_grid, cv5, scoringaccuracy, n_jobs-1 ) grid.fit(X_train, y_train) # 7. 评估 best_clf grid.best_estimator_ y_pred best_clf.predict(X_test) print(f最佳参数: {grid.best_params_}) print(classification_report(y_test, y_pred)) # 8. 可视化 plt.figure(figsize(20, 10)) plot_tree(best_clf, feature_nameslist(X.columns), class_names[Not Survived, Survived], filledTrue, roundedTrue, max_depth3) plt.show() # 9. 特征重要性 importance pd.DataFrame({ feature: X.columns, importance: best_clf.feature_importances_ }).sort_values(importance, ascendingFalse) print(importance)7.2 数据集获取与替换沉船数据集在公开数据平台很容易找到搜索“titanic dataset”即可。如果你想用自己的业务数据只需要保证有一个二分类或多分类标签列。特征列包含数值和类别混合类型。缺失值比例不要超过 50%否则该特征建议直接丢弃。替换数据后重点调整param_grid里的max_depth范围。数据量越大允许的深度可以适当增加。8. 从单棵树到集成模型下一步怎么走单棵决策树跑通之后你可能会发现准确率有天花板。这时候有两个方向方向一随机森林。用多棵树投票每棵树用随机抽样的数据和随机抽样的特征。sklearn 里就是RandomForestClassifier参数几乎和决策树一样加上n_estimators树的数量。在沉船数据集上随机森林通常比单棵树高 3-5 个百分点。方向二梯度提升树。比如GradientBoostingClassifier或HistGradientBoostingClassifier。它是一棵树一棵树地修正前面的错误效果通常更好但调参更复杂训练也更慢。我的建议是先把单棵树的max_depth、min_samples_leaf、ccp_alpha这三个参数玩熟理解它们对偏差和方差的影响。然后再上集成模型你会更容易理解为什么集成能提升效果。最后分享一个小技巧决策树的random_state会影响结果尤其是数据量小的时候。做交叉验证时固定random_state保证可复现做最终评估时可以跑 5-10 个不同的随机种子看准确率的均值和标准差这样对模型稳定性心里更有数。这个数据集和代码框架我后来直接套用到好几个业务分类问题上比如用户流失预测、订单风险识别。换数据、改特征、调深度基本一两天就能出一个可解释的基线模型。决策树不是最强大的算法但它是最容易让你理解“模型到底在学什么”的算法。把这一关过了后面学任何模型都会轻松很多。
RELATED

相关推荐

Java程序编译与运行机制全解析:从javac到JIT

Java程序编译与运行机制全解析:从javac到JIT

很多学Java的朋友都有过这种经历:背面试题的时候,对“Java程序是如何运行的”这个问题,标准回答往往是“先编译成字节码,再交给JVM解释执行”。这个说法不能算错,但它太粗了,粗到会让人产生误解——仿佛Jav…

📅 2026/10/21 12:36:15
智能体安全实战:从风险识别到权限管控与纵深防御

智能体安全实战:从风险识别到权限管控与纵深防御

做安全这么多年,我一直觉得“智能体安全”这个词特别容易被误解。有人以为它是让人工智能模型跑得更稳、别出乱子,也有人觉得它就是给聊天机器人加个内容过滤。实际上,当一个由大模型驱动、能自主思考、能调用外部工具、能跟真实系统交互的“…

📅 2026/10/21 12:31:14
图片转Excel全攻略:OCR表格识别与批量处理实战

图片转Excel全攻略:OCR表格识别与批量处理实战

不管是财务对账、业务报表整理,还是历史档案电子化,只要涉及“把纸上的表格弄进电脑”,就躲不开一个重复到让人想骂人的过程:先拍照或扫描,再把图片里的每个单元格手工敲进Excel。我见过太多同事花一下午去录几十张表格…

📅 2026/10/21 12:31:14
MORE NEWS

更多资讯

📰

知识工作插件集:打通收集、整理与输出的高效工作流

做知识工作这件事,最耗人的往往不是产出本身,而是前期那些找资料、存档、整理、来回切换工具的动作。我自己长期被这类低效消耗折磨,后来索性把散落在各处的插件按工作流重新收拾了一遍,攒成一个叫knowledge-work-plugins的工具集…

📰

NX二次开发:用代码实现组件实体WAVE链接到工作部件

做NX装配设计的人,十有八九都干过这么一件事:明明模型已经拆成组件了,可设计到一半发现,齿轮箱壳体上的某个安装座,必须跟里面那个零件的外轮廓完全贴合——实体关联、参数跟着走、改动之后马上同步。手工去插入WAVE几…

📰

从空标题到技能系统:拆解skills的底层逻辑与落地方法

1. 当“skills”成为一个项目标题:我看到的不是空壳,而是最危险的信号拿到“skills”这个标题的时候,我第一反应是愣了一下。没有项目正文,没有关键词,没有摘要描述,连一个标点符号的补充说明都没有。放在任…

📰

硅基誓言与协同叙事:从AI共生到科幻世界观搭建的创作框架

拿到《元创力》创世篇这个桥段的时候,我先是一愣,然后忍不住乐了。不是因为这设定多炫技,而是"一个硅基的誓言:不效忠,只协同"这两句话放在一起,几乎把整个世界观的地基都点透了。很多创作者写AI…

📰

算法入门不再怕公式:从数学恐惧到科学调参的实战路径

算法入门这件事,我曾经以为拼的是数学天赋。看到梯度下降公式那一刻,我整个人是懵的,脑子里只剩一个念头:这辈子的高数债,终究是要还的。后来摸爬滚打一年,从连最小二乘法都要查半天,到能对着训…

📰

移动机器人导航地图管理模块设计:多图层、坐标变换与调参实践

我最早接手 lingbot-map 这个地图模块时,其实有点不以为然。移动机器人导航这件事,听起来重点都在路径规划和定位算法上,地图模块不就是把建图算法吐出来的数据存一存、读一读吗?直到我在真实场景里连续折腾了三周,把多…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬