Source code for torch_concepts.nn.modules.low.encoders.exogenous
"""
Exogenous encoder module.
This module provides encoders that transform latent into exogenous variables
for concept-based models, supporting the Concept Embedding Models architecture.
"""
import numpy as np
import torch
from ..base.layer import BaseEncoder
from typing import Tuple
[docs]
class LinearZU(BaseEncoder):
"""
Exogenous encoder that creates concept exogenous.
Transforms input input into exogenous variables (external features) for
each concept, producing a 2D output of shape (out_features, exogenous_size).
Implements the 'embedding generators' from Concept Embedding Models (Zarlenga et al., 2022).
Attributes:
exogenous_size (int): Dimension of each concept's exogenous.
out_endogenous_dim (int): Number of output concepts.
encoder (nn.Sequential): The encoding network.
Args:
in_features: Number of input latent features.
out_features: Number of output concepts.
exogenous_size: Dimension of each concept's exogenous.
Example:
>>> import torch
>>> from torch_concepts.nn import LinearZU
>>>
>>> # Create exogenous encoder
>>> encoder = LinearZU(
... in_features=128,
... out_features=5,
... exogenous_size=16
... )
>>>
>>> # Forward pass
>>> latent = torch.randn(4, 128) # batch_size=4
>>> exog = encoder(latent)
>>> print(exog.shape)
torch.Size([4, 5, 16])
>>>
>>> # Each concept has its own 16-dimensional exogenous
>>> print(f"Concept 0 exogenous shape: {exog[:, 0, :].shape}")
Concept 0 exogenous shape: torch.Size([4, 16])
References:
Espinosa Zarlenga et al. "Concept Embedding Models: Beyond the
Accuracy-Explainability Trade-Off", NeurIPS 2022.
https://arxiv.org/abs/2209.09056
"""
[docs]
def __init__(
self,
in_features: int,
out_features: int,
exogenous_size: int
):
"""
Initialize the exogenous encoder.
Args:
in_features: Number of input latent features.
out_features: Number of output concepts.
exogenous_size: Dimension of each concept's exogenous.
"""
super().__init__(
in_features=in_features,
out_features=out_features,
)
self.exogenous_size = exogenous_size
self.out_endogenous_dim = out_features
self.out_exogenous_shape = (self.out_endogenous_dim, exogenous_size)
self.out_encoder_dim = np.prod(self.out_exogenous_shape).item()
self.encoder = torch.nn.Sequential(
torch.nn.Linear(
in_features,
self.out_encoder_dim
),
torch.nn.Unflatten(-1, self.out_exogenous_shape),
torch.nn.LeakyReLU(),
)
[docs]
def forward(
self,
input: torch.Tensor
) -> Tuple[torch.Tensor]:
"""
Encode latent into exogenous variables.
Args:
input: Input latent of shape (batch_size, in_features).
Returns:
Tuple[torch.Tensor]: Exogenous variables of shape
(batch_size, out_features, exogenous_size).
"""
return self.encoder(input)