ers/heman/Desktop/Notes/metrics/computervision/Instancesegmentation.py
Traceback (most recent call last):
  File "c:\Users\heman\Desktop\Notes\metrics\computervision\Instancesegmentation.py", line 239, in <module>
    example_usage()
  File "c:\Users\heman\Desktop\Notes\metrics\computervision\Instancesegmentation.py", line 233, in example_usage
    metrics = metrics_calculator.compute_metrics(predictions, ground_truths)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\heman\Desktop\Notes\metrics\computervision\Instancesegmentation.py", line 112, in compute_metrics
    mAP = self.mean_average_precision(iou_scores)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\heman\Desktop\Notes\metrics\computervision\Instancesegmentation.py", line 153, in mean_average_precision
    aps.append(torch.tensor(iou_scores) >= thresh).float().mean().item()
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'float'
PS C:\Users\heman\Desktop\Notes> & C:/Users/heman/AppData/Local/Programs/Python/Python312/python.exe c:/Users/heman/Desktop/Notes/metrics/computervision/Instancesegmentation.py
mAP: 0.7500
Instancewise_IoU: 0.7917
Pixel_Accuracy: 0.8889
Recall: 1.0000
Precision: 1.0000
Average_Recall: 0.7500
F1_Score: 1.0000
True_Positives: 4.0000
False_Negatives: 0.0000
False_Positives: 0.0000
PS C:\Users\heman\Desktop\Notes> & C:/Users/heman/AppData/Local/Programs/Python/Python312/python.exe c:/Users/heman/Desktop/Notes/metrics/computervision/Imagegeneration.py
INFO:__main__:Using device: cpu
INFO:__main__:Inception Score: 1.6349612474441528
ERROR:__main__:Error computing Frechet Inception Distance: linalg.cholesky: The factorization could not be completed because the input is not positive-definite (the leading minor of order 1 is not positive-definite).
Traceback (most recent call last):
  File "c:\Users\heman\Desktop\Notes\metrics\computervision\Imagegeneration.py", line 395, in <module>    
    fid_score = pipeline.frechet_inception_distance(real_imgs, gen_imgs)
                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\heman\Desktop\Notes\metrics\computervision\Imagegeneration.py", line 70, in frechet_inception_distance
    fid = self._calculate_frechet_distance(mu1, sigma1, mu2, sigma2)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "c:\Users\heman\Desktop\Notes\metrics\computervision\Imagegeneration.py", line 307, in _calculate_frechet_distance
    covmean = torch.linalg.cholesky(sigma1 @ sigma2)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
torch._C._LinAlgError: linalg.cholesky: The factorization could not be completed because the input is not positive-definite (the leading minor of order 1 is not positive-definite).

import torch
import torch.nn as nn
import torch.nn.functional as F
from typing import Tuple, List, Optional
import math
import logging

# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

class RobustnessMetricsPipeline:
    """
    A pipeline to compute various robustness metrics for image generation.
    """

    def __init__(self, device: Optional[torch.device] = None) -> None:
        """
        Initializes the pipeline.

        Args:
            device (torch.device, optional): The device to run computations on.
                Defaults to CUDA if available, else CPU.
        """
        self.device = device if device else torch.device('cuda' if torch.cuda.is_available() else 'cpu')
        logger.info(f"Using device: {self.device}")

    def inception_score(self, images: torch.Tensor, splits: int = 10) -> float:
        """
        Computes the Inception Score (IS) for the given images.

        Args:
            images (torch.Tensor): Generated images tensor of shape (N, C, H, W).
            splits (int): Number of splits.

        Returns:
            float: The Inception Score.
        """
        try:
            # Placeholder for Inception model predictions
            # In practice, replace with a pre-trained Inception model
            preds = self._fake_inception_predictions(images)

            # Compute the marginal distribution
            p_y = torch.mean(preds, dim=0, keepdim=True)
            kl_div = preds * (torch.log(preds) - torch.log(p_y))
            sum_kl_div = torch.sum(kl_div, dim=1)
            is_score = torch.exp(torch.mean(sum_kl_div)).item()
            logger.info(f"Inception Score: {is_score}")
            return is_score
        except Exception as e:
            logger.error(f"Error computing Inception Score: {e}")
            raise

    def frechet_inception_distance(self, real_images: torch.Tensor, generated_images: torch.Tensor) -> float:
        """
        Computes the Frechet Inception Distance (FID) between real and generated images.

        Args:
            real_images (torch.Tensor): Real images tensor of shape (N, C, H, W).
            generated_images (torch.Tensor): Generated images tensor of shape (N, C, H, W).

        Returns:
            float: The FID score.
        """
        try:
            # Placeholder for feature extraction
            mu1, sigma1 = self._compute_statistics(real_images)
            mu2, sigma2 = self._compute_statistics(generated_images)
            fid = self._calculate_frechet_distance(mu1, sigma1, mu2, sigma2)
            logger.info(f"Frechet Inception Distance: {fid}")
            return fid
        except Exception as e:
            logger.error(f"Error computing Frechet Inception Distance: {e}")
            raise

    def perceptual_similarity(self, real_images: torch.Tensor, generated_images: torch.Tensor) -> float:
        """
        Computes perceptual similarity between real and generated images.

        Args:
            real_images (torch.Tensor): Real images tensor of shape (N, C, H, W).
            generated_images (torch.Tensor): Generated images tensor of shape (N, C, H, W).

        Returns:
            float: The perceptual similarity score.
        """
        try:
            similarity = F.cosine_similarity(
                real_images.view(real_images.size(0), -1),
                generated_images.view(generated_images.size(0), -1)
            ).mean().item()
            logger.info(f"Perceptual Similarity: {similarity}")
            return similarity
        except Exception as e:
            logger.error(f"Error computing Perceptual Similarity: {e}")
            raise

    def mean_squared_error(self, real_images: torch.Tensor, generated_images: torch.Tensor) -> float:
        """
        Computes Mean Squared Error (MSE) between real and generated images.

        Args:
            real_images (torch.Tensor): Real images tensor.
            generated_images (torch.Tensor): Generated images tensor.

        Returns:
            float: The MSE value.
        """
        try:
            mse = F.mse_loss(generated_images, real_images).item()
            logger.info(f"Mean Squared Error: {mse}")
            return mse
        except Exception as e:
            logger.error(f"Error computing Mean Squared Error: {e}")
            raise

    def structural_similarity_index(self, real_images: torch.Tensor, generated_images: torch.Tensor) -> float:
        """
        Computes Structural Similarity Index (SSIM) between real and generated images.

        Args:
            real_images (torch.Tensor): Real images tensor.
            generated_images (torch.Tensor): Generated images tensor.

        Returns:
            float: The SSIM value.
        """
        try:
            # Parameters for SSIM
            C1 = 0.01 ** 2
            C2 = 0.03 ** 2

            real_mean = self._gaussian_filter(real_images)
            gen_mean = self._gaussian_filter(generated_images)

            real_var = self._variance_filter(real_images, real_mean)
            gen_var = self._variance_filter(generated_images, gen_mean)
            covariance = self._covariance_filter(real_images, generated_images, real_mean, gen_mean)

            ssim_map = ((2 * real_mean * gen_mean + C1) * (2 * covariance + C2)) / (
                (real_mean ** 2 + gen_mean ** 2 + C1) * (real_var + gen_var + C2)
            )
            ssim = ssim_map.mean().item()
            logger.info(f"Structural Similarity Index: {ssim}")
            return ssim
        except Exception as e:
            logger.error(f"Error computing Structural Similarity Index: {e}")
            raise

    def diversity_score(self, generated_images: torch.Tensor) -> float:
        """
        Computes Diversity Score of generated images.

        Args:
            generated_images (torch.Tensor): Generated images tensor.

        Returns:
            float: The diversity score.
        """
        try:
            norms = F.normalize(generated_images.view(generated_images.size(0), -1), dim=1)
            similarity_matrix = torch.matmul(norms, norms.t())
            # Exclude self-similarity by subtracting identity matrix
            similarity_matrix = similarity_matrix - torch.eye(similarity_matrix.size(0)).to(self.device)
            diversity = 1 - similarity_matrix.mean().item()
            logger.info(f"Diversity Score: {diversity}")
            return diversity
        except Exception as e:
            logger.error(f"Error computing Diversity Score: {e}")
            raise

    def mode_collapse_rate(self, generated_images: torch.Tensor, threshold: float = 0.99) -> float:
        """
        Computes Mode Collapse Rate of generated images.

        Args:
            generated_images (torch.Tensor): Generated images tensor.
            threshold (float): Similarity threshold to consider images as collapsed.

        Returns:
            float: The mode collapse rate.
        """
        try:
            norms = F.normalize(generated_images.view(generated_images.size(0), -1), dim=1)
            similarity_matrix = torch.matmul(norms, norms.t())
            similarity_matrix = similarity_matrix > threshold
            # Count number of similar pairs excluding self-comparison
            collapse_pairs = similarity_matrix.sum().item() - generated_images.size(0)
            total_pairs = generated_images.size(0) * (generated_images.size(0) - 1)
            mode_collapse = collapse_pairs / total_pairs if total_pairs > 0 else 0.0
            logger.info(f"Mode Collapse Rate: {mode_collapse}")
            return mode_collapse
        except Exception as e:
            logger.error(f"Error computing Mode Collapse Rate: {e}")
            raise

    def training_stability(self, loss_values: List[float]) -> float:
        """
        Analyzes training stability based on loss values.

        Args:
            loss_values (List[float]): List of loss values during training.

        Returns:
            float: The training stability metric (e.g., variance).
        """
        try:
            if not loss_values:
                raise ValueError("Loss values list is empty.")
            loss_tensor = torch.tensor(loss_values, device=self.device)
            variance = loss_tensor.var().item()
            logger.info(f"Training Stability (Variance of Loss): {variance}")
            return variance
        except Exception as e:
            logger.error(f"Error computing Training Stability: {e}")
            raise

    def quality_metrics(self, images: torch.Tensor, reference: torch.Tensor) -> dict:
        """
        Evaluates quality metrics based on specific application requirements.

        Args:
            images (torch.Tensor): Generated images tensor.
            reference (torch.Tensor): Reference images tensor.

        Returns:
            dict: A dictionary of quality metrics.
        """
        try:
            mse = self.mean_squared_error(reference, images)
            ssim = self.structural_similarity_index(reference, images)
            perceptual = self.perceptual_similarity(reference, images)
            quality = {
                'MSE': mse,
                'SSIM': ssim,
                'Perceptual_Similarity': perceptual
            }
            logger.info(f"Quality Metrics: {quality}")
            return quality
        except Exception as e:
            logger.error(f"Error computing Quality Metrics: {e}")
            raise

    def _fake_inception_predictions(self, images: torch.Tensor) -> torch.Tensor:
        """
        Placeholder function for Inception model predictions.

        Args:
            images (torch.Tensor): Images tensor.

        Returns:
            torch.Tensor: Fake predictions tensor.
        """
        # Simulate predictions as random probabilities
        num_classes = 1000
        N = images.size(0)
        preds = F.softmax(torch.randn(N, num_classes).to(self.device), dim=1)
        return preds

    def _compute_statistics(self, images: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
        """
        Computes the mean and covariance of features for FID.

        Args:
            images (torch.Tensor): Images tensor.

        Returns:
            Tuple[torch.Tensor, torch.Tensor]: Mean and covariance.
        """
        features = self._extract_features(images)
        mu = torch.mean(features, dim=0)
        sigma = torch_cov(features, features)
        return mu, sigma

    def _extract_features(self, images: torch.Tensor) -> torch.Tensor:
        """
        Extracts features from images.

        Args:
            images (torch.Tensor): Images tensor.

        Returns:
            torch.Tensor: Features tensor.
        """
        # Placeholder for feature extraction
        # In practice, use a pre-trained network like Inception
        features = images.view(images.size(0), -1).to(self.device)
        return features

    def _calculate_frechet_distance(
        self, mu1: torch.Tensor, sigma1: torch.Tensor, mu2: torch.Tensor, sigma2: torch.Tensor
    ) -> float:
        """
        Calculates the Frechet Distance between two Gaussians.

        Args:
            mu1 (torch.Tensor): Mean of the first distribution.
            sigma1 (torch.Tensor): Covariance of the first distribution.
            mu2 (torch.Tensor): Mean of the second distribution.
            sigma2 (torch.Tensor): Covariance of the second distribution.

        Returns:
            float: The Frechet Distance.
        """
        diff = mu1 - mu2
        covmean = torch.linalg.cholesky(sigma1 @ sigma2)
        fid = diff.dot(diff) + torch.trace(sigma1 + sigma2 - 2 * covmean).item()
        return fid

    def _gaussian_filter(self, images: torch.Tensor, kernel_size: int = 11, sigma: float = 1.5) -> torch.Tensor:
        """
        Applies a Gaussian filter to the images.

        Args:
            images (torch.Tensor): Images tensor.
            kernel_size (int): Size of the Gaussian kernel.
            sigma (float): Standard deviation.

        Returns:
            torch.Tensor: Filtered images.
        """
        channels = images.size(1)
        x = torch.arange(kernel_size).to(self.device)
        x = x - kernel_size // 2
        gauss = torch.exp(-x**2 / (2 * sigma**2))
        gauss = gauss / gauss.sum()
        gauss = gauss.view(1, 1, -1)

        padding = kernel_size // 2
        images = images.view(-1, 1, images.size(2), images.size(3))
        filtered = F.conv2d(images, gauss.expand(channels, 1, -1), padding=padding, groups=channels)
        return filtered.view(images.size(0)//channels, channels, images.size(2), images.size(3))

    def _variance_filter(self, images: torch.Tensor, mean: torch.Tensor) -> torch.Tensor:
        """
        Computes variance of images using a Gaussian filter.

        Args:
            images (torch.Tensor): Images tensor.
            mean (torch.Tensor): Mean of the images.

        Returns:
            torch.Tensor: Variance tensor.
        """
        variance = self._gaussian_filter(images ** 2) - mean ** 2
        return variance

    def _covariance_filter(
        self, images1: torch.Tensor, images2: torch.Tensor, mean1: torch.Tensor, mean2: torch.Tensor
    ) -> torch.Tensor:
        """
        Computes covariance between two sets of images.

        Args:
            images1 (torch.Tensor): First set of images.
            images2 (torch.Tensor): Second set of images.
            mean1 (torch.Tensor): Mean of the first set.
            mean2 (torch.Tensor): Mean of the second set.

        Returns:
            torch.Tensor: Covariance tensor.
        """
        covariance = self._gaussian_filter(images1 * images2) - mean1 * mean2
        return covariance

def torch_cov(x: torch.Tensor, y: torch.Tensor) -> torch.Tensor:
    """
    Computes the covariance matrix between two tensors.

    Args:
        x (torch.Tensor): Tensor of shape (N, D).
        y (torch.Tensor): Tensor of shape (N, D).

    Returns:
        torch.Tensor: Covariance matrix of shape (D, D).
    """
    N, D = x.size()
    mean_x = torch.mean(x, dim=0)
    mean_y = torch.mean(y, dim=0)
    cov = (x - mean_x).t() @ (y - mean_y) / (N - 1)
    return cov

# Example Usage
if __name__ == "__main__":
    pipeline = RobustnessMetricsPipeline()

    # Generate dummy data
    real_imgs = torch.randn(100, 3, 64, 64).to(pipeline.device)
    gen_imgs = torch.randn(100, 3, 64, 64).to(pipeline.device)
    loss_vals = [0.5 / (i + 1) for i in range(100)]

    # Compute Metrics
    is_score = pipeline.inception_score(gen_imgs)
    fid_score = pipeline.frechet_inception_distance(real_imgs, gen_imgs)
    perceptual_sim = pipeline.perceptual_similarity(real_imgs, gen_imgs)
    mse = pipeline.mean_squared_error(real_imgs, gen_imgs)
    ssim = pipeline.structural_similarity_index(real_imgs, gen_imgs)
    diversity = pipeline.diversity_score(gen_imgs)
    mode_collapse = pipeline.mode_collapse_rate(gen_imgs)
    stability = pipeline.training_stability(loss_vals)
    quality = pipeline.quality_metrics(gen_imgs, real_imgs)

    # Print Metrics
    print(f"Inception Score: {is_score}")
    print(f"FID Score: {fid_score}")
    print(f"Perceptual Similarity: {perceptual_sim}")
    print(f"MSE: {mse}")
    print(f"SSIM: {ssim}")
    print(f"Diversity Score: {diversity}")
    print(f"Mode Collapse Rate: {mode_collapse}")
    print(f"Training Stability: {stability}")
    print(f"Quality Metrics: {quality}")