INRs in Inro¶
An implicit neural representation (INR) represents a signal as a parametric function of its coordinates, fitted to samples of the signal by minimizing a reconstruction loss.
| Signal | Coordinates | Values |
|---|---|---|
| Image | a pixel, in \([-1, 1]^2\) | \(D = 3\), its color |
| Occupancy volume | a voxel, in \([-1, 1]^3\) | \(D = 1\), inside or outside |
| Radiance field | a position and a view direction | a density and a color |
Inro keeps this abstraction explicit: models are modules on coordinates, datasets are coordinate/value pairs, and one training loop serves every task that fits samples directly.
flowchart TB
datasets["<b>Datasets</b><br/>coordinates and targets"] --> train["<b>inro.train</b><br/>one loop"]
models["<b>Models</b><br/>FUTON, SIREN, InstantNGP, ..."] --> train
losses["<b>Losses</b>"] -.-> train
train --> evaluate["<b>inro.evaluate</b><br/>chunked_inference"]
evaluate --> metrics["<b>Metrics</b><br/>psnr, ssim, lpips, iou, ..."]
models --> field["<b>inro.nerf</b><br/>RadianceField, renderers, train, evaluate"]
Models¶
Every model maps a tensor of coordinates (..., in_features) in \([-1, 1]\) to values (..., out_features), so it can be called on a single point, a batch or a whole grid:
model = inro.FUTON(
2, 3, basis=("cosine", {"num_components": 64}), combiner=("cp", {"rank": 32}), head="linear"
)
model(torch.rand(10, 2) * 2 - 1).shape # (10, 3)
model(torch.rand(4, 5, 2) * 2 - 1).shape # (4, 5, 3)
The constructors share in_features, out_features and, where they apply, hidden_features, hidden_layers and output_activation. The model zoo lists them all.
Datasets¶
A dataset yields dictionaries with four keys:
| Key | Content |
|---|---|
id |
A name for the signal, used in logs and file names |
input |
Coordinates to evaluate the model at |
target |
The values to fit, in the model's output range |
_original |
The reference signal in its native form, used by the metrics; keys with a leading underscore stay on the CPU |
ImageCoordinateDataset and OccupancyCoordinateDataset follow this scheme; the conventions page tabulates their shapes.
- One signal, one item.
len(dataset) == 1, and random subsampling happens on access: withsubsample=0.1, everydataset[0]returns a fresh random tenth of the coordinates and their values, flattened. - Benchmarks. The occupancy benchmark trains this way, on \(2^{18}\) fresh voxels per step; the image benchmark takes the whole image at every step.
- Helpers.
preprocessmaps native values to targets,postprocessmaps model output back,savewrites a reconstruction to disk, andto(device)moves the training tensors so that subsampling runs on the GPU.
Training¶
inro.train(model, train_dataset, eval_dataset, **options) fits a model in place.
- Draw a batchthe whole signal or a random subset
- Compute the loss
loss_fn(batch, model) - StepAdam under a cosine schedule
- Log and evaluateevery few steps, off the clock
| Part | What it does | Options |
|---|---|---|
| Optimizer | Adam. Every benchmark trains every model with PyTorch's defaults, (0.9, 0.999) and 1e-8, so that the optimizer favors none of them |
lr, weight_decay, adam_betas, adam_eps |
| Schedule | Steps of one batch each, with CosineAnnealingLR down to lr / 100 |
num_steps |
| Loss | loss_fn(batch, model) -> (loss, metrics); by default the mean squared error, logging the PSNR for targets in \([-1, 1]\) |
loss_fn |
| Evaluation | inro.evaluate reconstructs each item in chunks, maps the output through postprocess, and calls each metric as fn(reconstruction, original) |
eval_interval, metrics, chunk_size |
| Logging | Records the training metrics of the step's batch, printed above a progress bar, and writes log.txt, log.json, each evaluation's reconstruction and checkpoint.pt |
log_interval, log_dir, ckpt_path |
The return value is {"config", "model_dict", "history"}: the run's settings, the final parameters, and one record per logged step with elapsed, the seconds spent training so far, the training metrics of the step's batch and, when the step was evaluated, the mean evaluation metrics under "eval". The records are plain dictionaries, so pd.DataFrame(result["history"]) gives a learning curve.
Evaluation and inference¶
inro.evaluate(model, dataset, metrics=...)runs the evaluation half of the loop on its own, for example on a checkpoint (ckpt_path) or on another grid than the one trained on.inro.chunked_inference(model, coordinates, chunk_size=...)evaluates a model on any coordinate tensor undertorch.no_grad(), flattening the leading dimensions for chunking and restoring them afterwards.
Radiance fields¶
Novel view synthesis does not fit samples of the signal directly: the supervision is posed images, and the INR is queried along rays. inro.nerf therefore has its own dataset (BlenderDataset), field (RadianceField, which wraps any model from the zoo as its density network), renderers, and train and evaluate functions with the same shape of return value. The radiance field tutorial covers them.