use of org.kie.kogito.explainability.model.PerturbationContext in project kogito-apps by kiegroup.
the class DataUtils method boostrapFeatureDistributions.
/**
* Generate feature distributions from an existing (evantually small) {@link DataDistribution} for each {@link Feature}.
* Each feature intervals (min, max) and density information (mean, stdDev) are generated using bootstrap, then
* data points are sampled from a normal distribution (see {@link #generateData(double, double, int, Random)}).
*
* @param dataDistribution data distribution to take feature values from
* @param perturbationContext perturbation context
* @param featureDistributionSize desired size of generated feature distributions
* @param draws number of times sampling from feature values is performed
* @param sampleSize size of each sample draw
* @param numericFeatureZonesMap high feature score zones
* @return a map feature name -> generated feature distribution
*/
public static Map<String, FeatureDistribution> boostrapFeatureDistributions(DataDistribution dataDistribution, PerturbationContext perturbationContext, int featureDistributionSize, int draws, int sampleSize, Map<String, HighScoreNumericFeatureZones> numericFeatureZonesMap) {
Map<String, FeatureDistribution> featureDistributions = new HashMap<>();
for (FeatureDistribution featureDistribution : dataDistribution.asFeatureDistributions()) {
Feature feature = featureDistribution.getFeature();
if (Type.NUMBER.equals(feature.getType())) {
List<Value> values = featureDistribution.getAllSamples();
double[] means = new double[draws];
double[] stdDevs = new double[draws];
double[] mins = new double[draws];
double[] maxs = new double[draws];
for (int i = 0; i < draws; i++) {
List<Value> sampledValues = DataUtils.sampleWithReplacement(values, sampleSize, perturbationContext.getRandom());
double[] data = sampledValues.stream().mapToDouble(Value::asNumber).toArray();
double mean = DataUtils.getMean(data);
double stdDev = Math.pow(DataUtils.getStdDev(data, mean), 2);
double min = Arrays.stream(data).min().orElse(Double.MIN_VALUE);
double max = Arrays.stream(data).max().orElse(Double.MAX_VALUE);
means[i] = mean;
stdDevs[i] = stdDev;
mins[i] = min;
maxs[i] = max;
}
double finalMean = DataUtils.getMean(means);
double finalStdDev = Math.sqrt(DataUtils.getMean(stdDevs));
double finalMin = DataUtils.getMean(mins);
double finalMax = DataUtils.getMean(maxs);
double[] doubles = DataUtils.generateData(finalMean, finalStdDev, featureDistributionSize, perturbationContext.getRandom());
double[] boundedData = Arrays.stream(doubles).map(d -> Math.min(Math.max(d, finalMin), finalMax)).toArray();
HighScoreNumericFeatureZones highScoreNumericFeatureZones = numericFeatureZonesMap.get(feature.getName());
double[] finaldata;
if (highScoreNumericFeatureZones != null) {
double[] filteredData = DoubleStream.of(boundedData).filter(highScoreNumericFeatureZones::test).toArray();
// only use the filtered data if it's not discarding more than 50% of the points
if (filteredData.length > featureDistributionSize / 2) {
finaldata = filteredData;
} else {
finaldata = boundedData;
}
} else {
finaldata = boundedData;
}
NumericFeatureDistribution numericFeatureDistribution = new NumericFeatureDistribution(feature, finaldata);
featureDistributions.put(feature.getName(), numericFeatureDistribution);
}
}
return featureDistributions;
}
use of org.kie.kogito.explainability.model.PerturbationContext in project kogito-apps by kiegroup.
the class LimeExplainer method getNewPerturbationContext.
private PerturbationContext getNewPerturbationContext(List<Feature> linearizedTargetInputFeatures, int noOfRetries, PerturbationContext perturbationContext) {
PerturbationContext newPerturbationContext;
int nextPerturbationSize = Math.max(perturbationContext.getNoOfPerturbations() + 1, linearizedTargetInputFeatures.size() / noOfRetries);
// make sure to stay within the max no. of features boundaries
nextPerturbationSize = Math.min(linearizedTargetInputFeatures.size() - 1, nextPerturbationSize);
Optional<Long> optionalSeed = perturbationContext.getSeed();
if (optionalSeed.isPresent()) {
Long seed = optionalSeed.get();
newPerturbationContext = new PerturbationContext(seed, perturbationContext.getRandom(), nextPerturbationSize);
} else {
newPerturbationContext = new PerturbationContext(perturbationContext.getRandom(), nextPerturbationSize);
}
return newPerturbationContext;
}
use of org.kie.kogito.explainability.model.PerturbationContext in project kogito-apps by kiegroup.
the class LimeExplainer method adjustAndRetry.
private CompletableFuture<Map<String, Saliency>> adjustAndRetry(PredictionProvider model, PredictionInput originalInput, List<Feature> linearizedTargetInputFeatures, List<Output> actualOutputs, LimeConfig executionConfig) {
if (limeConfig.isAdaptDatasetVariance()) {
PerturbationContext newPerturbationContext = getNewPerturbationContext(linearizedTargetInputFeatures, executionConfig.getNoOfRetries(), executionConfig.getPerturbationContext());
int newNoOfSamples = executionConfig.getNoOfSamples() + executionConfig.getNoOfSamples() / limeConfig.getNoOfRetries();
executionConfig = executionConfig.withSamples(newNoOfSamples).withPerturbationContext(newPerturbationContext);
}
return explainRetryCycle(model, originalInput, linearizedTargetInputFeatures, actualOutputs, executionConfig.withRetries(executionConfig.getNoOfRetries() - 1));
}
use of org.kie.kogito.explainability.model.PerturbationContext 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.PerturbationContext in project kogito-apps by kiegroup.
the class DmnTestUtils method getPredictionInputs.
private static List<PredictionInput> getPredictionInputs(PredictionInput predictionInput) {
List<PredictionInput> predictionInputs = new ArrayList<>();
Random random = new Random();
int noOfPerturbations = predictionInput.getFeatures().size();
PerturbationContext perturbationContext = new PerturbationContext(4L, random, noOfPerturbations);
for (int i = 0; i < 100; i++) {
List<Feature> perturbFeatures = DataUtils.perturbFeatures(predictionInput.getFeatures(), perturbationContext);
predictionInputs.add(new PredictionInput(perturbFeatures));
}
return predictionInputs;
}
Aggregations