Masking And Reconstruction
A mask selects node occurrences, hides their inputs, and optionally makes them reconstruction targets. The same mechanism supports supervised labels, self-supervised learning, input dropout, and ablation.
For a complete workflow that changes these objectives between training stages, see Pretraining And Fine-Tuning.
Choose The Intended Effect
| Goal | Field configuration |
|---|---|
| Ordinary input | mask=False |
| Train-only learned-mask dropout | mask=0.15 |
| Train-only structural dropout | mask=rf.Mask(rate=0.15, skip=True) |
| Reconstruct sampled values | mask=rf.Mask(rate=0.15, reconstruct=True) |
| Supervised target, never embedded | mask=True |
| Remove an input in every stage without a target | mask=rf.Mask(skip=True, dropout=False) |
mask=True is shorthand for rf.Mask(skip=True, dropout=False, reconstruct=True). It differs from mask=1.0, which hides every input using learned-mask dropout during training and creates no reconstruction objective.
Without skip, a selected value remains a present coordinate represented by a learned mask. With skip=True, its input content is not tensorized or embedded and its coordinate is absent from encoder attention. Reconstruction targets still retain their pristine values for the loss.
A mask selects whole values: one Number, Text value, or Set, for example. It does not select individual tokens inside Text or members inside Set.
Configure A Policy
| Option | Meaning |
|---|---|
query |
Boolean selector in the input; omitted means every retained owner occurrence is eligible. |
rate |
Fraction of eligible occurrences to sample; omitted means select all eligible occurrences. |
skip |
Omit selected input representations instead of using a learned mask. |
reconstruct |
Add an objective for selected observed values, or request predictions. |
dropout |
Apply only during training; defaults to not reconstruct. |
dropout=True and reconstruct=True cannot be combined. Rates must be between zero and one. A zero rate removes the policy; a rate of one normalizes to no rate, making selection deterministic.
The normalized policy determines when selection runs:
| Policy | Train | Validate/test | Predict |
|---|---|---|---|
dropout=True |
Active | Off | Off |
dropout=False, no rate |
Active | Active | Active |
dropout=False, fractional rate |
Sampled | Sampled with epoch fixed at zero | Off |
Validation and test use different random streams. Sampling depends on seed, stage, owner address, epoch, and record layout; do not assume that changing batching preserves exactly the same selections.
Select Within Nested Data
A selector is an ordinary Boolean value. It can arrive in the input or be created by a preprocessor. This is one record:
merchant: grocery
transactions:
- amount: 42.75
days_ago: 3
routing:
hide_amount: true
- amount: 18.50
days_ago: 1
routing:
hide_amount: falseThe leaf’s mask query starts from its containing transaction:
import relflow as rf
transactions = rf.Branch(
length=64,
amount=rf.Number(
mask=rf.Mask(query="routing.hide_amount", reconstruct=True),
),
days_ago=rf.Number,
)Selected amounts are hidden and reconstructed. routing need not be a model field. The selector must provide exactly one non-null Boolean per retained owner record; integers, lists, missing paths, and null selectors are rejected. A node’s value query selects its source value; Mask.query independently selects which owner occurrences receive the policy.
Place a policy on the branch when all descendants should share a decision:
transactions = rf.Branch(
length=64,
mask=rf.Mask(rate=0.10, skip=True),
amount=rf.Number,
days_ago=rf.Number,
)Each selected transaction hides both fields and any nested descendants. A root mask similarly broadcasts through an observation. Reconstructing branch or root policies create objectives on active descendant leaves, whose extensions must support reconstruction.
Derive A Selector
For a root-level amount field, a processor can derive a selector from an application verification flag. Keep this Boolean derivation outside the model:
import polars as pl
@rf.preprocess
def select_unverified(frame: pl.DataFrame) -> pl.DataFrame:
return frame.with_columns(
pl.col("amount_verified").fill_null(False).not_().alias("predict_amount")
)
amount = rf.Number(
mask=rf.Mask(query="predict_amount", reconstruct=True),
)Include amount in the model and pass preprocessor=select_unverified to its data module. For direct prediction pass preprocess=select_unverified to model.predict(...). The source retains the amount field and verification flag; the processor adds a non-null Boolean selector before mask resolution.
Combine Policies
Pass a tuple or list of rf.Mask objects to combine selections:
amount = rf.Number(
mask=(
rf.Mask(rate=0.10),
rf.Mask(query="routing.hide_amount", skip=True, reconstruct=True),
),
)Ancestor and local selections accumulate. A skip wins over a learned mask; reconstruction selections accumulate independently. A child policy cannot restore an input hidden by an ancestor. Duplicate policies are normalized away.
Targets During Training And Prediction
Fitting needs observed target values. Explicit nulls can contribute value-state objectives for nullable fields; structurally absent positions do not invent labels. A fractional reconstruction policy trains its decoder but does not request ordinary prediction output. To request deterministic reconstruction later, update the policy.
A directly bound, unconditional target such as label=rf.Boolean(mask=True) may be omitted from prediction input. For a repeated target, its containing branch records must remain to establish output positions. Targets using an explicit value query or conditional reconstruction selector still need their source structure and selector to be bindable.
Conditional predictions retain the schema’s fixed shape. Use each coordinate’s inferred flag to identify requested outputs. See Prediction Output for the Arrow contract.