arcisvlm / model /y_encoder.py
Hardik Sanghvi
feat: integrate Gemma 4 E2B backbone for production-quality VLM inference
7a564e3
Raw
History Blame Contribute Delete
3.23 kB
"""
Y-Encoder β€” Text Encoder for producing target embeddings during JEPA training.
The Y-Encoder takes tokenized text (answers/captions) and produces a dense
embedding in the same space as the predictor output. During training, the
InfoNCE loss aligns predictor embeddings with Y-encoder embeddings.
Trained with a slow learning rate (0.05x multiplier) to provide stable targets.
"""
import torch
import torch.nn as nn
from model.transformer import TransformerBlock
class YEncoder(nn.Module):
"""
Text encoder that produces target embeddings for contrastive JEPA training.
Architecture:
Token IDs β†’ Embedding β†’ + Positional β†’ N Γ— TransformerBlock β†’ AvgPool β†’ Linear β†’ L2 normalize
Output: 1536-D normalized embedding vector.
Args:
vocab_size: BPE vocabulary size (8192)
hidden_dim: Transformer dimension (768)
embed_dim: Output embedding dimension (1536)
num_heads: Number of attention heads (12)
num_blocks: Number of transformer blocks (6)
max_seq_len: Maximum sequence length (512)
dropout: Dropout rate
"""
def __init__(
self,
vocab_size: int = 8192,
hidden_dim: int = 768,
embed_dim: int = 1536,
num_heads: int = 12,
num_blocks: int = 6,
max_seq_len: int = 512,
dropout: float = 0.1,
):
super().__init__()
self.hidden_dim = hidden_dim
self.embed_dim = embed_dim
# Token and position embeddings
self.token_embed = nn.Embedding(vocab_size, hidden_dim, padding_idx=0)
self.pos_embed = nn.Parameter(torch.randn(1, max_seq_len, hidden_dim) * 0.02)
self.embed_dropout = nn.Dropout(dropout)
# Bidirectional transformer blocks
self.blocks = nn.ModuleList([
TransformerBlock(hidden_dim, num_heads, dropout, mode="bidirectional")
for _ in range(num_blocks)
])
self.norm = nn.LayerNorm(hidden_dim)
# Project to embedding space
self.proj = nn.Linear(hidden_dim, embed_dim)
def forward(self, token_ids: torch.Tensor, padding_mask: torch.Tensor | None = None) -> torch.Tensor:
"""
Args:
token_ids: [batch, seq_len] β€” BPE token IDs
padding_mask: [batch, seq_len] β€” True for non-pad positions
Returns:
[batch, embed_dim] β€” L2-normalized text embedding (1536-D)
"""
B, T = token_ids.shape
# Token + positional embedding
x = self.token_embed(token_ids) + self.pos_embed[:, :T, :]
x = self.embed_dropout(x)
# Pass through transformer blocks
for block in self.blocks:
x = block(x)
x = self.norm(x)
# Average pooling over non-padding positions
if padding_mask is not None:
mask = padding_mask.unsqueeze(-1).float() # [B, T, 1]
x = (x * mask).sum(dim=1) / mask.sum(dim=1).clamp(min=1) # [B, hidden_dim]
else:
x = x.mean(dim=1) # [B, hidden_dim]
# Project to embedding space
x = self.proj(x) # [B, embed_dim]
# L2 normalize
x = nn.functional.normalize(x, p=2, dim=-1)
return x