data_minning / src /face_backbones.py
Hnu-22-yudi's picture
Add files using upload-large-folder tool
11d0468 verified
Raw
History Blame Contribute Delete
4.3 kB
"""Backbone builders for scratch face-recognition experiments."""
from __future__ import annotations
from collections.abc import Sequence
import torch
from facenet_pytorch import InceptionResnetV1
from torch import nn
class Flatten(nn.Module):
def forward(self, x: torch.Tensor) -> torch.Tensor:
return x.view(x.size(0), -1)
class IRBlock(nn.Module):
def __init__(self, in_channels: int, out_channels: int, stride: int):
super().__init__()
self.residual = nn.Sequential(
nn.BatchNorm2d(in_channels),
nn.Conv2d(in_channels, out_channels, kernel_size=3, stride=1, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
nn.PReLU(out_channels),
nn.Conv2d(out_channels, out_channels, kernel_size=3, stride=stride, padding=1, bias=False),
nn.BatchNorm2d(out_channels),
)
if in_channels == out_channels and stride == 1:
self.shortcut = nn.Identity()
else:
self.shortcut = nn.Sequential(
nn.Conv2d(in_channels, out_channels, kernel_size=1, stride=stride, bias=False),
nn.BatchNorm2d(out_channels),
)
def forward(self, x: torch.Tensor) -> torch.Tensor:
return self.residual(x) + self.shortcut(x)
class IRResNet(nn.Module):
"""InsightFace-style IR-ResNet for 112x112 face crops."""
def __init__(self, layers: Sequence[int], embedding_size: int = 512, dropout: float = 0.4):
super().__init__()
self.input_layer = nn.Sequential(
nn.Conv2d(3, 64, kernel_size=3, stride=1, padding=1, bias=False),
nn.BatchNorm2d(64),
nn.PReLU(64),
)
self.body = nn.Sequential(
self._make_stage(64, 64, layers[0], stride=2),
self._make_stage(64, 128, layers[1], stride=2),
self._make_stage(128, 256, layers[2], stride=2),
self._make_stage(256, 512, layers[3], stride=2),
)
self.output_layer = nn.Sequential(
nn.BatchNorm2d(512),
nn.Dropout(dropout),
Flatten(),
nn.Linear(512 * 7 * 7, embedding_size),
nn.BatchNorm1d(embedding_size),
)
self._init_weights()
def _make_stage(self, in_channels: int, out_channels: int, blocks: int, stride: int) -> nn.Sequential:
layers = [IRBlock(in_channels, out_channels, stride)]
for _ in range(1, blocks):
layers.append(IRBlock(out_channels, out_channels, 1))
return nn.Sequential(*layers)
def _init_weights(self) -> None:
for module in self.modules():
if isinstance(module, nn.Conv2d):
nn.init.kaiming_normal_(module.weight, mode="fan_out", nonlinearity="relu")
elif isinstance(module, (nn.BatchNorm1d, nn.BatchNorm2d)):
nn.init.ones_(module.weight)
nn.init.zeros_(module.bias)
elif isinstance(module, nn.Linear):
nn.init.xavier_uniform_(module.weight)
nn.init.zeros_(module.bias)
def forward(self, x: torch.Tensor) -> torch.Tensor:
x = self.input_layer(x)
x = self.body(x)
return self.output_layer(x)
def canonical_backbone_name(name: str) -> str:
normalized = name.lower().replace("-", "_")
aliases = {
"inceptionresnetv1": "inception_resnet_v1",
"inception_resnet": "inception_resnet_v1",
"inception_resnet_v1": "inception_resnet_v1",
"ir18": "ir_resnet18",
"ir_resnet18": "ir_resnet18",
"ir34": "ir_resnet34",
"ir_resnet34": "ir_resnet34",
}
if normalized not in aliases:
raise ValueError(f"Unsupported backbone: {name}")
return aliases[normalized]
def build_backbone(name: str, pretrained_model: str | None = None) -> tuple[nn.Module, int, str]:
canonical = canonical_backbone_name(name)
if canonical == "inception_resnet_v1":
model = InceptionResnetV1(pretrained=pretrained_model, classify=False)
elif canonical == "ir_resnet18":
model = IRResNet([2, 2, 2, 2])
elif canonical == "ir_resnet34":
model = IRResNet([3, 4, 6, 3])
else:
raise ValueError(f"Unsupported backbone: {name}")
return model, 512, canonical