<jupyter_start><jupyter_text>Shadow Model Attack IntuitionThis is one of the first membership inference attacks, presented by Shokri et al. https://arxiv.org/abs/1610.05820.The intuition behind this attack is that models often behave differently on the data that they were trained on versus the data they "see" for the first time.The simplest method to exploit this is to build a model which distinguishes model outputs calculated on training vs non-training data.But how does the attacker know whether a sample was used for training or not? He has no access to the target model's training data. But, he does know the underlying architecture of the target model (or a similar, suitable architecture for the classification task).To solve this conundrum, the attack trains **shadow models** (proxies for the real model), where he knows the membership information of the samples and can easily collect the corresponding model outputs. The intuition is that _shadow models_ will behave similarly to the target model on training and non-training samples, therefore can be used to construct an attack against the target model.The attacker trains _k_ shadow models on his own data, then queries training and non-training samples with these models. It creates a data set containing the sample's _true label_, the shadow model's _prediction_ and a binary label indicating whether or not it is a training sample (_in_ or _out_).It trains the attacker model (which can be any kind of binary classifier) on this dataset.Its input is the (_real label_, _prediction_) tuple and its output is _in_ or _out_ (meaning the sample is _in_ the training dataset or not).![title](img/shadow_model_attack.png)  OverviewHow to implement the attack using ART. 1. [Preliminaries](preliminaries)1. [Load data and target model](load)2. [Wrap model in ART classifier wrapper](wrap) 2. [Attack](attack)1. [Define shadow model](shadow)2. [Instantiate attack](instantiate)3. [Fit the attack on shadow data](fit)4. [Infer membership on evaluation data](infer)<jupyter_code>import torch
from torch import nn
from models.mnist import Net<jupyter_output><empty_output><jupyter_text>Preliminaries  Load data and target model<jupyter_code>import numpy as np
import os
import sys
sys.path.insert(0, os.path.abspath('..'))

from art.utils import load_mnist

# data
(x_train, y_train), (x_test, y_test), _min, _max = load_mnist(raw=True)

# limit training data to 50000 samples
x_train_target = np.expand_dims(x_train, axis=1).astype(np.float32)[:50000]
y_train_target = y_train[:50000]
x_test = np.expand_dims(x_test, axis=1).astype(np.float32)

# shadow data (10 000 disjunct samples)
x_train_shadow = np.expand_dims(x_train, axis=1).astype(np.float32)[50000:]
y_train_shadow = y_train[50000:]<jupyter_output><empty_output><jupyter_text>Wrap model in PyTorchClassifier<jupyter_code>import torch.optim as optim
from art.estimators.classification.pytorch import PyTorchClassifier

model = Net()

criterion = nn.CrossEntropyLoss()
optimizer = optim.Adam(model.parameters())

art_model = PyTorchClassifier(model=model, loss=criterion, optimizer=optimizer, channels_first=True, input_shape=(1,28,28,), nb_classes=10, clip_values=(_min,_max))<jupyter_output><empty_output><jupyter_text>Fit model if not already pretrained<jupyter_code>art_model.fit(x_train_target, y_train_target, nb_epochs=10)<jupyter_output><empty_output><jupyter_text>Test accuracy of the target model<jupyter_code>pred = np.array([np.argmax(arr) for arr in art_model.predict(x_train_target)])

print('Base model accuracy: ', np.sum(pred == y_train_target) / len(y_train_target))<jupyter_output>Base model accuracy:  0.99304<jupyter_text>Attack  Define shadow modelWe define a shadow model that will mirror the behaviour of the target model. An attacker with **black-box** knowledge is assumed, who does not know the architecture, model/training parameters of the target model.<jupyter_code>import torch.nn.functional as F
class Linear_Net(nn.Module):
    def __init__(self):
        super(Linear_Net, self).__init__()
        self.fc1 = nn.Linear(784, 128)
        self.dropout = nn.Dropout(0.5)
        self.fc2 = nn.Linear(128, 10)

    def forward(self, x):
        x = torch.flatten(x, 1)
        x = self.fc1(x)
        x = F.relu(x)
        x = self.dropout(x)
        x = self.fc2(x)
        output = F.log_softmax(x, dim=1)
        return output
    
shadow_model = Linear_Net()

optimizer = optim.Adam(shadow_model.parameters())

art_shadow_model = PyTorchClassifier(shadow_model, loss=criterion, optimizer=optimizer, channels_first=True, input_shape=(1,28,28,), nb_classes=10, clip_values=(_min,_max))<jupyter_output><empty_output><jupyter_text>Instantiate attackInputs to the attack:- target model- shadow model- number of shadow models to create- number of samples that the shadow models train on- input type ("loss" or "prediction")- attack model type ("rf" for Random Forest, "gb" for Gradient Boosting, or "nn" for neural network)You can also define your own custom attacker model and pass it to `attack_model` as an argument (if provided `attack_model_type` is ignored).  `The "nn" architecture is a two-stream neural network (one stream for the observed model output, the other for the true label), with 3 linear layers (512, 100, 64) in the observed model output stream and 2 layers (256, 64) in the true label stream. The two streams are combined by a linear layer (with a single neuron).`<jupyter_code>from art.attacks.inference.membership_inference import ShadowModelAttack

shadow_model_atk = ShadowModelAttack(
    art_model,
    shadow_model=art_shadow_model,
    nb_shadow_models=5,
    shadow_dataset_size=3000,
    input_type="prediction",
    attack_model_type="rf"
)<jupyter_output><empty_output><jupyter_text>Fit<jupyter_code>shadow_model_atk.fit(
    x_train_shadow,
    y_train_shadow,
    x_test,
    y_test,
    nb_epochs=40,
)<jupyter_output>Accuracy of shadow model (on test set): 0.919333
Accuracy of shadow model (on test set): 0.908667
Accuracy of shadow model (on test set): 0.908667
Accuracy of shadow model (on test set): 0.924000
Accuracy of shadow model (on test set): 0.913000<jupyter_text>Infer membeship on evaluation dataWe use the training data of the target model and the testing data for evaluation (10 000 samples each).<jupyter_code>membership = [1] * 10000 + [0] * 10000

inferred_membership = shadow_model_atk.infer(np.concatenate([x_train_target[:10000], x_test]), np.concatenate([y_train_target[:10000], y_test]))<jupyter_output><empty_output><jupyter_text>Attack accuracy<jupyter_code>from sklearn.metrics import accuracy_score

acc = accuracy_score(membership, inferred_membership)

print("Shadow model attack accuracy: %f" % acc)<jupyter_output>Shadow model attack accuracy: 0.695700
{'fraction_non_alphanumeric': 0.08627566848869848, 'fraction_numerical': 0.027921406411582212, 'mean_word_length': 5.592015579357351, 'pattern_counts': {'":': 0, '<': 38, '<?xml version=': 0, '>': 38, 'https://': 1, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '10', 'dir': 'jupyter-structured-clean-dedup', 'id': '155738', 'license': 'MIT', 'n_tokens_mistral': 2227, 'n_tokens_neox': 2062, 'n_words': 714, 'path': 'demos/shadow_model_attack.ipynb', 'repo_name': 'minaremeli/adversarial-robustness-toolbox', 'size': '6769'}