Coverage for cafl4ds/ssl/augment.py: 100%
14 statements
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-17 14:39 +0000
« prev ^ index » next coverage.py v7.15.0, created at 2026-07-17 14:39 +0000
1"""Augmentation pipelines for the SSL methods.
3Joint-embedding methods (SimSiam here, BYOL/SimCLR later) learn by pulling together two
4*augmented views* of the same image, so :class:`TwoView` returns an independent pair. MAE
5needs only light spatial augmentation (its learning signal comes from masking), so it uses a
6single view.
8Simplification for the Phase-0 harness: a transform draws its random parameters **once per
9call** and applies them to the whole batch (fast on CPU). Two views therefore differ from
10each other (two independent draws) but samples within a view share a crop/jitter. That is
11enough to exercise the joint-embedding mechanics; per-sample augmentation can replace this
12without touching the method code.
13"""
15from __future__ import annotations
17import torch
18from torch import nn
19from torchvision.transforms import v2
22def make_ssl_augment(img_size: int, min_scale: float = 0.4) -> v2.Transform:
23 """Build a standard joint-embedding augmentation pipeline.
25 Args:
26 img_size: Output image side length (square).
27 min_scale: Lower bound of the random-resized-crop area fraction.
29 Returns:
30 A composed transform mapping a float image batch ``[B, C, H, W]`` in ``[0, 1]`` to an
31 augmented batch of the same shape.
32 """
33 return v2.Compose(
34 [
35 v2.RandomResizedCrop(size=img_size, scale=(min_scale, 1.0), antialias=True),
36 v2.RandomHorizontalFlip(p=0.5),
37 v2.RandomApply([v2.ColorJitter(0.4, 0.4, 0.4, 0.1)], p=0.8),
38 v2.RandomGrayscale(p=0.2),
39 ]
40 )
43def make_light_augment(img_size: int, min_scale: float = 0.6) -> v2.Transform:
44 """Build a light spatial augmentation pipeline (for MAE).
46 Args:
47 img_size: Output image side length (square).
48 min_scale: Lower bound of the random-resized-crop area fraction.
50 Returns:
51 A composed transform mapping a float image batch to an augmented batch.
52 """
53 return v2.Compose(
54 [
55 v2.RandomResizedCrop(size=img_size, scale=(min_scale, 1.0), antialias=True),
56 v2.RandomHorizontalFlip(p=0.5),
57 ]
58 )
61class TwoView(nn.Module): # type: ignore[misc] # nn.Module is Any without torch stubs (mypy hook env)
62 """Produce two independently augmented views of an image batch."""
64 def __init__(self, augment: v2.Transform) -> None:
65 """Wrap an augmentation transform to emit a positive-pair.
67 Args:
68 augment: The per-view augmentation to apply twice (independently).
69 """
70 super().__init__()
71 self.augment = augment
73 def forward(self, imgs: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
74 """Return two independently augmented views of ``imgs``.
76 Args:
77 imgs: Image batch ``[B, C, H, W]``.
79 Returns:
80 A ``(view_1, view_2)`` pair, each ``[B, C, H, W]``.
81 """
82 return self.augment(imgs), self.augment(imgs)