Refactor detector to allow for different standardizations of input features
Created by: AMHermansen
Is your feature request related to a problem? Please describe. Currently we are restricted to create a new detector class if we want to experiment with different standardization of input features. While it currently isn't an issue, we could easily in the future experience a large increase in detectors which for the most part are completely identical, except they do slightly different standardization.
Describe the solution you'd like
If we refactor the Detector class to contain include a class member containing the available features, and then for each feature we provide an instantiated FeatureStandardizer module, which handles the standardization of the given input feature. The classes would then look something like this:
class Detector(Model):
@property
@abstractmethod
def all_features(self):
"""Returns container (List/Tuple) containing all supported features by the detector."""
def __init__(self, used_features: List[str] | Tuple[str], transformations: List[FeatureStandardizer] | Tuple[FeatureStandardizer]):
if not (len(used_features) == len(transformations)):
raise ValueError("Length of used_features and transformations must be identical")
for feature in used_features:
if feature not in self.all_features:
raise ValueError(f"{feature} not a recognized feature for this detector. Recognized features are {self.all_features}")
self.used_features = used_features # features used
self.transformations = transformations # transformations used each index corresponds to the index in used
self.feature_map = {
feature: transformation for feature, transformation in zip(used_features, transformations)
}
def forward(self, x):
"""Applies the correct transformation from feature map to each feature in x, possibly also removes redundant feature, if not already done."""
...
class FeatureStandardizer(Model):
@abstractmethod
def forward(self, x):
"""Applies the given standardization procedure.
x: Contains only the correct feature. Applying the correct FeatureStandardizer to each feature should be taken care of in Detector"""
class LinearStandardizer(FeatureStandardizer):
def __init__(self, offset, scale):
self._offset = offset
self._scale = scale
def forward(self, x):
return (x - self._offset) / self._scale
I'm not well versed enough in config files, to know exactly how those should be made, but they should contain information about which FeatureStandardizers is used for each feature, and the arguments passed to the constructor for the given FeatureStandardizer.
Describe alternatives you've considered
In the constructor for the detector, one could also pass the feature map directly. The transformations might not be needed to save internally in the Detector either, but I think keeping the used_features is beneficial.
Additional context This feature request is inspired by comments from #558