OCFNet / config.yaml
Ryan-2026's picture
Upload folder using huggingface_hub
583065e verified
Raw History Blame Contribute Delete
5.23 kB
misc:
exp_dir: ocfnet_geo
mode: train
gpu_mode: True
verbose: True
verbose_freq: 500
snapshot_freq: 1
pretrain: ''
model:
model: OCFNet
in_feats_dim: 1
out_feats_dim: 32
conv1_kernel_size: 5
normalize_feature: True
dist_type: L2
overlap_attention_module:
attention_feats_dim: 256
num_head: 4
nets: ['self','cross','self','cross','self','cross'] # GeoTransformer / CoFiNet: 3 interleaved rounds
# 'self' layers are masked multi-head self-attention (torch's fused attention) with a 3D
# rotary position embedding on the voxel coordinates; 'cross' layers get no position
# embedding, the two clouds are not registered yet
# RoPE variant: positions enter the attention as a rotary embedding of the voxel
# coordinates (relative, applied to query and key) instead of the paper's geometric
# encoding added to the features. Set both to True to keep the two together.
rope: True
rope_theta: 100.0
geometric_pos_enc: False # CoFiNet ape: False -- no positional encoding at the coarse level
pos_enc_k: 5 # neighbours of the geometry-aware positional encoding (Eq. 1)
coarse_to_fine:
sinkhorn_iters: 100 # CoFiNet uses 100; at 20 the plan is still 1e-1 from its marginals
similarity: inner # CoFiNet Eq.(1): einsum(F_X, F_Y)/sqrt(d), unnormalised
sinkhorn_epsilon: 0.05
coarse_threshold: 0.05 # tau_t of Eq. (3)
# A patch is the Voronoi cell of its super-point -- disjoint, so a point belongs to
# exactly one patch -- capped at `patch_size` members.
use_overlap_head: True # head trained everywhere; only the marginals differ
coarse_marginals: uniform # CoFiNet's plain OT; 'overlap' is Eq. (5)
fine_marginals: uniform
patch_size: 64
max_coarse_matches: 256 # patches refined per training step
# the overlap head: the paper normalises before the sigmoid, which fixes its mean
# at 0.5 while the targets average `overlap_prior` (scripts/diagnose_ocfnet.py)
overlap_head_norm: True
overlap_prior: 0.08
# let the two patches of a match talk to each other before matching, so the
# transport marginal means "matchable against this patch" rather than
# "matchable somewhere" -- 74% of a patch's points have their partner elsewhere
patch_attention: True
readout_mutual: True # keep only mutually-agreed matches
readout_deduplicate: True # and one per source point: 0.356 -> 0.526 inlier ratio
min_coarse_matches: 128 # at inference, fall back to the top-k pairs if the
# threshold selects fewer than this
eval_pairs: 300 # benchmark pairs registered after every epoch
matching_radius: 0.1 # CoFiNet's pos_margin; the radius the inlier ratio uses
loss:
# L = L_C + L_F + L_CO + L_FO (section 2.4)
w_coarse_loss: 1.0
w_coarse_circle_loss: 1.0 # GeoTransformer overlap-aware circle loss on the super-point features
w_fine_loss: 1.0
w_inlier_loss: 1.0 # expected inlier ratio, maximised directly
w_coarse_inlier_loss: 1.0 # the same, for the coarse transport
w_coarse_overlap_loss: 1.0
w_fine_overlap_loss: 1.0
# ranking supervision the paper's losses lack: a circle loss on the descriptors
# the transport cost is built from, and a row-wise classification inside each
# patch, which trains the argmax that Eq. (6) reads out
w_descriptor_loss: 1.0
w_patch_infonce_loss: 1.0
w_coarse_infonce_loss: 1.0
pos_margin: 0.1
neg_margin: 1.4
log_scale: 24
pos_radius: 0.0375
safe_radius: 0.1
overlap_radius: 0.0375
matchability_radius: 0.05
w_circle_loss: 1.0
w_overlap_loss: 1.0
w_saliency_loss: 0.0
max_points: 256
optimiser:
# OCFNet optimises cross-entropy over transport plans, not Predator's circle loss, so the
# SGD schedule of the Predator configs does not carry over. This follows CoFiNet, whose
# loss is of the same family: Adam at 1e-4.
optimizer: ADAM
max_epoch: 150 # CoFiNet trains for 150
lr: 0.0003
momentum: 0.8
weight_decay: 0.000001
bn_momentum: 0.05
scheduler: ExpLR
scheduler_gamma: 0.95
scheduler_freq: 1
iter_size: 1
dataset:
dataset: indoor
benchmark: 3DMatch
root: data/indoor
voxel_size: 0.025
# The attention is masked per pair and the transport plans are solved per pair, so the
# pairs of a batch never mix (see scripts/test_batch_equivalence.py).
# Memory grows with the voxel count, about 20 GiB per million voxels on this model
# (scripts/bench_batch_size.py); 32 peaks around 18 GiB of the 63 GiB card, which leaves
# room for a batch that happens to draw only large scenes. Throughput is already flat
# here: 0.047 s/pair at 32 against 0.044 at 48.
# Aligned with CoFiNet (configs/tdmatch/tdmatch.yaml), whose published numbers this
# implementation is compared against: their effective batch is 4 (batch 1, iter_size 4),
# so an epoch is 5160 optimiser steps against 645 at batch 32 -- eight times more learning
# per epoch. Batch 8 recovers most of that while keeping the GPU busy.
batch_size: 12
num_workers: 6
augment_noise: 0.005
train_info: configs/indoor/train_info.pkl
val_info: configs/indoor/val_info.pkl
demo:
src_pcd: assets/cloud_bin_21.pth
tgt_pcd: assets/cloud_bin_34.pth
n_points: 1000