3

我正在使用 sklearn 的决策树将业务规则的混乱且难以维护的实现替换为长 if-elif-else 链。我使用所有标签的数千个测试用例来验证树,但有时我用作训练数据的规则表有错误,一些测试随机失败。

我需要一种方法来验证树,而不是结果的测试用例。假设如果所有叶节点的 gini = 0.0,那么使用不同随机种子生成的树之间的分类不会有随机变化,这是否正确?如果我需要在我的应用程序上强制执行,在更新训练数据时检查是否合理?

请注意,我的案例不是典型的分类问题,因为我已经在代码中实现了决策树,并且我想使用一种算法从精心定制的数据而不是真实世界的数据样本中生成等效树,仅仅是因为在我用业务规则维护数据集比维护代码更容易。

因此,在我的数据集中,这些特征将理想地涵盖所有可能的值范围,并为此提供一个明确的标签。例如,虽然现实世界的训练集可能类似于:

features = [[1], [1.1], [2], [2.3]]
labels = ['sativa', 'sativa', 'indica', 'indica']

一个算法可以随机产生一个 tree1,如:

if feature < 1.75:
    return 'sativa'
else:
    return 'indica'

还有一个 tree2 像:

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'

我的问题恰恰是我需要验证训练集是否有错误,是否存在随机变化的值差距,或者是否将同一组特征分配给不同的标签。修复随机状态并不能解决这个问题,它只能保证相同的数据总是会生成相同的树。

那么,除了在生成树之前验证这些问题的数据之外,还有什么方法可以确定这是否发生在树上,或者运行一次足够大的综合测试以排除随机变化?

4

1 回答 1

1

由于您有标记规则,并且知道特征的可能范围,因此您可以实现目标。

让我们用例子来形象化

基尼指数

基尼指数是什么意思?它试图将训练集拟合到您刚刚创建的树中,并告诉他们的项目将被标记的错误程度。因此,在 Tree1 和 Tree2 中,您的 gini = 0,因为训练集中的每个示例都将被正确标记。

Tree1 和 Tree2 具有相同的训练集,并且 gini = 0.0。但是,如果我们尝试标记 x=[1.7],我们会得到不同的结果。

解决方案

由于您了解将特征集与其各自标签绑定的规则,因此可以确保在给定训练集的情况下,它总是会生成一棵树,为任何可能的特征输出正确的结果,如果:

  • 连续值的特征在一个范围内,例如。[2,30]
  • 您可以有一个精度阈值,例如。上面的例子至少变化 0.1 步
  • 您可以为每种可能的组合生成示例
  • 你的树有 gini = 0.0

(基本上,我们说的是,因为 gini = 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)。

尽管您正在增加维度的数量,但您的决策树只会为这两个特征创建一个节点。如果它只检查第一个是否为真,那么第二个是多余的,因为它们是互斥的。

如果您的数据没有边界,这也是一种解决方案。如果您有没有范围的 ax 特征,您可以像 (x < 0, 0<= x < 5, 5 <= x) 一样将其二值化,将连续值转换为 (1 0 0)、(0 1 0) 或(0 0 1),允许您评估所有可能的组合。

这样,您可以从根本上减少组合集的大小。

希望就是这样。干杯!

于 2013-09-17T23:35:52.907 回答