multimodalart's picture
multimodalart HF Staff
Upload folder using huggingface_hub
6b3a024 verified
Raw
History Blame Contribute Delete
1.82 kB
# V-SPLADE
# Copyright (c) 2026-present NAVER Corp.
# Apache-2.0
"""
Output head β€” projects encoder representations into the retrieval space.
V_SPLADE uses SparseHead: the encoder's LM head is reused to project
hidden states to vocab-dim sparse weights via log1p(relu(.)).
"""
import torch
import torch.nn as nn
import torch.nn.functional as F
from enum import Enum
from typing import Tuple
class HeadType(Enum):
DENSE = "dense"
SPARSE = "sparse"
class DenseHead(nn.Module):
"""L2-normalized dense embedding."""
def forward(self, pooled: torch.Tensor) -> torch.Tensor:
return F.normalize(pooled, dim=-1)
class SparseHead(nn.Module):
"""Hidden states β†’ LM head β†’ log1p(relu) β†’ sparse vocab-dim weights.
Returns (h_raw, w_sparse). The LM head is held as a *reference* (not a
registered sub-module) to avoid duplicated state-dict keys, since the
encoder already owns the same module.
"""
def __init__(self, lm_head: nn.Module, hidden_size: int):
super().__init__()
object.__setattr__(self, "_lm_head_ref", lm_head)
self.scale = hidden_size ** -0.25
@property
def lm_head(self):
return self._lm_head_ref
def forward(self, hidden_states: torch.Tensor) -> Tuple[torch.Tensor, torch.Tensor]:
h = self._lm_head_ref(hidden_states) * self.scale
w = torch.log1p(torch.relu(h))
return h, w
def build_head(head_type: str, lm_head=None, hidden_size: int = 768, **kwargs) -> nn.Module:
if head_type == "dense":
return DenseHead()
if head_type == "sparse":
if lm_head is None:
raise ValueError("sparse head requires lm_head")
return SparseHead(lm_head, hidden_size)
raise ValueError(f"Unknown head_type: {head_type}. Choose: dense | sparse")