基于决策树的机器学习:预测幸福水平
1. 机器学习与决策树简介
机器学习涵盖了广泛的方法,其共同目标是在数据中寻找模式并利用这些模式进行预测。决策树是一种具有分支结构、类似树状的图表,可用于引导我们通过回答是/否问题,沿着特定路径得出最终的决策、预测或建议。创建能实现最优决策的决策树,是机器学习算法的典型应用。
1.1 决策树在现实场景中的应用
以急诊室的分诊工作为例,分诊人员需要为新入院的患者分配优先级。例如,一位 50 岁的女性因胸痛前来急诊,分诊人员需要判断她的疼痛更可能是烧心还是心脏病发作。这一决策过程需要考虑众多因素,如患者的年龄、性别、是否肥胖、是否吸烟、症状表现、医院繁忙程度等。
下面是一个简化的心脏病发作分诊决策树示例:
graph TD;
A[患者抱怨胸痛] --> B{患者是男性};
B -- 是 --> C{不肥胖};
C -- 是 --> D[低风险];
C -- 否 --> E[高风险];
B -- 否 --> F{是吸烟者};
F -- 是 --> G[高风险];
F -- 否 --> H{非糖尿病患者};
H -- 是 --> I[低风险];
H -- 否 --> J[高风险];
在这个决策树中,每个文本所在的位置都是一个节点。像“不肥胖”这样的节点是分支节点,因为在做出预测之前还有至少一个分支需要考虑;而“非糖尿病患者 = 低风险”这样的节点是终端节点,到达该节点后就无需再分支,我们可以直接得到决策树的最终分类。
如果能设计出一个完善的决策树,即使没有医学训练的人也可以对心脏病患者进行分诊,这将为世界各地的急诊室节省大量资金,甚至有可能用机器人取代人类分诊人员。而且,一个好的决策树可能比普通人做出的决策更好,因为它可以消除人类可能存在的无意识偏见。
1.2 决策树算法的执行与设计
决策树中的分支决策步骤构成了一个算法。执行这个算法很简单,只需在每个节点决定选择哪一个分支,并沿着分支走到终点。但设计一个能实现最佳决策的决策树才是难点。创建最优决策树是机器学习的应用,而仅仅遵循决策树则不属于机器学习。
2. 构建预测幸福水平的决策树
2.1 下载数据集
我们将使用欧洲社会调查(ESS)的数据来构建决策树。可以从以下链接下载所需文件:
- http://bradfordtuckfield.com/ess.csv
- http://bradfordtuckfield.com/variables.csv
这些文件最初来自 https://www.kaggle.com/pascalbliem/european - social - survey - ess - 8 - ed21 - 201617 ,可免费公开获取。ESS 是一项针对欧洲成年人的大规模调查,每两年进行一次,询问了各种个人问题,包括宗教信仰、健康状况、社交生活和幸福水平等。文件以 CSV 格式存储,这种格式是一种常见且简单的数据集存储方式,可由 Microsoft Excel、LibreOffice Calc、文本编辑器和一些 Python 模块打开。
variables.csv
文件包含了调查中每个问题的详细描述。例如,变量
happy
记录了调查对象对“综合考虑所有因素,你觉得自己有多幸福?”这一问题的回答,答案范围从 1(一点也不幸福)到 10(极其幸福)。其他变量如
sclmeet
记录了受访者与朋友、亲戚或同事社交见面的频率,
health
记录了主观总体健康状况,
rlgdgr
记录了受访者的宗教信仰程度等。
看到这些数据后,我们可以提出一些与幸福预测相关的假设。例如,我们可以合理推测,社交生活活跃且健康状况良好的人可能比其他人更幸福。而性别、家庭规模和年龄等变量对幸福的影响可能较难推测。
2.2 查看数据
首先,我们需要下载数据并将其保存为本地的
ess.csv
文件,然后使用
pandas
模块来处理数据:
import pandas as pd
ess = pd.read_csv('ess.csv')
需要注意的是,要确保
ess.csv
文件与运行 Python 的位置相同,或者根据实际情况修改文件路径。我们可以使用
pandas
数据框的
shape
属性查看数据的行数和列数:
print(ess.shape)
输出结果应为
(44387, 534)
,这表明我们的数据集有 44387 行(每个受访者一行)和 534 列(每个调查问题一列)。我们可以使用
pandas
模块的切片函数更仔细地查看我们感兴趣的列。例如,查看“happy”问题的前五个答案:
print(ess.loc[:,'happy'].head())
同样,我们也可以查看
sclmeet
变量的前五个答案:
print(ess.loc[:,'sclmeet'].head())
由于调查中可能存在部分问题的回答缺失的情况,对于我们的分析,我们只考虑那些感兴趣的变量没有缺失值的完整回答。可以通过以下代码限制
ess
数据,使其仅包含我们关心的变量的完整回答:
ess = ess.loc[ess['sclmeet'] <= 10,:].copy()
ess = ess.loc[ess['rlgdgr'] <= 10,:].copy()
ess = ess.loc[ess['hhmmb'] <= 50,:].copy()
ess = ess.loc[ess['netusoft'] <= 5,:].copy()
ess = ess.loc[ess['agea'] <= 200,:].copy()
ess = ess.loc[ess['health'] <= 5,:].copy()
ess = ess.loc[ess['happy'] <= 10,:].copy()
ess = ess.loc[ess['eduyrs'] <= 100,:].copy().reset_index(drop=True)
2.3 分割数据
我们可以通过多种方式利用这些数据来探索一个人的社交生活与幸福水平之间的关系。其中一种简单的方法是进行二元分割,比较社交生活活跃的人和社交生活不活跃的人的幸福水平。
import numpy as np
social = list(ess.loc[:,'sclmeet'])
happy = list(ess.loc[:,'happy'])
low_social_happiness = [hap for soc,hap in zip(social,happy) if soc <= 5]
high_social_happiness = [hap for soc,hap in zip(social,happy) if soc > 5]
meanlower = np.mean(low_social_happiness)
meanhigher = np.mean(high_social_happiness)
运行
print(meanlower)
和
print(meanhigher)
后,我们会发现社交活跃的人的平均幸福水平约为 7.8,而社交不活跃的人的平均幸福水平约为 7.2。这表明社交活跃的人自我报告的幸福水平略高于社交不活跃的同龄人。
我们可以用以下简单的决策树图来表示这个二元分割:
graph TD;
A[所有调查受访者] --> B{sclmeet <= 5};
B -- 是 --> C[平均幸福水平: 7.2];
B -- 否 --> D[平均幸福水平: 7.8];
这个简单的二元分割图已经开始类似于决策树。实际上,在数据集中进行二元分割并比较每一半的结果,正是决策树生成算法的核心过程。我们可以使用这个简单的决策树来预测幸福水平:如果某人的
sclmeet
值为 5 或更低,我们可以预测其幸福水平为 7.2;如果高于 5,则预测其幸福水平为 7.8。虽然这不是一个完美的预测,但它是一个开始,并且比随机猜测更准确。
2.4 更智能的分割
在之前的比较中,我们使用 5 作为分割点,将社交评分高于 5 的人定义为社交活跃,5 及以下的人定义为社交不活跃。但为了构建一个更准确的幸福预测器,我们应该寻找能使预测误差最小的分割点。
在机器学习问题中,有几种不同的方法来衡量准确性,最自然的方法是计算误差总和。在我们的例子中,误差是指我们对某人幸福评分的预测值与实际值之间的差异。例如,如果决策树预测你的幸福水平是 6,但实际是 8,那么该树对你的评分误差就是 2。我们可以通过以下代码计算误差总和:
lowererrors = [abs(lowhappy - meanlower) for lowhappy in low_social_happiness]
highererrors = [abs(highhappy - meanhigher) for highhappy in high_social_happiness]
total_error = sum(lowererrors) + sum(highererrors)
运行这段代码后,我们发现总误差约为 60224。虽然这个数字远大于零,但考虑到我们是用一个只有两个分支的树来预测 40000 多名受访者的幸福水平,这个误差似乎也可以接受。
为了找到最佳分割点,我们可以尝试不同的值。为了获得最高的准确性,我们应该依次检查所有可能的分割点,并选择导致最小误差的分割点。以下是一个实现此功能的函数:
def get_splitpoint(allvalues,predictedvalues):
lowest_error = float('inf')
best_split = None
best_lowermean = np.mean(predictedvalues)
best_highermean = np.mean(predictedvalues)
for pctl in range(0,100):
split_candidate = np.percentile(allvalues, pctl)
loweroutcomes = [outcome for value,outcome in zip(allvalues,predictedvalues) if value <= split_candidate]
higheroutcomes = [outcome for value,outcome in zip(allvalues,predictedvalues) if value > split_candidate]
if np.min([len(loweroutcomes),len(higheroutcomes)]) > 0:
meanlower = np.mean(loweroutcomes)
meanhigher = np.mean(higheroutcomes)
lowererrors = [abs(outcome - meanlower) for outcome in loweroutcomes]
highererrors = [abs(outcome - meanhigher) for outcome in higheroutcomes]
total_error = sum(lowererrors) + sum(highererrors)
if total_error < lowest_error:
best_split = split_candidate
lowest_error = total_error
best_lowermean = meanlower
best_highermean = meanhigher
return(best_split,lowest_error,best_lowermean,best_highermean)
通过这个函数,我们可以不断优化决策树的分割点,从而构建出更准确的决策树来预测幸福水平。
2.5 构建更复杂的决策树
前面我们通过二元分割构建了一个简单的决策树,仅考虑了社交活动频率对幸福水平的影响。为了构建更复杂、更准确的决策树,我们可以引入更多的变量。
2.5.1 选择更多变量
除了
sclmeet
变量,我们还可以考虑
health
(主观总体健康状况)、
rlgdgr
(宗教信仰程度)等变量。我们可以重复之前的步骤,分别对这些变量进行二元分割,找到每个变量的最佳分割点。
2.5.2 多变量决策树的构建流程
以下是构建多变量决策树的大致流程:
1. 选择一个变量作为根节点的分割变量。
2. 找到该变量的最佳分割点,将数据集分为两部分。
3. 对每一部分数据集,重复步骤 1 和 2,选择新的变量进行分割,直到满足停止条件(如达到最大深度、节点中的样本数小于某个阈值等)。
下面是一个简单的 Python 示例代码,展示如何构建一个简单的多变量决策树:
from sklearn.tree import DecisionTreeRegressor
import pandas as pd
import numpy as np
# 读取数据
ess = pd.read_csv('ess.csv')
# 处理缺失值,只保留感兴趣的变量的完整回答
ess = ess.loc[ess['sclmeet'] <= 10,:].copy()
ess = ess.loc[ess['rlgdgr'] <= 10,:].copy()
ess = ess.loc[ess['hhmmb'] <= 50,:].copy()
ess = ess.loc[ess['netusoft'] <= 5,:].copy()
ess = ess.loc[ess['agea'] <= 200,:].copy()
ess = ess.loc[ess['health'] <= 5,:].copy()
ess = ess.loc[ess['happy'] <= 10,:].copy()
ess = ess.loc[ess['eduyrs'] <= 100,:].copy().reset_index(drop=True)
# 选择特征和目标变量
features = ['sclmeet', 'health', 'rlgdgr']
X = ess[features]
y = ess['happy']
# 创建决策树回归模型
tree = DecisionTreeRegressor(max_depth=3) # 限制树的最大深度为 3
tree.fit(X, y)
2.6 决策树的评估
构建好决策树后,我们需要对其进行评估,以确定其性能。常见的评估指标包括均方误差(Mean Squared Error, MSE)、均方根误差(Root Mean Squared Error, RMSE)和决定系数(Coefficient of Determination, $R^2$)等。
以下是使用 Python 计算这些评估指标的示例代码:
from sklearn.metrics import mean_squared_error, r2_score
from sklearn.model_selection import train_test_split
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 创建决策树回归模型
tree = DecisionTreeRegressor(max_depth=3)
tree.fit(X_train, y_train)
# 进行预测
y_pred = tree.predict(X_test)
# 计算评估指标
mse = mean_squared_error(y_test, y_pred)
rmse = np.sqrt(mse)
r2 = r2_score(y_test, y_pred)
print(f"均方误差 (MSE): {mse}")
print(f"均方根误差 (RMSE): {rmse}")
print(f"决定系数 (R^2): {r2}")
2.7 决策树的可视化
为了更直观地理解决策树的结构和决策过程,我们可以将其可视化。在 Python 中,我们可以使用
graphviz
和
sklearn.tree.export_graphviz
来实现决策树的可视化。
以下是可视化决策树的示例代码:
from sklearn.tree import export_graphviz
import graphviz
# 导出决策树为 Graphviz 格式
dot_data = export_graphviz(tree, out_file=None,
feature_names=features,
filled=True, rounded=True,
special_characters=True)
# 创建 Graphviz 对象
graph = graphviz.Source(dot_data)
# 保存为图片
graph.render("decision_tree", format='png', cleanup=True)
3. 总结
3.1 决策树的优点和局限性
3.1.1 优点
- 易于理解和解释 :决策树的结构类似于流程图,直观易懂,即使是非专业人士也能轻松理解决策过程。
- 处理非线性关系 :决策树可以处理特征与目标变量之间的非线性关系,无需对数据进行复杂的变换。
- 不需要太多的数据预处理 :决策树对数据的尺度和分布不敏感,不需要进行归一化、标准化等预处理操作。
3.1.2 局限性
- 容易过拟合 :决策树容易在训练数据上过度拟合,导致在测试数据上的性能下降。可以通过限制树的深度、剪枝等方法来缓解过拟合问题。
- 对数据的微小变化敏感 :决策树对数据的微小变化比较敏感,可能会导致生成的决策树结构发生较大变化。
- 不适合处理高维数据 :当特征数量较多时,决策树的计算复杂度会增加,并且容易出现过拟合问题。
3.2 未来展望
决策树作为一种简单而有效的机器学习算法,在许多领域都有广泛的应用。未来,我们可以进一步探索以下方向:
-
集成学习
:将多个决策树组合成一个集成模型,如随机森林、梯度提升树等,以提高模型的性能和稳定性。
-
特征工程
:通过挖掘更多有价值的特征,提高决策树的预测能力。
-
实时决策
:将决策树应用于实时决策场景,如金融风险评估、医疗诊断等。
综上所述,决策树是一种强大的机器学习工具,通过合理的构建和优化,可以为我们提供有价值的预测和决策支持。希望本文能帮助你更好地理解决策树的原理和应用,为你的数据分析和机器学习实践提供有益的参考。
以下是一个简单的表格,总结决策树的优点和局限性:
| 优点 | 局限性 |
| ---- | ---- |
| 易于理解和解释 | 容易过拟合 |
| 处理非线性关系 | 对数据的微小变化敏感 |
| 不需要太多的数据预处理 | 不适合处理高维数据 |
下面是一个 mermaid 流程图,展示决策树的构建和评估流程:
graph TD;
A[数据准备] --> B[选择根节点变量];
B --> C[找到最佳分割点];
C --> D[分割数据集];
D --> E{是否满足停止条件};
E -- 否 --> B;
E -- 是 --> F[构建完成决策树];
F --> G[评估决策树];
G --> H[优化决策树];
H --> F;
超级会员免费看
41

被折叠的 条评论
为什么被折叠?



