决策树的损失函数为正则化的极大似然函数。
根据一定的准则依次选择具有好的分类能力的特征,作为决策树的结点,将数据集划分为子集,继续选择最优特征,划分子集,一层层向下,直到子集已经更够被基本正确分类,构建叶结点。就生成了一棵决策树。
但这样可能会过拟合,需要剪枝。
特征选择有以下几种:信息增益
信息增益
经验熵的计算:
def cal_H_D(data):
classes = set(data)
H_D = 0
for i in classes:
p = data[data == i].size / data.size
H_D += -1 * p * np.log2(p)
return H_D
计算信息增益,当k=1时计算信息增益率
def gain(D, A, k=0):
# D = np.array(D)
# A = np.array(A)
Aclasses = set(A)
H_D_A = 0
for i in Aclasses:
H_D_A += A[A == i].size / A.size * cal_H_D(D[A == i])
# k==1时表示信息增益率
if k == 1:
return 1 - H_D_A/cal_H_D(D)
return cal_H_D(D) - H_D_A
决策树的生成
需要先写出选择D中实例最大的类别;选择信息增益最大的特征;将D分割为非空子集三个函数:
def mostInstance(self, label):
if isinstance(label,list):
return max(label, key=label.count)
return max(label, key=label.tolist().count)
def selectFeature(self, data, A, k=0):
data = np.array(data).T
A = np.array(A)
gains = [self.gain(item, A, k) for item in data]
# print("gain:", gains)
return np.argmax(np.array(gains)), max(gains)
def subset(self, data, label, I, ai):
dataT = np.array(data).T
label = np.array(label)
temp = []
for i in range(len(dataT)):
temp.append(dataT[i][dataT[I] == ai])
subdata = np.array(temp[:I] + temp[I+1:])
sublabel = label[dataT[I] == ai]
return subdata.T, sublabel
建树。将决策树保存在一个字典中。
def creat(self, data, label, k=0):
epsilon = 0.01
if len(set(label)) <= 1:
return self.mostInstance(label)
featureId, featureGain = self.selectFeature(data, label, k)
# print("selected feature:",featureList[featureId])
if featureGain < epsilon:
return self.mostInstance(label)
dataT = np.array(data).T
tree = {}
for ai in set(dataT[featureId]):
subdata, sublabel = self.subset(data,label,featureId,ai)
tree[str(featureId)+"|"+str(ai)] = self.creat(subdata,sublabel,k)
# print(tree)
return tree
预测:递归查找tree,返回对应的子结点来预测结果
def predict(self, tree, x):
if isinstance(tree, dict):
currentFeatureStr = list(tree.keys())
currentFeaturt = currentFeatureStr[0].split("|")[0]
return self.predict(tree[str(currentFeaturt)+"|"+str(x[int(currentFeaturt)])], x)
return tree
对书中的例子运行如下
# 年龄,有工作,有自己的房子,信贷情况
example = [[1,0,0,1],[1,0,0,2],[1,1,0,2],[1,1,1,1],[1,0,0,1],
[2,0,0,1],[2,0,0,2],[2,1,1,2],[2,0,1,3],[2,0,1,3],
[3,0,1,3],[3,0,1,2],[3,1,0,2],[3,1,0,3],[3,0,0,1]]
labels = [0,0,1,1,0,0,0,1,1,1,1,1,1,1,0]
DT = DecisionTree()
Dtree = DT.creat(example, labels, 0)
print("Decision Tree:", Dtree)
print("[2,1,1,3] is class ", DT.predict(Dtree, [2,1,1,3]))
得到结果:
Decision Tree: {'2|0': {'1|0': 0, '1|1': 1}, '2|1': 1}
[2,1,1,3] is class 1
调用上面的算法时,k=1时便为C4.5算法。
剪枝问题:决策树的生成过程总是倾向于过拟合,需要通过剪枝来简化决策树,增强其泛化能力。可能以后准备好数据会写一下这部分代码(我太懒了qvq)
CART算法
CART算法用来解决输入变量为连续变量的问题,若输出变量为连续变量,则用平方误差最小化准则生成回归树;若为分类问题,用基尼指数最小化准则生成分类树。
对连续输入变量的处理:找到一个切分点将连续变量二值化,根据上述准则来选择切分点和最优切分变量
基尼指数的意义类似于熵,表示不确定性。