前回C4.5を理解しようとして失敗しましたが、ID3のバリアントを実装しました-もともとは好奇心のためでしたが、最終的には過剰キルの複数ディスパッチコードジェネレーターの一部として使用されました。ただし、これは大規模なデータセットを処理することはなく、良い仕事です。あなたは私がしたことのほとんどを真似るのは上手ではありませんが、いくつかの例外はありますが、もちろん間違いから少し学びました。
私はエキスパートシステムのディシジョンツリーを構築するという観点から考える傾向があるため、次の用語を使用する傾向があります。混乱を招く場合は申し訳ありません...
Column = Question ..... A question the expert system might ask
Row = Conclusion ... A possible conclusion the expert system might reach
Cell = Answer ....... For the question and conclusion, what answer should
the user be expected to give
実際、私の場合、論理ゲートの真理値表のように、別の列に結論を出しました。したがって、行番号は単なる行番号でした。これにより、同じ結論を複数の行に表示できない場合でも表現できないXORスタイルの問題を処理できました。これがあなたに関係があるかどうかはわかりません。いずれにせよ、私はこれを無視します-とにかく次に尋ねる質問の選択の詳細を見るまでは、それほど大きな違いはありません。データマイニングの場合、おそらくいずれにしても、ターゲットの結論として扱う特定の情報はありません。「結論」は、質問をやめることに決めたときに残っているものです。
したがって、これまでに導出された各決定木ノードについて、一連の未解決の質問(列)と、まだ排除されていない結論(行)のセットがあります。それが私がしたことです。追加する価値がある唯一の点は、ビットベクトルを使用したことです。
IIRC、C ++ std::vector<bool>
とstd::array<bool>
もビットベクトルとして実装されていますが、それでも一度に一つのアイテムを操作する一連の操作のためのSTLアルゴリズム、に依存しています。私は、一定期間にわたって徐々に構築され、基礎となるビット単位演算子を使用する独自のビットベクトルクラスを使用しましたstd::vector<CHUNK>
(ここCHUNK
で、unsigned int型は、通常32ビット幅です)。
C ++ 11またはBoostには、より優れたビットベクトルオプションがあり、いくつかの場所には優れたライブラリがいくつかあるはずです-符号なし整数のセットで作業することになるプログラムの種類はたくさんあります。自分の使い方から切り替えるのが面倒なので、私はそれらについてあまり知りません。
ただし、ビットベクトルは、セットがほぼ密集している場合に最適です。この場合、行のセットが明らかな問題です。デシジョンツリーのルートノードのみが完全に密な行セットを持ちます。ルートから遠ざかるにつれて、行セットはまばらになり、各質問に回答することで、行のセットが2つ以上の互いに素な次のノードの行セットに分散されます。
そのため、行番号の単純なソート済み配列がこれらのセットの最適な表現になる可能性があります。ただし、「スパースビットベクトル」が価値がある場合もあります。可能な実装の1つは、ソートされたペアの配列です。各ペアの最初のペアはブロックの最初の行IDで、2番目はそのブロックの固定サイズのビットベクトルです。たとえば、行番号35はブロック32(35 & ~(32 - 1)
)のビット位置3(35 & (32 - 1)
)に格納されます。ビットベクトルがゼロ以外であるペアのみを保存する場合、これにより、IDの並べ替えられた配列と単純なビットベクトルの間に何かが与えられます-特に、IDがセットで密にクラスター化する傾向がある場合は、スパース配列を適切に処理します。
また、サイズが十分に小さくなると、ビットベクトルからソートされた配列表現に切り替えることができるクラスを使用する価値があります。ルートの近くのいくつかのノードに利益をもたらすだけの余分な複雑さは、おそらく無意味です。
とにかく、これらのセットは単一の定数「データベース」を参照するため、これらのセットが表現されるため、アルゴリズムの実行時にデータのコピーとスペースの無駄を大幅に節約できます。しかし、その「データベース」を検討する価値はまだあります。
連想データ構造を使用して、質問IDと結論IDのタプルを使用して検索し、回答IDを取得しました。つまり、キー(質問IDと結論ID)のアイテムごとのオーバーヘッドがあり、この場合はB +スタイルのツリーオーバーヘッドもありました。理由-基本的に習慣。私は非常に柔軟なコンテナーを持っていますが、後で実際に必要になる機能を予測する手間を省くため、それらを頻繁に使用する傾向があります。それには代償がありますが、それは古い時期尚早な最適化の問題です。
あなたの場合、あなたはマトリックスを使用しています-私は、質問IDと回答IDでインデックス付けされた2次元配列を想定しています。
私のバージョンがあなたのバージョンよりも効率的であると想像できる唯一の方法は、ほとんどの答えが不明な場合です。マトリックスでは、既知の回答IDと同じスペースを使用して、そのための特別な不明な回答IDが必要です。連想コンテナでは、それらの行を除外します。
それでも、ソートされた配列は、私のB +ツリーベースのソリューションよりも効率的です。効率的な挿入を可能にする必要がないため、必要なオーバーヘッドはキーのみです。
問題となる可能性のある2つのキーフィールド(質問と結論、行と列)を使用している場合(私は覚えていません)-テーブルの1つのコピーを1つの並べ替え順序で保持できない場合があります。しかし、の行に沿って単一の計算されたキーを使用する場合(row * num_columns) + column
、基本的に、とにかく2次元のスパース配列を実装しています。
私にとって、特定の質問に対する未知/未定義の回答の存在は、その質問をすることはまだ許可されていないことを意味します-それも、アルゴリズムを最初に実装したときに使用した理論にすぎません。実際に使ったことはありません。私はそれを置くことができる使用法がありますが、私はそれに取り掛かりませんでした。参考までに、その複数ディスパッチコードジェネレーターでは、型のフィールドに基づいてディスパッチするというアイデアが1つありました。タイプ自体はポリモーフィックであるため、これらのフィールドは存在しない場合もあり、存在する必要があることを確認した後でのみ、フィールドを確認することが有効です。
不明/未定義の回答のアプリケーションがない場合は、既存のマトリックスがおそらく最良のソリューションです。
だから基本的にはそれだけです-私は明らかにより良いオプションを提供することはできません。ただし、考慮すべきトレードオフの可能性がいくつかあります。もちろん、これは時期尚早な(そしておそらく誤った)最適化ではないと仮定します。
主なトレードオフの問題は、値の疎なセットと密なセットの表現の効率に関連するため、C4.5または意思決定ツリーの構築に固有のものではありません。また、より「洗練された」アプローチは、慎重に選択された単純なアプローチよりも効率が悪いことがよくあります。