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

371 lines
14 KiB
Python

from typing import Dict, Any, List, Tuple, Optional, Union
import numpy as np
import scipy.sparse
from safetensors.numpy import load_file
from safetensors.numpy import save_file
import rasa.shared.utils.io
def _recursive_serialize(
array: Any, prefix: str, data_dict: Dict[str, Any], metadata: List[Dict[str, Any]]
) -> None:
"""Recursively serialize arrays and matrices for high dimensional data."""
if isinstance(array, np.ndarray) and array.ndim <= 2:
data_key = f"{prefix}_array"
data_dict[data_key] = array
metadata.append({"type": "dense", "key": data_key, "shape": array.shape})
elif isinstance(array, list) and all([isinstance(v, float) for v in array]):
data_key = f"{prefix}_list"
data_dict[data_key] = np.array(array, dtype=np.float32)
metadata.append({"type": "list", "key": data_key})
elif isinstance(array, list) and all([isinstance(v, int) for v in array]):
data_key = f"{prefix}_list"
data_dict[data_key] = np.array(array, dtype=np.int64)
metadata.append({"type": "list", "key": data_key})
elif isinstance(array, scipy.sparse.spmatrix):
data_key_data = f"{prefix}_data"
data_key_row = f"{prefix}_row"
data_key_col = f"{prefix}_col"
array = array.tocoo()
data_dict.update(
{
data_key_data: array.data,
data_key_row: array.row,
data_key_col: array.col,
}
)
metadata.append({"type": "sparse", "key": prefix, "shape": array.shape})
elif isinstance(array, list) or isinstance(array, np.ndarray):
group_metadata = {"type": "group", "subcomponents": []}
for idx, item in enumerate(array):
new_prefix = f"{prefix}_{idx}"
_recursive_serialize(
item, new_prefix, data_dict, group_metadata["subcomponents"]
)
metadata.append(group_metadata)
def _serialize_nested_data(
nested_data: Dict[str, Dict[str, List["FeatureArray"]]],
prefix: str,
data_dict: Dict[str, np.ndarray],
metadata: List[Dict[str, Union[str, List]]],
) -> None:
"""Handle serialization across dictionary and list levels."""
for outer_key, inner_dict in nested_data.items():
inner_metadata = {"key": outer_key, "components": []}
for inner_key, feature_arrays in inner_dict.items():
array_metadata = {
"key": inner_key,
"number_of_dimensions": feature_arrays[0].number_of_dimensions,
"features": [],
}
for idx, feature_array in enumerate(feature_arrays):
feature_prefix = f"{prefix}_{outer_key}_{inner_key}_{idx}"
_recursive_serialize(
feature_array.tolist(),
feature_prefix,
data_dict,
array_metadata["features"],
)
inner_metadata["components"].append( # type:ignore[attr-defined]
array_metadata
)
metadata.append(inner_metadata)
def serialize_nested_feature_arrays(
nested_feature_array: Dict[str, Dict[str, List["FeatureArray"]]],
data_filename: str,
metadata_filename: str,
) -> None:
data_dict: Dict[str, np.ndarray] = {}
metadata: List[Dict[str, Union[str, List]]] = []
_serialize_nested_data(nested_feature_array, "component", data_dict, metadata)
# Save serialized data and metadata
save_file(data_dict, data_filename)
rasa.shared.utils.io.dump_obj_as_json_to_file(metadata_filename, metadata)
def _recursive_deserialize(
metadata: List[Dict[str, Any]], data: Dict[str, Any]
) -> List[Any]:
"""Recursively deserialize arrays and matrices for high dimensional data."""
result = []
for item in metadata:
if item["type"] == "dense":
key = item["key"]
array = np.asarray(data[key]).reshape(item["shape"])
result.append(array)
elif item["type"] == "list":
key = item["key"]
result.append(list(data[key]))
elif item["type"] == "sparse":
data_vals = data[f"{item['key']}_data"]
row_vals = data[f"{item['key']}_row"]
col_vals = data[f"{item['key']}_col"]
sparse_matrix = scipy.sparse.coo_matrix(
(data_vals, (row_vals, col_vals)), shape=item["shape"]
)
result.append(sparse_matrix)
elif item["type"] == "group":
sublist = _recursive_deserialize(item["subcomponents"], data)
result.append(sublist)
return result
def _deserialize_nested_data(
metadata: List[Dict[str, Any]], data_dict: Dict[str, Any]
) -> Dict[str, Dict[str, List["FeatureArray"]]]:
"""Handle deserialization across all dictionary and list levels."""
result: Dict[str, Dict[str, List["FeatureArray"]]] = {}
for outer_item in metadata:
outer_key = outer_item["key"]
result[outer_key] = {}
for inner_item in outer_item["components"]:
inner_key = inner_item["key"]
feature_arrays = []
# Reconstruct the list of FeatureArrays
for feature_item in inner_item["features"]:
# Reconstruct the list of FeatureArrays
feature_array_data = _recursive_deserialize([feature_item], data_dict)
# Prepare the input for the FeatureArray;
# ensure it is np.ndarray compatible
input_array = np.array(feature_array_data[0], dtype=object)
feature_array = FeatureArray(
input_array, inner_item["number_of_dimensions"]
)
feature_arrays.append(feature_array)
result[outer_key][inner_key] = feature_arrays
return result
def deserialize_nested_feature_arrays(
data_filename: str, metadata_filename: str
) -> Dict[str, Dict[str, List["FeatureArray"]]]:
metadata = rasa.shared.utils.io.read_json_file(metadata_filename)
data_dict = load_file(data_filename)
return _deserialize_nested_data(metadata, data_dict)
class FeatureArray(np.ndarray):
"""Stores any kind of features ready to be used by a RasaModel.
Next to the input numpy array of features, it also received the number of
dimensions of the features.
As our features can have 1 to 4 dimensions we might have different number of numpy
arrays stacked. The number of dimensions helps us to figure out how to handle this
particular feature array. Also, it is automatically determined whether the feature
array is sparse or not and the number of units is determined as well.
Subclassing np.array: https://numpy.org/doc/stable/user/basics.subclassing.html
"""
def __new__(
cls, input_array: np.ndarray, number_of_dimensions: int
) -> "FeatureArray":
"""Create and return a new object. See help(type) for accurate signature."""
FeatureArray._validate_number_of_dimensions(number_of_dimensions, input_array)
feature_array = np.asarray(input_array).view(cls)
if number_of_dimensions <= 2:
feature_array.units = input_array.shape[-1]
feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
elif number_of_dimensions == 3:
feature_array.units = input_array[0].shape[-1]
feature_array.is_sparse = isinstance(input_array[0], scipy.sparse.spmatrix)
elif number_of_dimensions == 4:
feature_array.units = input_array[0][0].shape[-1]
feature_array.is_sparse = isinstance(
input_array[0][0], scipy.sparse.spmatrix
)
else:
raise ValueError(
f"Number of dimensions '{number_of_dimensions}' currently not "
f"supported."
)
feature_array.number_of_dimensions = number_of_dimensions
return feature_array
def __init__(
self, input_array: Any, number_of_dimensions: int, **kwargs: Any
) -> None:
"""Initialize. FeatureArray.
Needed in order to avoid 'Invalid keyword argument number_of_dimensions
to function FeatureArray.__init__ '
Args:
input_array: the array that contains features
number_of_dimensions: number of dimensions in input_array
"""
super().__init__(**kwargs)
self.number_of_dimensions = number_of_dimensions
def __array_finalize__(self, obj: Optional[np.ndarray]) -> None:
"""This method is called when the system allocates a new array from obj.
Args:
obj: A subclass (subtype) of ndarray.
"""
if obj is None:
return
self.units = getattr(obj, "units", None)
self.number_of_dimensions = getattr(
obj, "number_of_dimensions", None
) # type: ignore[assignment]
self.is_sparse = getattr(obj, "is_sparse", None)
default_attributes = {
"units": self.units,
"number_of_dimensions": self.number_of_dimensions,
"is_spare": self.is_sparse,
}
self.__dict__.update(default_attributes)
# pytype: disable=attribute-error
def __array_ufunc__(
self, ufunc: Any, method: str, *inputs: Any, **kwargs: Any
) -> Any:
"""Overwrite this method as we are subclassing numpy array.
Args:
ufunc: The ufunc object that was called.
method: A string indicating which Ufunc method was called
(one of "__call__", "reduce", "reduceat", "accumulate", "outer",
"inner").
*inputs: A tuple of the input arguments to the ufunc.
**kwargs: Any additional arguments
Returns:
The result of the operation.
"""
f = {
"reduce": ufunc.reduce,
"accumulate": ufunc.accumulate,
"reduceat": ufunc.reduceat,
"outer": ufunc.outer,
"at": ufunc.at,
"__call__": ufunc,
}
# convert the inputs to np.ndarray to prevent recursion, call the function,
# then cast it back as FeatureArray
output = FeatureArray(
f[method](*(i.view(np.ndarray) for i in inputs), **kwargs),
number_of_dimensions=kwargs["number_of_dimensions"],
)
output.__dict__ = self.__dict__ # carry forward attributes
return output
def __reduce__(self) -> Tuple[Any, Any, Any]:
"""Needed in order to pickle this object.
Returns:
A tuple.
"""
pickled_state = super(FeatureArray, self).__reduce__()
if isinstance(pickled_state, str):
raise TypeError("np array __reduce__ returned string instead of tuple.")
new_state = pickled_state[2] + (
self.number_of_dimensions,
self.is_sparse,
self.units,
)
return pickled_state[0], pickled_state[1], new_state
def __setstate__(self, state: Any, **kwargs: Any) -> None:
"""Sets the state.
Args:
state: The state argument must be a sequence that contains the following
elements version, shape, dtype, isFortan, rawdata.
**kwargs: Any additional parameter
"""
# Needed in order to load the object
self.number_of_dimensions = state[-3]
self.is_sparse = state[-2]
self.units = state[-1]
super(FeatureArray, self).__setstate__(state[0:-3], **kwargs)
# pytype: enable=attribute-error
@staticmethod
def _validate_number_of_dimensions(
number_of_dimensions: int, input_array: np.ndarray
) -> None:
"""Validates if the input array has given number of dimensions.
Args:
number_of_dimensions: number of dimensions
input_array: input array
Raises: ValueError in case the dimensions do not match
"""
# when loading the feature arrays from disk, the shape represents
# the correct number of dimensions
if len(input_array.shape) == number_of_dimensions:
return
_sub_array = input_array
dim = 0
# Go number_of_dimensions into the given input_array
for i in range(1, number_of_dimensions + 1):
_sub_array = _sub_array[0]
if isinstance(_sub_array, scipy.sparse.spmatrix):
dim = i
break
if isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
# sequence dimension is 0, we are dealing with "fake" features
dim = i
break
# If the resulting sub_array is sparse, the remaining number of dimensions
# should be at least 2
if isinstance(_sub_array, scipy.sparse.spmatrix):
if dim > 2:
raise ValueError(
f"Given number of dimensions '{number_of_dimensions}' does not "
f"match dimensions of given input array: {input_array}."
)
elif isinstance(_sub_array, np.ndarray) and _sub_array.shape[0] == 0:
# sequence dimension is 0, we are dealing with "fake" features,
# but they should be of dim 2
if dim > 2:
raise ValueError(
f"Given number of dimensions '{number_of_dimensions}' does not "
f"match dimensions of given input array: {input_array}."
)
# If the resulting sub_array is dense, the sub_array should be a single number
elif not np.issubdtype(type(_sub_array), np.integer) and not isinstance(
_sub_array, (np.float32, np.float64)
):
raise ValueError(
f"Given number of dimensions '{number_of_dimensions}' does not match "
f"dimensions of given input array: {input_array}."
)