分类——决策树模型

在分类问题中,表示基于特征对实例进行分类的过程,可以认为是if-then的集合,也可以认为是定义在特征空间与类空间上的条件概率分布。
决策树是一种预测模型,对未标识的实例进行分类;也是一种描述性模型,标识哪些诶特在可以将实例从不同类里区分不来。决策树分类器是基于信息熵的学习。决策树学习的本质:从训练集中归纳出一组分类规则,或者说是由训练数据集估计条件概率模型。决策树原理和问答判断相似,根据一系列数据,判断是否,然后给出问题答案。因此决策树分类器的可解释性质较好。

决策树学习用损失函数表示这一目标,其损失函数通常是正则化的极大似然函数,决策树学习的策略是以损失函数为目标函数的最小化。

树:
树有三种类型的节点

  1. 根节点
  2. 内部节点
  3. 叶子节点
    树的节点包含一个数据元素及若干指向其子树的分支。节点拥有的子树数称为结点的度(Degree)。度为0的结点称为叶子节点(Leaf)或终端节点。度不为0的节点称为非终端节点或分支节点。除根节点之外,分支节点也称为内部节点。树的度是树内各节点的度的最大值。
    在这里插入图片描述

信息熵:
X 信息熵定义为信息的期望值。
信息熵就是用来衡量一个随机变量取值的不确定性的一个指标,信息熵越大则不确定性越大,信息熵越小则不确定性也就越小。
假设一个随机变量X的概率分布如下:
在这里插入图片描述

p ( x i ) 是选择该分类的概率,n为分类数目,随机变量X的信息熵的计算公式如下:
在这里插入图片描述

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LTW2Y3dB-1603437684841)(C:\Users\RUCchen\AppData\Roaming\Typora\typora-user-images\image-20201023103353676.png)]

条件熵:
设二维随机变量(X, Y)的概率分布为:
在这里插入图片描述
随机变量X给定条件下随机变量Y的条件熵表示为H(Y|X),已知随机变量X的情况下随机变量Y的不确定性,给定X的条件下随机变量Y的熵对于X的期望值,其定义如下:在这里插入图片描述

当熵和条件熵中的概率由数据估计(特别是极大似然估计)得到时,所对应的分别为经验熵和经验条件熵,此时如果有0概率,令0*log0 = 0。
信息增益
给定X的能够使随机变量Y的确定性增加的程度。对于特征a对于数据集D的信息增益是g(D,a),定义为在集合D的信息熵H(D)和特征a给定条件下D的经验条件熵H(D|a)之差,即:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXE4Ywse-1603437684844)(C:\Users\RUCchen\AppData\Roaming\Typora\typora-user-images\image-20201023103921618.png)]

显然,对于数据集D而言,信息增益依赖于特征,不同的特征往往具有不同的信息增益,信息增益大的特征具有更强的分类能力。根据信息增益准则的特征选择方法:对训练数据集(或子集)D,计算其每个特征的信息增益,并比较它们的大小,选择信息增益最大的特征。

1.先计算信息熵 2.算特征下的的条件熵 3.算信息增益

信息增益率

信息增益值的大小是相对于训练数据集而言的,并没有绝对意义。在训练数据集的信息熵大的时候,信息增益值会偏大。反之,信息增益值会偏小。使用信息增益率(information gain ratio)可以对这一问题进行校正。

基尼系数

基尼指数是另一个选择的标准,代表了从样本中任意选择两个样本,类别不一致的概率,所以基尼指数越小,代表样本纯度越高。
在这里插入图片描述

属性a的基尼指数定义为属性a各类样本比率的基尼指数和:
在这里插入图片描述

构建决策树

决策树学习的算法通常是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得各个子数据集有一个最好的分类的过程。
1) 开始:构建根节点,将所有训练数据都放在根节点,选择一个最优特征,按着这一特征将训练数据集分割成子集,使得各个子集有一个在当前条件下最好的分类。
2) 如果这些子集已经能够被基本正确分类,那么构建叶节点,并将这些子集分到所对应的叶节点去。
3)如果还有子集不能够被正确的分类,那么就对这些子集选择新的最优特征,继续对其进行分割,构建相应内部节点,如果递归进行,直至所有训练数据子集被基本正确的分类,或者没有合适的特征为止。
4)每个子集都被分到叶节点上,即都有了明确的类,这样就生成了一颗决策树。

如何决定什么时候停止分支呢?

我们可以这样设置,当某个节点的信息熵小于某个阈值时我们就停止对这个节点的分支操作,那么此节点也就成为了叶子节点。
最终我们需要确定每个叶子结点的类别,即叶子结点中的样本集中,占比最大的那一个类别便是当前叶子节点的类别,当新来一个样本我们只需要按照决策树从顶层向下逐步判断,看样本最终落入那个叶子结点,所落入的叶子结点的类别便是当前样本的预测类别。

ID3算法

在决策树各个结点上应用信息增益准则选择特征,递归地构建决策树。具体方法是:从根结点开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为根结点的特征,由该特征的不同取值建立子结点。之后,对子结点递归地调用以上方法,构建决策树,直到所有特征的信息增益均很小或没有特征可以选择为止,最终得到一个决策树。ID3相当于用极大似然法进行概率模型的选择

输入:训练数据集合D,特征集A,阈值ϵ

输出:决策树T

1.若D中所有实例属于同一类Ck则T为单结点树,并将类Ck作为该结点的类标记,返回决策树T

2.若A是空集,T为单节点树,并将D的实例树最大的类Ck作为该节点的类标记,返回决策树

3.否则,计算A各特征对D的信息增益,选择信息增益最大的特征a

4.如果a的信息增益小于阈值ϵ,则T为单节点数,将D里的实例树最大的类Ck作为该节点

5.否则对于a的每一个可能值a*,按照a=a*将D分割为若干非空子集Dv,将Dv里的实例数最大的类作为该节点,构建子节点,由节点和子节点构成决策树T,返回决策树T。

6.对第 v个子结点,以Dv为训练集,以A−{a}为特征集,递归地调用第(1)步~第(5)步,得到子树T

C4.5算法

C4.5决策树算法不直接使用信息增益来选择划分属性,而是使用信息增益率来选择最优划分属性。

信息增益率越大就优先变成为节点。

评价

参数学习:先假定数据属于一定的分布,然后再进行操作

非参数学习:不做先验假设

决策树算法:

属于非参数学习;有强的解释力;计算效率不一定高;

在训练数据集合具有相关性(比如多重共线性)则不用决策树模型;

决策树模型对于处理移失值具有优势(利用基于概率的切分方法、基于替换的切分方法等)

非参数学习:不做先验假设

决策树算法:

属于非参数学习;有强的解释力;计算效率不一定高;

在训练数据集合具有相关性(比如多重共线性)则不用决策树模型;

决策树模型对于处理移失值具有优势(利用基于概率的切分方法、基于替换的切分方法等)

决策树是一种广泛使用的分类和回归方法,它可以用于多维分类问题。以下是使用C++类实现决策树的示例。 首先,我们需要定义一个节点类来表示决策树的节点: ``` class Node{ public: int feature; //特征编号 double threshold; //阈值 int label; //标签 Node* left; //左子树 Node* right; //右子树 Node() { feature = 0; threshold = 0.0; label = -1; left = NULL; right = NULL; } }; ``` 然后,我们需要定义一个决策树类来构建决策树: ``` class DecisionTree { public: DecisionTree(); ~DecisionTree(); void buildTree(const vector<vector<double>>& data, const vector<int>& labels); int predict(const vector<double>& data) const; private: Node* root; void destroy(Node* node); int getMajorityLabel(const vector<int>& labels) const; int getBestFeature(const vector<vector<double>>& data, const vector<int>& labels, vector<double>& thresholds) const; Node* buildSubTree(const vector<vector<double>>& data, const vector<int>& labels, const vector<double>& thresholds); }; ``` 其中,buildTree()函数用于构建决策树,predict()函数用于进行预测。 下面是buildTree()函数的实现: ``` void DecisionTree::buildTree(const vector<vector<double>>& data, const vector<int>& labels) { vector<double> thresholds(data[0].size(), 0.0); root = buildSubTree(data, labels, thresholds); } Node* DecisionTree::buildSubTree(const vector<vector<double>>& data, const vector<int>& labels, const vector<double>& thresholds) { Node* node = new Node; if (labels.empty()) { node->label = -1; return node; } int majorityLabel = getMajorityLabel(labels); if (majorityLabel == -1) { node->label = majorityLabel; return node; } int bestFeature = getBestFeature(data, labels, thresholds); if (bestFeature == -1) { node->label = majorityLabel; return node; } node->feature = bestFeature; node->threshold = thresholds[bestFeature]; vector<vector<double>> leftData; vector<int> leftLabels; vector<vector<double>> rightData; vector<int> rightLabels; for (int i = 0; i < data.size(); i++) { if (data[i][bestFeature] <= thresholds[bestFeature]) { leftData.push_back(data[i]); leftLabels.push_back(labels[i]); } else { rightData.push_back(data[i]); rightLabels.push_back(labels[i]); } } if (leftData.empty() || rightData.empty()) { node->label = majorityLabel; return node; } node->left = buildSubTree(leftData, leftLabels, thresholds); node->right = buildSubTree(rightData, rightLabels, thresholds); return node; } ``` 在buildSubTree()函数中,我们首先判断标签是否为空,如果为空,则返回一个空节点。然后,我们计算出标签数据中出现最多的标签,并将其作为节点的标签。接下来,我们选择最佳特征和阈值来划分数据。如果无法找到最佳特征,则返回一个具有多数标签的叶子节点。如果数据无法划分,则返回一个具有多数标签的叶子节点。否则,我们将数据分成左子树和右子树,并递归构建它们。 下面是predict()函数的实现: ``` int DecisionTree::predict(const vector<double>& data) const { Node* node = root; while (node->left != NULL && node->right != NULL) { if (data[node->feature] <= node->threshold) { node = node->left; } else { node = node->right; } } return node->label; } ``` 在predict()函数中,我们从根节点开始遍历决策树,并根据特征的值和阈值选择左子树或右子树,直到到达叶子节点。叶子节点的标签就是预测结果。 最后,我们需要定义一些辅助函数,如计算数据中出现最多的标签、选择最佳特征和阈值等等。这些函数的实现可以参考以下代码: ``` int DecisionTree::getMajorityLabel(const vector<int>& labels) const { int numLabels = labels.size(); if (numLabels == 0) { return -1; } unordered_map<int, int> labelCounts; for (int i = 0; i < numLabels; i++) { if (labelCounts.find(labels[i]) != labelCounts.end()) { labelCounts[labels[i]]++; } else { labelCounts[labels[i]] = 1; } } int majorityLabel = -1; int maxCount = -1; for (auto it = labelCounts.begin(); it != labelCounts.end(); it++) { if (it->second > maxCount) { maxCount = it->second; majorityLabel = it->first; } } return majorityLabel; } int DecisionTree::getBestFeature(const vector<vector<double>>& data, const vector<int>& labels, vector<double>& thresholds) const { int numFeatures = data[0].size(); int numLabels = labels.size(); double maxGain = -1.0; int bestFeature = -1; vector<double> featureValues(numLabels); for (int i = 0; i < numFeatures; i++) { for (int j = 0; j < numLabels; j++) { featureValues[j] = data[j][i]; } sort(featureValues.begin(), featureValues.end()); double threshold; for (int j = 0; j < numLabels - 1; j++) { threshold = (featureValues[j] + featureValues[j + 1]) / 2.0; vector<int> leftLabels; vector<int> rightLabels; for (int k = 0; k < numLabels; k++) { if (data[k][i] <= threshold) { leftLabels.push_back(labels[k]); } else { rightLabels.push_back(labels[k]); } } double entropy = 0.0; if (!leftLabels.empty()) { double leftProb = (double)leftLabels.size() / numLabels; entropy += -1.0 * leftProb * log2(leftProb); } if (!rightLabels.empty()) { double rightProb = (double)rightLabels.size() / numLabels; entropy += -1.0 * rightProb * log2(rightProb); } double gain = entropy; if (gain > maxGain) { maxGain = gain; bestFeature = i; thresholds[i] = threshold; } } } return bestFeature; } void DecisionTree::destroy(Node* node) { if (node == NULL) { return; } destroy(node->left); destroy(node->right); delete node; } DecisionTree::DecisionTree() { root = NULL; } DecisionTree::~DecisionTree() { destroy(root); } ``` 现在,我们可以使用上面定义的类来构建和使用决策树。以下是一个简单的例子: ``` int main() { // 训练数据 vector<vector<double>> data = {{1.0, 2.0}, {2.0, 1.0}, {3.0, 4.0}, {4.0, 3.0}}; // 训练标签 vector<int> labels = {0, 0, 1, 1}; // 创建决策树 DecisionTree dt; dt.buildTree(data, labels); // 预测测试数据 vector<double> testData = {3.5, 2.5}; int pred = dt.predict(testData); cout << "Prediction: " << pred << endl; // 销毁决策树 return 0; } ``` 输出结果应该是“Prediction: 1”,表示测试数据属于标签1。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值