Spatial-BEATs Training And Architecture Overview
This document summarizes the current Spatial-BEATs implementation in this repository:
- model architecture
- tensor shape flow
- dataset contract
- variable-length batching
- supervision and losses
- stage-1 training setup
- current
ov1/ov2/ov3presets
The implementation described here corresponds to:
- spatial_beats.py
- spatial_modules.py
- spatial_dataset.py
- spatial_loss.py
- train_spatial_beats.py
- spatial_beats_ov123_stage1_config.py
1. Goal
Spatial-BEATs is a separate spatial encoder for FOA audio.
It is designed to:
- reuse the BEATs backbone and pretrained weights
- take full FOA input instead of only the
Wchannel - learn spatial structure through explicit supervision
- output fixed-rate spatial tokens for an LLM
- stay separate from the original audio encoder used for semantic audio understanding
The current implementation follows the simplified design:
- the main objective is to train the FOA front-end and BEATs trunk to produce spatially informative embeddings
- the supervision heads are lightweight readout heads
- the final LLM tokens are taken from the encoder-side spatial embeddings, not from the final logits
2. High-Level Architecture
The end-to-end model path is:
FOA waveform
-> FOA spatial preprocessor
-> multi-channel patch embedding
-> BEATs trunk
-> frequency pooling
-> temporal resampling to 2.5 Hz
-> shallow temporal readout
-> spatial embeddings
-> fixed-slot supervision heads
-> projector
-> LLM spatial tokens
More concretely:
[B, 4, T]
-> [B, 7, T_f, 128]
-> [B, N_p, 512]
-> [B, N_p, 768]
-> [B, T_p, 768]
-> [B, T_s_max, 768]
-> [B, T_s_max, 768]
-> [B, T_s_max, 4, 768]
-> [B, T_s_max, d_llm]
Where:
B: batch sizeT: waveform length in samplesT_f: acoustic frame count before patchingN_p: number of BEATs patchesT_p: time-axis patch count after frequency poolingT_s_max: padded token count in the batch after resampling to2.5 Hzd_llm: spatial token width sent to the LLM
3. Input And Front-End
3.1 Input audio
The model expects:
- FOA waveform
- shape
[B, 4, T] - channel order:
W, X, Y, Z - sample rate:
16 kHz
3.2 Qwen-like low-level mel setup
The current front-end is aligned to the Qwen-2.5-Omni audio tower style low-level parameters:
sample_rate = 16000num_mel_bins = 128n_fft = 400win_length = 400hop_length = 160dither = 0.0
These parameters are shared between:
SpatialBEATsConfigSpatialDatasetConfig
This keeps the data pipeline and the model front-end consistent.
3.3 FOA feature construction
The preprocessor converts FOA waveform into a 7-channel feature map:
W_logmelX_logmelY_logmelZ_logmelIVxIVyIVz
Output shape:
foa_feat: [B, 7, T_f, 128]
This allows the whole FOA structure to enter the backbone instead of relying on only W.
4. Backbone And Spatial Embedding Path
4.1 Spatial patch embedding
The model replaces the original single-channel patch stem with a 7-channel patch embedding:
- input:
foa_feat [B, 7, T_f, 128] - output:
patch_tokens [B, N_p, 512] - also returns
grid_size = (T_p, F_p)
This is the first modified entry point for reusing BEATs on FOA input.
4.2 Reused BEATs trunk
The trunk reuses BEATs pretrained components:
layer_normpost_extract_projencoder.pos_conv- all transformer layers
encoder.layer_norm
Flow:
- input:
patch_tokens [B, N_p, 512] - output:
encoder_memory [B, N_p, 768]
4.3 Frequency pooling
The patch sequence is reshaped back into a patch grid and pooled over the frequency axis:
- input:
encoder_memory [B, N_p, 768]withgrid_size=(T_p, F_p) - reshaped internally to
[B, T_p, F_p, 768] - pooled output:
temporal_patch_tokens [B, T_p, 768]
This produces a time-aligned sequence before the final token-rate conversion.
4.4 Temporal resampling
The temporal resampler converts the patch-rate sequence into the final spatial token rate:
- target token rate:
2.5 Hz - per-sample target length:
T_s_i = round(duration_i * 2.5)
Batch handling:
- each sample is resampled independently
- the batch is padded to
T_s_max = max_i(T_s_i) - a temporal mask is produced
Outputs:
temporal_tokens: [B, T_s_max, 768]temporal_padding_mask: [B, T_s_max]
Mask convention:
False: valid time stepTrue: padded time step
4.5 Shallow temporal readout
The shallow temporal readout refines the resampled sequence with a lightweight transformer encoder:
- input:
temporal_tokens [B, T_s_max, 768] - output:
spatial_embeddings [B, T_s_max, 768]
This is the main representation used for both:
- spatial supervision
- final projection to LLM tokens
5. Supervision Heads
The current stage-1 design does not use a heavy decoder.
Instead, it uses a fixed-slot readout for supervision only.
5.1 Fixed-slot readout
The readout expands each time step into a small number of internal supervision slots:
- max slots per step:
K = 4 - input:
spatial_embeddings [B, T_s_max, 768] - output:
slot_latents [B, T_s_max, 4, 768]
Important:
K=4is only a supervision capacity- it does not change the final LLM token count
- the final LLM-visible token rate is still
2.5 Hz
5.2 Prediction heads
Each supervision slot predicts:
pred_activity: [B, T_s_max, 4]pred_azi_logits: [B, T_s_max, 4, 360]pred_ele_logits: [B, T_s_max, 4, 180]pred_dist: [B, T_s_max, 4, 1]pred_class_logits: [B, T_s_max, 4, C]
Where:
C = 65- the class vocabulary comes from:
/apdcephfs_cq12/share_302080740/user/schmittzhu/data/fsd50k/FSD50K.ground_truth/final_vocabulary.csv
These heads are used to supply explicit training loss and push the front-end plus BEATs trunk to learn spatial structure.
6. LLM Spatial Tokens
The final LLM tokens are not taken from slot logits.
They are projected from the encoder-side spatial embeddings:
- input:
spatial_embeddings [B, T_s_max, 768] - output:
llm_spatial_tokens [B, T_s_max, d_llm]
Therefore:
2.5 Hzmeans final LLM-visible tokens arrive at2.5 tokens/second- a
20 sclip produces about50spatial tokens - a
10 sclip produces about25spatial tokens
This is the externally visible spatial token interface.
7. Pretrained Weight Reuse
The model initializes from BEATs_iter3+ AS2M.
Current pretrained loading logic:
- selectively load BEATs trunk modules
- skip task-specific components that do not match
- inflate the old single-channel patch embedding into the new 7-channel stem
Patch stem initialization rule:
- original BEATs patch weight is copied into channel
0of the new 7-channel stem - remaining channels start from zero
This is a conservative initialization intended to preserve BEATs trunk stability while enabling FOA adaptation.
8. Dataset Contract
8.1 Supported manifests
The dataset loader currently supports:
ov1_foa.jsonlov2_foa.jsonlov3_foa.jsonl
It handles:
- single-source top-level manifest style
- nested multi-source manifest style with
sources
8.2 Required scene-level data
At scene level the dataset expects one FOA path, typically:
output_foa_path
or compatible fallback names already handled in the parser.
8.3 Required source-level data
For each source, the loader extracts:
- source class
- azimuth
- elevation
- distance
- weak time window
Internally each source is converted into a SourceEvent containing:
class_indexclass_labelazimuth_degelevation_degdistance_mstart_time_secondsend_time_seconds
8.4 Vocabulary mapping
Source labels are mapped to final_vocabulary.csv.
The loader supports several field aliases, including:
mono_target_labelmono_primary_labelfinal_labelsource_labellabel
and several id-style aliases if an integer class index is already present.
9. Variable-Length Batching
Handling mixed-length FOA clips is a core part of the current implementation.
9.1 Waveform padding
At batch time:
- each waveform is padded to the batch maximum waveform length
- the padded tensor has shape
[B, 4, T_max] - a waveform padding mask is created:
waveform_padding_mask: [B, T_max]
Mask convention:
False: valid waveform sampleTrue: padded sample
dui
9.2 Temporal token padding
After temporal resampling:
- each sample has its own
T_s_i = round(duration_i * 2.5) - the batch is padded to
T_s_max - the model returns:
temporal_padding_mask: [B, T_s_max]
target_num_steps: [B]
All temporal supervision, matching, and loss computation respect these lengths.
9.3 Long clip truncation
The current training presets cap clip duration at:
20.0 seconds
The dataset applies cropping before batching.
Preset crop policy:
crop_mode = "start"
This means:
- clips longer than 20 seconds are truncated from the beginning
- training and validation follow the same deterministic sequence policy
If needed later, the dataset also supports:
randomcenternone
10. Matching And Losses
10.1 Weak temporal supervision
The model uses weak source windows:
- each source provides
start_time_secondsandend_time_seconds - these define a valid supervision window, not guaranteed frame-level activity
The loss code first converts source windows into a time-window mask:
window_mask: [B, N_gt, T_s_max]
10.2 Per-step fixed-slot matching
Matching is performed per time step:
- only on valid temporal positions
- only within each source's weak time window
- between active GT sources and the
K=4slot predictions
The current matcher uses a detached cost built from:
- activity
- class
- azimuth
- elevation
- distance
The output contains the assigned GT target for each valid slot-time pair.
10.3 Multi-task loss terms
Current loss terms are:
loss_activityloss_aziloss_eleloss_distloss_cls_auxloss_temp
Their roles:
loss_activityBCEWithLogitson slot activity- computed over valid time steps
loss_azi- cross-entropy over 360 azimuth bins
loss_ele- cross-entropy over 180 elevation bins
loss_distSmoothL1Losson continuous distance regression
loss_cls_aux- auxiliary source class cross-entropy
loss_temp- temporal smoothness regularization over valid consecutive steps
The total loss is the weighted sum defined in SpatialLossConfig.
11. Stage-1 Training Flow
The current training entry is stage-1 encoder-focused training.
High-level flow per step:
batch
-> SpatialBEATs.forward()
-> match_fixed_slots()
-> compute_spatial_losses()
-> backward()
-> optimizer.step()
11.1 Trainable modules in stage 1
By default, stage 1 trains:
preprocessorpatch_embeddingfrequency_pooltemporal_resamplertemporal_readoutslot_readoutprediction_heads
It can also unfreeze the BEATs trunk.
The projector is kept frozen by default in stage 1.
11.2 Optimizer
The current trainer uses:
AdamW
Default preset values:
batch_size = 4num_epochs = 20learning_rate = 1e-4weight_decay = 0.05
12. Current Presets
12.1 OV123_STAGE1_CFG
Defined in:
This preset is intended to train on:
ov1_foa.jsonlov2_foa.jsonlov3_foa.jsonl
with split filtering:
- train:
("train",) - val:
("valid",) - test:
("test",)
and clip truncation:
max_clip_duration_seconds = 20.0crop_mode = "start"
12.2 OV23_STAGE1_CFG
This is the safer baseline preset using only:
ov2_foa.jsonlov3_foa.jsonl
It uses the same split and truncation policy.
12.3 Important note on ov1
The trainer is already written to use split filtering for ov1.
If the active ov1 manifest at the configured path does not yet contain split, then:
- the
OV123preset will not automatically include those samples in train, valid, or test - the fix is simply to point the preset at the updated
ov1manifest path
The code path itself already supports split-aware loading.
13. Current Runtime Status
The current implementation has already been checked on:
- a real FOA waveform file
- mixed-length real manifest samples
- full forward pass
- fixed-slot matching
- multi-task loss computation
- BEATs pretrained weight loading
The following paths are already operational:
- dataset parsing
- waveform batching
- mixed-length temporal masking
- model forward
- matching
- loss computation
- stage-1 optimization loop
14. Recommended Launch Pattern
Example usage:
from spatial_beats_ov123_stage1_config import OV123_STAGE1_CFG
from train_spatial_beats import main
main(OV123_STAGE1_CFG)
If ov1 still needs a different manifest path, update only:
OV123_STAGE1_CFG.train_manifest_paths
OV123_STAGE1_CFG.val_manifest_paths
OV123_STAGE1_CFG.test_manifest_paths
or rebuild the config through:
from train_spatial_beats import make_ov123_stage1_config
15. Summary
The current Spatial-BEATs implementation is a FOA-first BEATs-based spatial encoder with:
- Qwen-like low-level mel settings
- a 7-channel FOA front-end
- reused BEATs trunk
- fixed-rate
2.5 Hzspatial token output - fixed-slot supervision heads
- variable-length batching
- split-aware
ov1/ov2/ov3training presets
The central training idea is:
- use explicit spatial supervision to shape the front-end and BEATs trunk
- keep the supervision head lightweight
- use encoder-side spatial embeddings as the final source of LLM spatial tokens