Download train_defender.py from weathon/DecoyFace: direct link, hf CLI and curl.
- Browser
- Download file 4.11 kB
-
https://huggingface.co/weathon/DecoyFace/resolve/main/train_defender.py
- Command line
-
hf download hf://weathon/DecoyFace/train_defender.py
-
curl -L -o train_defender.py https://huggingface.co/weathon/DecoyFace/resolve/main/train_defender.py
4.11 kB
| """Defender training (paper Sec. IV-E): F_c frozen; train F_s + ArcFace head on | |
| the server-side canonicalized feature z_can with V=2 protected views and the | |
| multi-view consistency loss. L = L_id + lambda_cons * L_cons. | |
| Outputs checkpoints/defender.pt with the trained F_s and ArcFace head. | |
| """ | |
| import copy | |
| import argparse | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| from torch.utils.data import DataLoader | |
| import wandb | |
| from models import load_ir50, ClientEncoder, ServerBackbone | |
| from data import MS1MDataset | |
| from decoyface import DecoyProtector | |
| from common import ADAFACE_CKPT, CKPT, CFG, DEVICE | |
| V = 2 | |
| LAMBDA_CONS = 0.1 | |
| BATCH = 256 | |
| EPOCHS = 15 | |
| LR = 0.1 | |
| DECAY_EPOCHS = [4, 8, 12] | |
| class ArcFace(nn.Module): | |
| def __init__(self, in_dim, n_cls, s=64.0, m=0.5): | |
| super().__init__() | |
| self.W = nn.Parameter(torch.empty(n_cls, in_dim)) | |
| nn.init.xavier_normal_(self.W) | |
| self.s, self.m = s, m | |
| def forward(self, emb, y): | |
| emb = F.normalize(emb) | |
| w = F.normalize(self.W) | |
| cos = emb @ w.T | |
| theta = torch.acos(cos.clamp(-1 + 1e-6, 1 - 1e-6)) | |
| target = torch.cos(theta + self.m) | |
| onehot = F.one_hot(y, cos.shape[1]).float() | |
| logits = self.s * (onehot * target + (1 - onehot) * cos) | |
| return F.cross_entropy(logits, y) | |
| def main(): | |
| ap = argparse.ArgumentParser() | |
| ap.add_argument("--ms1m_root", required=True) | |
| ap.add_argument("--num_ids", type=int, default=30000) | |
| ap.add_argument("--epochs", type=int, default=EPOCHS) | |
| args = ap.parse_args() | |
| dev = DEVICE | |
| ir50 = load_ir50(ADAFACE_CKPT, dev) | |
| Fc = ClientEncoder(ir50).to(dev).eval() | |
| for p in Fc.parameters(): | |
| p.requires_grad_(False) | |
| Fs = ServerBackbone(copy.deepcopy(ir50)).to(dev).train() | |
| rssd = torch.load(CKPT / "rssd.pt", map_location=dev) | |
| bank = torch.load(CKPT / "bank.pt", map_location=dev) | |
| protector = DecoyProtector(rssd["P_rec"], rssd["P_N"], bank["mu_rec"], | |
| bank["aux_bank"].float(), bank["perm"], CFG, dev) | |
| ds = MS1MDataset(args.ms1m_root, args.num_ids) | |
| head = ArcFace(512, ds.num_classes).to(dev) | |
| loader = DataLoader(ds, batch_size=BATCH, shuffle=True, num_workers=12, | |
| drop_last=True, pin_memory=True, persistent_workers=True) | |
| params = list(Fs.parameters()) + list(head.parameters()) | |
| opt = torch.optim.SGD(params, lr=LR, momentum=0.9, weight_decay=5e-4) | |
| wandb.init(project="decoyface-repro", name="defender", mode="online", | |
| config={**CFG, "num_ids": args.num_ids, "epochs": args.epochs}) | |
| step = 0 | |
| for epoch in range(args.epochs): | |
| lr = LR * (0.1 ** sum(epoch >= d for d in DECAY_EPOCHS)) | |
| for g in opt.param_groups: | |
| g["lr"] = lr | |
| for x, y in loader: | |
| x, y = x.to(dev, non_blocking=True), y.to(dev, non_blocking=True) | |
| with torch.no_grad(): | |
| z = Fc(x) | |
| embs = [] | |
| id_loss = 0.0 | |
| for _ in range(V): | |
| z_B = z.roll(torch.randint(1, x.shape[0], (1,)).item(), 0) | |
| z_can = protector.server_recover(protector.protect(z, z_B)) | |
| e = Fs(z_can) | |
| id_loss = id_loss + head(e, y) | |
| embs.append(F.normalize(e)) | |
| id_loss = id_loss / V | |
| cons = ((embs[0] - embs[1]) ** 2).sum(1).mean() | |
| loss = id_loss + LAMBDA_CONS * cons | |
| opt.zero_grad(set_to_none=True) | |
| loss.backward() | |
| opt.step() | |
| if step % 100 == 0: | |
| wandb.log({"def/loss": loss.item(), "def/id": id_loss.item(), | |
| "def/cons": cons.item(), "def/lr": lr}, step=step) | |
| if step % 1000 == 0: | |
| print(f"ep{epoch} step{step} loss{loss.item():.3f} " | |
| f"id{id_loss.item():.3f} cons{cons.item():.4f}", flush=True) | |
| step += 1 | |
| torch.save({"Fs": Fs.state_dict(), "head": head.state_dict()}, | |
| CKPT / "defender.pt") | |
| wandb.finish() | |
| if __name__ == "__main__": | |
| main() | |