Rapid-Textual-Adversarial-Defense
/
textattack
/constraints
/pre_transformation
/max_modification_rate.py
| """ | |
| Max Modification Rate | |
| ----------------------------- | |
| """ | |
| import math | |
| from textattack.constraints import PreTransformationConstraint | |
| class MaxModificationRate(PreTransformationConstraint): | |
| """A constraint that prevents modifying words beyond certain percentage of | |
| total number of words. | |
| Args: | |
| max_rate (:obj:`float`): | |
| Percentage of words that can be modified. For example, given text of 20 words, `max_rate=0.1` will allow at most 2 words to be modified. | |
| min_threshold (:obj:`int`, optional, defaults to :obj:`1`): | |
| The minimum number of words that can be perturbed regardless of `max_rate`. For example, given text of 20 words and `max_rate=0.1`, | |
| setting`min_threshold=4` will still allow 4 words to be modified even though `max_rate=0.1` only allows 2 words. This is useful since | |
| text length can vary a lot between samples, and a `N%` modification limit might not make sense for very short text. | |
| """ | |
| def __init__(self, max_rate, min_threshold=1): | |
| assert isinstance(max_rate, float), "`max_rate` must be a float." | |
| assert max_rate >= 0 and max_rate <= 1, "`max_rate` must between 0 and 1." | |
| assert isinstance(min_threshold, int), "`min_threshold` must an int" | |
| self.max_rate = max_rate | |
| self.min_threshold = min_threshold | |
| def _get_modifiable_indices(self, current_text): | |
| """Returns the word indices in current_text which are able to be | |
| modified.""" | |
| threshold = max( | |
| math.ceil(current_text.num_words * self.max_rate), self.min_threshold | |
| ) | |
| if len(current_text.attack_attrs["modified_indices"]) >= threshold: | |
| return set() | |
| else: | |
| return set(range(len(current_text.words))) | |
| def extra_repr_keys(self): | |
| return ["max_rate", "min_threshold"] | |