- 回归决策树的逻辑
- 终止分类
- 分类思路
回归决策树简称回归树, 是解决回归问题的模型. 回归树 依据数据特征对数据二分类, 直到分到一定程度, 得到叶结点. 对每个叶结点数据都计算均值, 作为叶结点的值. 给一个数据样本预测其值, 即先看这个数据会跑到哪个叶结点里, 然后 赋以叶结点值.
因为是二分类, 分类过程都一样, 所以可以使用递归算法.
递归算法要解决的问题有两个:
一是不断重复部分(分类思路),
二是终止重复的部分(不再分下去).
这里给个玩具, 方便理解. 阶乘.
def f(x):
if x==1:
return x
return x*f(x-1)
终止分类
我们什么时候不再分类了?
一是 数据样本量不足, 假定只有 只有一个样本, 那么就没必要分下去了. 还有一种是一个叶结点只有一个样本会过拟合, 所以会设置较大的样本量阈值.
二是 深度.
这里我们想一下过程, 开始样本比较丰富, 但子类如果样本量不足, 就意味着这个子类不应当出现所以此时就不应当分类了.
数据一般是矩阵, 每一行是一个样本, 每一列是一个特征. 我们需要选最好的特征, 最好的分类点进行分类. 很明显这需要遍历所有特征, 所有特征的数值, 也即整个矩阵的所有元素.
那,什么叫最好?
我们是预测是取平均值的, 如果想均值能够有代表性, 我们需要结点内数值不是那么的分散, 也即两类子数据的值的方差和最小.
#分裂结点
import torch
def binode(x,y,feature, split):
x0 = x[x[:,feature] > split, :].squeeze(1)
y0 = y[x[:,feature] > split]
x1 = x[x[:,feature] <= split, :].squeeze(1)
y1 = y[x[:,feature] <= split]
return x0, y0, x1, y1
这里挤掉空客张量.
#最佳分裂点
def best_split(x, y, bs):
#"返回最佳特征编号, 最佳分裂点"
n, m =x.size()
# n 为数据 x 的样本量, m 为数据的特征个数
if n < bs:
return None, torch.mean(y)
#此时不需分裂, 不返回特征, 最佳分裂点定为 这个结点的数据标签的均值.
best_var = torch.inf
feature = 0
split = 0
for j in range(m):
for i in x[:, j].unique():
# 去掉重复值
x0, y0, x1, y1 = binode(x, y, j, i)
if (x0.size()[0] < bs) or (x1.size()[0] < bs):
continue
# 如果子类样本量过少就不再分下去, 考虑最佳分裂点也要丢掉这种情况.
sc = torch.var(y0)*y0.size()[0] + torch.var(y1)* y1.size()[0]
if sc < best_var:
best_var = sc
feature = j
split = i
x0, y0, x1, y1 = binode(x, y, feature, split)
if (x0.size()[0] < bs) or (x1.size()[0] < bs):
return None, torch.mean(y)
#最佳分裂点拿到后, 是否返回, 要看分裂是否必要.
return feature, split
# 构建回归树.
def creatree(x,y, bs):
feature, split = best_split(x, y, bs)
if feature == None:
return split
# 注意如果执行 if 语句, 这一轮的 createe() 结束.
retree = {}
retree["node_feature"] = feature
retree["node_split"] = split
x0, y0, x1, y1 = binode(x, y, feature)
retree["left"] = creatree(x0, y0, bs)
retree["right"] = creatree(x1, y1, bs)
return retree
#调用
x = torch.randn((100,20))
y = torch.randn((100,1))
creatree(x,y,3)



