use of org.kie.kogito.explainability.model.Saliency in project kogito-apps by kiegroup.
the class LimeExplainerServiceHandlerTest method testCreateSucceededResult.
@Test
public void testCreateSucceededResult() {
LIMEExplainabilityRequest request = new LIMEExplainabilityRequest(EXECUTION_ID, SERVICE_URL, MODEL_IDENTIFIER, Collections.emptyList(), Collections.emptyList());
Map<String, Saliency> saliencies = Map.of("s1", new Saliency(new Output("salary", Type.NUMBER), List.of(new FeatureImportance(new Feature("age", Type.NUMBER, new Value(25.0)), 5.0), new FeatureImportance(new Feature("dependents", Type.NUMBER, new Value(2)), -11.0))));
BaseExplainabilityResult base = handler.createSucceededResult(request, saliencies);
assertTrue(base instanceof LIMEExplainabilityResult);
LIMEExplainabilityResult result = (LIMEExplainabilityResult) base;
assertEquals(ExplainabilityStatus.SUCCEEDED, result.getStatus());
assertEquals(EXECUTION_ID, result.getExecutionId());
assertEquals(1, result.getSaliencies().size());
SaliencyModel saliencyModel = result.getSaliencies().iterator().next();
assertEquals(2, saliencyModel.getFeatureImportance().size());
assertEquals("age", saliencyModel.getFeatureImportance().get(0).getFeatureName());
assertEquals(5.0, saliencyModel.getFeatureImportance().get(0).getFeatureScore());
assertEquals("dependents", saliencyModel.getFeatureImportance().get(1).getFeatureName());
assertEquals(-11.0, saliencyModel.getFeatureImportance().get(1).getFeatureScore());
}
use of org.kie.kogito.explainability.model.Saliency in project kogito-apps by kiegroup.
the class JITDMNServiceImpl method buildSalienciesResponse.
private List<SaliencyResponse> buildSalienciesResponse(DMNModel dmnModel, Map<String, Saliency> saliencyMap) {
List<SaliencyResponse> saliencyModelResponse = new ArrayList<>();
for (Map.Entry<String, Saliency> entry : saliencyMap.entrySet()) {
DecisionNode decisionByName = dmnModel.getDecisionByName(entry.getKey());
saliencyModelResponse.add(new SaliencyResponse(decisionByName.getId(), decisionByName.getName(), entry.getValue().getPerFeatureImportance().stream().map(JITDMNServiceImpl::featureImportanceModelToResponse).filter(Objects::nonNull).collect(Collectors.toList())));
}
return saliencyModelResponse;
}
use of org.kie.kogito.explainability.model.Saliency in project kogito-apps by kiegroup.
the class JITDMNServiceImpl method evaluateModelAndExplain.
public DMNResultWithExplanation evaluateModelAndExplain(DMNEvaluator dmnEvaluator, Map<String, Object> context) {
LocalDMNPredictionProvider localDMNPredictionProvider = new LocalDMNPredictionProvider(dmnEvaluator);
DMNResult dmnResult = dmnEvaluator.evaluate(context);
Prediction prediction = new SimplePrediction(LocalDMNPredictionProvider.toPredictionInput(context), LocalDMNPredictionProvider.toPredictionOutput(dmnResult));
LimeConfig limeConfig = new LimeConfig().withSamples(explainabilityLimeSampleSize).withPerturbationContext(new PerturbationContext(new Random(), explainabilityLimeNoOfPerturbation));
LimeExplainer limeExplainer = new LimeExplainer(limeConfig);
Map<String, Saliency> saliencyMap;
try {
saliencyMap = limeExplainer.explainAsync(prediction, localDMNPredictionProvider).get(Config.INSTANCE.getAsyncTimeout(), Config.INSTANCE.getAsyncTimeUnit());
} catch (TimeoutException | InterruptedException | ExecutionException e) {
if (e instanceof InterruptedException) {
LOGGER.error("Critical InterruptedException occurred", e);
Thread.currentThread().interrupt();
}
return new DMNResultWithExplanation(new JITDMNResult(dmnEvaluator.getNamespace(), dmnEvaluator.getName(), dmnResult), new SalienciesResponse(EXPLAINABILITY_FAILED, EXPLAINABILITY_FAILED_MESSAGE, null));
}
List<SaliencyResponse> saliencyModelResponse = buildSalienciesResponse(dmnEvaluator.getDmnModel(), saliencyMap);
return new DMNResultWithExplanation(new JITDMNResult(dmnEvaluator.getNamespace(), dmnEvaluator.getName(), dmnResult), new SalienciesResponse(EXPLAINABILITY_SUCCEEDED, null, saliencyModelResponse));
}
use of org.kie.kogito.explainability.model.Saliency in project kogito-apps by kiegroup.
the class DummyDmnModelsLimeExplainerTest method testAllTypesDMNExplanation.
@Test
void testAllTypesDMNExplanation() throws ExecutionException, InterruptedException, TimeoutException {
DMNRuntime dmnRuntime = DMNKogito.createGenericDMNRuntime(new InputStreamReader(getClass().getResourceAsStream("/dmn/allTypes.dmn")));
assertThat(dmnRuntime.getModels().size()).isEqualTo(1);
final String namespace = "https://kiegroup.org/dmn/_24B9EC8C-2F02-40EB-B6BB-E8CDE82FBF08";
final String name = "new-file";
DecisionModel decisionModel = new DmnDecisionModel(dmnRuntime, namespace, name);
PredictionProvider model = new DecisionModelWrapper(decisionModel);
Map<String, Object> context = new HashMap<>();
context.put("stringInput", "test");
context.put("listOfStringInput", Collections.singletonList("test"));
context.put("numberInput", 1);
context.put("listOfNumbersInput", Collections.singletonList(1));
context.put("booleanInput", true);
context.put("listOfBooleansInput", Collections.singletonList(true));
context.put("timeInput", "h09:00");
context.put("dateInput", "2020-04-02");
context.put("dateAndTimeInput", "2020-04-02T09:00:00");
context.put("daysAndTimeDurationInput", "P1DT1H");
context.put("yearsAndMonthDurationInput", "P1Y1M");
Map<String, Object> complexInput = new HashMap<>();
complexInput.put("aNestedListOfNumbers", Collections.singletonList(1));
complexInput.put("aNestedString", "test");
complexInput.put("aNestedComplexInput", Collections.singletonMap("doubleNestedNumber", 1));
context.put("complexInput", complexInput);
context.put("listOfComplexInput", Collections.singletonList(complexInput));
List<Feature> features = new ArrayList<>();
features.add(FeatureFactory.newCompositeFeature("context", context));
PredictionInput predictionInput = new PredictionInput(features);
List<PredictionOutput> predictionOutputs = model.predictAsync(List.of(predictionInput)).get(Config.INSTANCE.getAsyncTimeout(), Config.INSTANCE.getAsyncTimeUnit());
Prediction prediction = new SimplePrediction(predictionInput, predictionOutputs.get(0));
Random random = new Random();
PerturbationContext perturbationContext = new PerturbationContext(0L, random, 3);
LimeConfig limeConfig = new LimeConfig().withSamples(10).withPerturbationContext(perturbationContext);
LimeExplainer limeExplainer = new LimeExplainer(limeConfig);
Map<String, Saliency> saliencyMap = limeExplainer.explainAsync(prediction, model).get(Config.INSTANCE.getAsyncTimeout(), Config.INSTANCE.getAsyncTimeUnit());
for (Saliency saliency : saliencyMap.values()) {
assertThat(saliency).isNotNull();
}
assertThatCode(() -> ValidationUtils.validateLocalSaliencyStability(model, prediction, limeExplainer, 1, 0.5, 0.2)).doesNotThrowAnyException();
String decision = "myDecision";
List<PredictionInput> inputs = new ArrayList<>();
for (int n = 0; n < 10; n++) {
inputs.add(new PredictionInput(DataUtils.perturbFeatures(features, perturbationContext)));
}
DataDistribution distribution = new PredictionInputsDataDistribution(inputs);
int k = 2;
int chunkSize = 5;
double precision = ExplainabilityMetrics.getLocalSaliencyPrecision(decision, model, limeExplainer, distribution, k, chunkSize);
assertThat(precision).isBetween(0d, 1d);
double recall = ExplainabilityMetrics.getLocalSaliencyRecall(decision, model, limeExplainer, distribution, k, chunkSize);
assertThat(recall).isBetween(0d, 1d);
double f1 = ExplainabilityMetrics.getLocalSaliencyF1(decision, model, limeExplainer, distribution, k, chunkSize);
assertThat(f1).isBetween(0d, 1d);
}
use of org.kie.kogito.explainability.model.Saliency in project kogito-apps by kiegroup.
the class DummyDmnModelsLimeExplainerTest method testFunctional1DMNExplanation.
@Test
void testFunctional1DMNExplanation() throws ExecutionException, InterruptedException, TimeoutException {
DMNRuntime dmnRuntime = DMNKogito.createGenericDMNRuntime(new InputStreamReader(getClass().getResourceAsStream("/dmn/functionalTest1.dmn")));
assertThat(dmnRuntime.getModels().size()).isEqualTo(1);
final String namespace = "https://kiegroup.org/dmn/_049CD980-1310-4B02-9E90-EFC57059F44A";
final String name = "functionalTest1";
DecisionModel decisionModel = new DmnDecisionModel(dmnRuntime, namespace, name);
PredictionProvider model = new DecisionModelWrapper(decisionModel);
Map<String, Object> context = new HashMap<>();
context.put("booleanInput", true);
context.put("notUsedInput", 1);
List<Feature> features = new ArrayList<>();
features.add(FeatureFactory.newCompositeFeature("context", context));
PredictionInput predictionInput = new PredictionInput(features);
List<PredictionOutput> predictionOutputs = model.predictAsync(List.of(predictionInput)).get(Config.INSTANCE.getAsyncTimeout(), Config.INSTANCE.getAsyncTimeUnit());
Prediction prediction = new SimplePrediction(predictionInput, predictionOutputs.get(0));
Random random = new Random();
PerturbationContext perturbationContext = new PerturbationContext(0L, random, 1);
LimeConfig limeConfig = new LimeConfig().withSamples(10).withPerturbationContext(perturbationContext);
LimeExplainer limeExplainer = new LimeExplainer(limeConfig);
Map<String, Saliency> saliencyMap = limeExplainer.explainAsync(prediction, model).get(Config.INSTANCE.getAsyncTimeout(), Config.INSTANCE.getAsyncTimeUnit());
for (Saliency saliency : saliencyMap.values()) {
assertThat(saliency).isNotNull();
List<FeatureImportance> topFeatures = saliency.getPositiveFeatures(2);
assertThat(topFeatures.isEmpty()).isFalse();
assertThat(topFeatures.get(0).getFeature().getName()).isEqualTo("booleanInput");
}
assertThatCode(() -> ValidationUtils.validateLocalSaliencyStability(model, prediction, limeExplainer, 1, 0.5, 0.5)).doesNotThrowAnyException();
String decision = "decision";
List<PredictionInput> inputs = new ArrayList<>();
for (int n = 0; n < 10; n++) {
inputs.add(new PredictionInput(DataUtils.perturbFeatures(features, perturbationContext)));
}
DataDistribution distribution = new PredictionInputsDataDistribution(inputs);
int k = 2;
int chunkSize = 5;
double precision = ExplainabilityMetrics.getLocalSaliencyPrecision(decision, model, limeExplainer, distribution, k, chunkSize);
assertThat(precision).isBetween(0d, 1d);
double recall = ExplainabilityMetrics.getLocalSaliencyRecall(decision, model, limeExplainer, distribution, k, chunkSize);
assertThat(recall).isBetween(0d, 1d);
double f1 = ExplainabilityMetrics.getLocalSaliencyF1(decision, model, limeExplainer, distribution, k, chunkSize);
assertThat(f1).isBetween(0d, 1d);
}
Aggregations