决策树 (Decision Tree) 原理简述及相关算法(ID3,C4.5)

本文深入浅出地介绍了决策树这一机器学习算法,包括其基本原理、构建过程、常见算法(如ID3、C4.5)及优缺点。通过实例解析,帮助读者理解决策树如何进行分类预测。

Decision Tree 决策树:
决策树是属于机器学习监督学习分类算法中比较简单的一种,决策树是一个预测模型;他代表的是对象属性与对象值之间的一种映射关系。树中每个节点表示某个对象,而每个分叉路径则代表的某个可能的属性值,而每个叶结点则对应从根节点到该叶节点所经历的路径所表示的对象的值。决策树仅有单一输出,若欲有复数输出,可以建立独立的决策树以处理不同输出。 
下面来看个范例,就能很快理解了。

范例:
假设,我们有以下数据,表示当天是否回去玩高尔夫:

用决策树建立起来后,能得到这样的模型:

至此可以看出,说白了,决策树就是If()语句的层层嵌套,知道最后能总结出点什么。(原谅我实在不会描述点什么,不过看了这图应该对决策树有个大致的了解了吧。)

决策树中的元素:
决策树中的元素基本和树中的差不多。
最上面的一个称为根节点,如上图的Outlook,用数据中的属性作为根节点或是节点,如Humidity,Windy等。
分支使用的是节点属性中的离散型数据,如果数据是连续型的,也需要转化成离散型数据才能在决策树中展示,如上图将Outlook属性作为根节点,sunny,overcast,rain作为该节点的三个分支。

信息熵 Entropy:
现在,问题来了,在算法中如何确定使用数据的哪个属性作为根节点或是节点。当然不能随便选,我们追求的一直都是最优解,即使是局部最优。因此我们需要引入信息熵这个概念。
1948年,香农提出了“信息熵”概念。一条信息的信息量大小和它的不确定性有直接的关系。我们对一样东西越是一无所知,想要了解它就需要越多的信息。
举个栗子,如果我随机一个1-8之间的数字,给你猜,只回答你是或否。那最好的猜测方式应该是,“是不是在1-4之间?”,如果得到否,我们就知道在5-8之间,如果得到是,我们继续猜“是否在1-2之间?”。这样的话,我们只需要猜3次就能知道这个数到底是几。转化为信息熵公式就是:

根据这公式和例子,我们能得到结果是3,这是因为我们对1-8数字可能被选取的概率一无所知,如果比如说1-8选取概率并不是均匀分布的,我们就能更快的找到相应的数字,因此信息熵也会相应的变小。
总结下,如果一个变量的不确定越大,熵值也越大。

决策树归纳算法 ID3:
Information Gain:
又称信息获取量或是信息增益,将样本的所有属性分割开,分别计算,熵之和,信息增益就是二者的差值。

简单理解就是,没有属性A时候的信息量-有A时候的信息量。

举个栗子,假设我们有以下数据,买电脑的人与不买电脑的人:


可以看出,在此数据中,总数据量14个,买电脑的人9个,不买电脑的人5个,因此,Info(D)计算方式如下:

然后,我们想计算下age属性的信息量,<30的5人,<30并买电脑的2人,不买的3人,其余31-40,>40方法同理,因此计算方式如下:


因此Gain(age) = 0.940-0.694 = 0.246
再对比下其余属性Gain(Income)=0.029,Gain(Student)=0.151,Gain(Credit_rating)=0.048,因此可以看出,age属性信息量最大,因此选择age属性作为根节点。计算节点方法同理。

C4.5算法:
ID3算法存在一个问题,就是偏向于多值属性,例如,如果存在唯一标识属性ID,则ID3会选择它作为分裂属性,这样虽然使得划分充分纯净,但这种划分对分类几乎毫无用处。ID3的后继算法C4.5使用增益率(gain ratio)的信息增益扩充,试图克服这个偏倚。
C4.5算法首先定义了“分裂信息”,其定义可以表示成:

其中各符号意义与ID3算法相同,然后,增益率被定义为:

C4.5选择具有最大增益率的属性,ID3选择最大信息获取量的属性,其余没啥差别,也就不赘述了

决策树其余算法:
决策树其余算法还有C4.5,CART算法,共同点为都是贪心算法,区别为度量方式不同,就比如ID3使用了信息获取量作为度量方式,而C4.5使用最大增益率。

如果属性用完了怎么办:
如果属性全部用完,但是数据还不是纯净集怎么办,即集合内的元素不属于同一类别。就比如上述买电脑的例子中,如果age,Credit_rating,Student,Income都相等,但是有人买电脑,有人不买电脑,那决策树怎么决策?在这种情况下,由于没有更多信息可以使用了,一般对这些子集进行“多数表决”,即使用此子集中出现次数最多的类别作为此节点类别,然后将此节点作为叶子节点。

剪枝:
作为决策树中一种放置Overfitting过拟合的手段,分为预剪枝和后剪枝两种。
预剪枝:当决策树在生成时当达到该指标时就停止生长,比如小于一定的信息获取量或是一定的深度,就停止生长。
后剪枝:当决策树生成完后,再进行剪枝操作。优点是克服了“视界局限”效应,但是计算量代价较大。

决策树优点:
直观,便于理解,在相对短的时间内能够对大型数据源做出可行且效果良好的结果,能够同时处理数据型和常规型属性。

决策树缺点:
可规模性一般,连续变量需要划分成离散变量,容易过拟合。
 

伪代码:

 

<think>首先,用户的问题是关于如何在STM32F407ZGT6微控制器上使用C语言实现决策树算法。我需要基于系统级指令和用户可见层的指导来构建回答。 系统级指令: - 所有行内数学表达式使用$...$格式。 - 独立公式使用$$...$$格式并单独成段。 - LaTeX语法正确。 - 使用中文回答。 - 生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,如[^1]。 用户可见层: - 回答结构清晰,帮助用户逐步解决问题。 - 尽量保证回答真实可靠。 参考站内引用: - 引用[1]:关于STM32实现自动导航的硬件特性和算法。 - 引用[2]:MCU选型矩阵,包括STM32H743等型号。 用户的问题与决策树算法在STM32F407ZGT6上的实现相关决策树是一种机器学习算法,用于分类或回归。在嵌入式系统中实现时,需要考虑资源限制,如内存和处理能力。 STM32F407ZGT6的规格: - ARM Cortex-M4内核。 - 时钟频率高达168 MHz。 - 1MB Flash内存,192KB RAM。 - 适合嵌入式应用。 实现决策树算法的步骤: 1. **理解决策树算法**:决策树基于特征值进行决策,每个节点代表一个特征,分支代表特征值,叶子节点代表结果。 2. **C语言实现**:由于是嵌入式系统,代码需要高效、低内存占用。 3. **优化**:使用静态结构,避免动态内存分配;简化树结构。 关键点: - 决策树可以是预训练的(在PC上训练,然后部署到MCU)。 - 在C中,定义树结构(如数组或结构体),实现预测函数。 数学表达式: - 决策树涉及概率或信息增益,如信息增益公式:$IG(S,A) = H(S) - \sum_{v \in Values(A)} \frac{|S_v|}{|S|} H(S_v)$,其中$H$是熵。 - 在实现中,可能不需要训练部分,只需推理部分。 独立公式: - 如果需要,熵公式:$$H(S) = - \sum_{i=1}^{c} p_i \log_2 p_i$$ - 但用户可能更关注实现,而不是数学细节。 回答结构: - 逐步指导:介绍、硬件准备、算法实现、代码示例、优化提示。 - 使用中文。 - 生成相关问题。 引用站内内容: - 引用[1]提到PID控制、卡尔曼滤波等,但决策树不同,所以可能不直接相关。 - 引用[2]有MCU规格,STM32F407ZGT6类似STM32H743但规格较低(RAM 192KB vs 512KB)。 - 自然地添加引用标识,如果相关。 实现步骤: 1. **定义决策树结构**:在C中,使用结构体表示节点。 2. **硬编码树**:或从文件加载(但Flash有限)。 3. **预测函数**:递归或迭代遍历树。 4. **输入处理**:从传感器获取数据(如引用[1]提到的加速度计等)。 5. **输出**:分类或回归结果。 代码示例:提供简单的C代码。 相关问题生成: - 基于主题,如优化、应用等。 最终回答框架: - 引言:解释决策树在嵌入式中的应用。 - 步骤1:准备硬件和开发环境(STM32CubeIDE)。 - 步骤2:定义决策树数据结构。 - 步骤3:实现预测算法。 - 步骤4:集成到STM32项目。 - 代码示例。 - 优化建议。 - 相关问题。</think>以下是针对您问题的完整回答。我将基于STM32F407ZGT6微控制器的特性(ARM Cortex-M4内核,168 MHz主频,192KB RAM,1MB Flash),逐步解释如何在C语言中实现决策树算法决策树是一种监督学习算法,用于分类或回归任务。在嵌入式系统中,我们通常**预训练模型**(在PC上完成训练),然后将模型部署到MCU进行推理(预测),以节省计算资源。实现过程需注重内存优化和实时性,避免动态内存分配。 回答结构分为:1) 核心概念简述;2) 实现步骤;3) C代码示例;4) 优化建议;5) 相关问题。所有数学表达式均遵循指令:行内公式用`$...$`,独立公式用`$$...$$`。 ### 1. 核心概念简述 决策树通过特征值分割数据,每个节点代表一个特征判断(如“温度 > 30°C”),分支代表判断结果,叶子节点代表输出(如“开启风扇”)。推理过程是从根节点递归遍历到叶子节点。数学上,节点分裂基于信息增益或基尼不纯度,例如基尼不纯度公式: $$Gini(S) = 1 - \sum_{i=1}^{c} p_i^2$$ 其中$S$是数据集,$p_i$是类别$i$的比例。但在STM32实现中,我们**仅需推理部分**,无需训练,以减少计算负担[^1]。 ### 2. 实现步骤(共5步) 在STM32F407ZGT6上实现,需使用STM32CubeIDE开发环境,并确保工程包含标准外设库(如HAL库)。以下是详细步骤: #### 步骤1: 硬件准备与开发环境设置 - 连接STM32F407ZGT6开发板(如Discovery Kit),通过传感器(如温度传感器)获取输入数据。引用[1]提到,STM32需与传感器(如加速度计)集成,实时收集数据以作为决策树输入。 - 在STM32CubeIDE中创建新工程,启用必要外设(如ADC用于模拟输入,UART用于调试输出)。 - 优化内存:由于RAM有限(192KB),将决策树模型存储在Flash而非RAM中,使用`const`修饰符。 #### 步骤2: 定义决策树数据结构 在C语言中,决策树可表示为静态结构体数组,每个节点包含: - 特征索引(用于输入数据数组)。 - 阈值(用于比较)。 - 左/右子节点索引(或指针)。 - 如果是叶子节点,存储输出值(分类标签或回归值)。 例如,定义一个节点结构体: ```c typedef struct { int feature_index; // 特征在输入数组中的索引 float threshold; // 判断阈值,如输入值 > threshold int left_child; // 左子节点索引(用于数组遍历) int right_child; // 右子节点索引 int is_leaf; // 标志位:1表示叶子节点,0表示内部节点 float value; // 叶子节点的输出值 } TreeNode; ``` #### 步骤3: 硬编码决策树模型 在嵌入式系统中,决策树应预定义(而非动态生成)。假设一个简单二元分类树(如根据温度和湿度控制风扇): - 训练模型在PC上完成(使用Python库如scikit-learn),然后导出参数。 - 在C代码中,将树存储为常量数组。例如,一个包含3个节点的树: ```c const TreeNode tree[] = { // 根节点: 特征0(温度)> 30.0? 是->右节点(索引1), 否->左节点(索引2) {0, 30.0, 2, 1, 0, 0.0}, // 索引0 // 右子节点(叶子): 输出1(开启风扇) {0, 0.0, -1, -1, 1, 1.0}, // 索引1 // 左子节点(叶子): 输出0(关闭风扇) {0, 0.0, -1, -1, 1, 0.0} // 索引2 }; ``` - **内存估算**:每个节点约20字节。STM32F407的Flash(1MB)可存储大型树(约50,000节点),但需测试实时性。 #### 步骤4: 实现预测函数 编写递归或迭代函数进行预测。推荐迭代方式(避免栈溢出)。函数输入为特征数组(如`float input[]`),输出为预测值。 ```c float predict(const TreeNode* tree, const float* input) { int current_index = 0; // 从根节点开始 while (1) { TreeNode node = tree[current_index]; if (node.is_leaf) { return node.value; // 到达叶子节点,返回输出 } // 比较输入特征与阈值,决定子节点 if (input[node.feature_index] > node.threshold) { current_index = node.right_child; } else { current_index = node.left_child; } } } ``` #### 步骤5: 集成到主程序 在`main.c`中,结合传感器数据调用预测函数。例如,使用ADC读取温度,并通过UART输出决策。 ```c #include "stm32f4xx_hal.h" extern const TreeNode tree[]; // 声明在别处定义的树 int main(void) { HAL_Init(); SystemClock_Config(); // 初始化ADC、UART等(省略具体外设配置代码) float sensor_data[2]; // 示例: [温度, 湿度] while (1) { sensor_data[0] = read_adc(ADC_CHANNEL_0); // 读取温度传感器 sensor_data[1] = read_adc(ADC_CHANNEL_1); // 读取湿度传感器 float decision = predict(tree, sensor_data); // 预测输出 if (decision > 0.5) { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_SET); // 开启风扇 } else { HAL_GPIO_WritePin(GPIOA, GPIO_PIN_0, GPIO_PIN_RESET); // 关闭风扇 } HAL_Delay(100); // 100ms周期 } } ``` ### 3. 优化建议 - **内存优化**:使用`uint8_t`代替`int`减少节点大小(如特征索引),或压缩树结构(如位域存储标志)。 - **速度优化**:展开循环或使用查找表;对于大型树,采用广度优先遍历(减少缓存未命中)。 - **输入处理**:引用[1]提到,传感器数据可通过卡尔曼滤波去噪,提升决策准确性[^1]。 - **资源限制**:STM32F407的RAM仅192KB,避免动态分配;Flash足够存储中型树(测试最大深度10层)。 - **测试与调试**:使用STM32的DWT计数器测量推理时间(目标 < 1ms),并通过UART输出日志。 ### 4. 相关问题 基于实现过程,生成以下相关问题供深入探讨: 1. 如何在STM32上优化决策树的内存占用? 2. 决策树算法在实时嵌入式系统中有哪些应用场景(如故障诊断)? 3. 如何将PC训练的决策树模型导出为C代码? 如果您提供具体决策树结构或应用场景(如分类任务),我可以进一步优化代码示例。实现时请参考STM32CubeMX配置外设,确保资源分配合理[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

THMAIL

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值