tta
Test-time augmentation (TTA) inferer.
Wraps any Inferer and runs it N times, each time under a different spatial augmentation of the input, then aggregates the per-point predictions. Because point cloud segmentation outputs are indexed by point ID rather than by spatial position, predictions from rotated or flipped views are already aligned and can be averaged directly without inverting the transform.
Classes:
-
TTAInfererβTest-time augmentation inferer.
Functions:
-
simple_tta_transformsβDefault test-time augmentation transforms for indoor semantic segmentation.
TTAInferer [source] ΒΆ
TTAInferer(
base: Inferer,
transforms: Union[
TransformFn, Sequence[TransformFn], None
] = None,
num_passes: Optional[int] = None,
include_identity: bool = False,
aggregate: AggregateMode = "mean",
ema_smoothing: float = 0.95,
softmax: bool = False,
pos_key: str = POS,
progress: bool = False,
)
Bases: Inferer
Test-time augmentation inferer.
Runs the wrapped base inferer once per augmentation pass and aggregates the per-point predictions across passes.
Three modes are supported:
- Single callable: re-sampled independently each pass. Use for random augmentations such as uniformly random rotation. Requires
num_passes. - Sequence of callables: each element is applied to exactly one pass in order. Use for a fixed view set (e.g. 8 evenly-spaced rotations).
num_passesis inferred from the sequence length. None: the input is left as-is and the base inferer is simply runnum_passestimes, voting over its own randomness (random sub-batches, random padding).
Parameters:
-
base(Inferer) βUnderlying
Infererinvoked once per pass. Any concrete inferer works:SimpleInferer(),SlidingWindowInferer(...),KNNWindowInferer(...). -
transforms(Union[TransformFn, Sequence[TransformFn], None], default:None) βSingle callable (re-sampled each pass), a sequence of callables for fixed views, or
Nonefor un-augmented passes. AnyDict[str, Any] -> Dict[str, Any]callable works, includingCompose. When a sequence is given,num_passesis ignored. -
num_passes(Optional[int], default:None) βNumber of TTA passes when
transformsis a single callable orNone. Must be \(\geq 1\). -
include_identity(bool, default:False) βIf
True, run one extra pass on the un-augmented input before the augmented passes (the "clean + N random views" voting protocol), so the total pass count isnum_passes + 1. -
aggregate(AggregateMode, default:'mean') βHow per-point predictions are combined across passes.
"mean"averages per-pass outputs."ema"maintains an exponential moving average of them. -
ema_smoothing(float, default:0.95) βEMA factor \(\alpha \in [0, 1)\) used when
aggregate="ema". -
softmax(bool, default:False) βIf
True, softmax each pass's output before aggregating. LeaveFalsewhen the base inferer already returns probabilities (e.g.SlidingWindowInferer(softmax=True)). -
pos_key(str, default:POS) βDict key for the position tensor (used for the empty-output fallback).
-
progress(bool, default:False) βIf
True, show atqdmprogress bar over the passes.
Example
A 4-pass TTA over random Z rotations and X/Y flips:
import torch
import torch_pointcloud as tp
from torch_pointcloud.inferers import SlidingWindowInferer, TTAInferer
from torch_pointcloud.transforms import Compose, RandomFlip, RandomRotate
model = tp.create_model("pointnet.s3dis-area5").eval()
data = {
"pos": torch.rand(4096, 3) * 10,
"x": torch.rand(4096, 9),
"batch": torch.zeros(4096, dtype=torch.long),
}
base = SlidingWindowInferer(block_size=6.0)
aug = Compose([
RandomRotate(keys="pos", angle_range=(-180.0, 180.0), axis=2, p=1.0),
RandomFlip(keys="pos", axes=[0, 1], p=0.5),
])
inferer = TTAInferer(base=base, transforms=aug, num_passes=4, aggregate="mean")
probs = inferer(data, predictor=lambda d: model(d["x"], d["pos"], d["batch"]))
Enumerated 8-view TTA (ScanNet ablation):
from torch_pointcloud.inferers import SlidingWindowInferer, TTAInferer
from torch_pointcloud.transforms import Compose, RandomRotate
base = SlidingWindowInferer(block_size=6.0)
views = [Compose([RandomRotate(keys="pos", angle_range=(a, a), axis=2, p=1.0)])
for a in (0.0, 45.0, 90.0, 135.0, 180.0, 225.0, 270.0, 315.0)]
inferer = TTAInferer(base=base, transforms=views, aggregate="mean")
simple_tta_transforms [source] ΒΆ
simple_tta_transforms(
rotations: Sequence[float] = (0.0, 90.0, 180.0, 270.0),
scales: Sequence[float] = (1.0, 0.95, 1.05),
flip: bool = True,
axis: int = 2,
keys: KeyCollection = (POS, NORMAL),
scale_keys: KeyCollection = POS,
) -> List[Compose]
Default test-time augmentation transforms for indoor semantic segmentation.
One view per (scale, rotation) pair, scales outermost, plus one x/y flip view when flip is set: the defaults give the 13-view precise-evaluation protocol of the ScanNet / ScanNet200 benchmarks. Rotations and the flip act on every key in keys (positions and normals); scales act on scale_keys only. Missing keys are skipped, so the same views serve scenes with and without normals. A rotation of \(0\) or a scale of \(1\) adds no transform.
Parameters:
-
rotations(Sequence[float], default:(0.0, 90.0, 180.0, 270.0)) βRotation angles in degrees about
axis, applied at every scale. -
scales(Sequence[float], default:(1.0, 0.95, 1.05)) βIsotropic scale factors.
-
flip(bool, default:True) βAppend one view mirrored along the x and y axes.
-
axis(int, default:2) βRotation axis (
2is the up axis). -
keys(KeyCollection, default:(POS, NORMAL)) βKeys rotated and flipped.
-
scale_keys(KeyCollection, default:POS) βKeys scaled.
Returns:
-
List[Compose]βA list of
len(rotations) * len(scales) + flipcomposed transforms forTTAInferer.