fix the apply defence api 
lpe.py
INFO:rldata.rlpedata:Loading dataset from zh-plus/tiny-imagenet
Fast image processor class <class 'transformers.models.vit.image_processing_vit_fast.ViTImageProcessorFast'> is available for this model. Using slow image processor class. To use the fast image processor class set `use_fast=True`.
Some weights of ViTForImageClassification were not initialized from the model checkpoint at google/vit-base-patch16-224-in21k and are newly initialized: ['classifier.bias', 'classifier.weight']
You should probably TRAIN this model on a down-stream task to be able to use it for predictions and inference.
INFO:model.modelloader:Successfully initialized transformers model: google/vit-base-patch16-224-in21k
INFO:art.estimators.classification.pytorch:Inferred 1 hidden layers on PyTorch classifier.
INFO:defences.defencerlpe:DefenseManager initialized with available defenses.
['SpatialSmoothing', 'FeatureSqueezing', 'JpegCompression']
INFO:defences.defencerlpe:Initializing defense: SpatialSmoothing
INFO:defences.defencerlpe:Applying preprocessor defense: SpatialSmoothing
INFO:defences.defencerlpe:Initializing defense: FeatureSqueezing
INFO:defences.defencerlpe:Applying preprocessor defense: FeatureSqueezing
INFO:defences.defencerlpe:Initializing defense: JpegCompression
INFO:defences.defencerlpe:Applying preprocessor defense: JpegCompression
ERROR:defences.defencerlpe:Defense application failed with both numpy and tensor inputs: Negative values in input `x` detected. The JPEG compression defence requires unnormalized input.
Traceback (most recent call last):
  File "/scratch/hemanth/Hemanth/RLPE/defences/defencerlpe.py", line 493, in apply_defense
    defended_x, _ = defense_instance(x=x_numpy, y=y_numpy)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/anaconda/envs/rlpe/lib/python3.12/site-packages/art/defences/preprocessor/jpeg_compression.py", line 124, in __call__
    raise ValueError(
ValueError: Negative values in input `x` detected. The JPEG compression defence requires unnormalized input.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/scratch/hemanth/Hemanth/RLPE/defences/defencerlpe.py", line 497, in apply_defense
    defended_x = defense_instance(x=x, y=y)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/anaconda/envs/rlpe/lib/python3.12/site-packages/art/defences/preprocessor/jpeg_compression.py", line 124, in __call__
    raise ValueError(
ValueError: Negative values in input `x` detected. The JPEG compression defence requires unnormalized input.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/scratch/hemanth/Hemanth/RLPE/rlpe.py", line 114, in <module>
    defence_pred_or_image=defence_generator.apply_defense(
                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/scratch/hemanth/Hemanth/RLPE/defences/defencerlpe.py", line 579, in apply_defense
    raise RuntimeError(error_msg) from e
                       ^^^^^^^^^
UnboundLocalError: cannot access local variable 'error_msg' where it is not associated with a value


    def apply_defense(
        self,
        defense_name: str,
        x: np.ndarray,
        y: np.ndarray,
        classifier=None,
        attacks=None,
        **kwargs: Any
    ) -> np.ndarray:
        """
        Apply the specified defense to the input data.

        Args:
            defense_name (str): The name of the defense to apply.
            x (np.ndarray): Input data with shape (batch_size, channels, height, width).
            y (np.ndarray): True labels corresponding to the input data.
            **kwargs (Any): Additional keyword arguments for specific defenses.

        Returns:
            np.ndarray: The defended input data.

        Raises:
            ValueError: If the defense_name is not recognized or application fails.
            TypeError: If the defense type is not supported.
            RuntimeError: If an unexpected error occurs during defense application.
        """
        logger.info(f"Initializing defense: {defense_name}")

        defense_class = self.defenses[defense_name]

        try:
            params = self.get_defense_parameters(defense_name)
            params.update(kwargs)
            defense_instance = defense_class(**params) if params else defense_class()

            if isinstance(defense_instance, ARTPreprocessor):
                logger.info(f"Applying preprocessor defense: {defense_name}")
                
                original_type = 'tensor' if isinstance(x, torch.Tensor) else 'numpy'
                
                # Convert to numpy if tensors
                if original_type == 'tensor':
                    x_numpy = x.cpu().numpy()
                    y_numpy = y.cpu().numpy()
                else:
                    x_numpy = x
                    y_numpy = y
                    
                try:
                    # Try with numpy arrays first
                    defended_x, _ = defense_instance(x=x_numpy, y=y_numpy)
                except:
                    try:
                        # If that fails, try with original format
                        defended_x = defense_instance(x=x, y=y)
                    except Exception as e:
                        logger.error(f"Defense application failed with both numpy and tensor inputs: {str(e)}")
                        raise
                        
                # Convert back to original type if needed
                if original_type == 'tensor' and isinstance(defended_x, np.ndarray):
                    defended_x = torch.from_numpy(defended_x).to(x.device)
                    
                return defended_x

            elif isinstance(defense_instance, ARTPostprocessor):
                logger.info(f"Applying postprocessor defense: {defense_name}")
                original_type = 'tensor' if isinstance(y, torch.Tensor) else 'numpy'
                # Convert to numpy if tensors
                if original_type == 'tensor':
                    x_numpy = x.cpu().numpy()
                    y_numpy = y.cpu().numpy()
                else:
                    x_numpy = x
                    y_numpy = y
                    try:
                        # Try with numpy arrays first
                        defended_y = defense_instance(preds=y_numpy)
                    except:
                        try:
                            # If that fails, try with original format
                            defended_y = defense_instance(preds=y)
                        except Exception as e:
                            logger.error(f"Defense application failed with both numpy and tensor inputs: {str(e)}")
                            raise
                # Convert back to original type if needed
                if original_type == 'tensor' and isinstance(defended_y, np.ndarray):
                    defended_y = torch.from_numpy(defended_y).to(y.device)
                return defended_y

            elif isinstance(defense_instance, ARTTrainer):
                logger.info(f"Applying trainer defense: {defense_name}")

                params = self.get_defense_parameters(defense_name)
                params.update(kwargs)
                # Correctly assign the classifier or estimator
                if "estimator" in params:
                    params["estimator"] = classifier
                if "classifier" in params:
                    params["classifier"] = classifier
                
                if "attacks" in params:
                    params["attacks"] = attacks
                defense_instance = defense_class(**params) if params else defense_class()
                original_type = 'tensor' if isinstance(x, torch.Tensor) else 'numpy'
                # Convert to numpy if tensors
                if original_type == 'tensor':
                    x_numpy = x.cpu().numpy()
                    y_numpy = y.cpu().numpy()
                else:
                    x_numpy = x
                    y_numpy = y
                try:
                    # Try with numpy arrays first
                    defense_instance.fit(x=x_numpy, y=y_numpy)
                    pred=defense_instance.predict(x)
                except:
                    try:
                        # If that fails, try with original format
                        defense_instance.fit(x=x, y=y)
                        pred=defense_instance.predict(x)

                    except Exception as e:
                        logger.error(f"Defense application failed with both numpy and tensor inputs: {str(e)}")
                        raise
                # Convert back to original type if needed
                if original_type == 'tensor' and isinstance(pred, np.ndarray):
                    defended_pred = torch.from_numpy(pred).to(x.device)
                return defended_pred

            else:
                error_msg = f"Defense '{defense_name}' type is not supported."
                logger.error(error_msg)
                raise TypeError(error_msg)

        except Exception as e:
            raise RuntimeError(error_msg) from e