code: def _run_trained_attack(
      attack_input: AttackInputData,
      attack_type: AttackType,
      balance_attacker_training: bool = True,
      cross_validation_folds: int = 2,
      backend: Optional[str] = None,
  ) -> Optional[SingleAttackResult]:
      """Classification attack done by ML models."""
      prepared_attacker_data = models.create_attacker_data(
          attack_input, balance=balance_attacker_training
      )
      indices = prepared_attacker_data.fold_indices
      left_out_indices = prepared_attacker_data.left_out_indices
      features = prepared_attacker_data.features_all
      labels = prepared_attacker_data.labels_all
      sample_weights = prepared_attacker_data.sample_weights_all
  
      # We are going to train multiple models on disjoint subsets of the data
      # (`features`, `labels`), so we can get the membership scores of all samples,
      # and each example gets its score assigned only once.
      # An alternative implementation is to train multiple models on overlapping
      # subsets of the data, and take an average to get the score for each sample.
      # `scores` will record the membership score of each sample, initialized to nan
      scores = np.full(features.shape[0], np.nan)
  
      # We use StratifiedKFold to create disjoint subsets of samples. Notice that
      # the index it returns is with respect to the samples shuffled with `indices`.
      kf = model_selection.StratifiedKFold(
          cross_validation_folds, shuffle=False
      )
      try:
          shuffled_indices = kf.split(
              features[indices], labels[indices]
          )
          for (
              train_indices_in_shuffled,
              test_indices_in_shuffled,
          ) in shuffled_indices:
              # `train_indices_in_shuffled` is with respect to the data shuffled with
              # `indices`. We convert it to `train_indices` to work with the original
              # data (`features` and 'labels').
              train_indices = indices[train_indices_in_shuffled]
              test_indices = indices[test_indices_in_shuffled]
              # Make sure one sample only got score predicted once
              assert np.all(np.isnan(scores[test_indices]))
  
              # Setup sample weights if provided.
              if sample_weights is not None:
                  # If sample weights are provided, only the weights at the training
                  # indices are used for training. The weights at the test indices are not
                  # used during prediction. Not that 'train' and 'test' refer to the data
                  # for the attack models, not the data for the original models.
                  sample_weights_train = np.squeeze(
                      sample_weights[train_indices]
                  )
              else:
                  sample_weights_train = None
  
              attacker = models.create_attacker(
                  attack_type, backend=backend
              )
              attacker.train_model(
                  features[train_indices],
                  labels[train_indices],
                  sample_weight=sample_weights_train,
              )
              scores[test_indices] = attacker.predict(
                  features[test_indices]
              )
      except ValueError as ve:
          if (
              "cannot be greater than the number of members in each class."
              in str(ve)
          ):
              logging.warning(
                  "kf.split in _run_trained_attack fails with: %s",
                  str(ve),
              )
              return None
          raise ValueError(str(ve)) from ve
  
      # Predict the left out with the last attacker
      if left_out_indices.size:
          assert np.all(np.isnan(scores[left_out_indices]))
          scores[left_out_indices] = attacker.predict(
              features[left_out_indices]
          )
      assert not np.any(np.isnan(scores))
  
      # Generate ROC curves with scores.
      # Different from the threshold attacker which uses the loss, we do not negate
      # the scores here, because the attacker returns the probability of the
      # positive class.
      fpr, tpr, thresholds = metrics.roc_curve(labels, scores)
      # 'test_train_ratio' is the ratio of test data size to train data size. It is
      # used to compute the Positive Predictive Value.
      test_train_ratio = (prepared_attacker_data.data_size.ntest) / (
          prepared_attacker_data.data_size.ntrain
      )
      roc_curve = RocCurve(
          tpr=tpr,
          fpr=fpr,
          thresholds=thresholds,
          test_train_ratio=test_train_ratio,
      )
  
      in_train_indices = labels == 1
      epsilon_lower_bound_value = EpsilonLowerBoundValue(
          bounds=elb.EpsilonLowerBound(
              pos_scores=scores[in_train_indices],
              neg_scores=scores[~in_train_indices],
              alpha=EPSILON_ALPHA,
              two_sided_threshold=True,
          ).compute_epsilon_lower_bounds(
              methods=EPSILON_METHODS, k=EPSILON_K
          )
      )
      return SingleAttackResult(
          slice_spec=_get_slice_spec(attack_input),
          data_size=prepared_attacker_data.data_size,
          attack_type=attack_type,
          membership_scores_train=scores[in_train_indices],
          membership_scores_test=scores[~in_train_indices],
          roc_curve=roc_curve,
          epsilon_lower_bound_value=epsilon_lower_bound_value,
      )
  
imports: import logging
  from typing import Iterable, List, Optional, Union
  import numpy
  from scipy import special
  from sklearn import metrics
  from sklearn import model_selection
  from tensorflow_privacy.privacy.privacy_tests import epsilon_lower_bound
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack import models
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import AttackInputData
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import AttackResults
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import AttackType
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import DataSize
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import EPSILON_ALPHA
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import EPSILON_K
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import EPSILON_METHODS
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import EpsilonLowerBoundValue
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import MembershipProbabilityResults
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import PrivacyReportMetadata
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import RocCurve
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import SingleAttackResult
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import SingleMembershipProbabilityResult
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import SingleSliceSpec
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.data_structures import SlicingSpec
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.dataset_slicing import get_single_slice_specs
  from tensorflow_privacy.privacy.privacy_tests.membership_inference_attack.dataset_slicing import get_slice
label_desc: Use image node_id 2 for calling a global function with example usage: _run_trained_attack(attack_input, attack_type, balance_attacker_training, cross_validation_folds, backend) and returns: SingleAttackResult, None