我正在使用sklearn的决策树来替换混乱且难以维护的业务规则实现,将其替换为长长的if-elif-else链。我使用所有标签的数千个测试用例来验证树,但有时我用作训练数据的规则表会出现错误,并且某些测试会随机失败。
我需要一种方法来验证树,而不是结果的测试用例。假设如果所有叶节点的 gini = 0.0,则使用不同的随机种子生成的树之间的分类不会随机变化,这是否正确?如果我需要在应用程序上强制执行,在更新训练数据时检查是否合理?
请注意,我的情况不是一个典型的分类问题,因为我已经在代码中实现了决策树,并且我想使用一种算法从精心定制的数据而不是真实世界的数据样本中生成等效树,这仅仅是因为在我的情况下,使用业务规则维护数据集比维护代码更容易。
因此,在我的数据集中,理想情况下,特征将涵盖所有可能的值范围,并为此提供一个明确的标签。例如,虽然现实世界的训练集可能是这样的:
features = [[1], [1.1], [2], [2.3]]
labels = ['sativa', 'sativa', 'indica', 'indica']
算法可以随机得出一棵树1,如下所示:
if feature < 1.75:
return 'sativa'
else:
return 'indica'
还有一棵树2,比如:
if feature < 1.55:
return 'sativa'
else:
return 'indica'
但是,我的训练集不会有随机发生的间隙。就像:
features = [[1], [1.9], [2], [2.3]]
labels = ['sativa', 'sativa', 'indica', 'indica']
因此,无论初始随机状态如何,树将始终是(显然,忽略低于 0.1 的差异):
if feature < 1.95:
return 'sativa'
else:
return 'indica'
我的问题恰恰是我需要验证训练集是否有错误,并且存在可能发生随机变化的值间隙,或者是否将同一组特征分配给不同的标签。修复随机状态并不能解决这个问题,它只能保证相同的数据将始终生成相同的树。
那么,除了在生成树之前验证这些问题的数据,或者运行足够大数倍的全面测试以排除随机变化之外,有没有办法确定树是否发生这种情况?
由于您具有标记规则,并且知道功能的可能范围,因此可以实现目标。
让我们用例子来可视化这一点
基尼缕稻
基尼指数是什么意思?它试图将训练集拟合到你刚刚创建的树中,并告诉他们的项目被标记得有多错误。因此,在 Tree1 和 Tree2 中,你有 gini = 0,因为训练集中的每个示例都会被正确地标记。
树 1 和树 2 具有相同的训练集,基尼 = 0.0。但是,如果我们尝试标记 x=[1.7],我们将得到不同的结果。
溶液
由于您了解将特征集与其各自的标签绑定的规则,因此可以确保给定一个训练集,它将始终为任何可能的特征生成输出正确结果的树,如果:
- 连续值的要素位于一个范围内,例如 [2,30]
- 您可以有一个精度阈值,例如,上面的示例至少在 0.1 步中变化
- 您可以为每种可能的组合生成示例
- 你的树有基尼系数 = 0.0
(基本上,我们告诉,由于基尼= 0.0,那么如果你给出训练集中的输入,它将被正确标记。这里没有巨大的跳跃结论)。
因此,如果:
feature1 one of numpy.linspace(1,2,11) = [1., 1.1, 1.2, 1.3, ..., 2]
feature2 one of True or False
和:
Your examples have all the possible linear combinations
You have sum(all gini nodes) = 0
The future examples are inside the condition boundaries of the training set
然后,您可以确定:
You covered all possible examples
All possible examples are labeled correctly
这样,随机初始化的随机树将具有相同的输出。它们可能仅在训练集范围之外的示例或值不遵循阈值规则的示例上有所不同。
猜猜你已经这样做了,但最好使用测试覆盖率工具创建一个训练集,以确保创建所有可能的示例。
关于在连续值中使用所有可能值的必要性
当我说你需要拥有连续值的所有可能值时,我格外小心,例如 [1.0, 1.1, 1.2, ...1.9, 2.0].如果仅在一个节点中使用该功能,则可以仅使用边界值(示例中为 1.9 和 2.0)。但是,如果您的 if-else 更复杂,我们可能会有一些不可预测的复杂场景。在评估特征 f1 的节点之后,我们稍后可以在左侧生成的节点上有一个条件,例如(如果 f2> 5),并在右侧节点中有一个条件(如果 f2 <3),或者类似的东西。你可能会得到错误的结果。
如果组合太大,最好将特征二值化。如果您有连续功能,例如:
if f1 > 3: f1 = 'many'
if 3 <= f1 < 0 = 'little'
您可以使用 DictVectorizer 对象将其转换为 (1 0) 和 (0 1)。
如果您正在增加维度的数量,您的决策树只会为这两个特征创建一个节点。如果它只检查第一个是否为真,则第二个是多余的,因为它们是互斥的。
如果您没有数据边界,这也是一种解决方案。如果你有一个没有范围的x特征,你可以像(x <0, 0<= x <5, 5 <= x)一样将其二值化,将连续值转换为(1 0 0)、(0 1 0)或(0 0 1),从而评估所有可能的组合。
通过这种方式,您可以从根本上减小组合集的大小。
希望就是这样。干杯!