def _sub_noise(self, num_noises: int, basis: np.ndarray):
        """
        Create subspace random perturbation.

        :param num_noises: Number of random subspace noises.
        :param basis: Subspace bases.
        :return: Random subspace perturbations.
        """
        noise = np.random.normal(size=(basis.shape[1], self.nb_channels * num_noises)) * (self.clip_max - self.clip_min)
        sub_noise = np.array(np.matmul(basis, noise).transpose((1, 0)).astype(ART_NUMPY_DTYPE))

        if self.estimator.channels_first:
            subspace_shape = (num_noises,) + self.estimator.input_shape
        else:
            subspace_shape = (
                num_noises,
                self.estimator.input_shape[2],
                self.estimator.input_shape[0],
                self.estimator.input_shape[1],
            )

        r_list = sub_noise.reshape(subspace_shape)

        if not self.estimator.channels_first:
            r_list = r_list.transpose((0, 2, 3, 1))

        return r_list

        Create subspace random perturbation.

        :param num_noises: Number of random subspace noises.
        :param basis: Subspace bases.
        :return: Random subspace perturbations.
        
art/attacks/evasion/geometric_decision_based_attack.py