def _adaptive_extraction(
        self, x: np.ndarray, y: np.ndarray, thieved_classifier: "CLASSIFIER_TYPE"
    ) -> "CLASSIFIER_TYPE":
        """
        Extract with the adaptive sampling strategy.

        :param x: An array with the source input to the victim classifier.
        :param y: Target values (class labels) one-hot-encoded of shape (nb_samples, nb_classes) or indices of shape
                  (nb_samples,).
        :param thieved_classifier: A thieved classifier to be stolen.
        :return: The stolen classifier.
        """
        # Compute number of actions
        if len(y.shape) == 2:
            nb_actions = len(np.unique(np.argmax(y, axis=1)))
        elif len(y.shape) == 1:
            nb_actions = len(np.unique(y))
        else:
            raise ValueError("Target values `y` has a wrong shape.")

        # We need to keep an average version of the victim output
        if self.reward in ("div", "all"):
            self.y_avg = np.zeros(self.estimator.nb_classes)

        # We need to keep an average and variance version of rewards
        if self.reward == "all":
            self.reward_avg = np.zeros(3)
            self.reward_var = np.zeros(3)

        # Implement the bandit gradients algorithm
        h_func = np.zeros(nb_actions)
        learning_rate = np.zeros(nb_actions)
        probs = np.ones(nb_actions) / nb_actions
        selected_x = []
        queried_labels = []

        avg_reward = 0.0
        for iteration in trange(1, self.nb_stolen + 1, desc="Knock-off nets", disable=not self.verbose):
            # Sample an action
            action = np.random.choice(np.arange(0, nb_actions), p=probs)

            # Sample data to attack
            sampled_x = self._sample_data(x, y, action)
            selected_x.append(sampled_x)

            # Query the victim classifier
            y_output = self.estimator.predict(x=np.array([sampled_x]), batch_size=self.batch_size_query)
            fake_label = np.argmax(y_output, axis=1)
            fake_label = to_categorical(labels=fake_label, nb_classes=self.estimator.nb_classes)
            queried_labels.append(fake_label[0])

            # Train the thieved classifier
            thieved_classifier.fit(
                x=np.array([sampled_x]),
                y=fake_label,
                batch_size=self.batch_size_fit,
                nb_epochs=1,
                verbose=False,
            )

            # Test new labels
            y_hat = thieved_classifier.predict(x=np.array([sampled_x]), batch_size=self.batch_size_query)

            # Compute rewards
            reward = self._reward(y_output, y_hat, iteration)
            avg_reward = avg_reward + (1.0 / iteration) * (reward - avg_reward)

            # Update learning rate
            learning_rate[action] += 1

            # Update H function
            for i_action in range(nb_actions):
                if i_action != action:
                    h_func[i_action] = (
                        h_func[i_action] - 1.0 / learning_rate[action] * (reward - avg_reward) * probs[i_action]
                    )
                else:
                    h_func[i_action] = h_func[i_action] + 1.0 / learning_rate[action] * (reward - avg_reward) * (
                        1 - probs[i_action]
                    )

            # Update probs
            aux_exp = np.exp(h_func)
            probs = aux_exp / np.sum(aux_exp)

        # Train the thieved classifier the final time
        thieved_classifier.fit(
            x=np.array(selected_x),
            y=np.array(queried_labels),
            batch_size=self.batch_size_fit,
            nb_epochs=self.nb_epochs,
        )

        return thieved_classifier

        Extract with the adaptive sampling strategy.

        :param x: An array with the source input to the victim classifier.
        :param y: Target values (class labels) one-hot-encoded of shape (nb_samples, nb_classes) or indices of shape
                  (nb_samples,).
        :param thieved_classifier: A thieved classifier to be stolen.
        :return: The stolen classifier.
        
art/attacks/extraction/knockoff_nets.py