BioInteract / src /models /target_encoder.py
AI4deeperScience's picture
release: publish claim-safe Q9UQB9 custom prediction workflow
5b3b0dc verified
Raw
History Blame Contribute Delete
3.44 kB
"""Protein target encoder combining ESM-2 with residue descriptors.
It accepts frozen ESM-2 embeddings, four physicochemical descriptors, and an
optional categorical label channel. The browser demo supplies the ``NONE``
label for arbitrary user inputs; that channel is not Pfam/InterPro annotation
or evidence of protein function.
"""
import torch
import torch.nn as nn
class TargetEncoder(nn.Module):
"""
Protein residue-level encoder.
Input:
- ESM-2 embeddings: (B, L, 1280) — pretrained, frozen
- Physicochemical features: (B, L, 4) — amino acid properties
- Domain labels: (B, L) — functional domain type indices
Output:
- Residue representations: (B, L, projection_dim)
The optional label embedding is a model input channel. In the browser demo
it is the shared ``NONE`` label and is not a domain, binding-site, or
functional assignment.
"""
def __init__(self,
esm2_dim: int = 1280,
projection_dim: int = 256,
physchem_dim: int = 4,
domain_embed_dim: int = 32,
num_domain_types: int = 50,
use_domain_features: bool = True,
dropout: float = 0.1):
super().__init__()
self.use_domain = use_domain_features
self.projection_dim = projection_dim
# total input dimension
input_dim = esm2_dim + physchem_dim
if use_domain_features:
input_dim += domain_embed_dim
self.domain_embedding = nn.Embedding(
num_domain_types + 1, # +1 for padding
domain_embed_dim,
padding_idx=num_domain_types,
)
# projection: compress concatenated features to target dim
self.projection = nn.Sequential(
nn.Linear(input_dim, projection_dim * 2),
nn.LayerNorm(projection_dim * 2),
nn.GELU(),
nn.Dropout(dropout),
nn.Linear(projection_dim * 2, projection_dim),
nn.LayerNorm(projection_dim),
)
def forward(self,
esm2_embedding: torch.Tensor,
physicochemical: torch.Tensor,
domain_labels: torch.Tensor = None,
protein_mask: torch.Tensor = None) -> torch.Tensor:
"""
Args:
esm2_embedding: (B, L, 1280) pretrained embeddings
physicochemical: (B, L, 4) amino acid properties
domain_labels: (B, L) LongTensor of domain type indices
protein_mask: (B, L) boolean mask (True = valid residue)
Returns:
residue_repr: (B, L, projection_dim) residue representations
"""
parts = [esm2_embedding, physicochemical]
if self.use_domain and domain_labels is not None:
domain_emb = self.domain_embedding(domain_labels)
parts.append(domain_emb)
# concatenate all feature sources
combined = torch.cat(parts, dim=-1)
# project
residue_repr = self.projection(combined)
# zero out padded positions
if protein_mask is not None:
residue_repr = residue_repr * protein_mask.unsqueeze(-1).float()
return residue_repr