决策树

1、什么是决策树?

答:决策树是一种分类和回归的基本模型,可从三个角度来理解它,即:

  • 一棵树
  • if-then规则的集合,该集合是决策树上的所有从根节点到叶节点的路径的集合
  • 定义在特征空间与类空间上的条件概率分布,决策树实际上是将特征空间划分成了互不相交的单元,每个从根到叶的路径对应着一个单元。决策树所表示的条件概率分布由各个单元给定条件下类的条件概率分布组成。实际中,哪个类别有较高的条件概率,就把该单元中的实例强行划分为该类别。

2、和其他模型比,它的优点?

答:主要的优点有两个:

  • 模型具有可解释性,容易向业务部门人员描述。
  • 分类速度快

当然也有其他优点,比如可以同时处理类别数据和数值数据。在运用决策树分类时,我们的数据一般会进行离散化。

一般采用二分法对连续属性离散化:对n个数值型数据从小到大排序,取相邻两个数据的均值作为二分点,会得到(n-1)个二分点,然后计算样本集基于各二分点二分后的信息增益,选择信息增益最大的二分点作为最优划分点。

3、如何学习一棵决策树?

可从模型,策略,算法三个角度说:
模型:决策树的学习本质上就是从训练数据集中归纳出一组分类规则,使它与训练数据矛盾较小的同时具有较强的泛华能力。从另一个角度看,学习也是基于训练数据集估计条件概率模型。
策略:决策树的损失函数通常是正则化的极大似然函数,学习的策略是以损失函数为目标函数的最小化。
算法由于这个最小化问题是一个NP完全问题,现实中,我们通常采用启发式算法(这里,面试官可能会问什么是启发式算法,要有准备,SMO算法就是启发式算法)来近似求解这一最优化问题,得到的决策树是次最优的。
该启发式算法可分为三步:

  • 特征选择(信息增益、信息增益比、基尼指数)
  • 模型生成(ID3、C4.5、CART)
  • 决策树的剪枝(一般剪枝、CART剪枝)

4、能具体谈谈这三个步骤吗?

:从总体上看,这三个步骤就是一个递归地选择最优特征,并根据该特征对训练数据进行分割,使得对各个子数据集有一个最好的分类的过程,这个过程就是划分特征空间,构建决策树的过程。

4.1 特征选择

面试官插问:如何选择最优特征呢?
简单讲,根据特征的分类能力去选择最优特征,特征分类能力的衡量通常采用信息增益或信息增益比。
面试官:谈谈你对信息增益和信息增益比的理解。
要理解信息增益,首先要理解熵这个概念。从概率统计的角度看,熵是对随机变量不确定性的度量,也可以说是对随机变量的概率分布的一个衡量。熵越大,随机变量的不确定性越大。对同一个随机变量,当它的概率分布为均匀分布时,不确定性最大,熵也最大。对有相同概率分布的不同的随机变量,取值越多的随机变量熵越大。(这是精华)。熵的公式如下:
X X X 是一个取有限个值的离散随机变量,其概率分布为
P ( X = x i ) = p i , i = 1 , 2 , ⋯   , n P\left(X=x_{i}\right)=p_{i}, \quad i=1,2, \cdots, n P(X=xi)=pi,i=1,2,,n
则随机变量 X X X 的熵定义为
H ( X ) = − ∑ i = 1 n p i log ⁡ p i H(X)=-\sum_{i=1}^{n} p_{i} \log p_{i} H(X)=i=1npilogpi

其次,要理解条件熵的概念。正如熵是对随机变量不确定性的度量一样,条件熵是指,有相关的两个随机变量X和Y,在已知随机变量X的条件下随机变量Y的不确定性。定义为X给定条件下Y的条件概率分布的熵对X的数学期望。公式如下:
H ( Y ∣ X ) = ∑ i = 1 n p i H ( Y ∣ X = x i ) \begin{array}{r} H(Y \mid X)=\sum_{i=1}^{n} p_{i} H\left(Y \mid X=x_{i}\right)\end{array} H(YX)=i=1npiH(YX=xi)
这里, p i = P ( X = x i ) , i = 1 , 2 , ⋯   , n p_{i}=P\left(X=x_{i}\right), i=1,2, \cdots, n pi=P(X=xi),i=1,2,,n

当熵和条件熵中的概率由数据估计(特别是极大似然估计)得到时,所对应的熵与条件熵分别为经验熵与经验条件熵

4.1.1信息增益

所谓信息增益,也叫互信息,就是指集合 D D D的经验熵 H ( D ) H(D) H(D)与特征 A A A给定条件下D的经验条件熵 H ( D ∣ A ) H(D|A) H(DA)之差,公式如下:
g ( D , A ) = H ( D ) − H ( D ∣ A ) g(D, A)=H(D)-H(D \mid A) g(D,A)=H(D)H(DA)
它定义为一个特征能够为分类系统带来多少信息,带来的信息越多,说明该特征越重要,相应的信息增益也就越大 H ( D ) H(D) H(D)表示系统的不确定性, H ( D ∣ A ) H(D|A) H(DA)表示引入特征A后系统的不确定性,二者相减就代表特征 A A A为系统降低不确定性的程度,也就表明特征A包含的信息足够多。

4.1.2信息增益比

信息增益去选择特征有一个问题,即偏向于选择取值较多的特征。解决思路就是对取值较多的特征进行适当的惩罚(这里如果面试官感兴趣,可以说说惩罚思想,如L1,L2都是一种惩罚的做法),这就是信息增益比所干的事。公式如下:
g R ( D , A ) = g ( D , A ) H A ( D ) g_{R}(D, A)=\frac{g(D, A)}{H_{A}(D)} gR(D,A)=HA(D)g(D,A)
其中, H A ( D ) = − ∑ i = 1 n ∣ D i ∣ ∣ D ∣ log ⁡ 2 ∣ D i ∣ ∣ D ∣ , n H_{A}(D)=-\sum_{i=1}^{n} \frac{\left|D_{i}\right|}{|D|} \log _{2} \frac{\left|D_{i}\right|}{|D|}, n HA(D)=i=1nDDilog2DDi,n 是特征 A A A 取值的个数.
当特征A有较多的取值时,分母 H ( D ) H(D) H(D)会变大,因此即使 g ( D , A ) g(D,A) g(D,A) A A A有较多的取值而比较大,但是通过一个较大的分母给其惩罚。

4.1.3基尼指数

在CART算法里详讲

4.2 决策树生成

4.2.1 ID3算法

ID3算法的核心是在决策树各个结点上对应信息增益准则选择特征,递归地构建决策树
具体方法是:

  • 1)从根结点(root node)开始,对结点计算所有可能的特征的信息增益,选择信息增益最大的特征作为结点的特征。
  • 2)由该特征的不同取值建立子节点,再对子结点递归地调用以上方法,构建决策树;直到所有特征的信息增益均很小或没有特征可以选择为止;
  • 3)最后得到一个决策树。

ID3相当于用极大似然法进行概率模型的选择
算法步骤:

输入: 训练数据集 D , D, D, 特征集 A , A, A, 國值 ε \varepsilon ε :
输出: 决笨树 T T T.
(1) 若 D D D 中所有实例属于同一类 C k , C_{k}, Ck, T T T 为单结点树,并将类 C k C_{k} Ck 作为该结 点的类标记,返回 T T T;
(2) 若 A = ∅ , A=\varnothing, A=, T T T 为单结点树,并将 D D D 中实例数最大的类 C k C_{k} Ck 作为该结点的 类标记,返回 T T T :
(3)否则,计算 A A A 中各特征对 D D D 的信息增益,选择信息增益最大 的特征 A g A_{g} Ag;
(4)如果 A g A_{g} Ag 的信息增益小于阈值 ε \varepsilon ε, 则置 T T T 为单结点树,并将 D D D 中实例数最 大的类 C k C_{k} Ck 作为该结点的类标记,返回 T T T;
(5) 否则, 对 A g A_{g} Ag 的每一可能值 a i , a_{i}, ai, A s = a i A_{s}=a_{i} As=ai D D D 分割为若干非空子集 D i , D_{i}, Di, D i D_{i} Di 中实例数最大的类作为标记,构建子结点,由结点及其子结点构成树 T , T, T, 返回 T T T
(6)对第 i i i 个子结点, 以 D i D_{i} Di 为训练集,以 A − { A g } A-\left\{A_{g}\right\} A{Ag} 为特征集,递归地调用 ( 1 ) ∼ ( 5 ) , (1) \sim (5), (1)(5), 得到子树 T i , T_{i}, Ti, 返回 T i . T_{i} . Ti.

注意点

  • 熵表示的是数据中包含的信息量大小。熵越小,数据的纯度越高,也就是说数据越趋于一致,这是我们希望的划分之后每个子节点的样子。
  • 信息增益 = 划分前熵 - 划分后熵。信息增益越大,则意味着使用属性 a 来进行划分所获得的 “纯度提升” 越大。也就是说,用属性 a 来划分训练集,得到的结果中纯度比较高。
  • ID3 仅仅适用于二分类问题。ID3 仅仅能够处理离散属性。对于连续性数据需先进行离散化处理
  • ID3算法只有树的生成,所以该算法生成的树容易产生过拟合。
4.2.2 C4.5算法

与ID3算法相似,但是做了改进,将信息增益比作为选择特征的标准。
输入: 训练数据集 D , D, D, 特征集 A , A, A, 國值 ε \varepsilon ε :
输出: 决笨树 T T T.
(1) 若 D D D 中所有实例属于同一类 C k , C_{k}, Ck, T T T 为单结点树,并将类 C k C_{k} Ck 作为该结 点的类标记,返回 T T T;
(2) 若 A = ∅ , A=\varnothing, A=, T T T 为单结点树,并将 D D D 中实例数最大的类 C k C_{k} Ck 作为该结点的 类标记,返回 T T T :
(3)否则,计算 A A A 中各特征对 D D D 的信息增益比,选择信息增益比最大 的特征 A g A_{g} Ag;
(4)如果 A g A_{g} Ag 的信息增益比小于阈值 ε \varepsilon ε, 则置 T T T 为单结点树,并将 D D D 中实例数最 大的类 C k C_{k} Ck 作为该结点的类标记,返回 T T T;
(5) 否则, 对 A g A_{g} Ag 的每一可能值 a i , a_{i}, ai, A s = a i A_{s}=a_{i} As=ai D D D 分割为若干非空子集 D i , D_{i}, Di, D i D_{i} Di 中实例数最大的类作为标记,构建子结点,由结点及其子结点构成树 T , T, T, 返回 T T T
(6)对第 i i i 个子结点, 以 D i D_{i} Di 为训练集,以 A − { A g } A-\left\{A_{g}\right\} A{Ag} 为特征集,递归地调用 ( 1 ) ∼ ( 5 ) , (1) \sim (5), (1)(5), 得到子树 T i , T_{i}, Ti, 返回 T i . T_{i} . Ti.

注意

  • C4.5 克服了 ID3 仅仅能够处理离散属性的问题,以及信息增益偏向选择取值较多特征的问题,使用信息增益比来选择特征。信息增益比 = 信息增益 / 划分前熵 选择信息增益比最大的作为最优特征。
  • C4.5 处理连续特征是先将特征取值排序,以连续两个值中间值作为划分标准。尝试每一种划分,并计算修正后的信息增益,选择信息增益最大的分裂点作为该属性的分裂点。

4.2.3 CART算法

CART单独介绍

4.3 剪枝(pruning)

决策树生成算法递归产生的决策树,直到不能继续下去为止,这样产生的树往往对训练数据的分类很准确,并且很复杂,但是对于未知的测试数据分类效果较差,即出现过拟合现象。因此,需要对树进行简化,也就是剪枝

4.3.1一般剪枝算法

不管是一般剪枝算法还是CART剪枝算法,都基于同一个思想,即减小决策树模型的整体损失函数,前面提到过,这个整体损失函数是正则化的极大似然函数
决策树学习的损失函数定义为:
C α ( T ) = ∑ i = 1 ∣ T ∣ N t H t ( T ) + α ∣ T ∣ C_{α}(T)=\sum_{i=1}^{|T|} N_tH_t(T)+ α|T| Cα(T)=i=1TNtHt(T)+αT

T T T:表示这棵子树的叶子节点,
H t ( T ) H_t(T) Ht(T):表示第t个叶子的熵,
N t N_t Nt:表示该叶子所含的训练样例的个数,
α α α:惩罚系数。

将第一项记为C(T),于是有下式:
C α ( T ) = C ( T ) + α ∣ T ∣ C_{α}(T)=C(T)+ α|T| Cα(T)=C(T)+αT

对公式的几点说明:

  • C ( T ) C(T) C(T)表示决策树T对训练数据集的预测误差, ∣ T ∣ |T| T表示决策树的叶节点个数,即模型复杂度。 α α α是权衡因子。 C ( T ) C(T) C(T)通常有两种衡量方法,一种用熵,一种用基尼指数。(CART部分讲)
  • C ( T ) C(T) C(T)表示模型对训练数据的预测误差,即模型与训练数据的拟合程度, ∣ T ∣ |T| T表示模型的复杂度,参数 α > = 0 α>=0 α>=0控制二者之间的影响,较大的α促使选择较简单的树,较小的 α α α促使选择较复杂的树,
  • 剪枝,就是当 α α α确定后,选择损失函数最小的模型。
  • 决策树的生成只考虑通过信息增益或信息增益比对训练数据更好的拟合,而决策树剪枝通过优化损失函数还考虑了减少模型复杂度;
  • 定义的损失函数的极小化等价于正则化的极大似然估计,所以,利用损失函数最小原则进行剪枝就是用正则化的极大似然估计进行模型选择。( C ( T ) C(T) C(T)用熵表示时前面有负号,去掉符号后就相当于最大化极大似然估计)

树的剪枝算法
核心步骤是:假设一组叶节点回缩到其父节点之前与之后的整体树分别为 T B T_B TB T A T_A TA,其对应的损失函数值分别是 C α ( T B ) C_α(T_B) Cα(TB) C α ( T A ) C_α(T_A) Cα(TA),如果
C α ( T A ) ≦ C α ( T B ) C_α(T_A) ≦C_α(T_B) Cα(TA)Cα(TB)
则进行剪枝,将父节点变为新的叶节点。
递归地进行以上步骤,知道不能继续为止,得到损失函数最小的子树 T α T_α Tα
注意:

  • 式子 C α ( T A ) ≦ C α ( T B ) C_α(T_A) ≦C_α(T_B) Cα(TA)Cα(TB)只考虑两个树的损失函数的差,其计算可以在局部进行,所以,决策树的剪枝算法可以由动态规划实现。
  • 参数α已经固定

4.3.2 CART剪枝

CART单独讲

面试官插问:递归的终止条件是什么呢?
通常有两个终止条件,一是所有训练数据子集被基本正确分类。二是没有合适的特征可选,即可用特征为0,或者可用特征的信息增益或信息增益比都很小了。更具体如下:

  • (1)最小节点数  
    当节点的数据量小于一个指定的数量时,不继续分裂。两个原因:一是数据量较少时,再做分裂容易强化噪声数据的作用;二是降低树生长的复杂性。提前结束分裂一定程度上有利于降低过拟合的影响。
  • (2)熵或者基尼值小于阀值。
    由上述可知,熵和基尼值的大小表示数据的复杂程度,当熵或者基尼值过小时,表示数据的纯度比较大,如果熵或者基尼值小于一定程度时,节点停止分裂。
  • (3)决策树的深度达到指定的条件 
    节点的深度可以理解为节点与决策树跟节点的距离,如根节点的子节点的深度为1,因为这些节点与跟节点的距离为1,子节点的深度要比父节点的深度大1。决策树的深度是所有叶子节点的最大深度,当深度到达指定的上限大小时,停止分裂。
  • (4)所有特征已经使用完毕,不能继续进行分裂。
    被动式停止分裂的条件,当已经没有可分的属性时,直接将当前节点设置为叶子节点。

面试官提问:什么是决策树剪枝,怎么剪枝?
答:由于根据训练数据生成的决策树往往过于复杂,导致泛华能力比较弱,也就是会过拟合。所以,实际的决策树学习中,会将已生成的决策树进行简化,以提高其泛华能力,这一过程叫做剪枝。具体说就是在已生成的决策树上裁掉一些子树或叶节点,并将其根节点或父节点作为新的叶节点。

5、CART算法

CART(classification and regression tree),分类与回归树,同样由特征选择、树的生成以及剪枝组成,既可以用于分类又可用于回归。
CART 与 ID3,C4.5 不同之处在于 CART 生成的树必须是二叉树 。也就是说,无论是回归还是分类问题,无论特征是离散的还是连续的,无论属性取值有多个还是两个,内部节点只能根据属性值进行二分。
决策树的生成就是递归地构建二叉决策树的过程。对回归树用平方误差最小化准则,对分类树用基尼指数最小化准则,进行特征选择,生成二叉树。

5.1 CART生成树

5.1.1 分类树

分类树采用基尼指数选择最优特征,同时决定该特征的最优二值切分点。

5.1.1.1 特征选择

基尼指数定义:在分类问题中,假设有K个类别,第k个类别的概率为pk, 则基尼系数的表达式为:
Gini ⁡ ( p ) = ∑ k = 1 K p k ( 1 − p k ) = 1 − ∑ k = 1 K p k 2 \operatorname{Gini}(p)=\sum_{k=1}^{K} p_{k}\left(1-p_{k}\right)=1-\sum_{k=1}^{K} p_{k}^{2} Gini(p)=k=1Kpk(1pk)=1k=1Kpk2
如果是二类分类问题,计算就更加简单了,如果属于第一个样本输出的概率是p,那属于第二个类别的概率为(1-p)则基尼系数的表达式为:
G i n i ( p ) = 2 p ( 1 − p ) {Gini}(p)=2p(1-p) Gini(p)=2p(1p)
对于给定的样本D,假设有k个类别,第k个类别的数量为Ck,则样本D的基尼系数表达式为:
Gini ⁡ ( D ) = 1 − ∑ k = 1 K ( ∣ C k ∣ ∣ D ∣ ) 2 \operatorname{Gini}(D)=1-\sum_{k=1}^{K}\left(\frac{\left|C_{k}\right|}{|D|}\right)^{2} Gini(D)=1k=1K(DCk)2
特别的,对于样本D,如果根据特征A的某个值a,把D分成D1和D2两部分,则在特征A的条件下,D的基尼系数表达式为:
Gini ⁡ ( D , A ) = ∣ D 1 ∣ ∣ D ∣ G i n i ( D 1 ) + ∣ D 2 ∣ ∣ D ∣ G i n i ( D 2 ) \operatorname{Gini}(D,A)= \frac{\left|D_{1}\right|}{|D|}{Gini}(D_1)+ \frac{\left|D_{2}\right|}{|D|}{Gini}(D_2) Gini(D,A)=DD1Gini(D1)+DD2Gini(D2)
gini指数Gini(D,A)表示特征A不同分组的数据集D的不确定性。gini指数值越大,样本集合的不确定性也就越大,这一点与熵的概念比较类似所以在此,基于以上的理论,我们可以通过gini指数来确定某个特征的最优切分点(也即只需要确保切分后某点的gini指数值最小),这就是决策树CART算法中类别变量切分的关键所在。
因此,gini指数来衡量数据的不纯度或者不确定性,同时用gini指数来决定类别变量的最优二分值得切分问题。

基尼指数与熵的比较:

  • Gini 指数的计算不需要对数运算,更加高效;
  • Gini 指数更偏向于连续属性,熵更偏向于离散属性。
  • Gini 指数表示集合的不确定性,或者是不纯度。基尼指数越大,集合不确定性越高,不纯度也越大。这一点和熵类似。另一种理解基尼指数的思路是,基尼指数是为了最小化误分类的概率。(基尼指数越小,纯度越大,特征越好)
  • 熵越小,数据的纯度越高;信息增益越大,则意味着使用属性 a 来进行划分所获得的 “纯度提升” 越大 。也就是说,用属性 a 来划分训练集,得到的结果中纯度比较高。(信息增益(比)越大,纯度越大,特征越好)

5.1.1.2 CART生成算法

根据训练数据集,从根结点开始,递归地对每个节点进行以下操作,构建二叉决策树:

输入:训练数据集 D , D, D, 特征 A , A, A, 阈值 ε \varepsilon ε
输出:CART决策树

  1. 设结点的训练数据集为 D D D, 对每一个特征 A A A, 对其可能取的每个值 a a a, 根据样本点对 A = a A=a A=a的测试为“是”或“否”将 D D D分割成 D 1 D_1 D1 D 2 D_2 D2两部分,并计算 G i n i ( D , A ) Gini(D,A) Gini(D,A)
  2. 在所有可能的特征 A A A 以及其所有可能的切分点 a a a中 , 选 择 基 尼 指 数 最 小 的 特 征 及 其 对应的切分点作为最优特征与最优切分点。依此从现结点生成两个子结点, 将训练数据集依特征分配到两个子结点中去。
  3. 对两个子结点递归地调用1.和2., 直至满足停止条件。
  4. 生成CART决策树 T T T

算法停止计算的条件是结点中的样本个数小于预定阈值, 或样本集的基尼指数小于预定阈值 (样本基本属于同一类),或者没有更多特征。

注意,CART与C4.5对离散数据和连续数据的异同

  • 对于连续数据
    具体的思路如下,比如m个样本的连续特征A有m个,从小到大排列为a1,a2,…,am,则CART算法取相邻两样本值的平均数,一共取得m-1个划分点。。对于这m-1个点,分别计算以该点作为二元分类点时的基尼系数。选择基尼系数最小的点作为该连续特征的二元离散分类点。比如取到的基尼系数最小的点为at,则小于at的值为类别1,大于at的值为类别2,这样我们就做到了连续特征的离散化。要注意的是,与ID3或者C4.5处理离散属性不同的是,如果当前节点为连续属性,则该属性后面还可以参与子节点的产生选择过程
  • 对于离散数据
    ID3或者C4.5,如果某个特征A被选取建立决策树节点,如果它有A1,A2,A3三种类别,我们会在决策树上一下建立一个三叉的节点。这样导致决策树是多叉树。但是CART分类树使用的方法不同,他采用的是不停的二分,还是这个例子,CART分类树会考虑把A分成{A1}和{A2,A3}, {A2}和{A1,A3}, {A3}和{A1,A2}三种情况,找到基尼系数最小的组合,比如{A2}和{A1,A3},然后建立二叉树节点,一个节点是A2对应的样本,另一个节点是{A1,A3}对应的节点。同时,由于这次没有把特征A的取值完全分开,后面我们还有机会在子节点继续选择到特征A来划分A1和A3。这和ID3或者C4.5不同,在ID3或者C4.5的一棵子树中,离散特征只会参与一次节点的建立。
  • C4.5不一定是二叉树,但CART一定是二叉树。

5.1.2 回归树

5.1.2.1 回归树的生成

假设 X X X Y Y Y 分别为输入和输出变量, 并且 Y Y Y 是连续变量, 给定训练数据集
D = { ( x 1 , y 1 ) , ( x 2 , y 2 ) , ⋯   , ( x N , y N ) } D=\left\{\left(x_{1}, y_{1}\right),\left(x_{2}, y_{2}\right), \cdots,\left(x_{N}, y_{N}\right)\right\} D={(x1,y1),(x2,y2),,(xN,yN)}
可选择第 j j j 个变量 x j x_{j} xj 及其取值 s s s作为切分变量和切分点,并定义两个区域
R 1 ( j , s ) = { x ∣ x j ≤ s } , R 2 ( j , s ) = { x ∣ x j > s } R_{1}(j, s)=\left\{x \mid x_{j} \leq s\right\}, \quad R_{2}(j, s)=\left\{x \mid x_{j}>s\right\} R1(j,s)={xxjs},R2(j,s)={xxj>s}
然后寻找最优切分变量 x j x_{j} xj 及最优切分点 s {s} s 具体地, 求解
j , s = arg ⁡ min ⁡ j , s [ min ⁡ c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min ⁡ c 2 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] j, s=\arg \min _{j, s}\left[\min _{c_{1}} \sum_{x_{i} \in R_{1}(j, s)}\left(y_{i}-c_{1}\right)^{2}+\min _{c_{2}} \sum_{x_{i} \in R_{2}(j, s)}\left(y_{i}-c_{2}\right)^{2}\right] j,s=argj,sminc1minxiR1(j,s)(yic1)2+c2minxiR2(j,s)(yic2)2
其中, c m c_{m} cm 是区域 R m R_{m} Rm 上的回归决策树输出 , , , 是区域 R m R_{m} Rm 上所有输入实例 x i x_{i} xi 对应的输出 y i y_{i} yi
的均值
c m = ave ⁡ ( y i ∣ x i ∈ R m ) , m = 1 , 2 c_{m}=\operatorname{ave}\left(y_{i} \mid x_{i} \in R_{m}\right), \quad m=1,2 cm=ave(yixiRm),m=1,2
对每个区域 R 1 R_{1} R1 R 2 R_{2} R2 重复上述过程, 将输入空间划分为 M M M 个区域 R 1 , R 2 , ⋯   , R M , R_{1}, R_{2}, \cdots, R_{M}, R1,R2,,RM, 在 每个区域上的输出为 c m , m = 1 , 2 , ⋯   , M , c_{m}, m=1,2, \cdots, M, cm,m=1,2,,M, 最小二乘回归树可表示为

f ( x ) = ∑ m = 1 M c m I ( x ∈ R m ) f(x)=\sum_{m=1}^{M} c_{m} I\left(x \in R_{m}\right) f(x)=m=1McmI(xRm)

最小二乘回归树生成算法

输入:训练数据集 D D D
输出:回归树 f ( x ) f(x) f(x)

  1. 选择最优切分变量 x j {x} j xj 与切分点 s {s} s
    j , s = arg ⁡ min ⁡ j , s [ min ⁡ c 1 ∑ x i ∈ R 1 ( j , s ) ( y i − c 1 ) 2 + min ⁡ c 2 ∑ x i ∈ R 2 ( j , s ) ( y i − c 2 ) 2 ] j, s=\arg \min _{j, s}\left[\min _{c_{1}} \sum_{x_{i} \in R_{1}(j, s)}\left(y_{i}-c_{1}\right)^{2}+\min _{c_{2}} \sum_{x_{i} \in R_{2}(j, s)}\left(y_{i}-c_{2}\right)^{2}\right] j,s=argj,sminc1minxiR1(j,s)(yic1)2+c2minxiR2(j,s)(yic2)2
  2. 用最优切分变量 x j x_{j} xj 与切分点 s {s} s 划分区域并决定相应的输出值
    R 1 ( j , s ) = { x ∣ x j ≤ s } , R 2 ( j , s ) = { x ∣ x j > s } c m = 1 N ∑ x i ∈ R m ( j , s ) y i , m = 1 , 2 \begin{array}{l} R_{1}(j, s)=\left\{x \mid x_{j} \leq s\right\}, \quad R_{2}(j, s)=\left\{x \mid x_{j}>s\right\} \\ \\ c_{m}=\frac{1}{N} \sum_{x_{i} \in R_{m}(j, s)} y_{i}, \quad m=1,2 \end{array} R1(j,s)={xxjs},R2(j,s)={xxj>s}cm=N1xiRm(j,s)yi,m=1,2
  3. 将输入空间划分为 M M M 个区域 R 1 , R 2 , ⋯   , R M , R_{1}, R_{2}, \cdots, R_{M}, R1,R2,,RM, 生成夫策树
    f ( x ) = ∑ m = 1 M c ^ m I ( x ∈ R m ) f(x)=\sum_{m=1}^{M} \hat{c}_{m} I\left(x \in R_{m}\right) f(x)=m=1Mc^mI(xRm)

5.1.2 分类树与回归树异同

CART回归树和CART分类树的建立算法大部分是类似的,所以这里我们只讨论CART回归树和CART分类树的建立算法不同的地方。

首先,我们要明白,什么是回归树,什么是分类树。两者的区别在于样本输出,如果样本输出是离散值,那么这是一棵分类树。如果果样本输出是连续值,那么那么这是一棵回归树

除了概念的不同,CART回归树和CART分类树的建立和预测的区别主要有下面两点:
1)连续值的处理方法不同
2)决策树建立后做预测的方式不同。

对于连续值的处理,我们知道CART分类树采用的是用基尼系数的大小来度量特征的各个划分点的优劣情况。这比较适合分类模型,但是对于回归模型,我们使用了常见的和方差的度量方式,CART回归树的度量目标是,对于任意划分特征 A A A,对应的任意划分点s两边划分成的数据集 D 1 D1 D1 D 2 D2 D2,求出使 D 1 D1 D1 D 2 D2 D2各自集合的均方差最小,同时 D 1 D1 D1 D 2 D2 D2的均方差之和最小所对应的特征和特征值划分点。表达式为:
min ⁡ ⏟ A , s [ min ⁡ ⏟ c 1 ∑ x i ∈ D 1 ( A , s ) ( y i − c 1 ) 2 + min ⁡ ⏟ c 2 ∑ x i ∈ D 2 ( A , s ) ( y i − c 2 ) 2 ] \underbrace{\min }_{A, s}[\underbrace{\min }_{c_{1}} \sum_{x_{i} \in D_{1}(A, s)}\left(y_{i}-c_{1}\right)^{2}+\underbrace{\min }_{c_{2}} \sum_{x_{i} \in D_{2}(A, s)}\left(y_{i}-c_{2}\right)^{2}] A,s min[c1 minxiD1(A,s)(yic1)2+c2 minxiD2(A,s)(yic2)2]
其中, c 1 c1 c1 D 1 D1 D1数据集的样本输出均值, c 2 c2 c2 D 2 D2 D2数据集的样本输出均值。
对于决策树建立后做预测的方式,上面讲到了CART分类树采用叶子节点里概率最大的类别作为当前节点的预测类别。而回归树输出不是类别,它采用的是用最终叶子的均值或者中位数来预测输出结果

5.2 CART剪枝算法

相比一般剪枝算法,CART剪枝算法的优势在于,不用提前确定 α α α值,而是在剪枝的同时找到最优的 α α α值。

CART剪枝算法从“完全生长“的决策树的底端剪去一些子树,使决策树变小(模型变简单),从而能够对未知数据有更准确的预测。CART剪枝算法由两步组成:首先从生成算法产生的决策树T0底端开始不断剪枝,直到 T 0 T_0 T0的根节点,形成一个子树序列 T 0 , T 1 , … , T n {T_0,T_1 ,…, T_n} T0,T1,,Tn;然后通过交叉验证法在独立的验证数据集上对子树序列进行测试,从中选择最优子树。

1、剪枝,形成一个子树序列

子树的损失函数: C α ( T ) = C ( T ) + α ∣ T ∣ C_{α}(T)=C(T)+ α|T| Cα(T)=C(T)+αT
T T T为任意子树, C ( T ) C(T) C(T)为对训练数据的预测误差,可以是基尼指数, ∣ T ∣ |T| T为子树 T T T的叶子节点个数, α > = 0 α>= 0 α>=0为参数, C α ( T ) C_α(T) Cα(T)为参数是 α α α时的子树T的整体损失, ∣ T ∣ |T| T衡量树的复杂度, α α α权衡训练数据的拟合程度与树的复杂度。
对于固定的 α α α,一定存在使损失函数 C α ( T ) C_α(T) Cα(T)最小的子树,表示为 T α T_α Tα T α T_α Tα在损失函数 C α ( T ) C_α(T) Cα(T)最小的意义下也是最优的,并且是唯一的。 α α α大的时候,最优子树 T α T_α Tα偏小(因为 α α α大,子树也大的话,损失函数必然也会很大, α α α作为惩罚项不会让子树很大);当 α α α小的时候,最优子树 T α T_α Tα偏大;极端情况,当 α = 0 α=0 α=0时,整体树是最优的,当 α α α趋向无穷大时,根节点组成的单节点树是最优的。

那么问题来了, α α α是未知的,从函数和以上的分析中我们可以看出 α α α的值对剪枝结果有很大的影响,我们如何找到这个合适的 α α α来使拟合程度与复杂度之间达到最好的平衡呢,最好的办法就是,我们将 α α α从0取到正无穷,对于每一个固定的 α α α,我们都可以找到使得 C α ( T ) C_α(T) Cα(T)最小的最优子树 T ( α ) T(α) T(α) 。当 α α α很小的时候, T 0 T_0 T0是最优子树,当 α α α很大的时候,单独一个根节点 T n T_n Tn是最优的子树。(Breiman等人证明:可以用递归的方法对树进行剪枝,将 a a a从小增大, a 0 < a 1 < … < a n < + 无 穷 a_0<a_1<…<a_n<+无穷 a0<a1<<an<+,产生一系列的区间 [ a i , a i + 1 ] , i = 0 , 1 , … , n [a_i,a_{i+1}],i =0,1,…,n [aiai+1]i=0,1,,n;剪枝得到的子树序列对应着区间 [ a i , a i + 1 ] , i = 0 , 1 , … , n [a_i,a_{i+1}],i =0,1,…,n [aiai+1]i=0,1,,n的最优子树序列 T 0 , T 1 , … , T n {T0, T1, … , Tn} T0,T1,,Tn,序列中的子树是嵌套的。)

通过无限个 α α α去求有最优的子树序列式很困难的,是一个NP完全问题,那怎么办呢?但是根据剪枝的核心思想我们知道,无论多么复杂的决策树,生成的最优子树序列都是有限的,这里记作 T 0 , T 1 , … , T n {T_0,T_1 ,…, T_n} T0,T1,,Tn,那么我们只需要寻找每一个最优子树对应的α不就可以了嘛。

先假设我们找到了当前的最优子树,且必然发生剪枝(一定要注意这句话,这是我们接下来所有推导的前提)。实际上,这棵树就是用基尼指数生成的树。具体地,从整体树 T 0 T_0 T0开始剪枝,我们每次剪枝剪的都是某个内部节点的子节点,也就是将某个内部节点的所有子节点回退到这个内部节点里,并将这个内部节点作为叶子节点。因此在计算整体的损失函数时,这个内部节点以外的值都没变,只有这个内部节点的局部损失函数改变了,因此我们本需要计算全局的损失函数,但现在只需要计算内部节点剪枝前和剪枝后的损失函数。分析方法如下:

对任意内部节点t,
剪枝前的状态:有 ∣ T t ∣ |T_t| Tt 个叶子节点,预测误差是 C ( T t ) C(T_t) C(Tt)
剪枝后的状态:只有本身一个叶子节点,预测误差是 C ( t ) C(t) C(t)
剪枝前以t结点为根结点的子树的损失函数是:
C α ( T t ) = C ( T t ) + α ∣ T t ∣ C_{α}(T_t)=C(T_t)+ α|T_t| Cα(Tt)=C(Tt)+αTt
剪枝以后以 t t t为单结点树的损失函数是:
C α ( t ) = C ( t ) + α C_{α}(t)=C(t)+ α Cα(t)=C(t)+α
α = 0 α=0 α=0 α α α充分小时,有不等式:
C α ( T t ) < C α ( t ) C_{α}(T_t)<C_{α}(t) Cα(Tt)<Cα(t)
这是因为以 t t t为单节点的树虽然简单但是分类误差较大,所以损失函数较大;当 α α α很小时,整树是最优的,所以以t为根节点的树虽然稍微复杂但是分类误差较小,所以损失函数较小。
α α α增大时,在某一 α α α有:
C α ( T t ) = C α ( t ) C ( T t ) + α ∣ T t ∣ = C ( t ) + α α = C ( t ) − C ( T t ) ∣ T t ∣ − 1 \begin{array}{c} C_{\alpha}\left(T_{t}\right)=C_{\alpha}(t) \\ \\ C\left(T_{t}\right)+\alpha\left|T_{t}\right|=C(t)+\alpha \\\\ \alpha=\frac{C(t)-C\left(T_{t}\right)}{\left|T_{t}\right|-1} \end{array} Cα(Tt)=Cα(t)C(Tt)+αTt=C(t)+αα=Tt1C(t)C(Tt)
α α α再增大时,不等式将会反方向,只要 α α α取上式的值,二者的损失函数就是相等的,但是 t t t的节点更少,因此, t t t T t T_t Tt更可取,对 T t T_t Tt进行剪枝。
对此,对 T 0 T_0 T0中计算每一内部节点 t t t,计算 g ( t ) g(t) g(t)
g ( t ) = C ( t ) − C ( T t ) ∣ T t ∣ − 1 g(t)=\frac{C(t)-C\left(T_{t}\right)}{\left|T_{t}\right|-1} g(t)=Tt1C(t)C(Tt)
它表示剪枝后整体损失函数减少的程度在T0中剪去g(t)最小的Tt,将得到的子树作为 T 1 T_1 T1,同时将最小的 g ( t ) g(t) g(t)设为 α 1 α_1 α1 T 1 T_1 T1为区间 [ α 1 , α 2 ) [α_1, α_2) [α1,α2)的最优子树。那为什么是最小的g(t)呢?
在这里插入图片描述

以图中两个点为例,结点1和结点2, g ( t ) 2 g(t)_2 g(t)2大于 g ( t ) 1 g(t)_1 g(t)1, 假设在所有结点中 g ( t ) 1 g(t)_1 g(t)1最小, g ( t ) 2 g(t)_2 g(t)2最大,两种选择方法:当选择最大值 g ( t ) 2 g(t)_2 g(t)2,即结点2进行剪枝,但此时结点1的不修剪的误差大于修剪之后的误差,即如果不修剪的话,误差变大,依次类推,对其它所有的结点的 g ( t ) g(t) g(t)都是如此,从而造成整体的累计误差更大。反之,如果选择最小值 g ( t ) 1 g(t)_1 g(t)1,即结点1进行剪枝,则其余结点不剪的误差要小于剪后的误差,不修剪为好,且整体的误差最小。从而以最小g(t)剪枝获得的子树是该 α α α值下的最优子树!

通过以上的说明,我们现在应该可以理解,将 α α α从小增大,产生一系列的区间,剪去 g ( t ) g(t) g(t)属于 [ α i , α i + 1 ) [α_i, α_{i+1}) [αi,αi+1)的对应的结点,就会得到该区间上的最优子树,如此剪下去,直到得到根结点。在这一过程中,不断增加α的值,产生新的区间。

实际上,每个区间 [ α i , α i + 1 ) [α_i, α_{i+1}) [αi,αi+1) α i α_i αi就对应着一个 g ( t ) g(t) g(t),此时 g ( t ) = α i g(t)=α_i g(t)=αi,同时也就对应着一个最优子树Ti。所有的最优子树的损失函数应该是一样的,但是它们对应α不一样,并且每棵子树的大小是不一样的。

2. 从剪枝得到的子树序列中通过交叉验证选取最优子树Tα。

具体地,利用独立的验证数据集,测试子树序列 T 0 , T 1 , … , T n T_0, T_1, … , T_n T0,T1,,Tn中各棵子树的平方误差或基尼指数。平方误差或基尼指数最小的决策树被认为是最优的决策树。在子树序列中,每棵子树 T 0 , T 1 , … , T n T_0, T_1, … , T_n T0,T1,,Tn都对应于一个参数 α 0 , α 1 , … , α n α_0, α_1, … ,α_n α0,α1,,αn。所以,当最优子树 T k T_k Tk确定时,对应的 α k α_k αk也确定了,即得到最优决策树 T α T_α Tα

CART剪枝算法
输入:CART决策树
输出:最优决策树

  1. k = 0 , T = T 0 k=0, T=T_{0} k=0,T=T0
  2. α = + ∞ \alpha=+\infty α=+
  3. 自下而上地对各内部结点 t t t 计算 C ( T t ) , ∣ T t ∣ , C\left(T_{t}\right),\left|T_{t}\right|, C(Tt),Tt, 以及
    g ( t ) = C ( t ) − C ( T t ) ∣ T t ∣ − 1 α = min ⁡ ( α , g ( t ) ) \begin{array}{l} g(t)=\frac{C(t)-C\left(T_{t}\right)}{\left|T_{t}\right|-1} \\ \alpha=\min (\alpha, g(t)) \end{array} g(t)=Tt1C(t)C(Tt)α=min(α,g(t))
    其中, T t T_t Tt表示以 t t t 为根结点的子树, C ( T t ) C\left(T_{t}\right) C(Tt) 是对训练数据的预测误差, ∣ T t ∣ \left|T_{t}\right| Tt T t T_{t} Tt 的叶结点个数。
  4. 自下而上地访问内部结点 t , t, t, 如果有 g ( t ) = α , g(t)=\alpha, g(t)=α, 则进行剪枝, 并对叶结点以多数表决法决定其类别, 得到树 T T T
  5. k = k + 1 , α k = α , T k = T k=k+1, \alpha_{k}=\alpha, T_{k}=T k=k+1,αk=α,Tk=T
  6. 如果 T T T 不是由根结点单独构成的树, 则回到步骤4.
  7. 采用交叉验证法在子树序列 T 0 , T 1 , ⋯   , T n T_{0}, T_{1}, \cdots, T_{n} T0,T1,,Tn 中选取最优子树 T α T_{\alpha} Tα

6 决策树中的剪枝

6.1 预剪枝

预剪枝主要是再节点进行扩展之前,先计算当前的划分是否能带来模型泛化能力的提升,若不能,则停止生长。
停止树生长的方法:

  • 1)当树到达一定深度的时候,停止书的生长
  • 2)当到达当前节点的样本数量小于某个阈值时,停止树的生长
  • 3)计算每次分裂对测试集准确度提升。当小于某个阈值时,不再生长

这种方法思想简单、算法简单、效率高,适于处理大规模数据。但如何准确地估计何时停止树的生长(即上述方法中的深度或阈值),针对不同问题会有很大差别,需要一定经验判断。且预剪枝存在一定局限性,有欠拟合的风险,虽然当前的划分会导致测试集准确率降低,但在之后的划分中,准确率可能会有显著上升。

6.2后剪枝

后剪枝的核心思想是让算法生成一棵完全生长的决策树,然后从最底层向上计算是否剪枝。剪枝过程将子树删除,用一个叶子结点替代,该结点的类别同样按照多数投票的原则进行判断。同样地,后剪枝也可以通过在测试集上的准确率进行判断,如果剪枝过后准确率有所提升,则进行剪枝。相比于预剪枝,后剪枝方法通常可以得到泛化能力更强的决策树,但时间开销会更大。
常见的后剪枝方法包括错误率降低剪枝(Reduced Error Pruning,REP)、悲观剪枝(Pessimistic Error Pruning,PEP)、代价复杂度剪枝(Cost ComplexityPruning,CCP)、最小误差剪枝(Minimum Error Pruning,MEP)、CVP(Critical Value Pruning)、OPP(OptimalPruning)等方法,这些剪枝方法各有利弊,关注不同的优化角度,下面选取著名的CART剪枝方法CCP进行介绍。
详见第5部分CART剪枝

7 小结

7.1 ID3,C4.5和CART的一个比较

算法支持模型树结构特征选择连续值处理缺失值处理剪枝
ID3分类多叉树信息增益不支持不支持不支持
C4.5分类多叉树信息增益比不支持不支持不支持
CART分类,回归二叉树基尼系数,均方差支持支持支持
7.1.1 ID3算法的不足
  • 没有考虑连续特征,比如长度、密度等连续值无法再ID3中运用
  • ID3采用信息增益大的特征优先建立决策树的节点。很快就被人发现,在相同条件下,取值比较多的特征比取值少的特征信息增益大。比如一个变量有2个值,各为1/2,另一个变量为3个值,各为1/3,其实他们都是完全不确定的变量,但是取3个值的比取2个值的信息增益大。
  • ID3未考虑缺失值
  • 未考虑过拟合问题
7.1.2 C4.5算法的不足
  • 1.因为决策树算法特别容易过拟合,所以对于生成的决策数一定要进行剪枝
  • 2.C4.5生成的是多叉树,一个父节点可以对于多个子节点,这样的结构会比二叉树的效率低。
  • 3.C4.5只能用于分类
  • 4.C4.5由于使用了熵模型,里面有大量的耗时的对数运算,如果是连续值还有大量的排序运算
7.1.3 CART算法的不足

(1)应该大家有注意到,无论是ID3,C4.5还是CART,在做特征选择的时候都是选择最优的一个特征来做分类决策,但是大多数,分类决策不应该是由某一个特征决定的,而是应该由一组特征决定的。这样决策得到的决策树更加准确。这个决策树叫做多变量决策树(multl-varlate declsion tree).在选择最优特征的时候,多变量决策树不是选择某一个最优特征,而是选择最优的一个特征线性组合来做决策。这个算法的代表是OC1,这里不多介绍。
(2)如果样本发生一点点的改动,就会导致树结构的剧烈改变。这个可以通过集成学习里面的随机森林之类的方法解决。

7.2 决策树算法优缺点

7.2.1 决策树算法优点
  • 1)简单直观,生成决策树很直观。
  • 2)基本不需要预处理,不需要提前归一化,处理缺失值。
  • 3)使用决策树预测的代价是O(log2m)。 m为样本数。
  • 4)既可以处理离散值也可以处理连续值。很多算法只是专注于离散值或者连续值。
  • 5)可以处理多维度输出的分类问题。
  • 6)相比于神经网络之类的黑盒分类模型,决策树在逻辑上可以得到很好的解释
  • 7)可以交叉验证的剪枝来选择模型,从而提高泛化能力。
  • 8) 对于异常点的容错能力好,健壮性高。
7.2.2 决策树算法缺点
  • 1)决策树算法非常容易过拟合,导致泛化能力不强。可以通过设置节点最少样本数量和限制决策树深度来改进。
  • 2)决策树会因为样本发生一点点的改动,就会导致树结构的剧烈改变。这个可以通过集成学习之类的方法解决。
  • 3)寻找最优的决策树是一个NP难的问题,我们一般是通过启发式方法,容易陷入局部最优。可以通过集成学习之类的方法来改善。
  • 4)有些比较复杂的关系,决策树很难学习,比如异或。这个就没有办法了,一般这种关系可以换神经网络分类方法来解决。
  • 5)如果某些特征的样本比例过大,生成决策树容易偏向于这些特征。这个可以通过调节样本权重来改善。

7.3 寻找最优的决策树为什么是一个NP问题

在决策树算法中,寻找最优决策树是一个NP完全问题。决策树的这一特点,说明我们无法利用计算机在多项式时间内,找出全局最优的解。
也正因为如此,大多数决策树算法都采用启发式的算法,如贪心算法,来指导对假设空间的搜索。可以说,决策树最后的结果,是在每一步、每一个节点上做的局部最优选择。决策树得到的结果,是没法保证为全局最优的。

7.4 树形结构为什么不需要归一化?

因为数值缩放不影响分裂点位置,对树模型的结构不造成影响。
按照特征值进行排序的,排序的顺序不变,那么所属的分支以及分裂点就不会有不同。而且,树模型是不能进行梯度下降的,因为构建树模型(回归树)寻找最优点时是通过寻找最优分裂点完成的,因此树模型是阶跃的,阶跃点是不可导的,并且求导没意义,也就不需要归一化。
既然树形结构(如决策树、RF)不需要归一化,那为何非树形结构比如Adaboost、SVM、LR、Knn、KMeans之类则需要归一化。
对于线性模型,特征值差别很大时,运用梯度下降的时候,损失等高线是椭圆形,需要进行多次迭代才能到达最优点。
但是如果进行了归一化,那么等高线就是圆形的,促使SGD往原点迭代,从而导致需要的迭代次数较少。

7.5 决策树学习过程

决策树学习的本质是从训练数据集中归纳出一组分类规则。能正确分类训练数据集的决策树可能有多个,也可能一个没有。我们需要一个分类错误少,且具有良好泛化能力的决策树。

由于从所有可能决策树中选择最优决策树是NP完全问题,所以现实生活中决策树学习算法通常采用启发式方法,近似求解最优化问题。这样得到的决策树是次最优sub-optimal。
决策树的学习包括三个过程:

  • 特征选择
  • 决策树的生成(考虑局部最优)
  • 决策树的剪枝(考虑全局最优)
    • 预剪枝(Pre-Pruning):在决策树生成同时进行剪枝
    • 后剪枝(Post-Pruning):在决策树构造完成后进行剪枝(通常后剪枝效果好于预剪枝,因此后面该节讨论的都是后剪枝)

决策树生成的具体步骤如下:

  • 1、选择一个最优特征(使得各个子集有一个在当前条件下最好的分类),然后按照该特征划分数据集。
  • 2、如果这些子集能够基本正确分类,则构建叶节点
  • 3、否则,对这些子集继续选择新的最优特征,然后继续分割
  • 4、循环往复,直到所有训练数据子集已经被基本正确分类,或没有合适的特征为止

可知,在生成决策树时,我们采取的是【贪心】的方法。

对于上述生成步骤生成的决策树,对于训练数据拟合会很好,但是泛化能力却很差,有过拟合的问题,因此需要剪枝步骤来防止过拟合。

而决策树的剪枝步骤,则是通过极小化决策树的整体损失函数(loss function)来实现的。常见损失函数的形式为
C α = C ( T ) + α T C_α=C(T)+αT Cα=C(T)+αT
C ( T ) C(T) C(T)是模型对训练数据的误差, ∣ T ∣ |T| T是模型复杂度
而剪枝,就是在 α α α确定时,选择令损失函数最小的模型。具体来说,就是通过递归的从树的叶子结点往上缩,判断剪枝后的树是否比未剪枝的树的 C α C_α Cα值小,小则剪枝成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值