web-dev-qa-db-ja.com

巨大な決定木を作成する

イベントコリレータを作成します。システムの基本的な部分は、記録された状態とログファイルに基づいて障害の原因を認識するディシジョンツリーです。

多くの事故は細かい部分で異なり、多くの決定はファジー、不完全、または信頼性の低いデータに基づいて行われますが、これらの決定のほとんどはバイナリロジック関数として書き留めることができます。

物事は、それらの多くがあります。デシジョンツリーには少なくとも100個のノードがあることを期待しています。値を1桁下回る可能性があります。その上、新しい予期しないパターンが出現したり、予期しない障害が発生して新しいトレースが残されたり、システムの拡張により新しい障害モードが実行可能になったりすると、ディシジョンツリーを維持する必要があります。

(そして、それは常に純粋なツリー構造であるとは限りません-同じ効果のいくつかのフォールトには2つ以上の外観モードがあり、これらのいくつかは異なるモードに分岐します。たとえば、イベントAは失敗X、イベントBはイベントCを意味します。Cがtrueの場合、それは失敗Xでもありますが、そうでない場合、失敗Yです。技術的には同じですが、ツリーの観点とは異なるX1とX2に常に正規化できますが、ツリーは次のようになります。多くの場合、かなり深くネストされているため、ネストされた一連の単純なif()がすぐに制御不能になります。

今私の質問は、そのツリーを保存/書き込み/構築して、マシンが消化できるものにコンパイルできるようにする方法ですが、開発者にとっては引き続き保守可能ですか?

これは組み込みシステム用であるため、JBossなどのヘビーウェイトソリューションはコンパイラ側にしか表示されない限り実際には適合せず、最終的なシステムはコンパイルされたルールセットをはるかにマシンフレンドリーなもので実行します。

(システムはC++で記述されており、JSONも幅広く使用しており、助けになればARM9 CPUで実行されます。)

A sample of the tree

5
SF.

ここにそのようなDSLのアイデアがあります。 6つの入力変数(「イベント」)があるとします。それらの変数がバイナリであると仮定しましょう(アイデアは非バイナリ変数に一般化できます)。これで、DSLには次のようなステートメントが含まれるはずです

(0|*|1|0|1|1) => Action_A()
(0|0|1|0|0|*) => Action_B()
(1|1|1|0|*|*) => Action_C()
default       => ActionDefault()

各行には前提条件(=>の左側の部分)が含まれており、どの入力変数がどの値を持つ必要があるかを示しています。 =>の右側の部分は、適用されるアクションです。 「*」は、対応する入力変数が0または1のいずれかであることを意味します。セマンティクスは、awk、Perl、またはxsltのパターンマッチングメカニズムと比較できます。

これはどのように役立ちますか?さて、今あなたは2つのことをすることができます

  • すべての前提条件が相互に排他的であることを確認する単純なバリデーターを記述します。 「デフォルト」アクションが不要または必要な場合は、これを許可せず、代わりにバリデーターに、ケースを逃していないことを追加確認させます。

  • そのDSLをC++コードに変換するコードジェネレーターを記述します。これは次のようになります。

    if(event_1==0 && event_3==1 && event_4==0 && event_5==1 && event_6==1)
        Action_A();
    

バリデーターはあなたが物事を保守しやすい状態に保つのを助けます、そして生成されたC++コードはあなたが必要とするように「機械に優しい」何かにコンパイルするべきです。

入力変数がバイナリではない場合(バイナリ入力だけでは100ノードの深さが2から3の決定では不可能)、そのDSLを拡張する必要があります。それを行う方法は簡単です。たとえば、前提条件(0,1,4|*|*)には(event_1==0 || event_1==1||event_1==4)を使用します。

編集:多くの入力変数があり、それらのいくつかだけをチェックする必要がある場合、これは「名前付きパラメーター」を使用して適応できます。たとえば、

    (var_1==[0,1,4]|var_3==0|var_25==[1-10]) => Action_A()

もちろん、これをC++で直接コーディングすることもできますが、それほど長くはなりませんが、小さな正式なDSLを使用する利点は、これに対してバリデーターを作成する機会があることです。

4
Doc Brown

私はC++開発者ではありませんが、ルール宣言DSLは純粋にC++で実装できると思います。

最初に、すべての入力パラメーターを含む(たとえば、マップとして)要求オブジェクトがあります。このオブジェクトは、すでにチェックされた条件、ルールチェックトレース、および場合によってはいくつかの中間データもキャッシュできます。

class Request {
public:
    template<T> Value<T> get(Parameter<T>); // Returns the given parameter value.
    /* Returns a 3-state status for the given condition:
       - not checked yet
       - condition succeed
       - condition failed */
    ConditionCheckStatus getCheckStatus(Condition);
    void setCheckStatus(Condition, bool); // Caches the condition check status.
}

このようなオブジェクトは、リクエストごとに1回作成し、最上位のルールに渡す必要があります。

条件は、Conditionクラスサブクラスを使用して表現できます。 Conditionは、評価済みかどうかを確認します。そうでない場合は、それ自体を評価し、評価チェックのステータスをキャッシュします。複雑な条件を構築するには、&|および!演算子をオーバーライドすることをお勧めします。

class Condition {
public:
    inline bool check(Request &request) {
        switch (request->getCheckStatus(this)) {
        case COND_SUCCESS:
            return true;
        case COND_FAILURE:
            return false;
        }
        bool result = this->checkCondition(request);
        request.setCheckStatus(this, result);
        return result;
    }
    inline Condition &operator & (Condition &other) {return *new AndCondition(*this, other)}
    inline Condition &operator | (Condition &other) {return *new OrCondition(*this, other)}
    inline Condition &operator ! () {return *new NotCondition(*this)}
protected:
    virtual bool checkCondition(Request &) = 0;
}

具体的な条件はグローバル定数として宣言でき、論理演算子と組み合わせてより複雑な条件を構築できます。

Parameterテンプレートを使用すると、タイプセーフな方法でRequestパラメータにアクセスできます。具体的なパラメーターはグローバル定数として宣言できます。パラメータは、次のように演算子をオーバーライドしてConditionsを構築することもできます。

// Declare a "counter" input parameter.
const Parameter<int> Counter;

// Construct a "more than once" condition.    
const Condition &MoreThanOnce = Counter > 1;

ルールセットは、Ruleオブジェクトを使用して宣言できます(グローバル定数としても宣言されます)。

const Rule FailIfMoreThanOnce(
    MoreThanOnce, /* The condition */
    Failure,      /* The rule invoked if condition succeed */
    OtherRule     /* The rule invoked if condition failed */);


// ... Other rule definitions

const Rule TopRule(SomeCondition1 | SomeCondition2, SomeRule1, SomeRule2);

ディシジョンツリーの評価は次のようになります。

Request request;
// Fill the request.
bool result = TopRule.check(request);
// Handle the result.

Parameters、ConditionsおよびRulesはすべてステートレスであることに注意してください。唯一の変更可能なオブジェクトはRequestで、リクエストごとに1回作成され、評価されたデータを収集します。

ルール、パラメーター、条件には意味のある名前を付けることができ、演算子を使用して構築できます。宣言の構文オーバーヘッドはほとんどありません。さらに短い宣言は、マクロを使用して行うことができます。

1
lorus