决策树用一连串是否问题把样本分成几类。我先说明它怎么选问题,再用一组「天气、温度 → 是否出门」的数据把树建出来。
决策树从根节点开始,每次选一个问题把样本拆开,走到叶子就给出类别。选哪个问题,看拆完之后类别变得有多纯。
纯度用熵衡量。一组样本里类别越杂,熵越大;全部属于同一类时,熵为 0。某一问法拆分前后熵的下降量叫信息增益,树在当前节点选择增益最大的问题。
8 条样本、两个特征,标签是是否出门:
| 天气 | 温度 | 出门 |
|---|---|---|
| 晴 | 热 | 否 |
| 晴 | 适中 | 否 |
| 阴 | 热 | 是 |
| 雨 | 适中 | 是 |
| 雨 | 凉 | 是 |
| 阴 | 凉 | 是 |
| 晴 | 凉 | 是 |
| 雨 | 热 | 否 |
其中 5 条是「是」、3 条是「否」,整组熵约 0.954。按温度拆,信息增益是 0.36;按天气拆,增益是 0.266。根节点因此先问温度。
- 凉:3 条全是「是」,成为叶子。
- 热:再问天气。晴、雨为「否」,阴为「是」。
- 适中:再问天气。晴为「否」,雨为「是」。
新样本「晴、凉」走到「凉」这片叶子,预测出门;「雨、热」走到「热 → 雨」,预测不出门。
import math
from collections import Counter
def entropy(labels):
n = len(labels)
h = 0.0
for count in Counter(labels).values():
p = count / n
h -= p * math.log2(p)
return h
def majority(labels):
return Counter(labels).most_common(1)00
def build(rows, features):
labels = row\[-1 for row in rows]
if len(set(labels)) == 1 or not features:
return {"leaf": majority(labels)}
base = entropy(labels)
best_gain = -1
best = None
for index, name in features:
groups = {}
for row in rows:
groups.setdefault(rowindex, \[\]).append(row)
remain = 0.0
for group in groups.values():
remain += len(group) / len(rows) * entropy(row\[-1 for row in group])
gain = base - remain
if gain > best_gain:
best_gain = gain
best = (index, name, groups, gain)
index, name, groups, gain = best
rest = (i, n) for i, n in features if i != index
return {
"feature": name,
"gain": round(gain, 3),
"branches": {value: build(group, rest) for value, group in groups.items()},
}
def predict(node, sample):
while "leaf" not in node:
node = node"branches"sample\[node\["feature"]]
return node"leaf"
rows = [
("晴", "热", "否"),
("晴", "适中", "否"),
("阴", "热", "是"),
("雨", "适中", "是"),
("雨", "凉", "是"),
("阴", "凉", "是"),
("晴", "凉", "是"),
("雨", "热", "否"),
]
tree = build(rows, list(enumerate("天气", "温度")))
print(predict(tree, {"天气": "晴", "温度": "凉"}))
print(predict(tree, {"天气": "雨", "温度": "热"}))
输出是 是 和 否。
特征是数值时,问题变成「是否小于某个阈值」。算法把这一列的取值排序,尝试相邻值的中点,仍然取信息增益最大的那个切分。CART 用基尼不纯度代替熵,比较的仍是拆分之后哪一组更纯。
样本很少时,树可以一直拆到每片叶子只有一个样本,这时它记住的是这批数据的细节。限制树的深度,或规定节点里至少还要有若干样本才继续拆,叶子会保留多数类,换一批新样本时更稳。