Privacy

Classes

DPController

DPController(dp_args=None)

Controls DP action during training.

Parameters:

Name Type Description Default
dp_args Union[Dict, None]

Arguments for differential privacy

None
Source code in fedbiomed/common/privacy/_dp_controller.py
def __init__(self, dp_args: Union[Dict, None] = None) -> None:
    """Constructs DPController with given model.

    Args:
        dp_args: Arguments for differential privacy
    """
    self._privacy_engine = PrivacyEngine()
    self._dp_args = dp_args or {}
    self._is_active = dp_args is not None
    logger.debug(
        "Initializing DP controller: active=%s provided_args=%s",
        self._is_active,
        sorted(self._dp_args.keys()),
    )
    # Configure/validate dp arguments
    if self._is_active:
        self._configure_dp_args()

Functions

after_training
after_training(params, renaming=True)

DP actions after the training.

Parameters:

Name Type Description Default
params Dict

Contains model parameters after training with DP

required
renaming bool

whether to modify parameters names

True

Returns: params fixed model parameters after applying differential privacy

Source code in fedbiomed/common/privacy/_dp_controller.py
def after_training(self, params: Dict, renaming: bool = True) -> Dict:
    """DP actions after the training.

    Args:
        params: Contains model parameters after training with DP
        renaming: whether to modify parameters names
    Returns:
        `params` fixed model parameters after applying differential privacy
    """
    if self._is_active:
        logger.debug(
            "Applying DP after training: dp_type=%s parameter_count=%d",
            self._dp_args.get("type"),
            len(params),
        )
        params = self._postprocess_dp(params, renaming)
    return params
before_training
before_training(optimizer, loader)

DP action before starting training.

Parameters:

Name Type Description Default
optimizer NativeTorchOptimizer

NativeTorchOptimizer for training

required
loader DataLoader

Data loader for training

required

Returns:

Type Description
Tuple[NativeTorchOptimizer, DPDataLoader]

Differential privacy applied Optimizer and data loader

Source code in fedbiomed/common/privacy/_dp_controller.py
def before_training(
    self, optimizer: NativeTorchOptimizer, loader: DataLoader
) -> Tuple[NativeTorchOptimizer, DPDataLoader]:
    """DP action before starting training.

    Args:
        optimizer: NativeTorchOptimizer for training
        loader: Data loader for training

    Returns:
        Differential privacy applied Optimizer and data loader
    """
    # Before training is called once per node per round.
    # The effects remain throughout the batch steps inside that round
    #
    # dp_type=local: Per-batch dp, inside the local training loop of a single round
    # This is done per batch thanks to the Opacus `PrivacyEngine` `make_private` function that is attached to the model, optimizer and data loader.
    #
    # dp_type=central: Noise is added once after training, in the `after_training` function.
    # before_training() still runs once at the start of the round, but with sigma=0.0 after normalization, so the training-time noise path is disabled, and it only does clipping.

    if self._is_active:
        logger.debug(
            "Applying DP before training: optimizer_type=%s loader_type=%s dp_type=%s",
            type(optimizer.optimizer).__name__,
            type(loader).__name__,
            self._dp_args.get("type"),
        )
        if not isinstance(optimizer.optimizer, torch.optim.Optimizer):
            raise FedbiomedDPControllerError(
                f"{ErrorNumbers.FB616.value}: "
                f"Optimizer must be an instance of torch.optim.Optimizer, but got {optimizer}"
                "\nDeclearn optimizers are not yet compatible with Differential Privacy"
            )
        if not isinstance(loader, DataLoader):
            raise FedbiomedDPControllerError(
                f"{ErrorNumbers.FB616.value}: "
                "Data loader must be an instance of torch.utils.data.DataLoader"
            )
        try:
            optimizer._model.model, optimizer.optimizer, loader = (
                self._privacy_engine.make_private(
                    module=optimizer._model.model,
                    optimizer=optimizer.optimizer,
                    data_loader=loader,
                    noise_multiplier=float(self._dp_args["sigma"]),
                    max_grad_norm=float(self._dp_args["clip"]),
                )
            )
            logger.debug(
                "DP privacy engine attached successfully: dp_type=%s loader_type=%s",
                self._dp_args.get("type"),
                type(loader).__name__,
            )
        except Exception as e:
            raise FedbiomedDPControllerError(
                f"{ErrorNumbers.FB616.value}: "
                f"Error while running privacy engine: {e}"
            ) from e
    return optimizer, loader
rename_params
rename_params(params)

Rename the model parameters modified by Opacus

Parameters:

Name Type Description Default
params Dict

Contains model parameters after training with DP

required

Returns:

Name Type Description
renamed_params Dict

parameters with Opacus prefix stripped.

rename_mapping Dict[str, str]

maps new names to original Opacus names, only for parameters whose names were actually modified.

Source code in fedbiomed/common/privacy/_dp_controller.py
def rename_params(self, params: Dict) -> Tuple[Dict, Dict[str, str]]:
    """Rename the model parameters modified by Opacus

    Args:
        params: Contains model parameters after training with DP

    Returns:
        renamed_params: parameters with Opacus prefix stripped.
        rename_mapping: maps new names to original Opacus names,
            only for parameters whose names were actually modified.
    """
    renamed_params = {}
    rename_mapping = {}
    for key, param in params.items():
        new_key = key.replace("_module.", "")
        renamed_params[new_key] = param
        if new_key != key:
            rename_mapping[new_key] = key
    return renamed_params, rename_mapping
revert_rename_params
revert_rename_params(params, rename_mapping)

Restore the original Opacus parameter names using a rename mapping.

Parameters:

Name Type Description Default
params Dict

Contains model parameters with stripped names.

required
rename_mapping Dict[str, str]

Maps stripped names back to original Opacus names, as returned by rename_params.

required

Returns: Parameters with original Opacus names restored.

Source code in fedbiomed/common/privacy/_dp_controller.py
def revert_rename_params(
    self, params: Dict, rename_mapping: Dict[str, str]
) -> Dict:
    """Restore the original Opacus parameter names using a rename mapping.

    Args:
        params: Contains model parameters with stripped names.
        rename_mapping: Maps stripped names back to original Opacus names,
            as returned by rename_params.
    Returns:
        Parameters with original Opacus names restored.
    """
    return {rename_mapping.get(key, key): param for key, param in params.items()}
validate_and_fix_model
validate_and_fix_model(model)

Validate and Fix model to be DP-compliant.

Parameters:

Name Type Description Default
model Module

An instance of Module

required

Returns:

Type Description
Module

Fixed or validated model

Source code in fedbiomed/common/privacy/_dp_controller.py
def validate_and_fix_model(self, model: Module) -> Module:
    """Validate and Fix model to be DP-compliant.

    Args:
        model: An instance of [`Module`][torch.nn.Module]

    Returns:
        Fixed or validated model
    """
    if self._is_active and not ModuleValidator.is_valid(model):
        logger.debug("DP model validation failed, attempting automatic fix")
        try:
            model = ModuleValidator.fix(model)
        except Exception as e:
            raise FedbiomedDPControllerError(
                f"{ErrorNumbers.FB616.value}: "
                f"Error while making model DP-compliant: {e}"
            ) from e
        logger.debug("DP model automatic fix applied successfully")
    elif self._is_active:
        logger.debug("DP model validation passed without modification")
    return model