CART算法

  • 引言
  • 1、概述
  • 2、CART算法
    • 2.1 CART生成
      • 2.1.1 回归树的生成
      • 2.1.2 分类树的生成
    • 2.2 CART剪枝
      • 2.2.1 剪枝,形成一个子树序列
      • 2.2.2 在剪枝得到的子树序列T0,T1,T2,T3......TnT_0,T_1,T_2,T_3......T_nT0​,T1​,T2​,T3​......Tn​中通过交叉验证选取最优子树TαT_\alphaTα​
      • 2.2.3 CART剪枝算法
  • 3、基于scikit-learn决策树算法类库实现CART算法
    • 3.1 scikit-learn决策树算法类库概述
    • 3.2 实战1—分类树
    • 3.3 实战2—回归树
  • 4、决策树算法小结

引言

\quad \quad在决策树、ID3、C4.5算法一文中,简单地介绍了决策树模型,以及决策树生成算法ID3算法和ID3算法的改进版C4.5算法;在决策时剪枝算法一文中,简单地介绍了剪枝的算法。我们也提到了它的不足,比如模型是用较为复杂的熵来度量,使用了相对较为复杂的多叉树,只能处理分类不能处理回归等。对于这些问题, CART算法大部分做了改进。CART算法也就是我们下面的重点了。由于CART算法可以做回归,也可以做分类,我们分别加以介绍,先从CART分类树算法开始,重点比较和C4.5算法的不同点。接着介绍CART回归树算法,重点介绍和CART分类树的不同点。然后我们讨论CART树的建树算法和剪枝算法,最后总结决策树算法的优缺点。

1、概述

\quad \quad所谓CART算法,全名叫Classification and Regression Tree,即分类与回归树。顾名思义,相较于此前的ID3算法和C4.5算法,CART除了可以用于分类任务外,还可以完成回归分析。完整的CART算法包括特征选择、决策树生成和决策树剪枝三个部分。

\quad \quad有以下特点:

(1)CART是一棵二叉树;
(2)CART算法主要包括回归树和分类树两种。回归树用于目标变量为连续型的建模任务,其特征选择准则用的是平方误差最小准则。分类树用于目标变量为离散型的的建模任务,其特征选择准则用的是基尼指数(Gini Index),这也有别于此前ID3的信息增益准则和C4.5的信息增益比准则。无论是回归树还是分类树,其算法核心都在于递归地选择最优特征构建决策树。
(3)CART作为一种单模型,也是GBDT的基模型。当很多棵CART分类树或者回归树集成起来的时候,就形成了GBDT模型。关于GBDT,笔者将在后续中进行详细讲述,这里不再展开。

2、CART算法

\quad \quadCART算法由以下两步生成:
(1)决策树生成:递归地构建二叉决策树的过程,基于训练数据集生成决策树,生成的决策树要尽量大;自上而下从根开始建立节点,在每个节点处要选择一个最好的属性来分裂,使得子节点中的训练集尽量的纯。不同的算法使用不同的指标来定义"最好"。
(2)决策树剪枝:用验证数据集对已生成的树进行剪枝并选择最优子树,这时用损失函数最小作为剪枝的标准。【剪枝可以视为决策树算法的一种正则化手段,作为一种基于规则的非参数监督学习方法,决策树在训练很容易过拟合,导致最后生成的决策树泛化性能不高。】

2.1 CART生成

\quad \quadCART算法的决策树生成实现过程如下:

  • 使用CART算法选择特征

    • 对回归树用平方误差最小化准测,进行特征选择;
    • 对分类树用基尼指数(GINI)最小化准则,进行特征选择,
  • 根据特征切分数据集合

  • 构建树

【代码实现】简单例子:根据特征切分数据集合

import numpy as np
# 函数说明:根据给定特征和特征值,将数据集分为两个区域
"""
Parameters:dataSet - 数据集合feature - 待切分的特征value - 特征的某个值
Returns:mat0-切分的数据集合0mat1-切分的数据集1
"""def binSplitDataSet(dataSet,feature,value):mat0=dataSet[np.nonzero(dataSet[:,feature]>value)[0],:]mat1=dataSet[np.nonzero(dataSet[:,feature]<=value)[0],:]return mat0,mat1
if __name__=='__main__':testMat=np.mat(np.eye(4))mat0,mat1=binSplitDataSet(testMat,1,0.5)mat0, mat1 = binSplitDataSet(testMat, 1, 0.5)print("原始集合:\n", testMat)print("mat0:\n", mat0)print("mat1:\n", mat1)

原始集合:
[[1. 0. 0. 0.]
[0. 1. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]
mat0:
[[0. 1. 0. 0.]]
mat1:
[[1. 0. 0. 0.]
[0. 0. 1. 0.]
[0. 0. 0. 1.]]

2.1.1 回归树的生成

划分的准则是平方误差最小化

\quad \quad假设X与Y分别为输入和输出变量,并且Y是连续变量,给定训练数据集D={(x1,y1),(x2,y2),...,(xN,yn)}D=\{(x_1,y_1),(x_2,y_2),...,(x_N,y_n)\}D={(x1​,y1​),(x2​,y2​),...,(xN​,yn​)}
假定已将输入空间划分为M个单元R1,R2,...,RMR_1,R_2,...,R_MR1​,R2​,...,RM​,并且在每个单元RMR_MRM​上有一个固定的输出值cmc_mcm​,则

回归模型:
f(x)=∑m=1McmI(x∈Rm)f(x)=\sum_{m=1}^Mc_mI(x\in R_m)f(x)=m=1∑M​cm​I(x∈Rm​)
预测误差:平方误差
∑xi∈Rm(yi−f(xi))2\sum_{x_i\in R_m}(y_i-f(x_i))^2xi​∈Rm​∑​(yi​−f(xi​))2

如何选择每一个单元上的最优输出值cmc_mcm​?

\quad \quad用平方误差最小的准则求解每个单元上的最优输出值得单元RMR_MRM​上的cmc_mcm​的最优值cm^\hat{c_m}cm​^​是RMR_MRM​上的所有输入实例xix_ixi​对应的输出yiy_iyi​的均值,即
cm^=ave(yi∣xi∈Rm)\hat{c_m}=ave(y_i|x_i\in R_m)cm​^​=ave(yi​∣xi​∈Rm​)

如何对输入空间进行划分?

\quad \quad采用启发式即二元切分的方法,假设选择第j个变量x(j)x^{(j)}x(j)和它的取值s,作为切分变量和切分点,那么就会得到两个区域:
R1(j,s)={x∣x(j)≤s}和R2(j,s)={x∣x(j)>s}R_1(j,s)=\{x|x^{(j)}\leq s\} \ 和\ R_2(j,s)=\{x|x^{(j)}> s\}R1​(j,s)={x∣x(j)≤s} 和 R2​(j,s)={x∣x(j)>s}
当j和s固定时,我们要找到两个区域的代表值c1,c2使各自区间上的平方差最小:
minj,s[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2]\mathop{min}\limits_{j,s}[\mathop{min}\limits_{c_1}\sum_{x_i\in R_1(j,s)}(y_i-c_1)^2+\mathop{min}\limits_{c_2}\sum_{x_i\in R_2(j,s)}(y_i-c_2)^2]j,smin​[c1​min​xi​∈R1​(j,s)∑​(yi​−c1​)2+c2​min​xi​∈R2​(j,s)∑​(yi​−c2​)2]
前面已经知道c1,c2为区间上的平均:
c1^=ave(yi∣xi∈R1(j,s))和c2^=ave(yi∣xi∈R2(j,s))\hat{c_1}=ave(y_i|x_i\in R_1(j,s)) \ 和 \ \hat{c_2}=ave(y_i|x_i\in R_2(j,s))c1​^​=ave(yi​∣xi​∈R1​(j,s)) 和 c2​^​=ave(yi​∣xi​∈R2​(j,s))

那么对固定的 j 只需要找到最优的s,然后通过遍历所有的变量,我们可以找到最优的j,这样我们就可以得到最优对(j,s),并得到两个区间。
\quad \quad这样的回归树通常称为最小二乘回归树。算法具体流程如下:

算法5.5 (最小二乘回归树生成算法)
输入:训练数据集D
输出:回归树f(x)f(x)f(x)
\quad \quad在训练数据集所在的输入空间中,递归地将每一个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树:
(1)选择最优切分变量 j 和切分点 s,求解
minj,s[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2]\mathop{min}\limits_{j,s}[\mathop{min}\limits_{c_1}\sum_{x_i\in R_1(j,s)}(y_i-c_1)^2+\mathop{min}\limits_{c_2}\sum_{x_i\in R_2(j,s)}(y_i-c_2)^2]j,smin​[c1​min​xi​∈R1​(j,s)∑​(yi​−c1​)2+c2​min​xi​∈R2​(j,s)∑​(yi​−c2​)2]
遍历变量j,对固定的切分变量j扫描切分点s,选择使上式达到最小值的对(j,s)
(2)用选定的对 (j,s) 划分区域,并决定相应的输出值 c:
R1(j,s)={x∣x(j)≤s},R2(j,s)={x∣x(j)>s}cm^=1Nm∑xi∈Rm(j,s)yix∈Rm,m=1,2R_1(j,s)=\{x|x^{(j)}\leq s\} \ ,\ R_2(j,s)=\{x|x^{(j)}> s\}\\ \hat{c_m}=\frac{1}{N_m}\sum_{x_i\in R_m(j,s)}y_i\,x\in R_m,m=1,2R1​(j,s)={x∣x(j)≤s} , R2​(j,s)={x∣x(j)>s}cm​^​=Nm​1​xi​∈Rm​(j,s)∑​yi​x∈Rm​,m=1,2
(3)继续对两个子区域调用步骤(1)(2),直至满足停止条件。
(4)将输入空间划分为M个区域R1,R2,...,RMR_1,R_2,...,R_MR1​,R2​,...,RM​,生成决策树:
f(x)=∑m=1Mcm^I(x∈Rm)f(x)=\sum_{m=1}^M\hat{c_m}I(x\in R_m)f(x)=m=1∑M​cm​^​I(x∈Rm​)

\quad \quad除此之外,我们再定义两个参数,tolS和tolN,分别用于控制误差变化限制和切分特征最少样本数。这两个参数的意义是什么呢?就是防止过拟合,提前设置终止条件,实际上是在进行一种所谓的预剪枝(prepruning)操作,在下一小节会进行进一步讲解。

【python代码实现CART回归树】
1、创建小数据集并可视化

#-*- coding:utf-8 -*-
import matplotlib.pyplot as plt
import numpy as npdef loadDataSet(fileName):"""函数说明:加载数据Parameters:fileName - 文件名Returns:dataMat - 数据矩阵"""dataMat = []fr = open(fileName)for line in fr.readlines():curLine = line.strip().split('\t')fltLine = list(map(float, curLine))                    #转化为float类型dataMat.append(fltLine)return dataMatdef plotDataSet(filename):"""函数说明:绘制数据集Parameters:filename - 文件名Returns:无"""dataMat = loadDataSet(filename)                                        #加载数据集n = len(dataMat)                                                    #数据个数xcord = []; ycord = []                                                #样本点for i in range(n):                                                    xcord.append(dataMat[i][0]); ycord.append(dataMat[i][1])        #样本点fig = plt.figure()ax = fig.add_subplot(111)                                            #添加subplotax.scatter(xcord, ycord, s = 20, c = 'blue',alpha = .5)                #绘制样本点plt.title('DataSet')                                                #绘制titleplt.xlabel('X')plt.show()if __name__ == '__main__':filename = 'E:\jupyter-notebook\CARTdata.txt'plotDataSet(filename)


2、找到最佳切分函数

#-*- coding:utf-8 -*-
import numpy as npdef loadDataSet(fileName):#加载数据集"""函数说明:加载数据Parameters:fileName - 文件名Returns:dataMat - 数据矩阵"""dataMat = []fr = open(fileName)for line in fr.readlines():curLine = line.strip().split('\t')fltLine = list(map(float, curLine))                    #转化为float类型dataMat.append(fltLine)return dataMatdef binSplitDataSet(dataSet, feature, value):# 二元法划分数据集"""函数说明:根据特征切分数据集合Parameters:dataSet - 数据集合feature - 带切分的特征value - 该特征的值Returns:mat0 - 切分的数据集合0mat1 - 切分的数据集合1"""mat0 = dataSet[np.nonzero(dataSet[:,feature] > value)[0],:]mat1 = dataSet[np.nonzero(dataSet[:,feature] <= value)[0],:]return mat0, mat1
def regLeaf(dataSet):"""函数说明:生成叶结点Parameters:dataSet - 数据集合Returns:目标变量的均值"""return np.mean(dataSet[:,-1])
def regErr(dataSet):"""函数说明:误差估计函数Parameters:dataSet - 数据集合Returns:目标变量的总方差"""return np.var(dataSet[:,-1]) * np.shape(dataSet)[0]def chooseBestSplit(dataSet, leafType = regLeaf, errType = regErr, ops = (1,4)):"""函数说明:找到数据的最佳二元切分方式函数Parameters:dataSet - 数据集合leafType - 生成叶结点regErr - 误差估计函数ops - 用户定义的参数构成的元组Returns:bestIndex - 最佳切分特征bestValue - 最佳特征值"""import types#tolS允许的误差下降值,tolN切分的最少样本数tolS = ops[0]; tolN = ops[1]#如果当前所有值相等,则退出。(根据set的特性)if len(set(dataSet[:,-1].T.tolist()[0])) == 1:return None, leafType(dataSet)#统计数据集合的行m和列nm, n = np.shape(dataSet)#默认最后一个特征为最佳切分特征,计算其误差估计S = errType(dataSet)#分别为最佳误差,最佳特征切分的索引值,最佳特征值bestS = float('inf'); bestIndex = 0; bestValue = 0#遍历所有特征列for featIndex in range(n - 1):#遍历所有特征值for splitVal in set(dataSet[:,featIndex].T.A.tolist()[0]):#根据特征和特征值切分数据集mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal)#如果数据少于tolN,则退出if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN): continue#计算误差估计newS = errType(mat0) + errType(mat1)#如果误差估计更小,则更新特征索引值和特征值if newS < bestS:bestIndex = featIndexbestValue = splitValbestS = newS#如果误差减少不大则退出if (S - bestS) < tolS:return None, leafType(dataSet)#根据最佳的切分特征和特征值切分数据集合mat0, mat1 = binSplitDataSet(dataSet, bestIndex, bestValue)#如果切分出的数据集很小则退出if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN):return None, leafType(dataSet)#返回最佳切分特征和特征值return bestIndex, bestValueif __name__ == '__main__':myDat = loadDataSet('E:\jupyter-notebook\CARTdata.txt')myMat = np.mat(myDat)feat, val = chooseBestSplit(myMat, regLeaf, regErr, (1, 4))print(feat)print(val)

3、创建回归树

#-*- coding:utf-8 -*-
import numpy as npdef loadDataSet(fileName):"""函数说明:加载数据Parameters:fileName - 文件名Returns:dataMat - 数据矩阵"""dataMat = []fr = open(fileName)for line in fr.readlines():curLine = line.strip().split('\t')fltLine = list(map(float, curLine))                    #转化为float类型dataMat.append(fltLine)return dataMatdef binSplitDataSet(dataSet, feature, value):"""函数说明:根据特征切分数据集合Parameters:dataSet - 数据集合feature - 带切分的特征value - 该特征的值Returns:mat0 - 切分的数据集合0mat1 - 切分的数据集合1"""mat0 = dataSet[np.nonzero(dataSet[:,feature] > value)[0],:]mat1 = dataSet[np.nonzero(dataSet[:,feature] <= value)[0],:]return mat0, mat1def regLeaf(dataSet):"""函数说明:生成叶结点Parameters:dataSet - 数据集合Returns:目标变量的均值"""return np.mean(dataSet[:,-1])def regErr(dataSet):"""函数说明:误差估计函数Parameters:dataSet - 数据集合Returns:目标变量的总方差"""return np.var(dataSet[:,-1]) * np.shape(dataSet)[0]def chooseBestSplit(dataSet, leafType = regLeaf, errType = regErr, ops = (1,4)):"""函数说明:找到数据的最佳二元切分方式函数Parameters:dataSet - 数据集合leafType - 生成叶结点regErr - 误差估计函数ops - 用户定义的参数构成的元组Returns:bestIndex - 最佳切分特征bestValue - 最佳特征值"""import types#tolS允许的误差下降值,tolN切分的最少样本数tolS = ops[0]; tolN = ops[1]#如果当前所有值相等,则退出。(根据set的特性)if len(set(dataSet[:,-1].T.tolist()[0])) == 1:return None, leafType(dataSet)#统计数据集合的行m和列nm, n = np.shape(dataSet)#默认最后一个特征为最佳切分特征,计算其误差估计S = errType(dataSet)#分别为最佳误差,最佳特征切分的索引值,最佳特征值bestS = float('inf'); bestIndex = 0; bestValue = 0#遍历所有特征列for featIndex in range(n - 1):#遍历所有特征值for splitVal in set(dataSet[:,featIndex].T.A.tolist()[0]):#根据特征和特征值切分数据集mat0, mat1 = binSplitDataSet(dataSet, featIndex, splitVal)#如果数据少于tolN,则退出if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN): continue#计算误差估计newS = errType(mat0) + errType(mat1)#如果误差估计更小,则更新特征索引值和特征值if newS < bestS:bestIndex = featIndexbestValue = splitValbestS = newS#如果误差减少不大则退出if (S - bestS) < tolS:return None, leafType(dataSet)#根据最佳的切分特征和特征值切分数据集合mat0, mat1 = binSplitDataSet(dataSet, bestIndex, bestValue)#如果切分出的数据集很小则退出if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN):return None, leafType(dataSet)#返回最佳切分特征和特征值return bestIndex, bestValuedef createTree(dataSet, leafType = regLeaf, errType = regErr, ops = (1, 4)):"""函数说明:树构建函数Parameters:dataSet - 数据集合leafType - 建立叶结点的函数errType - 误差计算函数ops - 包含树构建所有其他参数的元组Returns:retTree - 构建的回归树"""#选择最佳切分特征和特征值feat, val = chooseBestSplit(dataSet, leafType, errType, ops)#r如果没有特征,则返回特征值if feat == None: return val#回归树retTree = {}retTree['spInd'] = featretTree['spVal'] = val#分成左数据集和右数据集lSet, rSet = binSplitDataSet(dataSet, feat, val)#创建左子树和右子树retTree['left'] = createTree(lSet, leafType, errType, ops)retTree['right'] = createTree(rSet, leafType, errType, ops)return retTree  if __name__ == '__main__':myDat = loadDataSet('E:\jupyter-notebook\CARTdata.txt')myMat = np.mat(myDat)print(createTree(myMat))

{‘spInd’: 0, ‘spVal’: 0.48813, ‘left’: 1.0180967672413792, ‘right’: -0.04465028571428572}

2.1.2 分类树的生成

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

定义(基尼指数):

\quad \quad分类问题中,假设有KKK个类别,样本点属于第kkk类别的概率为pkp_kpk​, 则概率分布的基尼指数定义为:
Gini(p)=∑k=1Kpk(1−pk)=1−∑k=1Kpk2Gini(p)=\sum_{k=1}^{K}p_k(1-p_k)=1-\sum_{k=1}^{K}p_k^2Gini(p)=k=1∑K​pk​(1−pk​)=1−k=1∑K​pk2​
\quad \quad对于二分类问题,若样本点属于第1个类的概率是p,则概率分布的基尼指数为Gini(p)=2p(1−p)Gini(p)=2p(1-p)Gini(p)=2p(1−p)
\quad \quad对于给定的样本集合DDD,假设有KKK个类别, 第k个类别的数量为CkC_kCk​,则样本D的基尼系数表达式为:
Gini(D)=1−∑k=1K(∣Ck∣∣D∣)2Gini(D)=1-\sum_{k=1}^{K}(\frac{|C_k|}{|D|})^2Gini(D)=1−k=1∑K​(∣D∣∣Ck​∣​)2

\quad \quad特别的,对于样本DDD,如果根据特征AAA的某个值a,把DDD分成D1D_1D1​和D2D_2D2​两部分,则在特征AAA的条件下,DDD的基尼指数表达式为:

Gini(D,A)=∣D1∣∣D∣Gini(D1)+∣D2∣∣D∣Gini(D2)Gini(D,A)=\frac{|D_1|}{|D|}Gini(D_1)+\frac{|D_2|}{|D|}Gini(D_2)Gini(D,A)=∣D∣∣D1​∣​Gini(D1​)+∣D∣∣D2​∣​Gini(D2​)

\quad \quad基尼指数Gini(D)Gini(D)Gini(D)表示集合DDD的不确定性,基尼指数Gini(D,A)Gini(D,A)Gini(D,A)表示经A=aA=aA=a分割后集合DDD的不确定性。基尼指数越大,样本集合的不确定性也就越大,这一点与熵相似。

\quad \quad对于二类分类,基尼指数和熵之半以及分类误差率的曲线如下:

\quad \quad从上图可以看出,基尼系数和熵之半的曲线非常接近,仅仅在45度角附近误差稍大。因此,基尼系数可以做为熵模型的一个近似替代。而CART分类树算法就是使用的基尼系数来选择决策树的特征。同时,为了进一步简化,CART分类树算法每次仅仅对某个特征的值进行二分,而不是多分,这样CART分类树算法建立起来的是二叉树,而不是多叉树。这样一可以进一步简化基尼系数的计算,二可以建立一个更加优雅的二叉树模型。

\quad \quad分类树生成算法具体如下:

输入:训练数据集,停止计算的条件
输出:CART决策树即分类树
根据训练数据集,从根结点开始,递归地对每个结点进行以下操作,构建二叉决策树
(1)训练数据集为D,计算现有特征对训练数据集的基尼指数,此时对于每一个特征A,对其可能取得每一个值a,根据此值将训练样本切分为D1D_1D1​和D2D_2D2​两部分,然后根据上式计算A=a基尼指数。
(2)在所有可能的特征A以及所有可能的切分点a中,选择基尼指数最小的特征及其对应的切分点作为最优的特征及切分点,从结点生成两个子结点,将训练数据集分配到子结点中去。
(3)递归的调用(1 ),(2), 直到满足停止的条件
(4)生成分类决策树

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

【python手写实现CART分类树】

from math import log
import operatordef createDataSet1():"""创造示例数据/读取数据@param dataSet: 数据集@return dataSet labels:数据集 特征集"""# 数据集dataSet = [('青年', '否', '否', '一般', '不同意'),('青年', '否', '否', '好', '不同意'),('青年', '是', '否', '好', '同意'),('青年', '是', '是', '一般', '同意'),('青年', '否', '否', '一般', '不同意'),('中年', '否', '否', '一般', '不同意'),('中年', '否', '否', '好', '不同意'),('中年', '是', '是', '好', '同意'),('中年', '否', '是', '非常好', '同意'),('中年', '否', '是', '非常好', '同意'),('老年', '否', '是', '非常好', '同意'),('老年', '否', '是', '好', '同意'),('老年', '是', '否', '好', '同意'),('老年', '是', '否', '非常好', '同意'),('老年', '否', '否', '一般', '不同意')]# 特征集labels = ['年龄', '有工作', '有房子', '信贷情况']return dataSet,labelsdef calcProbabilityEnt(dataSet):"""样本点属于第1个类的概率p,即计算2p(1-p)中的p@param dataSet: 数据集@return probabilityEnt: 数据集的概率"""numEntries = len(dataSet)  # 数据条数feaCounts = 0fea1 = dataSet[0][len(dataSet[0]) - 1]for featVec in dataSet:  # 每行数据if featVec[-1] == fea1:feaCounts += 1probabilityEnt = float(feaCounts) / numEntriesreturn probabilityEntdef splitDataSet(dataSet, index, value):"""划分数据集,提取含有某个特征的某个属性的所有数据@param dataSet: 数据集@param index: 属性值所对应的特征列@param value: 某个属性值@return retDataSet: 含有某个特征的某个属性的数据集"""retDataSet = []for featVec in dataSet:# 如果该样本该特征的属性值等于传入的属性值,则去掉该属性然后放入数据集中if featVec[index] == value:reducedFeatVec = featVec[:index] + featVec[index+1:] # 去掉该属性的当前样本retDataSet.append(reducedFeatVec) # append向末尾追加一个新元素,新元素在元素中格式不变,如数组作为一个值在元素中存在return retDataSetdef chooseBestFeatureToSplit(dataSet):"""选择最优特征@param dataSet: 数据集@return bestFeature: 最优特征所在列"""numFeatures = len(dataSet[0]) - 1 # 特征总数if numFeatures == 1:  # 当只有一个特征时return 0bestGini = 1  # 最佳基尼系数bestFeature = -1  # 最优特征for i in range(numFeatures):uniqueVals = set(example[i] for example in dataSet) # 去重,每个属性值唯一feaGini = 0 # 定义特征的值的基尼系数# 依次计算每个特征的值的熵for value in uniqueVals:subDataSet = splitDataSet(dataSet,i,value) # 根据该特征属性值分的类# 参数:原数据、循环次数(当前属性值所在列)、当前属性值prob = len(subDataSet) / float(len(dataSet))probabilityEnt = calcProbabilityEnt(subDataSet)feaGini += prob * (2 * probabilityEnt * (1 - probabilityEnt))if (feaGini < bestGini):  # 基尼系数越小越好bestGini = feaGinibestFeature = ireturn bestFeaturedef majorityCnt(classList):"""对最后一个特征分类,出现次数最多的类即为该属性类别,比如:最后分类为2男1女,则判定为男@param classList: 数据集,也是类别集@return sortedClassCount[0][0]: 该属性的类别"""classCount = {}# 计算每个类别出现次数for vote in classList:try:classCount[vote] += 1except KeyError:classCount[vote] = 1sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1),reverse = True) # 出现次数最多的类别在首位# 对第1个参数,按照参数的第1个域来进行排序(第2个参数),然后反序(第3个参数)return sortedClassCount[0][0] # 该属性的类别def createTree(dataSet,labels):"""对最后一个特征分类,按分类后类别数量排序,比如:最后分类为2同意1不同意,则判定为同意@param dataSet: 数据集@param labels: 特征集@return myTree: 决策树"""classList = [example[-1] for example in dataSet]  # 获取每行数据的最后一个值,即每行数据的类别# 当数据集只有一个类别if classList.count(classList[0]) == len(classList):return classList[0]# 当数据集只剩一列(即类别),即根据最后一个特征分类if len(dataSet[0]) == 1:return majorityCnt(classList)# 其他情况bestFeat = chooseBestFeatureToSplit(dataSet) # 选择最优特征(所在列)bestFeatLabel = labels[bestFeat] # 最优特征del(labels[bestFeat]) # 从特征集中删除当前最优特征uniqueVals = set(example[bestFeat] for example in dataSet) # 选出最优特征对应属性的唯一值myTree = {bestFeatLabel:{}} # 分类结果以字典形式保存for value in uniqueVals:subLabels = labels[:] # 深拷贝,拷贝后的值与原值无关(普通复制为浅拷贝,对原值或拷贝后的值的改变互相影响)myTree[bestFeatLabel][value] = createTree(splitDataSet(dataSet,bestFeat,value),subLabels) # 递归调用创建决策树return myTreeif __name__ == '__main__':dataSet, labels = createDataSet1()  # 创造示列数据print(createTree(dataSet, labels))  # 输出决策树模型结果

2.2 CART剪枝

\quad \quad由于决策时算法很容易对训练集过拟合,而导致泛化能力差,为了解决这个问题,我们需要对CART树进行剪枝,即类似于线性回归的正则化,来增加决策树的泛化能力。但是,有很多的剪枝方法,我们应该这么选择呢?CART采用的办法是后剪枝法,即先生成决策树,然后产生所有可能的剪枝后的CART树,然后使用交叉验证来检验各种剪枝的效果,选择泛化能力最好的剪枝策略。

\quad \quadCART回归树和CART分类树的剪枝策略除了在度量损失的时候一个使用均方差,一个使用基尼系数,算法基本完全一样。

\quad \quadCART剪枝算法由两步组成:

  • 首先从生成算法产生的决策树T0T_0T0​底端开始不断的剪枝,直到T0T_0T0​的根结点,形成子树序列{T0,T1,T2,T3......Tn}\{T_0,T_1,T_2,T_3......T_n\}{T0​,T1​,T2​,T3​......Tn​} T0T_0T0​就是没剪的,T1就是剪了一个叶结点的,T2就是又剪了一点的这样子哦!

  • 然后通过交叉验证法在独立的验证数据集上对子树序列进行测试,从中选择最优子树,具体操作如下。

2.2.1 剪枝,形成一个子树序列

\quad \quad在剪枝的过程中,对于任意的一刻子树T,其损失函数为:
Cα(T)=C(T)+α∣T∣C_\alpha(T)=C(T)+\alpha|T|Cα​(T)=C(T)+α∣T∣

\quad \quad其中,TTT为任意子树,C(T)为训练数据的预测误差,分类树是用基尼系数度量,回归树是均方差度量,∣T∣|T|∣T∣为子树的叶节点个数,α≥0\alpha\geq0α≥0为正则化参数,权衡训练数据的拟合程度与模型的复杂度。
\quad \quad当α=0时,即没有正则化,原始的生成的CART树即为最优子树。当α=∞时,即正则化强度达到最大,此时由原始的生成的CART树的根节点组成的单节点树为最优子树。当然,这是两种极端情况。一般来说,α越大,则剪枝剪的越厉害,生成的最优子树相比原生决策树就越偏小。对于固定的α,一定存在使损失函数Cα(T)C_α(T)Cα​(T)最小的唯一子树。

剪枝的思路:
\quad \quad可以用递归的方法对树进行剪枝。将α\alphaα从小增大,0=α0<α1<...<αn<+∞0=\alpha_0<\alpha_1<...<\alpha_n<+∞0=α0​<α1​<...<αn​<+∞,产生一系列的区间[αi,αi+1),i=0,1,...,n[\alpha_i,\alpha_{i+1}),i=0,1,...,n[αi​,αi+1​),i=0,1,...,n;剪枝得到的子树序列对应着区间α∈[αi,αi+1),i=0,1,...,n\alpha\in[\alpha_i,\alpha_{i+1}),i=0,1,...,nα∈[αi​,αi+1​),i=0,1,...,n的最优子树序列{T0,T1,...,Tn}\{T_0,T_1,...,T_n\}{T0​,T1​,...,Tn​},序列中的子树是嵌套的。

\quad \quad具体地,从整体数T0T_0T0​开始剪枝。

  • 对T0T_0T0​的任意内部结点t,以t为单结点树的损失函数为
    Cα(t)=C(t)+αC_\alpha(t)=C(t)+\alphaCα​(t)=C(t)+α
  • 以t为根节点的子树TtT_tTt​的损失函数是
    Cα(Tt)=C(Tt)+α∣Tt∣C_\alpha(T_t)=C(T_t)+\alpha|T_t|Cα​(Tt​)=C(Tt​)+α∣Tt​∣
  • 当α=0或者α很小时,Cα(Tt)<Cα(t)C_α(T_t)<C_α(t)Cα​(Tt​)<Cα​(t) , 当α增大到一定的程度时,在某一α\alphaα有
    Cα(Tt)=Cα(t)C_α(T_t)=C_α(t)Cα​(Tt​)=Cα​(t)
    当α继续增大时不等式反向,也就是说,如果满足下式:

α=C(T)−C(Tt)∣Tt∣−1α=\frac{C(T)−C(T_t)}{|T_t|−1}α=∣Tt​∣−1C(T)−C(Tt​)​
TtT_tTt​和t有相同的损失函数,但是t节点更少,因此t比TtT_tTt​更可取,对TtT_tTt​进行剪枝。

\quad \quad为此,对T0T_0T0​中每一内部结点t,计算
g(t)=C(T)−C(Tt)∣Tt∣−1g(t)=\frac{C(T)−C(T_t)}{|T_t|−1}g(t)=∣Tt​∣−1C(T)−C(Tt​)​
它表示剪枝后整体损失函数减少的程度。在T0T_0T0​中剪去g(t)g(t)g(t)最小的TtT_tTt​,将得到的子树作为T1T_1T1​,同时将最小的g(t)g(t)g(t)设为α1\alpha_1α1​。T1T_1T1​为区间[α1,α2)[\alpha_1,\alpha_2)[α1​,α2​)的最优子树。

\quad \quad如此剪枝下去,直至得到根结点。在这一过程中,不断地增加α\alphaα的值,产生新的区间。

2.2.2 在剪枝得到的子树序列T0,T1,T2,T3......TnT_0,T_1,T_2,T_3......T_nT0​,T1​,T2​,T3​......Tn​中通过交叉验证选取最优子树TαT_\alphaTα​

\quad \quad利用平方误差准则或者是基尼指数准则,在新的验证集中分别测试子树序列,选取里面最优的子树进行输出,便是裁剪之后的子树,即得到最优决策树

2.2.3 CART剪枝算法

算法 5.7 (CART 剪枝算法)
输入:CART算法生成的决策树
输出:最优决策树TαT_\alphaTα​
(1)设k=0k=0k=0,T=T0T=T_0T=T0​
(2)设α=+∞\alpha=+∞α=+∞(正无穷)
(3)自下而上的对内部结点t进行计算C(Tt)C(T_t)C(Tt​),∣Tt∣|T_t|∣Tt​∣和g(t)=C(t)−C(Tt)∣Tt∣−1,α=min(α,g(t))g(t)=\frac{C(t)-C(T_{t})}{|T_{t}|-1},\alpha=min(\alpha,g(t))g(t)=∣Tt​∣−1C(t)−C(Tt​)​,α=min(α,g(t))
其中,TtT_tTt​表示以t为根结点的子树,C(Tt)C(T_t)C(Tt​)是对训练数据的预测误差,∣Tt∣|T_t|∣Tt​∣是TtT_tTt​的叶结点个数。
(4)自上而下地访问内部结点t,如果有g(t)=αg(t)=\alphag(t)=α的内部结点,则进行剪枝,并对叶结点t以多数表决法决定其类,得到树T
(5) 设k=k+1,αk=α,Tk=Tk=k+1,\alpha_k=\alpha,T_k=Tk=k+1,αk​=α,Tk​=T
(6 )如果T不是由根结点单独构成的树,回到步骤4,
(7 )采用交叉验证法在子树序列上进行验证选取最优子树TαT_\alphaTα​

# -*- coding: utf-8 -*-import numpy as np
import pickle
import treePlotterdef loadDataSet(filename):'''输入:文件的全路径功能:将输入数据保存在datamat输出:datamat'''fr = open(filename)datamat = []for line in fr.readlines():cutLine = line.strip().split('\t')floatLine = map(float,cutLine)datamat.append(floatLine)return datamatdef binarySplitDataSet(dataset,feature,value):'''输入:数据集,数据集中某一特征列,该特征列中的某个取值功能:将数据集按特征列的某一取值换分为左右两个子数据集输出:左右子数据集'''matLeft = dataset[np.nonzero(dataset[:,feature] <= value)[0],:]matRight = dataset[np.nonzero(dataset[:,feature] > value)[0],:]return matLeft,matRight#--------------回归树所需子函数---------------#def regressLeaf(dataset):'''输入:数据集功能:求数据集输出列的均值输出:对应数据集的叶节点'''return np.mean(dataset[:,-1])def regressErr(dataset):'''输入:数据集(numpy.mat类型)功能:求数据集划分左右子数据集的误差平方和之和输出: 数据集划分后的误差平方和'''#由于回归树中用输出的均值作为叶节点,所以在这里求误差平方和实质上就是方差return np.var(dataset[:,-1]) * np.shape(dataset)[0]def regressData(filename):fr = open(filename)return pickle.load(fr)#--------------回归树子函数  END  --------------#def chooseBestSplit(dataset,leafType=regressLeaf,errType=regressErr,threshold=(1,4)):#函数做为参数,挺有意思thresholdErr = threshold[0];thresholdSamples = threshold[1]#当数据中输出值都相等时,feature = None,value = 输出值的均值(叶节点)if len(set(dataset[:,-1].T.tolist()[0])) == 1:return None,leafType(dataset)m,n = np.shape(dataset)Err = errType(dataset)bestErr = np.inf; bestFeatureIndex = 0; bestFeatureValue = 0for featureindex in range(n-1):for featurevalue in dataset[:,featureindex]:matLeft,matRight = binarySplitDataSet(dataset,featureindex,featurevalue)if (np.shape(matLeft)[0] < thresholdSamples) or (np.shape(matRight)[0] < thresholdSamples):continuetemErr = errType(matLeft) + errType(matRight)if temErr < bestErr:bestErr = temErrbestFeatureIndex = featureindexbestFeatureValue = featurevalue#检验在所选出的最优划分特征及其取值下,误差平方和与未划分时的差是否小于阈值,若是,则不适合划分if (Err - bestErr) < thresholdErr:return None,leafType(dataset)matLeft,matRight = binarySplitDataSet(dataset,bestFeatureIndex,bestFeatureValue)#检验在所选出的最优划分特征及其取值下,划分的左右数据集的样本数是否小于阈值,若是,则不适合划分if (np.shape(matLeft)[0] < thresholdSamples) or (np.shape(matRight)[0] < thresholdSamples):return None,leafType(dataset)return bestFeatureIndex,bestFeatureValuedef createCARTtree(dataset,leafType=regressLeaf,errType=regressErr,threshold=(1,4)):'''输入:数据集dataset,叶子节点形式leafType:regressLeaf(回归树)、modelLeaf(模型树)损失函数errType:误差平方和也分为regressLeaf和modelLeaf、用户自定义阈值参数:误差减少的阈值和子样本集应包含的最少样本个数功能:建立回归树或模型树输出:以字典嵌套数据形式返回子回归树或子模型树或叶结点'''feature,value = chooseBestSplit(dataset,leafType,errType,threshold)#当不满足阈值或某一子数据集下输出全相等时,返回叶节点if feature == None: return valuereturnTree = {}returnTree['bestSplitFeature'] = featurereturnTree['bestSplitFeatValue'] = valueleftSet,rightSet = binarySplitDataSet(dataset,feature,value)returnTree['left'] = createCARTtree(leftSet,leafType,errType,threshold)returnTree['right'] = createCARTtree(rightSet,leafType,errType,threshold)return returnTree#----------回归树剪枝函数----------#
def isTree(obj):#主要是为了判断当前节点是否是叶节点return (type(obj).__name__ == 'dict')def getMean(tree):#树就是嵌套字典if isTree(tree['left']): tree['left'] = getMean(tree['left'])if isTree(tree['right']): tree['right'] = getMean(tree['right'])return (tree['left'] + tree['right'])/2.0def prune(tree, testData):if np.shape(testData)[0] == 0: return getMean(tree)#存在测试集中没有训练集中数据的情况if isTree(tree['left']) or isTree(tree['right']):leftTestData, rightTestData = binarySplitDataSet(testData,tree['bestSplitFeature'],tree['bestSplitFeatValue'])#递归调用prune函数对左右子树,注意与左右子树对应的左右子测试数据集if isTree(tree['left']): tree['left'] = prune(tree['left'],leftTestData)if isTree(tree['right']): tree['right'] = prune(tree['right'],rightTestData)#当递归搜索到左右子树均为叶节点时,计算测试数据集的误差平方和if not isTree(tree['left']) and not isTree(tree['right']):leftTestData, rightTestData = binarySplitDataSet(testData,tree['bestSplitFeature'],tree['bestSplitFeatValue'])errorNOmerge = sum(np.power(leftTestData[:,-1] - tree['left'],2)) +sum(np.power(rightTestData[:,-1] - tree['right'],2))errorMerge = sum(np.power(testData[:,1] - getMean(tree),2))if errorMerge < errorNOmerge:print 'Merging'return getMean(tree)else: return treeelse: return tree#---------回归树剪枝END-----------#    #-----------模型树子函数-----------#
def linearSolve(dataset):m,n = np.shape(dataset)X = np.mat(np.ones((m,n)));Y = np.mat(np.ones((m,1)))X[:,1:n] = dataset[:,0:(n-1)]Y = dataset[:,-1]xTx = X.T * Xif np.linalg.det(xTx) == 0:raise NameError('This matrix is singular, cannot do inverse,\n\try increasing the second value of threshold')ws = xTx.I * (X.T * Y)return ws, X,Ydef modelLeaf(dataset):ws,X,Y = linearSolve(dataset)return wsdef modelErr(dataset):ws,X,Y = linearSolve(dataset)yHat = X * wsreturn sum(np.power(Y - yHat,2))#------------模型树子函数END-------##------------CART预测子函数------------#def regressEvaluation(tree, inputData):#只有当tree为叶节点时,才会输出return float(tree)def modelTreeEvaluation(model,inputData):#inoutData为采样数为1的特征行向量n = np.shape(inputData)X = np.mat(np.ones((1,n+1)))X[:,1:n+1] = inputDatareturn float(X * model)def treeForeCast(tree, inputData, modelEval = regressEvaluation):if not isTree(tree): return modelEval(tree,inputData)if inputData[tree['bestSplitFeature']] <= tree['bestSplitFeatValue']:if isTree(tree['left']):return treeForeCast(tree['left'],inputData,modelEval)else:return modelEval(tree['left'],inputData)else:if isTree(tree['right']):return treeForeCast(tree['right'],inputData,modelEval)else:return modelEval(tree['right'],inputData)def createForeCast(tree,testData,modelEval=regressEvaluation):m = len(testData)yHat = np.mat(np.zeros((m,1)))for i in range(m):yHat = treeForeCast(tree,testData[i],modelEval)return yHat#-----------CART预测子函数 END------------#    if __name__ == '__main__':trainfilename = 'e:\\python\\ml\\trainDataset.txt'testfilename = 'e:\\python\\ml\\testDataset.txt'trainDataset = regressData(trainfilename)testDataset = regressData(testfilename)cartTree = createCARTtree(trainDataset,threshold=(1,4))pruneTree=prune(cartTree,testDataset)treePlotter.createPlot(cartTree)y=createForeCast(cartTree,np.mat([0.3]),modelEval=regressEvaluation)

3、基于scikit-learn决策树算法类库实现CART算法

3.1 scikit-learn决策树算法类库概述

官方文档
 \quad \quadscikit-learn决策树算法类库内部实现是使用了调优过的CART树算法,既可以做分类,又可以做回归。分类决策树的类对应的是DecisionTreeClassifier,而回归决策树的类对应的是DecisionTreeRegressor。两者的参数定义几乎完全相同,但是意义不全相同。下面就对DecisionTreeClassifier和DecisionTreeRegressor的重要参数做一个总结,重点比较两者参数使用的不同点和调参的注意点。

中文详情见此博客

3.2 实战1—分类树

具体参数使用方法参考官方文档

import numpy as np
import matplotlib.pyplot as pltfrom sklearn.datasets import load_iris
from sklearn.tree import DecisionTreeClassifier, plot_tree# Parameters
n_classes = 3
plot_colors = "ryb"
plot_step = 0.02# Load data
iris = load_iris()for pairidx, pair in enumerate([[0, 1], [0, 2], [0, 3],[1, 2], [1, 3], [2, 3]]):# We only take the two corresponding featuresX = iris.data[:, pair]y = iris.target# Trainclf = DecisionTreeClassifier().fit(X, y)# Plot the decision boundaryplt.subplot(2, 3, pairidx + 1)x_min, x_max = X[:, 0].min() - 1, X[:, 0].max() + 1y_min, y_max = X[:, 1].min() - 1, X[:, 1].max() + 1xx, yy = np.meshgrid(np.arange(x_min, x_max, plot_step),np.arange(y_min, y_max, plot_step))plt.tight_layout(h_pad=0.5, w_pad=0.5, pad=2.5)Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])Z = Z.reshape(xx.shape)cs = plt.contourf(xx, yy, Z, cmap=plt.cm.RdYlBu)plt.xlabel(iris.feature_names[pair[0]])plt.ylabel(iris.feature_names[pair[1]])# Plot the training pointsfor i, color in zip(range(n_classes), plot_colors):idx = np.where(y == i)plt.scatter(X[idx, 0], X[idx, 1], c=color, label=iris.target_names[i],cmap=plt.cm.RdYlBu, edgecolor='black', s=15)plt.suptitle("Decision surface of a decision tree using paired features")
plt.legend(loc='lower right', borderpad=0, handletextpad=0)
plt.axis("tight")plt.figure()
clf = DecisionTreeClassifier().fit(iris.data, iris.target)
plot_tree(clf, filled=True)
plt.show()

3.3 实战2—回归树

具体参数参考官方文档

from sklearn.datasets import load_diabetes
from sklearn.model_selection import cross_val_score
from sklearn.tree import DecisionTreeRegressor
X, y = load_diabetes(return_X_y=True)
regressor = DecisionTreeRegressor(random_state=0)
cross_val_score(regressor, X, y, cv=10)

4、决策树算法小结

首先我们看看决策树算法的优点:

1)简单直观,生成的决策树很直观。

2)基本不需要预处理,不需要提前归一化,处理缺失值。

3)使用决策树预测的代价是O(log2m)。 m为样本数。

4)既可以处理离散值也可以处理连续值。很多算法只是专注于离散值或者连续值。

5)可以处理多维度输出的分类问题。

6)相比于神经网络之类的黑盒分类模型,决策树在逻辑上可以得到很好的解释

7)可以交叉验证的剪枝来选择模型,从而提高泛化能力。

8) 对于异常点的容错能力好,健壮性高。

我们再看看决策树算法的缺点:

1)决策树算法非常容易过拟合,导致泛化能力不强。可以通过设置节点最少样本数量和限制决策树深度来改进。

2)决策树会因为样本发生一点点的改动,就会导致树结构的剧烈改变。这个可以通过集成学习之类的方法解决。

3)寻找最优的决策树是一个NP难的问题,我们一般是通过启发式方法,容易陷入局部最优。可以通过集成学习之类的方法来改善。

4)有些比较复杂的关系,决策树很难学习,比如异或。这个就没有办法了,一般这种关系可以换神经网络分类方法来解决。

5)如果某些特征的样本比例过大,生成决策树容易偏向于这些特征。这个可以通过调节样本权重来改善。

参考资料:
1、李航《统计学习方法》
2、https://www.cnblogs.com/pinard/p/6053344.html

机器学习笔记21——决策树之CART算法原理及python实现案例相关推荐

  1. 机器学习:朴素贝叶斯模型算法原理(含实战案例)

    机器学习:朴素贝叶斯模型算法原理 作者:i阿极 作者简介:Python领域新星作者.多项比赛获奖者:博主个人首页

  2. 机器学习笔记16——决策树剪枝算法原理及python实现案例

    决策树剪枝算法 引言 1.算法目的 2.算法基本思路: 3.决策树损失函数 4.剪枝类型: 4.1 预剪枝 4.2 后剪枝 4.3 两种剪枝策略对比 引言 \quad \quad在决策树.ID3.C4 ...

  3. 利用计算机语言实现ID3算法,机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf...

    机器学习之决策树学习-id3算法-原理分析及c语言代码实现.pdf 还剩 23页未读, 继续阅读 下载文档到电脑,马上远离加班熬夜! 亲,很抱歉,此页已超出免费预览范围啦! 如果喜欢就下载吧,价低环保 ...

  4. 决策树一CART算法(第四部分)

    决策树一CART算法(第四部分) CART树的剪枝:算法步骤 输入:CART算法生成的决策树. 输出:最优决策树T 设K=0,T=T0K=0,T=T_0K=0,T=T0​ ,从完整的决策树出发 ​ k ...

  5. 2021李宏毅机器学习笔记--21 Anomaly Detection

    2021李宏毅机器学习笔记--21 Anomaly Detection(异常侦测) 摘要 一.问题描述 二.Anomaly异常 三.Anomaly Detection(异常侦测)做法 3.1 Bina ...

  6. 神经网络与机器学习 笔记—LMS(最小均方算法)和学习率退火

    神经网络与机器学习 笔记-LMS(最小均方算法)和学习率退火 LMS算法和Rosenblatt感知器算法非常想,唯独就是去掉了神经元的压制函数,Rosenblatt用的Sgn压制函数,LMS不需要压制 ...

  7. 决策树一一CART算法(第三部分)

    决策树一一CART算法(第三部分) CART-回归树模型 ​ 如果输出变量是 连续 的,对应的就是 回归 问题,对于决策树而言,输出的信息一定就是叶子结点,所以需要将连续变量按照一定的要求划分. 回归 ...

  8. LM(Levenberg–Marquardt)算法原理及其python自定义实现

    LM算法原理及其python自定义实现 LM(Levenberg–Marquardt)算法原理 LM算法python实现 实现步骤: 代码: 运行结果: LM(Levenberg–Marquardt) ...

  9. 匈牙利算法原理与Python实现

    匈牙利算法原理与Python实现 今天学习一个新的算法-匈牙利算法,用于聚类结果分析,先用图表示我当前遇到的问题: 这两列值是我用不同算法得到的聚类结果,从肉眼可以看出第一列聚类为0的结果在第二列中对 ...

  10. Apriori 算法原理以及python实现详解

    Apriori 算法原理以及python实现 ​ Apriori算法是第一个关联规则挖掘算法,也是最经典的算法.它利用逐层搜索的迭代方法找出数据库中项集的关系,以形成规则,其过程由连接(类矩阵运算)与 ...

最新文章

  1. python学习一(python与pip工具下载与安装)
  2. Galaxy 生信平台(二):生产环境部署
  3. Tomat启动自动运行一个类
  4. 七夕节脱单“神助攻”!AI教你写情话
  5. python open
  6. foxmail邮箱怎么导入邮件_163企业邮箱登录后怎么导入联系人?
  7. 一文详解神经网络模型
  8. Win10安装后必做的优化,解决磁盘100%占用
  9. python install zabbix.4.0
  10. dj鲜生-用户中心-历史购物
  11. python实现多表格合并_python 如何把两个表格数据,合并为一个呢?
  12. PAT 1044. 火星数字
  13. 从零开始:什么是Makefile分析
  14. django得到Model的全部字段名(field)
  15. 数据可视化可视化营养含量
  16. C#开源爬虫NCrawler源代码解读以及将其移植到python3.2(4)
  17. 2021苏州大学计算机考研分数,2021苏州大学考研分数线已公布
  18. NTC PTC 压敏热敏电阻
  19. koa教程--busboy模块
  20. 计算机组成原理课程笔记

热门文章

  1. 超级好用的Caps Lock大小写锁定提示及使用配置
  2. 中国程序员鼓励师都干啥? 美媒:按摩谈心样样通
  3. vscode git error: would clobber existing tag
  4. 计算机内存条能装几个,怎么查看电脑可以插多少内存条
  5. 【解决办法】解决OneDrive登陆界面空白的方法
  6. sm专用计算机是啥意思,计算机CPU的主频代表的是什么意思
  7. 在家用群晖搭建wordpress博客
  8. matlab图无线型,如何使用MATLAB进行移动无线信道模型的建模资料概述
  9. 嵌入式Linux misc 设备驱动
  10. linux 部署应用服务器,(小白指南)在 Linux 服务器上安装 Nodejs、Nginx 以及部署 Web 应用...