pytorch / pytorch/vision

[RFC] Fused RoIAlign Face Embedding Extractor & Numerically Stable Angular Margin Loss (`torchvision.ops` & `torchvision.losses`)

Open
#9,670 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Python
Stars
17.9k
Forks
7.3k
Avg merge
1d 15h
Merged PRs (30d)
13

Description

When scaling face recognition pipelines in PyTorch, developers hit two recurring infrastructure barriers:

  1. NaN Gradient Crashes in Angular Margin Losses: Standard Additive Angular Margin Loss (ArcFace) computes $\arccos(\theta)$ on normalized feature dot products. Floating-point imprecision in FP16/AMP (and extreme FP32 runs) routinely causes dot products to evaluate outside $[-1.0, 1.0]$ (e.g., $1.0000001$), generating NaN autograd gradients.
  2. CPU Memory Transfer Bottlenecks: Slicing face crops via Python loops (img[:, y1:y2, x1:x2]) introduces severe VRAM-to-RAM host transfers, causes CPU overhead, and triggers graph breaks + re-compilations under torch.compile due to dynamic crop shapes.

We propose adding AdaptiveArcFaceLoss to standard loss layers and demonstrating a trace-safe zero-copy extraction workflow via roi_align:

import math
import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision.ops import roi_align

class AdaptiveArcFaceLoss(nn.Module):
    """
    Numerically stable ArcFace Loss enforcing safe-clamping bounds on arccos inputs
    to eliminate NaN gradient explosions in FP16/FP32 training.
    """
    def __init__(self, in_features: int, num_classes: int, scale: float = 64.0, margin: float = 0.50):
        super().__init__()
        self.scale = scale
        self.cos_m, self.sin_m = math.cos(margin), math.sin(margin)
        self.th = math.cos(math.pi - margin)
        self.mm = math.sin(math.pi - margin) * margin
        self.weight = nn.Parameter(torch.empty(num_classes, in_features))
        nn.init.xavier_uniform_(self.weight)

    def forward(self, embeddings: torch.Tensor, labels: torch.Tensor) -> torch.Tensor:
        # EPS Safeguard: Prevent float overflow outside [-1.0, 1.0] bounds
        cosine = F.linear(F.normalize(embeddings), F.normalize(self.weight)).clamp(-1.0 + 1e-7, 1.0 - 1e-7)
        sine = torch.sqrt((1.0 - torch.pow(cosine, 2)).clamp(0.0, 1.0))
        phi = torch.where(cosine > self.th, cosine * self.cos_m - sine * self.sin_m, cosine - self.mm)
        
        one_hot = torch.zeros_like(cosine).scatter_(1, labels.view(-1, 1).long(), 1.0)
        logits = (one_hot * phi) + ((1.0 - one_hot) * cosine)
        return F.cross_entropy(logits * self.scale, labels)


class VectorizedFaceExtractor(nn.Module):
    """
    Fused GPU RoI crop & normalize wrapper operating zero-copy in VRAM.
    """
    def __init__(self, backbone: nn.Module, target_size=(112, 112)):
        super().__init__()
        self.backbone = backbone
        self.target_size = target_size

    def forward(self, images: torch.Tensor, boxes: torch.Tensor, box_indices: torch.Tensor) -> torch.Tensor:
        rois = torch.cat([box_indices.unsqueeze(1).float(), boxes], dim=1)
        aligned = roi_align(images, rois, output_size=self.target_size, spatial_scale=1.0)
        return F.normalize(self.backbone(aligned), p=2, dim=1)
  1. Key Technical Verification
    Autograd Stability: clamp(-1 + 1e-7, 1 - 1e-7) guarantees valid, non-NaN backward passes under extreme FP16 scaling.

torch.compile Compatibility: Passes fullgraph=True without triggering graph breaks or dynamic shape re-compilations.

We have unit test suites and autograd boundary verification ready for a PR upon core team review.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reviewing the proposed entry points in torchvision.ops and torchvision.losses, including roi_align and the suggested AdaptiveArcFaceLoss. Assess the numerical-stability and torch.compile claims against the proposed unit and autograd boundary tests; done means the API scope and verification requirements are agreed before implementation.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
computer-vision, machine-learning
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.