Feature Extraction
Transformers
Safetensors
cage_detector
audio
watermark
watermark-detection
provenance
vocbulwark
custom_code
Instructions to use mlr2000/vocoder-large-watermark-detector with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use mlr2000/vocoder-large-watermark-detector with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("feature-extraction", model="mlr2000/vocoder-large-watermark-detector", trust_remote_code=True)# Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("mlr2000/vocoder-large-watermark-detector", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
| """ | |
| Coarse-to-Fine Gated Extractor (Cage) from VocBulwark (Appendix B.2). | |
| Architecture from paper: | |
| - Three branches (fine/mid/coarse), each with 4 GSCMs | |
| - Channel progression: 1 β 32 β 64 β 128 β 128 | |
| - Fine: k=3, s=2, p=1; Mid: k=5, s=2, p=2; Coarse: k=7, s=2, p=3 | |
| - Each GSCM has InstanceNorm + LeakyReLU | |
| - Each branch output β 1D adaptive average pooling | |
| - Fusion: GSCM(384β256, k=1, s=1, p=0) β GSCM(256, k=3, s=1, p=1) | |
| - Dual-path pooling (AAP + AMP) β arithmetic mean | |
| - Decoder: Linear(256, 512) β Linear(512, watermark_bits) | |
| """ | |
| import torch | |
| import torch.nn as nn | |
| from transformers import PreTrainedModel | |
| from .cage_config import CageExtractorConfig | |
| class GatedSeparableConvModule(nn.Module): | |
| """GSCM: Gated Separable Convolution Module (Sec 4.3 + Appendix B.2). | |
| Depthwise separable conv with gating mechanism. | |
| Uses InstanceNorm and LeakyReLU as per paper. | |
| Supports stride and different input/output channels. | |
| """ | |
| def __init__( | |
| self, | |
| in_channels: int, | |
| out_channels: int, | |
| kernel_size: int = 3, | |
| stride: int = 1, | |
| padding: int = 1, | |
| ): | |
| super().__init__() | |
| # Content branch (DSC): depthwise β pointwise β InstanceNorm β LeakyReLU | |
| self.content = nn.Sequential( | |
| nn.Conv1d(in_channels, in_channels, kernel_size, | |
| stride=stride, padding=padding, groups=in_channels), | |
| nn.Conv1d(in_channels, out_channels, 1), | |
| nn.InstanceNorm1d(out_channels), | |
| nn.LeakyReLU(0.2), | |
| ) | |
| # Gating branch: same structure but Sigmoid output | |
| self.gate = nn.Sequential( | |
| nn.Conv1d(in_channels, in_channels, kernel_size, | |
| stride=stride, padding=padding, groups=in_channels), | |
| nn.Conv1d(in_channels, out_channels, 1), | |
| nn.InstanceNorm1d(out_channels), | |
| nn.Sigmoid(), | |
| ) | |
| def forward(self, x: torch.Tensor) -> torch.Tensor: | |
| return self.content(x) * self.gate(x) | |
| def _make_branch(kernel_size: int, stride: int, padding: int): | |
| """Build a 4-GSCM branch with channel progression 1 β 32 β 64 β 128 β 128.""" | |
| return nn.Sequential( | |
| GatedSeparableConvModule(1, 32, kernel_size, stride, padding), | |
| GatedSeparableConvModule(32, 64, kernel_size, stride, padding), | |
| GatedSeparableConvModule(64, 128, kernel_size, stride, padding), | |
| GatedSeparableConvModule(128, 128, kernel_size, stride, padding), | |
| ) | |
| class CageExtractor(PreTrainedModel): | |
| config_class = CageExtractorConfig | |
| def __init__(self, config: CageExtractorConfig): | |
| super().__init__(config) | |
| # Three branches processing raw audio directly (paper B.2) | |
| # Fine-grained: k=3, s=2, p=1 | |
| self.fine_branch = _make_branch( | |
| kernel_size=config.fine_kernel, stride=2, padding=1) | |
| # Mid-grained: k=5, s=2, p=2 | |
| self.mid_branch = _make_branch( | |
| kernel_size=config.mid_kernel, stride=2, padding=2) | |
| # Coarse-grained: k=7, s=2, p=3 | |
| self.coarse_branch = _make_branch( | |
| kernel_size=config.coarse_kernel, stride=2, padding=3) | |
| # Fusion: concat 3 branches (3*128=384) β reduce to 256 β refine | |
| # GSCM (k=1, s=1, p=0) to reduce channel dimension to 256 | |
| self.fusion_reduce = GatedSeparableConvModule( | |
| 384, 256, kernel_size=1, stride=1, padding=0) | |
| # GSCM (k=3, s=1, p=1) that maintains the dimensions | |
| self.fusion_refine = GatedSeparableConvModule( | |
| 256, 256, kernel_size=3, stride=1, padding=1) | |
| # Decoder: two FC layers (paper B.2) | |
| self.decoder = nn.Sequential( | |
| nn.Linear(256, 512), | |
| nn.LeakyReLU(0.2), | |
| nn.Linear(512, config.watermark_bits), | |
| ) | |
| def forward(self, audio: torch.Tensor) -> torch.Tensor: | |
| """ | |
| Args: | |
| audio: [B, 1, T] waveform (or [B, T] which gets unsqueezed) | |
| Returns: | |
| logits: [B, watermark_bits] raw logits | |
| """ | |
| if audio.dim() == 2: | |
| audio = audio.unsqueeze(1) | |
| # Multi-scale branches (each processes raw audio) | |
| x_fine = self.fine_branch(audio) # [B, 128, T'] | |
| x_mid = self.mid_branch(audio) # [B, 128, T''] | |
| x_coarse = self.coarse_branch(audio) # [B, 128, T'''] | |
| # 1D adaptive average pooling to standardize temporal dimension | |
| pool_t = min(x_fine.shape[-1], x_mid.shape[-1], x_coarse.shape[-1]) | |
| x_fine = nn.functional.adaptive_avg_pool1d(x_fine, pool_t) | |
| x_mid = nn.functional.adaptive_avg_pool1d(x_mid, pool_t) | |
| x_coarse = nn.functional.adaptive_avg_pool1d(x_coarse, pool_t) | |
| # Fusion | |
| x_fuse = torch.cat([x_fine, x_mid, x_coarse], dim=1) # [B, 384, pool_t] | |
| x_fuse = self.fusion_reduce(x_fuse) # [B, 256, pool_t] | |
| x_fuse = self.fusion_refine(x_fuse) # [B, 256, pool_t] | |
| # Dual-path Pooling: AAP + AMP β arithmetic mean | |
| x_aap = nn.functional.adaptive_avg_pool1d(x_fuse, 1).squeeze(-1) # [B, 256] | |
| x_amp = nn.functional.adaptive_max_pool1d(x_fuse, 1).squeeze(-1) # [B, 256] | |
| x_pool = (x_aap + x_amp) / 2 | |
| # Decode | |
| logits = self.decoder(x_pool) # [B, watermark_bits] | |
| return logits | |