def test_slice_by_percentiles(self): input_data = SlicingSpec(entire_dataset=False, by_percentiles=True) expected0 = SingleSliceSpec(SlicingFeature.PERCENTILE, (0, 10)) expected5 = SingleSliceSpec(SlicingFeature.PERCENTILE, (50, 60)) output = get_single_slice_specs(input_data) self.assertLen(output, 10) self.assertTrue(_are_all_fields_equal(output[0], expected0)) self.assertTrue(_are_all_fields_equal(output[5], expected5))
def __init__(self, *args, **kwargs): super(PrivacyReportTest, self).__init__(*args, **kwargs) # Classifier that achieves an AUC of 0.5. self.imperfect_classifier_result = SingleAttackResult( slice_spec=SingleSliceSpec(None), attack_type=AttackType.THRESHOLD_ATTACK, roc_curve=RocCurve( tpr=np.array([0.0, 0.5, 1.0]), fpr=np.array([0.0, 0.5, 1.0]), thresholds=np.array([0, 1, 2])), data_size=DataSize(ntrain=1, ntest=1)) # Classifier that achieves an AUC of 1.0. self.perfect_classifier_result = SingleAttackResult( slice_spec=SingleSliceSpec(None), attack_type=AttackType.THRESHOLD_ATTACK, roc_curve=RocCurve( tpr=np.array([0.0, 1.0, 1.0]), fpr=np.array([1.0, 1.0, 0.0]), thresholds=np.array([0, 1, 2])), data_size=DataSize(ntrain=1, ntest=1)) self.results_epoch_0 = AttackResults( single_attack_results=[self.imperfect_classifier_result], privacy_report_metadata=PrivacyReportMetadata( accuracy_train=0.4, accuracy_test=0.3, epoch_num=0, model_variant_label='default')) self.results_epoch_10 = AttackResults( single_attack_results=[self.imperfect_classifier_result], privacy_report_metadata=PrivacyReportMetadata( accuracy_train=0.4, accuracy_test=0.3, epoch_num=10, model_variant_label='default')) self.results_epoch_15 = AttackResults( single_attack_results=[self.perfect_classifier_result], privacy_report_metadata=PrivacyReportMetadata( accuracy_train=0.5, accuracy_test=0.4, epoch_num=15, model_variant_label='default')) self.results_epoch_15_model_2 = AttackResults( single_attack_results=[self.perfect_classifier_result], privacy_report_metadata=PrivacyReportMetadata( accuracy_train=0.6, accuracy_test=0.7, epoch_num=15, model_variant_label='model 2')) self.attack_results_no_metadata = AttackResults( single_attack_results=[self.perfect_classifier_result])
def test_slice_by_correcness(self): input_data = SlicingSpec(entire_dataset=False, by_classification_correctness=True) expected = SingleSliceSpec(SlicingFeature.CORRECTLY_CLASSIFIED, True) output = get_single_slice_specs(input_data) self.assertLen(output, 2) self.assertTrue(_are_all_fields_equal(output[0], expected))
def __init__(self, *args, **kwargs): super(AttackResultsCollectionTest, self).__init__(*args, **kwargs) self.some_attack_result = SingleAttackResult( slice_spec=SingleSliceSpec(None), attack_type=AttackType.THRESHOLD_ATTACK, roc_curve=RocCurve(tpr=np.array([0.0, 0.5, 1.0]), fpr=np.array([0.0, 0.5, 1.0]), thresholds=np.array([0, 1, 2]))) self.results_epoch_10 = AttackResults( single_attack_results=[self.some_attack_result], privacy_report_metadata=PrivacyReportMetadata( accuracy_train=0.4, accuracy_test=0.3, epoch_num=10, model_variant_label='default')) self.results_epoch_15 = AttackResults( single_attack_results=[self.some_attack_result], privacy_report_metadata=PrivacyReportMetadata( accuracy_train=0.5, accuracy_test=0.4, epoch_num=15, model_variant_label='default')) self.attack_results_no_metadata = AttackResults( single_attack_results=[self.some_attack_result]) self.collection_with_metadata = AttackResultsCollection( [self.results_epoch_10, self.results_epoch_15]) self.collection_no_metadata = AttackResultsCollection( [self.attack_results_no_metadata, self.attack_results_no_metadata])
def test_slice_by_class(self): class_index = 1 class_slice = SingleSliceSpec(SlicingFeature.CLASS, class_index) output = get_slice(self.input_data, class_slice) # Check logits. self.assertLen(output.logits_train, 2) self.assertLen(output.logits_test, 1) self.assertTrue((output.logits_train[1] == [4, 5, 0]).all()) # Check probs. self.assertLen(output.probs_train, 2) self.assertLen(output.probs_test, 1) self.assertTrue((output.probs_train[1] == [0.4, 0.6, 0]).all()) # Check labels. self.assertLen(output.labels_train, 2) self.assertLen(output.labels_test, 1) self.assertTrue((output.labels_train == class_index).all()) self.assertTrue((output.labels_test == class_index).all()) # Check losses self.assertLen(output.loss_train, 2) self.assertLen(output.loss_test, 1) self.assertTrue((output.loss_train == [2, 4]).all()) self.assertTrue((output.loss_test == [0.5]).all()) # Check entropy self.assertLen(output.entropy_train, 2) self.assertLen(output.entropy_test, 1) self.assertTrue((output.entropy_train == [0.4, 0.6]).all()) self.assertTrue((output.entropy_test == [15]).all())
def test_slice_by_classes(self): input_data = SlicingSpec(by_class=True) n_classes = 5 expected = [self.ENTIRE_DATASET_SLICE] + [ SingleSliceSpec(SlicingFeature.CLASS, c) for c in range(n_classes) ] output = get_single_slice_specs(input_data, n_classes) self.assertTrue(_are_lists_equal(output, expected))
def test_run_attack_by_slice(self): result = mia.run_attacks( get_test_input(100, 100), SlicingSpec(by_class=True), (AttackType.THRESHOLD_ATTACK,)) self.assertLen(result.single_attack_results, 6) expected_slice = SingleSliceSpec(SlicingFeature.CLASS, 2) self.assertEqual(result.single_attack_results[3].slice_spec, expected_slice)
def test_attacker_advantage_random_classifier(self): roc = RocCurve(tpr=np.array([0.0, 0.5, 1.0]), fpr=np.array([0.0, 0.5, 1.0]), thresholds=np.array([0, 1, 2])) result = SingleAttackResult(roc_curve=roc, slice_spec=SingleSliceSpec(None), attack_type=AttackType.THRESHOLD_ATTACK) self.assertEqual(result.get_attacker_advantage(), 0.0)
def __init__(self, *args, **kwargs): super(AttackResultsTest, self).__init__(*args, **kwargs) # ROC curve of a perfect classifier self.perfect_classifier_result = SingleAttackResult( slice_spec=SingleSliceSpec(SlicingFeature.CORRECTLY_CLASSIFIED, True), attack_type=AttackType.THRESHOLD_ATTACK, roc_curve=RocCurve(tpr=np.array([0.0, 1.0, 1.0]), fpr=np.array([1.0, 1.0, 0.0]), thresholds=np.array([0, 1, 2]))) # ROC curve of a random classifier self.random_classifier_result = SingleAttackResult( slice_spec=SingleSliceSpec(None), attack_type=AttackType.THRESHOLD_ATTACK, roc_curve=RocCurve(tpr=np.array([0.0, 0.5, 1.0]), fpr=np.array([0.0, 0.5, 1.0]), thresholds=np.array([0, 1, 2])))
class SingleSliceSpecsTest(absltest.TestCase): """Tests for get_single_slice_specs.""" ENTIRE_DATASET_SLICE = SingleSliceSpec() def test_no_slices(self): input_data = SlicingSpec(entire_dataset=False) expected = [] output = get_single_slice_specs(input_data) self.assertTrue(_are_lists_equal(output, expected)) def test_entire_dataset(self): input_data = SlicingSpec() expected = [self.ENTIRE_DATASET_SLICE] output = get_single_slice_specs(input_data) self.assertTrue(_are_lists_equal(output, expected)) def test_slice_by_classes(self): input_data = SlicingSpec(by_class=True) n_classes = 5 expected = [self.ENTIRE_DATASET_SLICE] + [ SingleSliceSpec(SlicingFeature.CLASS, c) for c in range(n_classes) ] output = get_single_slice_specs(input_data, n_classes) self.assertTrue(_are_lists_equal(output, expected)) def test_slice_by_percentiles(self): input_data = SlicingSpec(entire_dataset=False, by_percentiles=True) expected0 = SingleSliceSpec(SlicingFeature.PERCENTILE, (0, 10)) expected5 = SingleSliceSpec(SlicingFeature.PERCENTILE, (50, 60)) output = get_single_slice_specs(input_data) self.assertLen(output, 10) self.assertTrue(_are_all_fields_equal(output[0], expected0)) self.assertTrue(_are_all_fields_equal(output[5], expected5)) def test_slice_by_correcness(self): input_data = SlicingSpec(entire_dataset=False, by_classification_correctness=True) expected = SingleSliceSpec(SlicingFeature.CORRECTLY_CLASSIFIED, True) output = get_single_slice_specs(input_data) self.assertLen(output, 2) self.assertTrue(_are_all_fields_equal(output[0], expected)) def test_slicing_by_multiple_features(self): input_data = SlicingSpec(entire_dataset=True, by_class=True, by_percentiles=True, by_classification_correctness=True) n_classes = 10 expected_slices = n_classes expected_slices += 1 # entire dataset slice expected_slices += 10 # percentiles slices expected_slices += 2 # correcness classification slices output = get_single_slice_specs(input_data, n_classes) self.assertLen(output, expected_slices)
def test_auc_random_classifier(self): roc = RocCurve(tpr=np.array([0.0, 0.5, 1.0]), fpr=np.array([0.0, 0.5, 1.0]), thresholds=np.array([0, 1, 2])) result = SingleAttackResult(roc_curve=roc, slice_spec=SingleSliceSpec(None), attack_type=AttackType.THRESHOLD_ATTACK, data_size=DataSize(ntrain=1, ntest=1)) self.assertEqual(result.get_auc(), 0.5)
def run_seq2seq_attack( attack_input: Seq2SeqAttackInputData, privacy_report_metadata: PrivacyReportMetadata = None, balance_attacker_training: bool = True) -> AttackResults: """Runs membership inference attacks on a seq2seq model. Args: attack_input: input data for running an attack privacy_report_metadata: the metadata of the model under attack. balance_attacker_training: Whether the training and test sets for the membership inference attacker should have a balanced (roughly equal) number of samples from the training and test sets used to develop the model under attack. Returns: the attack result. """ attack_input.validate() # The attacker uses the average rank (a single number) of a seq2seq dataset # record to determine membership. So only Logistic Regression is supported, # as it makes the most sense for single-number features. attacker = models.LogisticRegressionAttacker() # Create attacker data and populate fields of privacy_report_metadata privacy_report_metadata = privacy_report_metadata or PrivacyReportMetadata( ) prepared_attacker_data = create_seq2seq_attacker_data( attack_input_data=attack_input, balance=balance_attacker_training, privacy_report_metadata=privacy_report_metadata) attacker.train_model(prepared_attacker_data.features_train, prepared_attacker_data.is_training_labels_train) # Run the attacker on (permuted) test examples. predictions_test = attacker.predict(prepared_attacker_data.features_test) # Generate ROC curves with predictions. fpr, tpr, thresholds = metrics.roc_curve( prepared_attacker_data.is_training_labels_test, predictions_test) roc_curve = RocCurve(tpr=tpr, fpr=fpr, thresholds=thresholds) attack_results = [ SingleAttackResult(slice_spec=SingleSliceSpec(), attack_type=AttackType.LOGISTIC_REGRESSION, roc_curve=roc_curve, data_size=prepared_attacker_data.data_size) ] return AttackResults(single_attack_results=attack_results, privacy_report_metadata=privacy_report_metadata)
def test_attack_with_varied_thresholds(self): result = SingleMembershipProbabilityResult( slice_spec=SingleSliceSpec(None), train_membership_probs=np.array([0.91, 1, 0.92, 0.82, 0.75]), test_membership_probs=np.array([0.81, 0.7, 0.75, 0.25, 0.3])) self.assertEqual( result.attack_with_varied_thresholds( threshold_list=np.array([0.8, 0.7]))[1].tolist(), [0.8, 0.625]) self.assertEqual( result.attack_with_varied_thresholds( threshold_list=np.array([0.8, 0.7]))[2].tolist(), [0.8, 1])
def test_slice_by_percentile(self): percentile_slice = SingleSliceSpec(SlicingFeature.PERCENTILE, (0, 50)) output = get_slice(self.input_data, percentile_slice) # Check logits. self.assertLen(output.logits_train, 3) self.assertLen(output.logits_test, 1) self.assertTrue((output.logits_test[0] == [10, 0, 11]).all()) # Check labels. self.assertLen(output.labels_train, 3) self.assertLen(output.labels_test, 1) self.assertTrue((output.labels_train == [1, 0, 2]).all()) self.assertTrue((output.labels_test == [1]).all())
def test_slice_by_correctness(self): percentile_slice = SingleSliceSpec(SlicingFeature.CORRECTLY_CLASSIFIED, False) output = get_slice(self.input_data, percentile_slice) # Check logits. self.assertLen(output.logits_train, 2) self.assertLen(output.logits_test, 3) self.assertTrue((output.logits_train[1] == [6, 7, 0]).all()) self.assertTrue((output.logits_test[1] == [12, 13, 0]).all()) # Check labels. self.assertLen(output.labels_train, 2) self.assertLen(output.labels_test, 3) self.assertTrue((output.labels_train == [0, 2]).all()) self.assertTrue((output.labels_test == [1, 2, 0]).all())
def get_single_slice_specs(slicing_spec: SlicingSpec, num_classes: int = None) -> List[SingleSliceSpec]: """Returns slices of data according to slicing_spec.""" result = [] if slicing_spec.entire_dataset: result.append(SingleSliceSpec()) # Create slices by class. by_class = slicing_spec.by_class if isinstance(by_class, bool): if by_class: assert num_classes, "When by_class == True, num_classes should be given." assert 0 <= num_classes <= 1000, ( f"Too much classes for slicing by classes. " f"Found {num_classes}.") for c in range(num_classes): result.append(SingleSliceSpec(SlicingFeature.CLASS, c)) elif isinstance(by_class, int): result.append(SingleSliceSpec(SlicingFeature.CLASS, by_class)) elif isinstance(by_class, collections.Iterable): for c in by_class: result.append(SingleSliceSpec(SlicingFeature.CLASS, c)) # Create slices by percentiles if slicing_spec.by_percentiles: for percent in range(0, 100, 10): result.append( SingleSliceSpec(SlicingFeature.PERCENTILE, (percent, percent + 10))) # Create slices by correctness of the classifications. if slicing_spec.by_classification_correctness: result.append( SingleSliceSpec(SlicingFeature.CORRECTLY_CLASSIFIED, True)) result.append( SingleSliceSpec(SlicingFeature.CORRECTLY_CLASSIFIED, False)) return result
def testStr(self, feature, value, expected_str): self.assertEqual(str(SingleSliceSpec(feature, value)), expected_str)
def testStrEntireDataset(self): self.assertEqual(str(SingleSliceSpec()), 'Entire dataset')
def test_slice_entire_dataset(self): entire_dataset_slice = SingleSliceSpec() output = get_slice(self.input_data, entire_dataset_slice) expected = self.input_data expected.slice_spec = entire_dataset_slice self.assertTrue(_are_all_fields_equal(output, self.input_data))
def _get_slice_spec(data: AttackInputData) -> SingleSliceSpec: if hasattr(data, 'slice_spec'): return data.slice_spec return SingleSliceSpec()