<jupyter_start><jupyter_text>R-GAP: Recursive Gradient Attack on Privacy This notebook shows an example for a **single image gradient inversion** as described in "R-GAP: Recursive Gradient Attack on Privacy". The setting is a custom CNN from the paper for CIFAR-10 and the federated learning algorithm is **fedSGD**.Paper URL: https://openreview.net/forum?id=RSU17UoKfJFThis is attack is probably the least modular, compared to other attacks implemented in this framework, the code will likely not work for any other model than the custom CNN. This is partly a code problem, partly a question of how R-GAP would be used with other activation functions aside from leaky ReLU. The code here is a port of the original implementation from https://github.com/JunyiZhu-AI/R-GAP  AbstractFederated learning frameworks have been regarded as a promising approach to break the dilemma between demands on privacy and the promise of learning from large collections of distributed data. Many such frameworks only ask collaborators to share their local update of a common model, i.e. gradients with respect to locally stored data, instead of exposing their raw data to other collaborators. However, recent optimization-based gradient attacks show that raw data can often be accurately recovered from gradients. It has been shown that minimizing the Euclidean distance between true gradients and those calculated from estimated data is often effective in fully recovering private data. However, there is a fundamental lack of theoretical understanding of how and when gradients can lead to unique recovery of original data. Our research fills this gap by providing a closed-form recursive procedure to recover data from gradients in deep neural networks. We name it Recursive Gradient Attack on Privacy (R-GAP). Experimental results demonstrate that R-GAP  works as well as or even better than optimization-based approaches at a fraction of the computation under certain conditions. Additionally, we propose a Rank Analysis method, which can be used to estimate the risk of gradient attacks inherent in certain network architectures, regardless of whether an optimization-based or closed-form-recursive attack is used. Experimental results demonstrate the utility of the rank analysis towards improving the network's security. Source code is available for download from https://github.com/JunyiZhu-AI/R-GAP.  Startup<jupyter_code>try:
    import breaching
except ModuleNotFoundError:
    # You only really need this safety net if you want to run these notebooks directly in the examples directory
    # Don't worry about this if you installed the package or moved the notebook to the main directory.
    import os; os.chdir("..")
    import breaching
    
import torch
%load_ext autoreload
%autoreload 2

# Redirects logs directly into the jupyter notebook
import logging, sys
logging.basicConfig(level=logging.INFO, handlers=[logging.StreamHandler(sys.stdout)], format='%(message)s')
logger = logging.getLogger()<jupyter_output><empty_output><jupyter_text>Initialize cfg object and system setup: This will load the full configuration object. This includes the configuration for the use case and threat model as `cfg.case` and the hyperparameters and implementation of the attack as `cfg.attack`. All parameters can be modified below, or overriden with `overrides=` as if they were cmd-line arguments.<jupyter_code>cfg = breaching.get_config(overrides=["attack=rgap", "case=1_single_image_small", "case.model=cnn6"])
          
device = torch.device('cpu')
torch.backends.cudnn.benchmark = cfg.case.impl.benchmark
setup = dict(device=device, dtype=getattr(torch, cfg.case.impl.dtype))
setup<jupyter_output>Investigating use case single_image_small with server type honest_but_curious.<jupyter_text>Modify config options here You can use `.attribute` access to modify any of these configurations for the attack, or the case:<jupyter_code>cfg.case.user.user_idx = 1

cfg.case.user.num_data_points = 1 # The attack is designed for only one data point<jupyter_output><empty_output><jupyter_text>Instantiate all parties The following lines generate "server, "user" and "attacker" objects and print an overview of their configurations.<jupyter_code>user, server, model, loss_fn = breaching.cases.construct_case(cfg.case, setup)
attacker = breaching.attacks.prepare_attack(server.model, server.loss, cfg.attack, setup)
breaching.utils.overview(server, user, attacker)<jupyter_output>Files already downloaded and verified
Model architecture cnn6 loaded with 154,266 parameters and 0 buffers.
Overall this is a data ratio of      50:1 for target shape [1, 3, 32, 32] given that num_queries=1.
User (of type UserSingleStep) with settings:
    Number of data points: 1

    Threat model:
    User provides labels: True
    User provides buffers: False
    User provides number of data points: True

    Data:
    Dataset: CIFAR10
    user: 1
    
        
Server (of type HonestServer) with settings:
    Threat model: Honest-but-curious
    Number of planned queries: 1
    Has external/public data: False

    Model:
        model specification: cnn6
        model state: default
        

    Secrets: {}
    
Attacker (of type RecursiveAttacker) with settings:
               inversion:
                - step size: 0.01
                - steps    : 0.01<jupyter_text>Simulate an attacked FL protocol This exchange is a simulation of a single query in a federated learning protocol. The server sends out a `server_payload` and the user computes an update based on their private local data. This user update is `shared_data` and contains, for example, the parameter gradient of the model in the simplest case. `true_user_data` is also returned by `.compute_local_updates`, but of course not forwarded to the server or attacker and only used for (our) analysis.<jupyter_code>server_payload = server.distribute_payload()
shared_data, true_user_data = user.compute_local_updates(server_payload)
user.plot(true_user_data)<jupyter_output><empty_output><jupyter_text>Reconstruct user data: Now we launch the attack, reconstructing user data based on only the `server_payload` and the `shared_data`.<jupyter_code>reconstructed_user_data, stats = attacker.reconstruct([server_payload], [shared_data], {}, dryrun=cfg.dryrun)<jupyter_output>Linear(in_features=3200, out_features=10, bias=True)
LeakyReLU(negative_slope=0.2)
Flatten(start_dim=1, end_dim=-1)
Conv2d(64, 128, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
lstsq residual: [8.5700687e-16], rank: 3136 -> 3136, max/min singular value: 1.38e+00/2.58e-01
LeakyReLU(negative_slope=0.2)
Conv2d(36, 64, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=False)
lstsq residual: [9.679749e-16], rank: 4356 -> 4356, max/min singular value: 1.27e+00/1.36e-02
LeakyReLU(negative_slope=0.2)
Conv2d(36, 36, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
lstsq residual: [2.3853817e-13], rank: 4356 -> 4356, max/min singular value: 1.25e+00/1.66e-02
LeakyReLU(negative_slope=0.2)
Conv2d(36, 36, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1), bias=False)
lstsq residual: [7.583809e-11], rank: 4356 -> 4356, max/min singular value: 1.25e+00/6.82e-03
LeakyReLU(negative_slope=0.2)
Conv2d(12, 36, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1), bias=Fa[...]<jupyter_text>Next we'll evaluate metrics, comparing the `reconstructed_user_data` to the `true_user_data`.<jupyter_code>metrics = breaching.analysis.report(reconstructed_user_data, true_user_data, [server_payload], 
                                    server.model, order_batch=True, compute_full_iip=False, 
                                    cfg_case=cfg.case, setup=setup)<jupyter_output>Files already downloaded and verified
METRICS: | MSE: 0.0000 | PSNR: 49.01 | FMSE: 8.9096e-12 | LPIPS: 0.00|
 R-PSNR: 49.01 | IIP-pixel: 100.00% | SSIM: 0.9796 | max R-PSNR: 49.01 | max SSIM: 0.9796 | Label Acc: 100.00%<jupyter_text>And finally, we also plot the reconstructed data:<jupyter_code>user.plot(reconstructed_user_data)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.08170096702206794, 'fraction_numerical': 0.033473840813290356, 'mean_word_length': 4.8626453488372094, 'pattern_counts': {'":': 0, '<': 29, '<?xml version=': 0, '>': 33, 'https://': 3, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 5, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '8', 'dir': 'jupyter-structured-clean-dedup', 'id': '314869', 'license': 'MIT', 'n_tokens_mistral': 2542, 'n_tokens_neox': 2303, 'n_words': 896, 'path': 'examples/R-GAP  - Recursive Attack - Small ConvNet on CIFAR-10.ipynb', 'repo_name': 'ZHTushar23/breaching', 'size': '8066'}