Scalaルールベースの推論エンジン

みなさんこんにちは! 作業タイトルScala推論エンジン(sie)code )の下で、 ファジーロジックをサポートするオープンフォワードチェーン エンジンを公​​開します

UPD。
ライブラリは中央のMavenリポジトリに投稿されます。
<dependency> <groupId>net.sf.brunneng.fusie</groupId> <artifactId>fusie</artifactId> </dependency> 



独自の種類の中でこのエンジンの場所


多くの優れたjavm出力エンジンとエキスパートシステムがあります。 そのうちのいくつかの簡単な概要を紹介しましょう(ただし、これは完全で信頼できるふりをするものではなく、私の最初の印象は大まかなものです)。

Drools-フォワードチェーンを使用した、適切に構成可能な成人向けのオープンソースエンジン。 ここで規則を定義するための構文を見つけることができます

d3web-エキスパートシステムを構築するための十分に成熟したプラットフォーム。 ルール、質問の編集、入力データを受け入れるためのフォームの作成、単体テスト用の独自のウィキがあります。 ルールを定義するためのシンプルな言語。

jColibry-私が理解しているように、このライブラリは多数のオプションからのインタラクティブなデータ取得のために設計されています。

InfoSapientは、ファジーロジックをサポートする後方チェーンを使用する出力エンジンです。 ルールを記述するためのヒューマノイド言語の使用を許可します。 しかし、私の意見では、 ここ (19-20ページ)で説明さているいくつかの重大な欠点があります。
「現在の設計では、ルールベースは現在、外部データにアクセスできません。 これ
相談セッション中に、「クライアント」が目標を提供する必要があることを意味します
解決され、すべてのサポート情報も同様です。* "

「ルール構文は計算を許可しません。つまり、((a + b)がmより大きい場合)x; または、外部プログラム、スクリプト、または外部オブジェクトでのメソッドの実行*つまり、クライアントは、相談セッションの結果に基づいて、他の外部オブジェクトまたはスクリプトを実行する必要があります。

Jena-標準のRDF形式(セマンティックネットワーク)でデータを提示し、特別なクエリ言語SPARQLを使用して目的のデータを抽出しようとします。

mandaraxはルールコンパイラです。 欠点は、静的であるということです。ルールの各セットはJavaコードとしてコンパイルされている必要があり、これは動的に実行できません。

その他

ご覧のとおり、すべてのエンジンは非常に異なります。フォワードを使用するエンジン、バックワードチェーンを使用するエンジン、ファジーロジックを使用するエンジン、使用しないエンジンがあります。 ルールを定義するための単純な構文を持つものもあれば、複雑なものなどもあります。

つまり、明確でファジーな出力の可能性、ルールの定義の簡単さ、柔軟な構成を組み合わせようとしました。 もともと選択されたのはScala(javaではなく)でした。関数スタイルで記述できるため、記述されるアルゴリズムの複雑さを克服できるからです。 それにもかかわらず、エンジンはMavenアーティファクトとしてアセンブルされ、その後、Javaの任意のMavenプロジェクトに(scala-libraryへの追加の依存関係を使用して)カットでき、すべてが機能します。

キラー機能


「重複ルール」の概念を紹介します。 これらは、同じ変数について結論を出す規則です。 例えば
 when a > 300 then b = 5 when a < 400 then b = 10 

この場合、「a」が300から400の間の値を取る場合、両方のルールが満たされ、「b」が同時に5と10にならないため、システムはさらに出力するパスを決定する必要があります。競合状況を解決する方法:
  1. 最初/最後のルールを選択
  2. ルールの優先順位をどこかに設定する
  3. より単純な条件が一般的なケースを決定し、より複雑な条件-特殊なケースを決定するという仮定に基づいて、より複雑な条件を持つルールを選択します(ただし、この場合、条件には1つの複雑さがあります)。

Droolsの紛争解決戦略
現在の実装(そして、私はこのような実装を見たことがありません)は、別の方法で進みます。
4)両方のルールが同等であり、最初のルールが満たされる確率は0.5、2番目のルールは0.5であると仮定します(Nのルールが重複する場合、一般的な場合は1 / Nの確率)。
さらに、出力は複数の部分に分割され、目的の変数が見つかるまで分岐ごとに個別に継続されます。 続いて、出力のすべてのブランチについて、目的の変数の可能な値ごとに合計確率が考慮されます。

ルールが前提条件で使用される変数で互いに奇妙に依存している可能性があるため、結論には異なる変数への割り当てを含めることができるため(変数aでXとYが交差し、変数でYと交差するなど、交差するルールのグループが可能です) b)アイデアの簡単な言葉による説明は、それほど単純ではない実装に変換されます。 達成された主な目標は、目的の変数がとる値の確率を正しく計算することです。

したがって、エンジンは交差する可能性のある競合するルールのベースにうまく対処します。
  1. 意図せずに、ルールが異なる専門家によって作成され、誰もが自分の意見を持っている場合。
  2. 意図的に、同じ変数を異なる方法で計算できる場合、たとえば:
     when graphicCardType == "Top" then graphicCard = "Nvidia super card" when graphicCardType == "Top" then graphicCard = "Radeon super card" 

    この例では、ビデオカード選択アドバイザーは、NvidiaのカードとRadeonのカードの両方を同じ確率でアドバイスできます。

このようにして、出力のファジー要素がサポートされます。

問題定義言語


問題定義構造:
  1. ユーザー変数が設定されます-出力プロセス中に要求される変数。
  2. 前提条件、結論、および必要に応じてルールが満たされる確率で構成される推論ルール(0〜1、デフォルトは1)。
  3. ターゲットは、検出される変数の名前です。

問題は完全にプログラムで決定できますが、プログラミング経験のある人が簡潔ですぐに理解できるように開発された特別な(非XML)構文を使用してこれを行う方がはるかに便利です。

例1:ファイナンシャルアドバイザー

 int amountSaved <- "How many savings you have?" int earnings <- "What is you year income?" bool steady <- "Your year income is stable?" int dependents = min: 0 <- "How many dependents you have?" when true then minincome = 15000 + (4000 * dependents) when true then minsavings = 5000 * dependents when savingsAccount == "inadequate" then investment = "savings" when (savingsAccount == "adequate") && (income == "adequate") then investment = "stocks" when savingsAccount == "adequate" income == "inadequate" then investment = "combination" when amountSaved > minsavings then savingsAccount = "adequate" when amountSaved <= minsavings then savingsAccount = "inadequate" when steady earnings > minincome then income = "adequate" when steady earnings <= minincome then income = "inadequate" when !steady then income = "inadequate" find investment 


最初に変数定義ブロックがあります:
 int amountSaved <- "How many savings you have?" int earnings <- "What is you year income?" bool steady <- "Your year income is stable?" int dependents = min: 0 <- "How many dependents you have?" 


これらは出力されない変数ですが、ルールで使用されます。 最初にタイプ、次に変数の名前、次にオプションで有効な値の検証(最小:0-値が0より小さいことを意味する)は、オプションで、この変数を要求するときのユーザーへの<質問の後に来ます。
サポートされているタイプは、bool、int、double、enum(別名文字列)です。

前提条件と結論では、任意の複雑な表現を使用できます(この例で最も難しいのはminincome = 15000 + (4000 * dependents))が、これは限界からはほど遠いです)
算術演算のセマンティクスは、Javaと同じです。 必要に応じて、intからdoubleへの暗黙的な変換がサポートされています。 デフォルトでは、java.lang.Mathからの関数呼び出しがサポートされていますが、関数を登録することもできます。

ルールを表示
 when true then minsavings = 5000 * dependents 

事実を決定する 彼の序文は常に満たされています。

投稿
 when (savingsAccount == "adequate") && (income == "adequate") then investment = "stocks"  when savingsAccount == "adequate" income == "adequate" then investment = "stocks" 

と同等 前提条件の行の間では、&&(および)演算は暗黙的です。

ところで、問題は部分的に定義できます。たとえば、ユーザー変数のないルールのみです。 そして、プログラムで変数定義を追加します。 データソースをスリップしてカスタム変数を引き出すこともできます。

パーサー機能:


パーサーはscala.util.parsing.JavaTokenParsersからの継承者として実装されました。
その前にパーサーを書いた経験が私にはささやかなものがあるという事実にもかかわらず、私はそれを書くことが喜びだったとしか言えません。 このツールの威力は、解析のパターンを設定し、結果をエンティティに即座にマッピングできることにあります。

テスト中


テストには特別な注意が払われました。 ユニットテストは、構文解析ルール(単純な構造から問題全体の定義まで)、出力の正確さ(単純なタスクから複雑なルールまで、複雑なルールの交差)をチェックするために記述されています。


com.greentea.sie.examplesパッケージには、 FinancialAdviser, ProgrammingLanguageAdviser, LoanarAdviserさまざまなサブジェクト領域のルール定義の実例をcom.greentea.sie.examplesクラスがいくつかあります。

今後の方向性


  1. エンジンはブラックボックスであってはなりません。 ユーザーが理解できる出力プロセスの説明を改善する必要があります。
  2. ファジー比較のサポート。
    タイプ:
    気分は良いです
    ここで、気分は数値変数であり、良好は付属機能によって定義されるファジー概念です。
  3. ルールの大規模な配列でのパフォーマンスとメモリ使用量のテスト。
  4. 重複ルールのその他の競合解決オプション。

Source: https://habr.com/ru/post/J180869/


All Articles