ARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
WARNING:__main__:Tensor is not 4-dimensional for 4D scatter plot.
import logging
from pathlib import Path
from typing import Any, Callable, Dict, List, Optional, Union

import numpy as np
import plotly.graph_objs as go
import torch
import torch.nn as nn
from plotly.subplots import make_subplots
from transformers import AutoModelForCausalLM

# Configure logging
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)


class ModelVisualizer:
    """A class to visualize tensors from a PyTorch model using scatter plots."""

    def __init__(self, output_dir: Union[str, Path] = "visualizations") -> None:
        """
        Initialize the ModelVisualizer.

        Args:
            output_dir (Union[str, Path]): Directory to save visualizations.
        """
        self.output_dir = Path(output_dir)
        self.html_dir = self.output_dir / "html"
        self.png_dir = self.output_dir / "png"
        self.html_dir.mkdir(parents=True, exist_ok=True)
        self.png_dir.mkdir(parents=True, exist_ok=True)
        logger.info(f"Visualizations will be saved in: {self.output_dir}")

    def visualize_tensor(self, tensor: torch.Tensor, metadata: Dict[str, Any]) -> None:
        """
        Visualize a tensor based on its dimensionality.

        Args:
            tensor (torch.Tensor): The tensor to visualize.
            metadata (Dict[str, Any]): Metadata for the visualization.
        """
        dim = tensor.dim()
        shape = tensor.shape
        logger.info(f"Visualizing a tensor with shape: {shape}")

        try:
            if dim == 1:
                self._plot_1d(tensor, metadata)
            elif dim == 2:
                self._plot_2d(tensor, metadata)
            elif dim == 3:
                self._plot_3d(tensor, metadata)
            elif dim == 4:
                self._plot_4d(tensor, metadata)
            else:
                logger.warning(f"Unsupported tensor dimension: {dim}. Skipping visualization.")
        except Exception as e:
            logger.error(f"Error in visualizing tensor: {e}", exc_info=True)

    def _plot_1d(self, tensor: torch.Tensor, metadata: Dict[str, Any]) -> None:
        """
        Plot a 1D tensor as a scatter plot.

        Args:
            tensor (torch.Tensor): The tensor to plot.
            metadata (Dict[str, Any]): Metadata for the plot.
        """
        tensor = tensor.cpu().squeeze()
        x = np.arange(tensor.size(0))
        y = tensor.numpy()

        fig = go.Figure(go.Scatter(x=x, y=y, mode='markers+lines'))
        title = f"1D Scatter Plot: {metadata.get('layer_name', 'Unknown Layer')}"
        fig.update_layout(title=title, xaxis_title='Index', yaxis_title='Value')
        self._save_and_export(fig, f"{metadata.get('layer_name', 'layer')}_1d_scatter")

    def _plot_2d(self, tensor: torch.Tensor, metadata: Dict[str, Any]) -> None:
        """
        Plot a 2D tensor as a scatter plot.

        Args:
            tensor (torch.Tensor): The tensor to plot.
            metadata (Dict[str, Any]): Metadata for the plot.
        """
        tensor = tensor.cpu().squeeze()
        if tensor.dim() != 2:
            logger.warning("Tensor is not 2-dimensional for 2D scatter plot.")
            return

        x = tensor[:, 0].numpy()
        y = tensor[:, 1].numpy()

        fig = go.Figure(go.Scatter(x=x, y=y, mode='markers'))
        title = f"2D Scatter Plot: {metadata.get('layer_name', 'Unknown Layer')}"
        fig.update_layout(title=title, xaxis_title='Dimension 1', yaxis_title='Dimension 2')
        self._save_and_export(fig, f"{metadata.get('layer_name', 'layer')}_2d_scatter")

    def _plot_3d(self, tensor: torch.Tensor, metadata: Dict[str, Any]) -> None:
        """
        Plot a 3D tensor as a scatter plot.

        Args:
            tensor (torch.Tensor): The tensor to plot.
            metadata (Dict[str, Any]): Metadata for the plot.
        """
        tensor = tensor.cpu().squeeze()
        if tensor.dim() != 2 or tensor.size(1) != 3:
            logger.warning("Tensor is not suitable for 3D scatter plot. Expecting shape (n, 3).")
            return

        x, y, z = tensor[:, 0].numpy(), tensor[:, 1].numpy(), tensor[:, 2].numpy()
        fig = go.Figure(
            data=go.Scatter3d(
                x=x,
                y=y,
                z=z,
                mode='markers',
                marker=dict(size=3)
            )
        )
        title = f"3D Scatter Plot: {metadata.get('layer_name', 'Unknown Layer')}"
        fig.update_layout(title=title, scene=dict(
            xaxis_title='X',
            yaxis_title='Y',
            zaxis_title='Z'
        ))
        self._save_and_export(fig, f"{metadata.get('layer_name', 'layer')}_3d_scatter")

    def _plot_4d(self, tensor: torch.Tensor, metadata: Dict[str, Any]) -> None:
        """
        Plot a 4D tensor by creating multiple 3D scatter plots.

        Args:
            tensor (torch.Tensor): The tensor to plot.
            metadata (Dict[str, Any]): Metadata for the plot.
        """
        tensor = tensor.cpu().squeeze()
        if tensor.dim() != 4:
            logger.warning("Tensor is not 4-dimensional for 4D scatter plot.")
            return

        num_channels = tensor.size(0)
        num_plots = min(num_channels, 4)  # Limit to 4 plots for readability
        subplot_titles = [f"Channel {i}" for i in range(num_plots)]
        fig = make_subplots(
            rows=2,
            cols=2,
            specs=[[{'type': 'scatter3d'}]*2]*2,
            subplot_titles=subplot_titles
        )

        for i in range(num_plots):
            channel_data = tensor[i].view(-1, 3).numpy()
            x, y, z = channel_data[:, 0], channel_data[:, 1], channel_data[:, 2]
            scatter = go.Scatter3d(
                x=x,
                y=y,
                z=z,
                mode='markers',
                marker=dict(size=2),
                name=f'Channel {i}'
            )
            row, col = divmod(i, 2)
            fig.add_trace(scatter, row=row+1, col=col+1)

        title = f"4D Scatter Plots: {metadata.get('layer_name', 'Unknown Layer')}"
        fig.update_layout(title=title, height=800)
        self._save_and_export(fig, f"{metadata.get('layer_name', 'layer')}_4d_scatter")

    def _save_and_export(self, fig: go.Figure, name: str) -> None:
        """
        Save the plot as HTML and PNG.

        Args:
            fig (go.Figure): The Plotly figure to save.
            name (str): The base name for the saved files.
        """
        try:
            html_path = self.html_dir / f"{name}.html"
            png_path = self.png_dir / f"{name}.png"
            fig.write_html(str(html_path))
            fig.write_image(str(png_path))
            logger.info(f"Saved visualization: {html_path} and {png_path}")
        except Exception as e:
            logger.error(f"Failed to save visualization {name}: {e}", exc_info=True)

    def _sanitize_filename(self, filename: str) -> str:
        """
        Sanitize the filename by removing illegal characters.

        Args:
            filename (str): The original filename.

        Returns:
            str: The sanitized filename.
        """
        return "".join(c for c in filename if c.isalnum() or c in (' ', '_', '-')).rstrip()


class AdvancedModelWrapper(nn.Module):
    """A wrapper class for advanced model visualization."""

    def __init__(self, model: nn.Module, visualizer: ModelVisualizer) -> None:
        """
        Initialize the AdvancedModelWrapper.

        Args:
            model (nn.Module): The PyTorch model to wrap.
            visualizer (ModelVisualizer): The visualizer instance.
        """
        super().__init__()
        self.model = model
        self.visualizer = visualizer
        self.hooks: List[Callable] = []
        self._register_hooks()

    def _register_hooks(self) -> None:
        """Register forward hooks for all suitable modules in the model."""
        for name, module in self.model.named_modules():
            if self._is_visualizable_module(module):
                hook = module.register_forward_hook(self._make_hook(name))
                self.hooks.append(hook)
                logger.debug(f"Hook registered for layer: {name}")

    @staticmethod
    def _is_visualizable_module(module: nn.Module) -> bool:
        """
        Determine if the module is suitable for visualization.

        Args:
            module (nn.Module): The module to check.

        Returns:
            bool: True if visualizable, False otherwise.
        """
        # Refine this method to include only specific layer types if needed
        return True

    def _make_hook(self, name: str) -> Callable:
        """
        Create a hook function for the given module name.

        Args:
            name (str): Name of the module.

        Returns:
            Callable: The hook function.
        """

        def hook(module: nn.Module, input: Any, output: Any) -> None:
            metadata: Dict[str, Any] = {
                'model_type': type(self.model).__name__,
                'layer_name': name,
                'layer_type': type(module).__name__
            }
            tensors = self._extract_tensors(output)
            if not tensors:
                logger.warning(f"No tensors found in output of layer {name}.")
                return
            for idx, tensor in enumerate(tensors):
                meta = metadata.copy()
                if len(tensors) > 1:
                    meta['output_index'] = idx
                self.visualizer.visualize_tensor(tensor, meta)

        return hook

    @staticmethod
    def _extract_tensors(output: Any) -> List[torch.Tensor]:
        """
        Recursively extract tensors from complex output structures.

        Args:
            output (Any): The output from a module's forward pass.

        Returns:
            List[torch.Tensor]: A list of tensors extracted from the output.
        """
        tensors: List[torch.Tensor] = []

        if isinstance(output, torch.Tensor):
            tensors.append(output)
        elif isinstance(output, (list, tuple)):
            for item in output:
                tensors.extend(AdvancedModelWrapper._extract_tensors(item))
        elif isinstance(output, dict):
            for item in output.values():
                tensors.extend(AdvancedModelWrapper._extract_tensors(item))
        elif hasattr(output, '__dict__'):
            for item in output.__dict__.values():
                tensors.extend(AdvancedModelWrapper._extract_tensors(item))
        return tensors

    def forward(self, *args: Any, **kwargs: Any) -> Any:
        """
        Forward pass through the wrapped model.

        Args:
            *args: Variable length argument list.
            **kwargs: Arbitrary keyword arguments.

        Returns:
            Any: The output of the wrapped model.
        """
        try:
            return self.model(*args, **kwargs)
        except Exception as e:
            logger.error(f"Error during forward pass: {e}", exc_info=True)
            raise

    def remove_hooks(self) -> None:
        """Remove all registered hooks."""
        for hook in self.hooks:
            hook.remove()
        logger.info("All hooks have been removed.")
        self.hooks.clear()


def visualize_model(
    model: nn.Module,
    input_data: torch.Tensor,
    output_dir: str = "visualizations"
) -> None:
    """
    Visualize the internals of a PyTorch model.

    Args:
        model (nn.Module): The PyTorch model to visualize.
        input_data (torch.Tensor): Sample input data for the model.
        output_dir (str): Directory to save visualization outputs.
    """
    visualizer = ModelVisualizer(output_dir)
    wrapped_model = AdvancedModelWrapper(model, visualizer)

    try:
        logger.info("Starting model visualization...")
        wrapped_model.eval()
        with torch.no_grad():
            wrapped_model(input_data)
        logger.info("Model visualization completed successfully.")
    except Exception as e:
        logger.error(f"Error during model visualization: {e}", exc_info=True)
    finally:
        wrapped_model.remove_hooks()


def main() -> None:
    """
    Main function to demonstrate model visualization.
    """
    try:
        model = AutoModelForCausalLM.from_pretrained("gpt2")
        logger.info("Model loaded successfully.")
    except Exception as e:
        logger.error(f"Failed to load model: {e}", exc_info=True)
        return

    # Create sample input data
    # Ensure that the input data matches the model's expected input dimensions and type
    try:
        input_tensor = torch.randint(
            low=0,
            high=50257,
            size=(1, 10),  # Batch size of 1, sequence length of 10
            dtype=torch.long
        )
        logger.info("Input tensor created successfully.")
    except Exception as e:
        logger.error(f"Failed to create input tensor: {e}", exc_info=True)
        return

    # Visualize the model
    visualize_model(model, input_tensor)


if __name__ == "__main__":
    main()