寻找C4.5算法的C ++实现

时间:2012-05-25 15:28:41

标签: c++ algorithm machine-learning decision-tree

我一直在寻找C4.5 algorithm的C ++实现,但我还没有找到它。我找到了Quinlan的C4.5 Release 8,但它是用C语言编写的......有没有人看过C4.5算法的任何开源C ++实现?

如果我找不到开源C ++实现,我正在考虑移植J48 source code(或者简单地在C版本周围编写一个包装器),但我希望我不必这样做那!如果您遇到过算法的C ++实现,请告诉我。

更新

我一直在考虑围绕C5.0算法(C5.0 is the improved version of C4.5)的C实现编写瘦C ++包装器的选项。我下载并编译了C5.0算法的C实现,但看起来它不容易移植到C ++。 C实现使用了很多全局变量,只是在C函数周围编写一个瘦C ++包装器不会导致面向对象的设计,因为每个类实例都将修改相同的全局参数。换句话说:我没有封装,这是我需要的一个非常基本的东西。

为了获得封装,我需要在C ++中创建一个完整的C代码端口,这与将Java版本(J48)移植到C ++中大致相同。

更新2.0

以下是一些具体要求:

  1. 每个分类器实例必须封装自己的数据(即除了常量变量之外没有全局变量)。
  2. 支持分类器的并发训练和分类器的并发评估。
  3. 这是一个很好的场景:假设我正在做10倍的交叉验证,我想同时用他们各自的训练集切片训练10个决策树。如果我只为每个切片运行C程序,我将不得不运行10个进程,这并不可怕。但是,如果我需要实时对数千个数据样本进行分类,那么我将不得不为每个我想要分类的样本开始一个新的过程,而且效率不高。

3 个答案:

答案 0 :(得分:2)

我可能找到了possible C++ "implementation" of C5.0 (See5.0),但我无法深入挖掘源代码,以确定它是否真的像广告宣传的那样。

为了重申我最初的担忧,该端口的作者陈述了关于C5.0算法的以下内容:

  

See5Sam [C5.0]的另一个缺点是不可能有超过   一个应用程序树同时。从中读取应用程序   每次运行可执行文件时都存档文件并存储在全局文件中   这里和那里的变量。

我会在有时间查看源代码后立即更新我的答案。

更新

看起来很不错,这是C ++界面:

class CMee5
{
  public:

    /**
      Create a See 5 engine from tree/rules files.
      \param pcFileStem The stem of the See 5 file system. The engine
             initialisation will look for the following files:
              - pcFileStem.names Vanilla See 5 names file (mandatory)
              - pcFileStem.tree or pcFileStem.rules Vanilla See 5 tree or rules
                file (mandatory)
              - pcFileStem.costs Vanilla See 5 costs file (mandatory)
    */
    inline CMee5(const char* pcFileStem, bool bUseRules);

    /**
      Release allocated memory for this engine.
    */
    inline ~CMee5();

    /**
      General classification routine accepting a data record.
    */
    inline unsigned int classifyDataRec(DataRec Case, float* pOutConfidence);

    /**
      Show rules that were used to classify the last case.
      Classify() will have set RulesUsed[] to
      number of active rules for trial 0,
      first active rule, second active rule, ..., last active rule,
      number of active rules for trial 1,
      first active rule, second active rule, ..., last active rule,
      and so on.
    */
    inline void showRules(int Spaces);

    /**
      Open file with given extension for read/write with the actual file stem.
    */
    inline FILE* GetFile(String Extension, String RW);

    /**
      Read a raw case from file Df.

      For each attribute, read the attribute value from the file.
      If it is a discrete valued attribute, find the associated no.
      of this attribute value (if the value is unknown this is 0).

      Returns the array of attribute values.
    */
    inline DataRec GetDataRec(FILE *Df, Boolean Train);
    inline DataRec GetDataRecFromVec(float* pfVals, Boolean Train);
    inline float TranslateStringField(int Att, const char* Name);

    inline void Error(int ErrNo, String S1, String S2);

    inline int getMaxClass() const;
    inline int getClassAtt() const;
    inline int getLabelAtt() const;
    inline int getCWtAtt() const;
    inline unsigned int getMaxAtt() const;
    inline const char* getClassName(int nClassNo) const;
    inline char* getIgnoredVals();

    inline void FreeLastCase(void* DVec);
}

我想说这是迄今为止我发现的最佳选择。

答案 1 :(得分:2)

C4.5的C ++实现名为 YaDT ,可在此处的“决策树”部分中找到:
http://www.di.unipi.it/~ruggieri/software.html

这是最新版本的源代码:
http://www.di.unipi.it/~ruggieri/YaDT/YaDT1.2.5.zip

从描述该工具的论文:

  

[...]在本文中,我们描述了一种新的从头开始的C ++实现的决策树归纳算法,它以C4.5的方式产生基于熵的决策树。该实现称为YaDT,是又一个决策树构建器的首字母缩写。本文的预期贡献是介绍允许获得高效系统的实现的设计原则。我们讨论了关于存储器表示和数据和元数据建模的选择,算法优化及其对存储器和时间性能的影响,以及修剪启发式的效率和准确性之间的权衡。 [...]

该论文可用here

答案 2 :(得分:1)

如果我正确地阅读它...它似乎不是组织为C API,而是组织为C程序。数据集被输入,然后它运行算法并返回一些规则描述。

我认为你应该采取的道路取决于你是否:

  1. 只是想要一个C ++接口来提供数据并从现有引擎中检索规则,或者......

  2. 想要一个可以修改的C ++实现,以便根据自己的意愿调整算法

  3. 如果你想要的是(1)那么你真的可以将程序作为一个进程生成,将它作为字符串输入,并将输出作为字符串。这可能是开发“包装器”的最简单和最具前瞻性的方法,然后您只需要开发C ++类来表示输入并对规则结果进行建模(或者将现有类与这些抽象相匹配)。 / p>

    但是如果你想要的是(2)...那么我建议你在C或Java的现有代码之上尝试任何你想到的黑客 - 无论你最舒服。您将以这种方式了解代码,如果您有任何改进,您可以将它们提供给作者上游。如果你建立一个长期的关系,那么也许你可以合作并将C代码库慢慢转发到C ++,这是一次一个方面,就像语言的设计一样。

    猜猜我只是认为“何时在罗马”的理念通常比Port-In-One-Go更好,特别是在开始时。


    响应更新:进程隔离会解决您的全局变量问题。至于性能和数据集大小,您只拥有尽可能多的内核/ CPU和内存。当你在谈论那个级别的规模问题时,你是否正在使用流程或线程通常不是问题。您遇到的开销是marshalling过于昂贵。

    证明编组是瓶颈,在多大程度上......你可以建立一个案例来解释为什么一个进程是一个线程问题。但是,可能会对现有代码进行小幅调整,以使编组更便宜,而不需要重写。