第4章 PMML モデルの実行
Business Central を使用して Red Hat Process Automation Manager に PMML ファイルをインポートする (Menu
プロジェクトのパッケージ化およびデプロイメントの方法に PMML アセットを追加する方法の詳細は、『Packaging and deploying a Red Hat Process Automation Manager project』を参照してください
Business Central の Decision Model and Notation (DMN) サービスの一部として、PMML モデルを追加することもできます。DMN ファイル内に PMML モデルを追加すると、その PMML モデルを DMN デシジョンノードまたはビジネスナレッジモデルノードのボックス関数式として呼び出すことができます。DMN サービスへの PMML モデルの追加に関する詳細は、『Designing a decision service using DMN models』を参照してください。
4.1. PMML 呼び出しの Java アプリケーションへの直接組み込み
KIE コンテナーは、呼び出しプログラムにナレッジアセットを直接組み込む場合や、KJAR 用 Maven 依存関係を使用して物理的にプルする場合は、ローカルとみなされます。コードのバージョンと、PMML 定義のバージョンとの間に密接な関係がある場合は、通常、ナレッジアセットをプロジェクトに直接組み込みます。意思決定への変更は、アプリケーションをアップデートして再デプロイしないと有効になりません。このアプローチに対する利点は、適切なオペレーションがランタイムへの外部の依存関係に依存していないことですが、ロックされた環境の場合は制限になる可能性があります。
Maven の依存関係を使用すると、システムプロパティーを使用して、アップデートを定期的にスキャンして自動的にアップデートするなど、特定バージョンの意思決定が動的に変更するため、柔軟性が高まります。これにより、外部の依存関係がサービスのデプロイ時間に影響を及ぼしますが、意思決定はローカルで実行されるため、ランタイム時に利用可能な外部サービスに対する信頼が低くなります。
前提条件
- 実行する PMML モデルを含む KJAR が作成されている。プロジェクトのパッケージ化に関する詳細は、『Packaging and deploying a Red Hat Process Automation Manager project』を参照してください。
手順
クライアントアプリケーションで、Java プロジェクトの関連クラスパスに以下の依存関係を追加します。
<!-- Required for the PMML compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>kie-pmml</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required for the KIE public API --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${rhpam.version}</version> </dependencies> <!-- Required if not using classpath KIE container --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${rhpam.version}</version> </dependency>
<version>
は、プロジェクトで現在使用する Red Hat Process Automation Manager の Maven アーティファクトバージョンです (例: 7.30.0.Final-redhat-00002)。注記個別の依存関係に対して Red Hat Process Automation Manager
<version>
を指定するのではなく、Red Hat Business Automation 部品表 (BOM) の依存関係をプロジェクトのpom.xml
ファイルに追加することを検討してください。Red Hat Business Automation BOM は、Red Hat Process Decision Manager と Red Hat Process Automation Manager の両方に適用します。BOM ファイルを追加すると、指定の Maven リポジトリーからの一時的な依存関係の内、正しいバージョンが、このプロジェクトに追加されます。BOM 依存関係の例:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.6.0.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
Red Hat Business Automation BOM (Bill of Materials) についての詳細情報は、What is the mapping between RHPAM product and maven library version? を参照してください。
classpath
またはReleaseId
から KIE コンテナーを作成します。KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "my-kjar", "1.0.0" ); KieContainer kieContainer = kieServices.newKieContainer( releaseId );
または、以下のオプションを使用します。
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer();
PMMLRequestData
クラスのインスタンスを作成し、PMML モデルをデータセットに適用します。public class PMMLRequestData { private String correlationId; 1 private String modelName; 2 private String source; 3 private List<ParameterInfo<?>> requestParams; 4 ... }
PMML4Result
クラスのインスタンスを作成します。このクラスでは、入力データに PMML ベースルールを適用した結果の出力情報を保持します。public class PMML4Result { private String correlationId; private String segmentationId; 1 private String segmentId; 2 private int segmentIndex; 3 private String resultCode; 4 private Map<String, Object> resultVariables; 5 ... }
通常の getter メソッドに加え、
PMML4Result
クラスも、結果となる変数の値を直接取得する以下の方法をサポートします。public <T> Optional<T> getResultValue(String objName, String objField, Class<T> clazz, Object...params) public Object getResultValue(String objName, String objField, Object...params)
ParameterInfo
クラスのインスタンスを作成します。このクラスは、PMMLRequestData
クラスの一部として使用する、基本的なデータタイプオブジェクトのラッパーとして機能します。public class ParameterInfo<T> { 1 private String correlationId; private String name; 2 private String capitalizedName; private Class<T> type; 3 private T value; 4 ... }
先ほど作成した対象の PMML クラスインスタンスをもとに PMML モデルを実行します。
public void executeModel(KieBase kbase, Map<String,Object> variables, String modelName, String correlationId, String modelPkgName) { RuleUnitExecutor executor = RuleUnitExecutor.create().bind(kbase); PMMLRequestData request = new PMMLRequestData(correlationId, modelName); PMML4Result resultHolder = new PMML4Result(correlationId); variables.entrySet().forEach( es -> { request.addRequestParam(es.getKey(), es.getValue()); }); DataSource<PMMLRequestData> requestData = executor.newDataSource("request"); DataSource<PMML4Result> resultData = executor.newDataSource("results"); DataSource<PMMLData> internalData = executor.newDataSource("pmmlData"); requestData.insert(request); resultData.insert(resultHolder); List<String> possiblePackageNames = calculatePossiblePackageNames(modelName, modelPkgName); Class<? extends RuleUnit> ruleUnitClass = getStartingRuleUnit("RuleUnitIndicator", (InternalKnowledgeBase)kbase, possiblePackageNames); if (ruleUnitClass != null) { executor.run(ruleUnitClass); if ( "OK".equals(resultHolder.getResultCode()) ) { // extract result variables here } } } protected Class<? extends RuleUnit> getStartingRuleUnit(String startingRule, InternalKnowledgeBase ikb, List<String> possiblePackages) { RuleUnitRegistry unitRegistry = ikb.getRuleUnitRegistry(); Map<String,InternalKnowledgePackage> pkgs = ikb.getPackagesMap(); RuleImpl ruleImpl = null; for (String pkgName: possiblePackages) { if (pkgs.containsKey(pkgName)) { InternalKnowledgePackage pkg = pkgs.get(pkgName); ruleImpl = pkg.getRule(startingRule); if (ruleImpl != null) { RuleUnitDescr descr = unitRegistry.getRuleUnitFor(ruleImpl).orElse(null); if (descr != null) { return descr.getRuleUnitClass(); } } } } return null; } protected List<String> calculatePossiblePackageNames(String modelId, String...knownPackageNames) { List<String> packageNames = new ArrayList<>(); String javaModelId = modelId.replaceAll("\\s",""); if (knownPackageNames != null && knownPackageNames.length > 0) { for (String knownPkgName: knownPackageNames) { packageNames.add(knownPkgName + "." + javaModelId); } } String basePkgName = PMML4UnitImpl.DEFAULT_ROOT_PACKAGE+"."+javaModelId; packageNames.add(basePkgName); return packageNames; }
ルールは
RuleUnitExecutor
クラスにより実行されます。RuleUnitExecutor
クラスは KIE セッションを作成し、必要なDataSource
オブジェクトをこれらのセッションに追加してから、run()
メソッドへのパラメーターとして渡されるRuleUnit
をもとに、ルールを実行します。calculatePossiblePackageNames
とgetStartingRuleUnit
メソッドは、run()
メソッドへのパラメーターとして渡されるRuleUnit
クラスの完全修飾名を決定します。
PMML モデル実行をスムーズに行うため、Red Hat Process Automation Manager でサポートされている PMML4ExecutionHelper
クラスも使用できます。PMML ヘルパークラスに関する詳細は、「PMML 実行ヘルパークラス」を参照してください。
4.1.1. PMML 実行ヘルパークラス
Red Hat Process Automation Manager には、PMML モデル実行に必要な PMMLRequestData
クラスの作成や RuleUnitExecutor
クラスを使用したルールの実行をサポートする PMML4ExecutionHelper
クラスがあります。
以下では、比較の目的で PMML4ExecutionHelper
クラスのある場合とない場合の PMML モデル実行の例を紹介しています。
PMML4ExecutionHelper
の使用なしの PMML モデル実行例
public void executeModel(KieBase kbase, Map<String,Object> variables, String modelName, String correlationId, String modelPkgName) { RuleUnitExecutor executor = RuleUnitExecutor.create().bind(kbase); PMMLRequestData request = new PMMLRequestData(correlationId, modelName); PMML4Result resultHolder = new PMML4Result(correlationId); variables.entrySet().forEach( es -> { request.addRequestParam(es.getKey(), es.getValue()); }); DataSource<PMMLRequestData> requestData = executor.newDataSource("request"); DataSource<PMML4Result> resultData = executor.newDataSource("results"); DataSource<PMMLData> internalData = executor.newDataSource("pmmlData"); requestData.insert(request); resultData.insert(resultHolder); List<String> possiblePackageNames = calculatePossiblePackageNames(modelName, modelPkgName); Class<? extends RuleUnit> ruleUnitClass = getStartingRuleUnit("RuleUnitIndicator", (InternalKnowledgeBase)kbase, possiblePackageNames); if (ruleUnitClass != null) { executor.run(ruleUnitClass); if ( "OK".equals(resultHolder.getResultCode()) ) { // extract result variables here } } } protected Class<? extends RuleUnit> getStartingRuleUnit(String startingRule, InternalKnowledgeBase ikb, List<String> possiblePackages) { RuleUnitRegistry unitRegistry = ikb.getRuleUnitRegistry(); Map<String,InternalKnowledgePackage> pkgs = ikb.getPackagesMap(); RuleImpl ruleImpl = null; for (String pkgName: possiblePackages) { if (pkgs.containsKey(pkgName)) { InternalKnowledgePackage pkg = pkgs.get(pkgName); ruleImpl = pkg.getRule(startingRule); if (ruleImpl != null) { RuleUnitDescr descr = unitRegistry.getRuleUnitFor(ruleImpl).orElse(null); if (descr != null) { return descr.getRuleUnitClass(); } } } } return null; } protected List<String> calculatePossiblePackageNames(String modelId, String...knownPackageNames) { List<String> packageNames = new ArrayList<>(); String javaModelId = modelId.replaceAll("\\s",""); if (knownPackageNames != null && knownPackageNames.length > 0) { for (String knownPkgName: knownPackageNames) { packageNames.add(knownPkgName + "." + javaModelId); } } String basePkgName = PMML4UnitImpl.DEFAULT_ROOT_PACKAGE+"."+javaModelId; packageNames.add(basePkgName); return packageNames; }
PMML4ExecutionHelper
を使用した PMML モデル実行例
public void executeModel(KieBase kbase, Map<String,Object> variables, String modelName, String modelPkgName, String correlationId) { PMML4ExecutionHelper helper = PMML4ExecutionHelperFactory.getExecutionHelper(modelName, kbase); helper.addPossiblePackageName(modelPkgName); PMMLRequestData request = new PMMLRequestData(correlationId, modelName); variables.entrySet().forEach(entry -> { request.addRequestParam(entry.getKey(), entry.getValue); }); PMML4Result resultHolder = helper.submitRequest(request); if ("OK".equals(resultHolder.getResultCode)) { // extract result variables here } }
PMML4ExecutionHelper
を使用する場合は、一般的な PMML モデル実行で行うように、考えられる名前または RuleUnit
クラスを指定する必要はありません。
PMML4ExecutionHelper
クラスを構築するには、PMML4ExecutionHelperFactory
クラスを使用して、PMML4ExecutionHelper
の取得方法を決定します。
以下は、PMML4ExecutionHelper
を構築するのに利用可能な PMML4ExecutionHelperFactory
クラスメソッドです。
- KIE ベースにある PMML アセット向けの PMML4ExecutionHelperFactory メソッド
PMML アセットがすでにコンパイルされており、既存の KIE ベースで使用されている場合には、これらのメソッドを使用します。
public static PMML4ExecutionHelper getExecutionHelper(String modelName, KieBase kbase) public static PMML4ExecutionHelper getExecutionHelper(String modelName, KieBase kbase, boolean includeMiningDataSources)
- プロジェクトクラスパスにある PMML アセット向けの PMML4ExecutionHelperFactory メソッド
PMML アセットがプロジェクトクラスパスにある場合にこれらのメソッドを使用します。
classPath
の引数は、PMML ファイルのプロジェクトクラスパスの場所を指します。public static PMML4ExecutionHelper getExecutionHelper(String modelName, String classPath, KieBaseConfiguration kieBaseConf) public static PMML4ExecutionHelper getExecutionHelper(String modelName,String classPath, KieBaseConfiguration kieBaseConf, boolean includeMiningDataSources)
- バイトアレイ形式の PMML アセット向けの PMML4ExecutionHelperFactory メソッド
PMML アセットがバイトアレイ形式の場合にこれらのメソッドを使用します。
public static PMML4ExecutionHelper getExecutionHelper(String modelName, byte[] content, KieBaseConfiguration kieBaseConf) public static PMML4ExecutionHelper getExecutionHelper(String modelName, byte[] content, KieBaseConfiguration kieBaseConf, boolean includeMiningDataSources)
Resource
にある PMML アセット向けの PMML4ExecutionHelperFactory メソッドPMML アセットが
org.kie.api.io.Resource
オブジェクトの形式の場合に、これらのメソッドを使用します。public static PMML4ExecutionHelper getExecutionHelper(String modelName, Resource resource, KieBaseConfiguration kieBaseConf) public static PMML4ExecutionHelper getExecutionHelper(String modelName, Resource resource, KieBaseConfiguration kieBaseConf, boolean includeMiningDataSources)
クラスパス、バイトアレイ、リソース PMML4ExecutionHelperFactory
メソッドは、生成されたルールおよび Java クラスの KIE コンテナーを作成します。このコンテナーは、RuleUnitExecutor
が使用する KIE ベースのソースとして使用します。ただし、このコンテナーには永続性はありません。PMML アセットの PMML4ExecutionHelperFactory
メソッドが KIE ベースにすでにあるには、この方法では KIE コンテナーは作成されません。
4.2. Process Server を使用した PMML モデルの実行
ApplyPmmlModelCommand
コマンドを設定済みの Process Server に送信して Process Server にデプロイした PMML モデルを実行できます。このコマンドを使用する場合は、PMMLRequestData
オブジェクトが Process Server に送信され、PMML4Result
の結果オブジェクトを返信として受信します。設定済みの Java クラスからの Process Server REST API または、REST クライアントから直接 Process Server に PMML 要求を送信できます。
前提条件
-
Process Server がインストールされ、設定されている (
kie-server
ロールが割り当てられているユーザーの既知のユーザー名と認証情報を含む)。インストールオプションは、『Planning a Red Hat Process Automation Manager installation』を参照してください。 - KIE コンテナーは、PMML モデルを含む KJAR の形式で Process Server にデプロイしている。プロジェクトのパッケージ化に関する詳細は、『Packaging and deploying a Red Hat Process Automation Manager project』を参照してください。
- PMML モデルを含む KIE コンテナーのコンテナー ID がある。
手順
クライアントアプリケーションで、Java プロジェクトの関連クラスパスに以下の依存関係を追加します。
<!-- Required for the PMML compiler --> <dependency> <groupId>org.drools</groupId> <artifactId>kie-pmml</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required for the KIE public API --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${rhpam.version}</version> </dependencies> <!-- Required for the Process Server Java client API --> <dependency> <groupId>org.kie.server</groupId> <artifactId>kie-server-client</artifactId> <version>${rhpam.version}</version> </dependency> <!-- Required if not using classpath KIE container --> <dependency> <groupId>org.kie</groupId> <artifactId>kie-ci</artifactId> <version>${rhpam.version}</version> </dependency>
<version>
は、プロジェクトで現在使用する Red Hat Process Automation Manager の Maven アーティファクトバージョンです (例: 7.30.0.Final-redhat-00002)。注記個別の依存関係に対して Red Hat Process Automation Manager
<version>
を指定するのではなく、Red Hat Business Automation 部品表 (BOM) の依存関係をプロジェクトのpom.xml
ファイルに追加することを検討してください。Red Hat Business Automation BOM は、Red Hat Process Decision Manager と Red Hat Process Automation Manager の両方に適用します。BOM ファイルを追加すると、指定の Maven リポジトリーからの一時的な依存関係の内、正しいバージョンが、このプロジェクトに追加されます。BOM 依存関係の例:
<dependency> <groupId>com.redhat.ba</groupId> <artifactId>ba-platform-bom</artifactId> <version>7.6.0.redhat-00002</version> <scope>import</scope> <type>pom</type> </dependency>
Red Hat Business Automation BOM (Bill of Materials) についての詳細情報は、What is the mapping between RHPAM product and maven library version? を参照してください。
classpath
またはReleaseId
から KIE コンテナーを作成します。KieServices kieServices = KieServices.Factory.get(); ReleaseId releaseId = kieServices.newReleaseId( "org.acme", "my-kjar", "1.0.0" ); KieContainer kieContainer = kieServices.newKieContainer( releaseId );
または、以下のオプションを使用します。
KieServices kieServices = KieServices.Factory.get(); KieContainer kieContainer = kieServices.getKieClasspathContainer();
Process Server への要求を送信して、応答を受信するクラスを作成します。
public class ApplyScorecardModel { private static final ReleaseId releaseId = new ReleaseId("org.acme","my-kjar","1.0.0"); private static final String containerId = "SampleModelContainer"; private static KieCommands commandFactory; private static ClassLoader kjarClassLoader; 1 private RuleServicesClient serviceClient; 2 // Attributes specific to your class instance private String rankedFirstCode; private Double score; // Initialization of non-final static attributes static { commandFactory = KieServices.Factory.get().getCommands(); // Specifications for kjarClassLoader, if used KieMavenRepository kmp = KieMavenRepository.getMavenRepository(); File artifactFile = kmp.resolveArtifact(releaseId).getFile(); if (artifactFile != null) { URL urls[] = new URL[1]; try { urls[0] = artifactFile.toURI().toURL(); classLoader = new KieURLClassLoader(urls,PMML4Result.class.getClassLoader()); } catch (MalformedURLException e) { logger.error("Error getting classLoader for "+containerId); logger.error(e.getMessage()); } } else { logger.warn("Did not find the artifact file for "+releaseId.toString()); } } public ApplyScorecardModel(KieServicesConfiguration kieConfig) { KieServicesClient clientFactory = KieServicesFactory.newKieServicesClient(kieConfig); serviceClient = clientFactory.getServicesClient(RuleServicesClient.class); } ... // Getters and setters ... // Method for executing the PMML model on KIE Server public void applyModel(String occupation, int age) { PMMLRequestData input = new PMMLRequestData("1234","SampleModelName"); 3 input.addRequestParam(new ParameterInfo("1234","occupation",String.class,occupation)); input.addRequestParam(new ParameterInfo("1234","age",Integer.class,age)); CommandFactoryServiceImpl cf = (CommandFactoryServiceImpl)commandFactory; ApplyPmmlModelCommand command = (ApplyPmmlModelCommand) cf.newApplyPmmlModel(request); 4 ServiceResponse<ExecutionResults> results = ruleClient.executeCommandsWithResults(CONTAINER_ID, command); 5 if (results != null) { 6 PMML4Result resultHolder = (PMML4Result)results.getResult().getValue("results"); if (resultHolder != null && "OK".equals(resultHolder.getResultCode())) { this.score = resultHolder.getResultValue("ScoreCard","score",Double.class).get(); Map<String,Object> rankingMap = (Map<String,Object>)resultHolder.getResultValue("ScoreCard","ranking"); if (rankingMap != null && !rankingMap.isEmpty()) { this.rankedFirstCode = rankingMap.keySet().iterator().next(); } } } } }
クラスインスタンスを実行して、PMML 呼び出し要求を Process Server に送信します。
または JMS および REST インターフェースを使用して、
ApplyPmmlModelCommand
コマンドを Process Server に送信できます。REST 要求については、ApplyPmmlModelCommand
コマンドを、JSON または JAXB、XStream 要求形式で、http://SERVER:PORT/kie-server/services/rest/server/containers/instances/{containerId}
へのPOST
要求として使用できます。POST エンドポイントの例
http://localhost:8080/kie-server/services/rest/server/containers/instances/SampleModelContainer
JSON 要求ボディーの例
{ "commands": [ { "apply-pmml-model-command": { "outIdentifier": null, "packageName": null, "hasMining": false, "requestData": { "correlationId": "123", "modelName": "SimpleScorecard", "source": null, "requestParams": [ { "correlationId": "123", "name": "param1", "type": "java.lang.Double", "value": "10.0" }, { "correlationId": "123", "name": "param2", "type": "java.lang.Double", "value": "15.0" } ] } } } ] }
エンドポイントよびボディーを含む curl 要求の例
curl -X POST "http://localhost:8080/kie-server/services/rest/server/containers/instances/SampleModelContainer" -H "accept: application/json" -H "content-type: application/json" -d "{ \"commands\": [ { \"apply-pmml-model-command\": { \"outIdentifier\": null, \"packageName\": null, \"hasMining\": false, \"requestData\": { \"correlationId\": \"123\", \"modelName\": \"SimpleScorecard\", \"source\": null, \"requestParams\": [ { \"correlationId\": \"123\", \"name\": \"param1\", \"type\": \"java.lang.Double\", \"value\": \"10.0\" }, { \"correlationId\": \"123\", \"name\": \"param2\", \"type\": \"java.lang.Double\", \"value\": \"15.0\" } ] } } } ]}"
JSON の応答例
{ "results" : [ { "value" : {"org.kie.api.pmml.DoubleFieldOutput":{ "value" : 40.8, "correlationId" : "123", "segmentationId" : null, "segmentId" : null, "name" : "OverallScore", "displayValue" : "OverallScore", "weight" : 1.0 }}, "key" : "OverallScore" }, { "value" : {"org.kie.api.pmml.PMML4Result":{ "resultVariables" : { "OverallScore" : { "value" : 40.8, "correlationId" : "123", "segmentationId" : null, "segmentId" : null, "name" : "OverallScore", "displayValue" : "OverallScore", "weight" : 1.0 }, "ScoreCard" : { "modelName" : "SimpleScorecard", "score" : 40.8, "holder" : { "modelName" : "SimpleScorecard", "correlationId" : "123", "voverallScore" : null, "moverallScore" : true, "vparam1" : 10.0, "mparam1" : false, "vparam2" : 15.0, "mparam2" : false }, "enableRC" : true, "pointsBelow" : true, "ranking" : { "reasonCh1" : 5.0, "reasonCh2" : -6.0 } } }, "correlationId" : "123", "segmentationId" : null, "segmentId" : null, "segmentIndex" : 0, "resultCode" : "OK", "resultObjectName" : null }}, "key" : "results" } ], "facts" : [ ] }