Customize attackers and defenders
OpenBackdoor provides extensible interfaces to customize new attackers/defenders. You can define your own attacker/defender class for this.
Customize Attacker
To write a custom attacker, you need to modify the base attacker class:
class Attacker(object):
def attack(self, victim: Victim, data: List, config: Optional[dict] = None, defender: Optional[Defender] = None):
"""
Attack the victim model with the attacker.
Args:
victim (:obj:`Victim`): the victim to attack.
data (:obj:`List`): the dataset to attack.
config (:obj:`dict`, optional): the config of attacker.
defender (:obj:`Defender`, optional): the defender.
Returns:
:obj:`Victim`: the attacked model.
"""
poison_dataset = self.poison(victim, data, "train")
if defender is not None and defender.pre is True:
poison_dataset["train"] = defender.correct(poison_data=poison_dataset['train'])
backdoored_model = self.train(victim, poison_dataset)
return backdoored_model
def poison(self, victim: Victim, dataset: List, mode: str):
"""
Default poisoning function.
Args:
victim (:obj:`Victim`): the victim to attack.
dataset (:obj:`List`): the dataset to attack.
mode (:obj:`str`): the mode of poisoning.
Returns:
:obj:`List`: the poisoned dataset.
"""
return self.poisoner(dataset, mode)
def train(self, victim: Victim, dataset: List):
"""
default training: normal training
Args:
victim (:obj:`Victim`): the victim to attack.
dataset (:obj:`List`): the dataset to attack.
Returns:
:obj:`Victim`: the attacked model.
"""
return self.poison_trainer.train(victim, dataset, self.metrics)
An attacker contains a poisoner and a trainer. The poisoner is used to poison the dataset. The trainer is used to train the backdoored model.
You can set your own data poisoning algorithm as a poisoner
class Poisoner(object):
def poison(self, data: List):
"""
Poison all the data.
Args:
data (:obj:`List`): the data to be poisoned.
Returns:
:obj:`List`: the poisoned data.
"""
return data
And control the training schedule by a trainer
class Trainer(object):
def train(self, model: Victim, dataset, metrics: Optional[List[str]] = ["accuracy"]):
"""
Train the model.
Args:
model (:obj:`Victim`): victim model.
dataset (:obj:`Dict`): dataset.
metrics (:obj:`List[str]`, optional): list of metrics. Default to ["accuracy"].
Returns:
:obj:`Victim`: trained model.
"""
return self.model
Customize Defender
To write a custom defender, you need to modify the base defender class. In OpenBackdoor, we define two basic methods for a defender.
detect: to detect the poisoned samplescorrect: to correct the poisoned samples
You can also implement other kinds of defenders.
class Defender(object):
"""
The base class of all defenders.
Args:
name (:obj:`str`, optional): the name of the defender.
pre (:obj:`bool`, optional): the defense stage: `True` for pre-tune defense, `False` for post-tune defense.
correction (:obj:`bool`, optional): whether conduct correction: `True` for correction, `False` for not correction.
metrics (:obj:`List[str]`, optional): the metrics to evaluate.
"""
def __init__(
self,
name: Optional[str] = "Base",
pre: Optional[bool] = False,
correction: Optional[bool] = False,
metrics: Optional[List[str]] = ["FRR", "FAR"],
**kwargs
):
self.name = name
self.pre = pre
self.correction = correction
self.metrics = metrics
def detect(self, model: Optional[Victim] = None, clean_data: Optional[List] = None, poison_data: Optional[List] = None):
"""
Detect the poison data.
Args:
model (:obj:`Victim`): the victim model.
clean_data (:obj:`List`): the clean data.
poison_data (:obj:`List`): the poison data.
Returns:
:obj:`List`: the prediction of the poison data.
"""
return [0] * len(poison_data)
def correct(self, model: Optional[Victim] = None, clean_data: Optional[List] = None, poison_data: Optional[Dict] = None):
"""
Correct the poison data.
Args:
model (:obj:`Victim`): the victim model.
clean_data (:obj:`List`): the clean data.
poison_data (:obj:`List`): the poison data.
Returns:
:obj:`List`: the corrected poison data.
"""
return poison_data