Files
wehub-resource-sync dc6079821b
Docs Tests / Check for file changes (push) Has been cancelled
Docs Tests / Test Documentation (push) Has been cancelled
Docs Tests / Documentation Linting Checks (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.10, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.8, test-policies) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-performance) (push) Has been cancelled
Continuous Integration / Run Tests (windows-2022, 3.9, test-policies) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (ubuntu-24.04, 3.9) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.10) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.8) (push) Has been cancelled
Continuous Integration / Run Flaky Tests (windows-2022, 3.9) (push) Has been cancelled
Continuous Integration / Check for file changes (push) Has been cancelled
Continuous Integration / Wait for docs tests (push) Has been cancelled
Continuous Integration / Code Quality (push) Has been cancelled
Continuous Integration / Check for changelog (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-cli) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-core-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-full-model-training) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-featurizers) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-nlu-predictors) (push) Has been cancelled
Continuous Integration / Run Tests (ubuntu-24.04, 3.10, test-other-unit-tests) (push) Has been cancelled
Continuous Integration / Upload coverage reports to codeclimate (push) Has been cancelled
Continuous Integration / Run Non-Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Run Broker Integration Tests (push) Has been cancelled
Continuous Integration / Run Sequential Integration Tests (push) Has been cancelled
Continuous Integration / Build Docker base images and setup environment (push) Has been cancelled
Continuous Integration / Build Docker (default) (push) Has been cancelled
Continuous Integration / Build Docker (full) (push) Has been cancelled
Continuous Integration / Build Docker (mitie-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-de) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-en) (push) Has been cancelled
Continuous Integration / Build Docker (spacy-it) (push) Has been cancelled
Continuous Integration / Deploy to PyPI (push) Has been cancelled
Continuous Integration / Notify Slack & Publish Release Notes (push) Has been cancelled
Publish Documentation / Evaluate release tag (push) Has been cancelled
Publish Documentation / Prebuild Docs (push) Has been cancelled
Publish Documentation / Preview Docs (push) Has been cancelled
Publish Documentation / Check for file changes (push) Has been cancelled
Publish Documentation / Publish Docs (push) Has been cancelled
Automatic PR Merger / mergepal (push) Has been cancelled
CI Github Actions / Run Tests (push) Has been cancelled
Semgrep / Semgrep Workflow Security Scan (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:24:47 +08:00

363 lines
12 KiB
Python

import logging
import itertools
import os
from functools import wraps
import numpy as np
from typing import Any, Callable, List, Optional, Text, TypeVar, Union, Tuple
import matplotlib
from matplotlib.ticker import FormatStrFormatter
import rasa.shared.utils.io
from rasa.constants import RESULTS_FILE
logger = logging.getLogger(__name__)
def _fix_matplotlib_backend() -> None:
"""Tries to fix a broken matplotlib backend."""
try:
backend = matplotlib.get_backend()
except Exception: # skipcq:PYL-W0703
logger.error(
"Cannot retrieve Matplotlib backend, likely due to a compatibility "
"issue with system dependencies. Please refer to the documentation: "
"https://matplotlib.org/stable/tutorials/introductory/usage.html#backends"
)
raise
# At first, matplotlib will be initialized with default OS-specific
# available backend
if backend == "TkAgg":
try:
# on OSX sometimes the tkinter package is broken and can't be imported.
# we'll try to import it and if it fails we will use a different backend
import tkinter
except (ImportError, ModuleNotFoundError):
logger.debug("Setting matplotlib backend to 'agg'")
matplotlib.use("agg")
# if no backend is set by default, we'll try to set it up manually
elif backend is None: # pragma: no cover
try:
# If the `tkinter` package is available, we can use the `TkAgg` backend
import tkinter
logger.debug("Setting matplotlib backend to 'TkAgg'")
matplotlib.use("TkAgg")
except (ImportError, ModuleNotFoundError):
logger.debug("Setting matplotlib backend to 'agg'")
matplotlib.use("agg")
ReturnType = TypeVar("ReturnType")
FuncType = Callable[..., ReturnType]
_MATPLOTLIB_BACKEND_FIXED = False
def _needs_matplotlib_backend(func: FuncType) -> FuncType:
"""Decorator to fix matplotlib backend before calling a function."""
@wraps(func)
def inner(*args: Any, **kwargs: Any) -> ReturnType: # type: ignore
"""Replacement function that fixes matplotlib backend."""
global _MATPLOTLIB_BACKEND_FIXED
if not _MATPLOTLIB_BACKEND_FIXED:
_fix_matplotlib_backend()
_MATPLOTLIB_BACKEND_FIXED = True
return func(*args, **kwargs)
return inner
@_needs_matplotlib_backend
def plot_confusion_matrix(
confusion_matrix: np.ndarray,
classes: Union[np.ndarray, List[Text]],
normalize: bool = False,
title: Text = "Confusion matrix",
color_map: Any = None,
zmin: int = 1,
output_file: Optional[Text] = None,
) -> None:
"""Print and plot the provided confusion matrix.
Normalization can be applied by setting `normalize=True`.
Args:
confusion_matrix: confusion matrix to plot
classes: class labels
normalize: If set to true, normalization will be applied.
title: title of the plot
color_map: color mapping
zmin:
output_file: output file to save plot to
"""
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
zmax = confusion_matrix.max() if len(confusion_matrix) > 0 else 1
plt.clf()
if not color_map:
color_map = plt.cm.Blues
plt.imshow(
confusion_matrix,
interpolation="nearest",
cmap=color_map,
aspect="auto",
norm=LogNorm(vmin=zmin, vmax=zmax),
)
plt.title(title)
plt.colorbar()
tick_marks = np.arange(len(classes))
plt.xticks(tick_marks, classes, rotation=90)
plt.yticks(tick_marks, classes)
if normalize:
confusion_matrix = (
confusion_matrix.astype("float")
/ confusion_matrix.sum(axis=1)[:, np.newaxis]
)
logger.info(f"Normalized confusion matrix: \n{confusion_matrix}")
else:
logger.info(f"Confusion matrix, without normalization: \n{confusion_matrix}")
thresh = zmax / 2.0
for i, j in itertools.product(
range(confusion_matrix.shape[0]), range(confusion_matrix.shape[1])
):
plt.text(
j,
i,
confusion_matrix[i, j],
horizontalalignment="center",
color="white" if confusion_matrix[i, j] > thresh else "black",
)
plt.ylabel("True label")
plt.xlabel("Predicted label")
# save confusion matrix to file before showing it
if output_file:
fig = plt.gcf()
fig.set_size_inches(20, 20)
fig.savefig(output_file, bbox_inches="tight")
def _extract_paired_histogram_specification(
histogram_data: List[List[float]],
num_bins: int,
density: bool,
x_pad_fraction: float,
y_pad_fraction: float,
) -> Tuple[List[float], List[List[float]], List[float], Tuple[float, float]]:
"""Extracts all information from the data needed to plot a paired histogram.
Args:
histogram_data: Two data vectors
num_bins: Number of bins to be used for the histogram
density: If true, generate information for a probability density histogram
x_pad_fraction: Percentage of extra space in the horizontal direction
y_pad_fraction: Percentage of extra space in the vertical direction
Returns:
The bins, values, ranges of either x-axis, and the range of the y-axis
Raises:
ValueError: If histogram_data does not contain values.
"""
if not histogram_data or not np.concatenate(histogram_data).size:
rasa.shared.utils.io.raise_warning("No data to plot paired histogram.")
raise ValueError("No data to plot paired histogram.")
min_data_value: float = np.min(np.concatenate(histogram_data))
max_data_value: float = np.max(np.concatenate(histogram_data))
bin_width = (max_data_value - min_data_value) / num_bins
bins = [
min_data_value + i * bin_width
# `bins` describes the _boundaries_ of the bins, so we need
# 2 extra - one at the beginning and one at the end
for i in range(num_bins + 2)
]
histograms = [
# A list of counts - how often a value in `data` falls into a particular bin
list(np.histogram(data, bins=bins, density=density)[0])
for data in histogram_data
]
y_padding = 0.5 * bin_width + y_pad_fraction * bin_width
if density:
# Get the maximum count across both histograms, and scale it
# with `x_pad_fraction`
v = max([(1.0 + x_pad_fraction) * max(histogram) for histogram in histograms])
# When we plot the PDF, let both x-axes run to the same value
# so it's easier to compare visually
x_ranges = [v, v]
else:
# For the left and right histograms, get the largest counts and scale them
# by `x_pad_fraction` to get the maximum x-values displayed
x_ranges = [(1.0 + x_pad_fraction) * max(histogram) for histogram in histograms]
try:
bin_of_first_non_zero_tally = min(
[[bool(v) for v in histogram].index(True) for histogram in histograms]
)
except ValueError:
bin_of_first_non_zero_tally = 0
y_range = (
# Start plotting where the data starts (ignore empty bins at the low end)
bins[bin_of_first_non_zero_tally] - y_padding,
# The y_padding adds half a bin width, as we want the bars to be
# _centered_ on the bins. We take the next-to-last element of `bins`,
# because that is the beginning of the last bin.
bins[-2] + y_padding,
)
return bins, histograms, x_ranges, y_range
@_needs_matplotlib_backend
def plot_paired_histogram(
histogram_data: List[List[float]],
title: Text,
output_file: Optional[Text] = None,
num_bins: int = 25,
colors: Tuple[Text, Text] = ("#009292", "#920000"), # (dark cyan, dark red)
axes_label: Tuple[Text, Text] = ("Correct", "Wrong"),
frame_label: Tuple[Text, Text] = ("Number of Samples", "Confidence"),
density: bool = False,
x_pad_fraction: float = 0.05,
y_pad_fraction: float = 0.10,
) -> None:
"""Plots a side-by-side comparative histogram of the confidence distribution.
Args:
histogram_data: Two data vectors
title: Title to be displayed above the plot
output_file: File to save the plot to
num_bins: Number of bins to be used for the histogram
colors: Left and right bar colors as hex color strings
axes_label: Labels shown above the left and right histogram,
respectively
frame_label: Labels shown below and on the left of the
histogram, respectively
density: If true, generate a probability density histogram
x_pad_fraction: Percentage of extra space in the horizontal direction
y_pad_fraction: Percentage of extra space in the vertical direction
"""
if num_bins <= 2:
rasa.shared.utils.io.raise_warning(
f"Number {num_bins} of paired histogram bins must be at least 3."
)
return
try:
bins, tallies, x_ranges, y_range = _extract_paired_histogram_specification(
histogram_data,
num_bins,
density=density,
x_pad_fraction=x_pad_fraction,
y_pad_fraction=y_pad_fraction,
)
except (ValueError, TypeError) as e:
rasa.shared.utils.io.raise_warning(
f"Unable to plot paired histogram '{title}': {e}"
)
return
yticks = [float(f"{x:.2f}") for x in bins]
import matplotlib.pyplot as plt
plt.gcf().clear()
fig, axes = plt.subplots(ncols=2, sharey=True)
for side in range(2):
axes[side].barh(
bins[:-1],
tallies[side],
height=np.diff(bins),
align="center",
color=colors[side],
linewidth=1,
edgecolor="white",
)
axes[side].set(title=axes_label[side])
axes[side].set(yticks=yticks, xlim=(0, x_ranges[side]), ylim=y_range)
axes[0].yaxis.set_major_formatter(FormatStrFormatter("%.2f"))
axes[0].yaxis.set_minor_formatter(FormatStrFormatter("%.2f"))
axes[0].invert_xaxis()
axes[0].yaxis.tick_right()
# Add the title
fig.suptitle(title, fontsize="x-large", fontweight="bold")
# Add hidden plot to correctly add x and y labels (frame_label)
fig.add_subplot(111, frameon=False)
# Hide tick and tick label of the unused axis
plt.tick_params(labelcolor="none", top=False, bottom=False, left=False, right=False)
plt.xlabel(frame_label[0])
plt.ylabel(frame_label[1])
if output_file:
fig = plt.gcf()
fig.set_size_inches(10, 10)
fig.tight_layout(w_pad=0)
fig.savefig(output_file, bbox_inches="tight")
@_needs_matplotlib_backend
def plot_curve(
output_directory: Text,
number_of_examples: List[int],
x_label_text: Text,
y_label_text: Text,
graph_path: Text,
) -> None:
"""Plot the results from a model comparison.
Args:
output_directory: Output directory to save resulting plots to
number_of_examples: Number of examples per run
x_label_text: text for the x axis
y_label_text: text for the y axis
graph_path: output path of the plot
"""
import matplotlib.pyplot as plt
plt.gcf().clear()
ax = plt.gca()
# load results from file
data = rasa.shared.utils.io.read_json_file(
os.path.join(output_directory, RESULTS_FILE)
)
x = number_of_examples
# compute mean of all the runs for different configs
for label in data.keys():
if len(data[label]) == 0:
continue
mean = np.mean(data[label], axis=0)
std = np.std(data[label], axis=0)
ax.plot(x, mean, label=label, marker=".")
ax.fill_between(
x,
[m - s for m, s in zip(mean, std)],
[m + s for m, s in zip(mean, std)],
color="#6b2def",
alpha=0.2,
)
ax.legend(loc=4)
ax.set_xlabel(x_label_text)
ax.set_ylabel(y_label_text)
plt.savefig(graph_path, format="pdf")
logger.info(f"Comparison graph saved to '{graph_path}'.")