[置顶] 《统计学习方法》 决策树 CART生成算法 回归树 Python实现
2017-08-23 15:21
651 查看
先说明一下在看《统计学习方法》Cart回归树的时候懵懵的,也没又例子。然后发现《机器学习实战》P162有讲到这个,仔细看了一下。
所以这下面是《机器学习实战》的代码,但书上没有什么原理,如果不太懂原理的话,会有点难以理解。而它的实现就是《统计学习方法》P69的算法5.5(最小二乘回归树的生成算法)。
引用《统计学习方法》P69的算法5.5
输入:训练数据集 D;
输出:回归树 f(x).
在训练数据集所在的输入空间中,递归地将每个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树:
(1) 选择最优切分变量 j 与切分点 s(比如选择切分向量第j维来划分,就根据这个s>特征值和<=特征值来划分成2个子集),求解
minj,s[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2]
遍历变量 j,对固定的切分变量 j 扫描切分点 s
(2) 用选定的对 (j,s) 划分区域并决定相应的输出值:
R1(j,s)=x|x(j)≤s,R2(j,s)=x|x(j)>s
c^m=1Nm∑xi∈Rm(j,s)yi,x∈Rm,m=1,2
(3) 继续对两个子区域调用步骤(1),(2),直至满足停止条件。
(4) 将输入空间分为 M 个区域 R1,R2,⋯,RM,生成决策树:
f(x)=∑Mm=1c^mI(x∈Rm)
好了,我们来看代码吧。
这上面是从文件中加载数据集的代码,没太多可以说的,接着看。
看下注释,其实就是对应上面的c^m=1Nm∑xi∈Rm(j,s)yi,x∈Rm,m=1,2这个公式了。
你可以先看下api:numpy.var就知道了它是一个计算方差的。
var = mean(abs(x - x.mean())**2)
方差乘以行数不就是mean(abs(x - x.mean())**2)然后行数,对应minc1∑(yi−c1)2了么。
所以这个函数对应着minj,s[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2].让我们乘胜逐北。
这个就是建树的代码了,大体思想就是通过判断是否需要继续划分,如果不需要说明已经是叶节点了,返回叶节点的值;如果需要划分,则继续用递归的形式继续划分下去。继续看下如何选择最好的特征划分。
这段代码,说白了就是上面算法(1)的步骤:
[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2]
对应这个代码
上面
就是寻找最小的误差值了。
然后这里有3个条件会被判定这个集合是作为叶子节点的(好不让他继续划分下去呀)。
1、类标签的值都是一样的,说明没必要划分了,直接返回None, 叶子节点值。
2、总体误差减去最小的误差如果小于容许的误差下降值(tolS=ops[0]),直接返回None, 叶子节点值。
3、如果划分的两个子集,任一方小于4(tolN=ops[1])个样本的话,直接返回None, 叶子节点值。
好了,测试一下。
你成功了吗?
下面贴下完整的代码。
所以这下面是《机器学习实战》的代码,但书上没有什么原理,如果不太懂原理的话,会有点难以理解。而它的实现就是《统计学习方法》P69的算法5.5(最小二乘回归树的生成算法)。
引用《统计学习方法》P69的算法5.5
输入:训练数据集 D;
输出:回归树 f(x).
在训练数据集所在的输入空间中,递归地将每个区域划分为两个子区域并决定每个子区域上的输出值,构建二叉决策树:
(1) 选择最优切分变量 j 与切分点 s(比如选择切分向量第j维来划分,就根据这个s>特征值和<=特征值来划分成2个子集),求解
minj,s[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2]
遍历变量 j,对固定的切分变量 j 扫描切分点 s
(2) 用选定的对 (j,s) 划分区域并决定相应的输出值:
R1(j,s)=x|x(j)≤s,R2(j,s)=x|x(j)>s
c^m=1Nm∑xi∈Rm(j,s)yi,x∈Rm,m=1,2
(3) 继续对两个子区域调用步骤(1),(2),直至满足停止条件。
(4) 将输入空间分为 M 个区域 R1,R2,⋯,RM,生成决策树:
f(x)=∑Mm=1c^mI(x∈Rm)
好了,我们来看代码吧。
def loadDataSet(self, fileName): #加载数据 dataMat = [] fr = open(fileName) for line in fr.readlines(): #遍历每一行 curLine = line.strip().split('\t') fltLine = map(float, curLine) #将里面的值映射成float,否则是字符串类型的 dataMat.append(fltLine) return dataMat
这上面是从文件中加载数据集的代码,没太多可以说的,接着看。
def regLeaf(self, dataSet): #将均值作为叶子节点 return np.mean(dataSet[:, -1]) #类别的均值
看下注释,其实就是对应上面的c^m=1Nm∑xi∈Rm(j,s)yi,x∈Rm,m=1,2这个公式了。
def regErr(self, dataSet):#计算误差 return np.var(dataSet[:, -1]) * np.shape(dataSet)[0] #方差乘以行数
你可以先看下api:numpy.var就知道了它是一个计算方差的。
var = mean(abs(x - x.mean())**2)
方差乘以行数不就是mean(abs(x - x.mean())**2)然后行数,对应minc1∑(yi−c1)2了么。
所以这个函数对应着minj,s[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2].让我们乘胜逐北。
def createTree(self, dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)): feat, val = self.chooseBestSplit(dataSet, leafType, errType, ops) if feat == None: return val #说明是叶节点,直接返回均值 retTree = {} retTree['spInd'] = feat #记录是用哪个特征作为划分 retTree['spVal'] = val #记录是用哪个特征作为划分(以便于查找的时候,相等进入左树,不等进入右树) lSet, rSet = self.binSplitDataSet(dataSet, feat, val) #按返回的特征来选择划分子集 retTree['left'] = self.createTree(lSet, leafType, errType, ops) #用划分的2个子集的左子集,递归建树 retTree['right'] = self.createTree(rSet, leafType, errType, ops) return retTree
这个就是建树的代码了,大体思想就是通过判断是否需要继续划分,如果不需要说明已经是叶节点了,返回叶节点的值;如果需要划分,则继续用递归的形式继续划分下去。继续看下如何选择最好的特征划分。
def chooseBestSplit(self, dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)): tolS = ops[0] #容许的误差下降值 tolN = ops[1] #划分的最少样本数 if len(set(dataSet[:, -1].T.tolist()[0])) == 1: #类标签的值都是一样的,说明没必要划分了,直接返回 return None, leafType(dataSet) m, n = np.shape(dataSet) #m是行数,n是列数 S = errType(self, dataSet) #计算总体误差 bestS = np.inf #np.inf是无穷大的意思,因为我们要找出最小的误差值,如果将这个值设得太小,遍历时很容易会将这个值当成最小的误差值了 bestIndex = 0 bestValue = 0 for featIndex in range(n-1): #遍历每一个维度 for splitVal in set(dataSet[:,featIndex].T.A.tolist()[0]): #选出不同的特征值,进行划分,勘误:这里跟书上不一样,需修改 mat0, mat1 = self.binSplitDataSet(dataSet, featIndex, splitVal) #子集的划分 if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN): #划分的两个数据子集,只要有一个小于4,就说明没必要划分 continue newS = errType(self, mat0) + errType(self, mat1) #计算误差 if newS < bestS: #更新最小误差值 bestIndex = featIndex bestValue = splitVal bestS = newS if (S - bestS) < tolS: #检查新切分能否降低误差 return None, leafType(self, dataSet) mat0, mat1 = self.binSplitDataSet(dataSet, bestIndex, bestValue) if (np.shape(mat0)[0] < tolN) or(np.shape(mat1)[0] < tolN): #检查是否需要划分(如果两个子集的任一方小于4则没必要划分) return None, leafType(self, dataSet) return bestIndex, bestValue
这段代码,说白了就是上面算法(1)的步骤:
[minc1∑xi∈R1(j,s)(yi−c1)2+minc2∑xi∈R2(j,s)(yi−c2)2]
对应这个代码
newS = errType(self, mat0) + errType(self, mat1)。
上面
for featIndex in range(n-1):以及里面的循环,
for splitVal in set(dataSet[:,featIndex].T.A.tolist()[0]):
就是寻找最小的误差值了。
然后这里有3个条件会被判定这个集合是作为叶子节点的(好不让他继续划分下去呀)。
1、类标签的值都是一样的,说明没必要划分了,直接返回None, 叶子节点值。
2、总体误差减去最小的误差如果小于容许的误差下降值(tolS=ops[0]),直接返回None, 叶子节点值。
3、如果划分的两个子集,任一方小于4(tolN=ops[1])个样本的话,直接返回None, 叶子节点值。
好了,测试一下。
if __name__ == '__main__': regTree = RegressionTree() myMat = regTree.loadDataSet('ex0.txt') myMat = np.mat(myMat) print regTree.createTree(myMat)
你成功了吗?
下面贴下完整的代码。
# --*-- coding:utf-8 --*--
import numpy as np
class RegressionTree: #回归树
def loadDataSet(self, fileName): #加载数据 dataMat = [] fr = open(fileName) for line in fr.readlines(): #遍历每一行 curLine = line.strip().split('\t') fltLine = map(float, curLine) #将里面的值映射成float,否则是字符串类型的 dataMat.append(fltLine) return dataMat
def binSplitDataSet(self, dataSet, feature, value): #按某列的特征值来划分数据集
mat0 = dataSet[np.nonzero(dataSet[:, feature] > value)[0], :] #勘误:这里跟书上不一样,需修改
mat1 = dataSet[np.nonzero(dataSet[:, feature] <= value)[0], :] #np.nonzero(...)[0]返回一个列表
return mat0, mat1
def regLeaf(self, dataSet): #将均值作为叶子节点
return np.mean(dataSet[:, -1])
def regErr(self, dataSet):#计算误差
return np.var(dataSet[:, -1]) * np.shape(dataSet)[0] #方差乘以行数
def createTree(self, dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)): feat, val = self.chooseBestSplit(dataSet, leafType, errType, ops) if feat == None: return val #说明是叶节点,直接返回均值 retTree = {} retTree['spInd'] = feat #记录是用哪个特征作为划分 retTree['spVal'] = val #记录是用哪个特征作为划分(以便于查找的时候,相等进入左树,不等进入右树) lSet, rSet = self.binSplitDataSet(dataSet, feat, val) #按返回的特征来选择划分子集 retTree['left'] = self.createTree(lSet, leafType, errType, ops) #用划分的2个子集的左子集,递归建树 retTree['right'] = self.createTree(rSet, leafType, errType, ops) return retTree
def chooseBestSplit(self, dataSet, leafType=regLeaf, errType=regErr, ops=(1, 4)): tolS = ops[0] #容许的误差下降值 tolN = ops[1] #划分的最少样本数 if len(set(dataSet[:, -1].T.tolist()[0])) == 1: #类标签的值都是一样的,说明没必要划分了,直接返回 return None, leafType(dataSet) m, n = np.shape(dataSet) #m是行数,n是列数 S = errType(self, dataSet) #计算总体误差 bestS = np.inf #np.inf是无穷大的意思,因为我们要找出最小的误差值,如果将这个值设得太小,遍历时很容易会将这个值当成最小的误差值了 bestIndex = 0 bestValue = 0 for featIndex in range(n-1): #遍历每一个维度 for splitVal in set(dataSet[:,featIndex].T.A.tolist()[0]): #选出不同的特征值,进行划分,勘误:这里跟书上不一样,需修改 mat0, mat1 = self.binSplitDataSet(dataSet, featIndex, splitVal) #子集的划分 if (np.shape(mat0)[0] < tolN) or (np.shape(mat1)[0] < tolN): #划分的两个数据子集,只要有一个小于4,就说明没必要划分 continue newS = errType(self, mat0) + errType(self, mat1) #计算误差 if newS < bestS: #更新最小误差值 bestIndex = featIndex bestValue = splitVal bestS = newS if (S - bestS) < tolS: #检查新切分能否降低误差 return None, leafType(self, dataSet) mat0, mat1 = self.binSplitDataSet(dataSet, bestIndex, bestValue) if (np.shape(mat0)[0] < tolN) or(np.shape(mat1)[0] < tolN): #检查是否需要划分(如果两个子集的任一方小于4则没必要划分) return None, leafType(self, dataSet) return bestIndex, bestValue
if __name__ == '__main__': regTree = RegressionTree() myMat = regTree.loadDataSet('ex0.txt') myMat = np.mat(myMat) print regTree.createTree(myMat)
# print myMat[:, 1]
# regTree.binSplitDataSet(np.mat(np.eye(4)), 1, 0.5)
# print myMat[[1, 2], :]
# print myMat
# print np.var(myMat[:, -1]) * np.shape(myMat)[0]
print myMat[:,1].T.A.tolist()[0]
相关文章推荐
- [置顶] 《统计学习方法》 决策树 CART生成算法 分类树 Python实现
- [置顶] 《统计学习方法》 决策树 ID3和C4.5 生成算法 Python实现
- 机器学习经典算法详解及Python实现--CART分类决策树、回归树和模型树
- CART分类决策树、回归树和模型树算法详解及Python实现
- 机器学习经典算法详解及Python实现--CART分类决策树、回归树和模型树
- [置顶] 【算法 机器学习】MATLAB、R、python三种编程语言实现简单线性回归算法比较
- 《统计学习方法》读书笔记-----决策树:ID3,C4.5生成算法和剪枝
- 《统计学习方法》读书笔记-----决策树:CART算法
- Python 实现决策树分类算法
- 机器学习算法的Python实现 (3):CART决策树与剪枝处理
- 逻辑回归算法简介及用python实现
- 决策树ID3 算法python实现
- 《统计学习方法》读书笔记-----决策树:ID3,C4.5生成算法和剪枝
- 李航《统计学习方法》第三章——用Python实现KNN算法(MNIST数据集)
- 《统计学习方法》读书笔记-----决策树:CART算法
- Logistic Regression 逻辑回归算法例子,python代码实现
- 《统计学习方法》读书笔记-----决策树:ID3,C4.5生成算法和剪枝
- 《统计学习方法》读书笔记-----决策树:CART算法
- 分类算法-----决策树(ID3)算法原理和Python实现
- 李航《统计学习方法》第五章——用Python实现决策树(MNIST数据集)