To implement a custom decoder, inherit from the DataDecoder class.
Constructor Requirements:
Your __init__ should accept out_features (output dimensionality) and n_batches (number of batches to account for batch effects).
The forward method:
You must implement the forward method, which accepts the following four arguments:
u: Cell embeddingsv: Feature embeddingsb: Batch indexl: Library size (computed by the encoder)
The method must return a likelihood distribution (e.g., a distribution object from scglue.models.prob).
Example of a Negative Binomial decoder using batch-specific trainable parameters:
class NBDataDecoder(DataDecoder):
def __init__(self, out_features: int, n_batches: int = 1) -> None:
super().__init__(out_features, n_batches=n_batches)
self.scale_lin = torch.nn.Parameter(torch.zeros(n_batches, out_features))
self.bias = torch.nn.Parameter(torch.zeros(n_batches, out_features))
self.log_theta = torch.nn.Parameter(torch.zeros(n_batches, out_features))
def forward(
self, u: torch.Tensor, v: torch.Tensor,
b: torch.Tensor, l: torch.Tensor
) -> D.NegativeBinomial:
scale = F.softplus(self.scale_lin[b])
logit_mu = scale * (u @ v.t()) + self.bias[b]
mu = F.softmax(logit_mu, dim=1) * l
log_theta = self.log_theta[b]
return D.NegativeBinomial(
log_theta.exp(),
logits=(mu + EPS).log() - log_theta
)