Skip to content

BrainOmni: rotate with the released checkpoint's RoPE cache; keep the released head init - #1244

Merged
bruAristimunha merged 6 commits into
braindecode:masterfrom
bruAristimunha:w41/fu-brainomni-rope
Oct 7, 2026
Merged

bruAristimunha merged 6 commits into
braindecode:masterfrom
bruAristimunha:w41/fu-brainomni-rope

Conversation

@bruAristimunha

@bruAristimunha bruAristimunha commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

What changes

RoPE. The released BrainOmni checkpoints (OpenTSLab/BrainOmni @ 9a4d3c70, tiny and base) store blocks.*.time_attn.rope_embedding_layer.rotate as a float32 tensor of shape (240, n_dim/2). It holds the cosines cos(t·freqs) (max-abs 6e-8 from the exact values). The released downstream loader (downstream/model_collection/BrainOmni.py, load_state_dict(strict=False)) copies this tensor into the complex64 rotate buffer of model_utils/attn.py::RotaryEmbedding. The sine part is therefore 0, and the released code rotates queries and keys with that cache. Every downstream sequence is shorter than 240 (Somato 2 s → 8 tokens), so all of them use the cached rotation. freqs is stored rounded to bfloat16 and is used only past 240 positions.

The port dropped both keys and recomputed cos and sin. With this PR:

  • _RotaryPositionalEmbedding has the release's freqs and rotate buffers. rotate is stored as the real view (seq, n_dim/2, 2) = (cos, sin), so it still runs in real arithmetic on devices without complex support.
  • The official keys are loaded. A real (seq, n_dim/2) cache is read as (cos, 0), which is what the release's copy_ into a complex buffer does.
  • Positions past the cache use freqs with both cos and sin, as in the release's _set_rotate_cache.
  • The buffers stay float32 under .half()/.bfloat16(), like the release's complex64 cache under a bf16 engine.
  • Native state dicts saved without the buffers still load.

Head init. final_layer keeps PyTorch's default Linear initialisation. The released head (downstream/model.py: Dropout → LazyLinear → SELU → Linear) is never passed through _init_weights, which brainomni/model.py applies only to the backbone.

Numbers

Forward parity on identical inputs and identical released weights: 8 test segments of SomatoMotor (ds006035, 372 channels × 512 samples) prepared by the released maker. The released code is run verbatim (complex RoPE, CPU fp32) with attention dropout set to 0, because the release applies SDPA dropout in eval as well. Values are the max-abs difference per layer output:

layer master, tiny master, base this PR, tiny this PR, base
tokenizer + projection 0 0 0 0
block 0 0.042 0.27 0 0
block 10 3.95 4.67 0 0
encode (L2-normalised) 0.248 0.205 0 0
pooled head input 0.089 0.088 0 0

All 11 blocks used by encode are 0 with this PR.

In bf16 (both models cast with .bfloat16()), encode differs from the released code by 0.0586. The released code's own bf16 output differs from its fp32 output by 0.0597.

Fine-tuning on SomatoMotor (372-channel input, BrainOmni tiny, the released downstream protocol on the same prepared segments and folds: 3 LRs × 5 subject folds × 2 seeds, best-val checkpoint tested, mean ± sd of test balanced accuracy at the LR with the best test mean, as in the released metrics_stat.py):

run balanced accuracy
released code + released weights (bf16, Gaudi) 0.8709 ± 0.1045
port, master, fp32 0.7472 ± 0.0897
port, master, bf16 0.7400 ± 0.0870
port, master + default head init 0.7523 ± 0.0954
port, master + the checkpoint's cos-only cache 0.8610 ± 0.1097
port, master + eval-mode attention dropout 0.7326 ± 0.0938

The paper reports 0.863 ± 0.128 for this cell. A full grid with this branch is running.

Not changed: the released attention applies dropout_p in eval mode too. The port keeps eval deterministic (row "eval-mode attention dropout" above).

Tests

  • test_spatial_temporal_block_matches_frozen_released_output: a seeded block whose RoPE buffers are in the released format, checked against values from the released SpatialTemporalAttentionBlock on the same state dict and input (offline).
  • test_rope_rotates_with_the_released_cosine_only_cache (5 / 240 / 250 positions), test_rope_buffers_stay_float32_and_keep_dtype, test_brainomni_state_dict_without_rope_buffers_loads, test_brainomni_head_keeps_default_linear_init; the official-key load test now checks the converted cache.
  • The network parity test is re-pinned to the released code's output on the same fixture (sum 51.254784; first 16 values max-abs 0.0 against the released code).
  • test_brainomni.py: 110 passed (network tests deselected locally). test_models / test_integration / test_return_features / test_model_contract / test_pretrained_compat / test_correctness / test_huggingface, -k brainomni: 19 passed, 1 skipped. pre-commit passes.

Copilot AI balanced review requested due to automatic review settings October 6, 2026 22:57

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

RoPE cache expansion is not persisted, causing later short-sequence results to diverge from the released implementation.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Updates BrainOmni to match released checkpoint behavior and head initialization.

Changes:

  • Loads and applies released RoPE buffers.
  • Preserves default classification-head initialization.
  • Adds parity and compatibility tests.
File Description
braindecode/​models/​brainomni.py Implements checkpoint-compatible RoPE and head initialization.
test/​unit_tests/​models/​test_brainomni.py Adds parity and compatibility coverage.
docs/​whats_new.rst Documents the fixes.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread braindecode/models/brainomni.py Outdated
@codecov

codecov Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.14286% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 88.54%. Comparing base (3b5413c) to head (bc0c1e2).
⚠️ Report is 2 commits behind head on master.

Additional details and impacted files
@@           Coverage Diff           @@
##           master    #1244   +/-   ##
=======================================
  Coverage   88.53%   88.54%           
=======================================
  Files         158      158           
  Lines       19314    19330   +16     
=======================================
+ Hits        17100    17116   +16     
  Misses       2214     2214           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copilot AI balanced review requested due to automatic review settings October 7, 2026 03:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation matches the released behavior and includes comprehensive regression coverage.

Review effort: Balanced
Findings: None

Resolved since last review (1)

Copilot AI balanced review requested due to automatic review settings October 7, 2026 05:02

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Long-sequence half/bfloat16 RoPE rebuilding still differs from the released implementation.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)

Comment thread braindecode/models/brainomni.py
Copilot AI balanced review requested due to automatic review settings October 7, 2026 08:39

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Strict loading currently masks partially missing RoPE buffers, potentially producing silently inconsistent attention behavior.

Review effort: Balanced
Findings: 1 High severity

Open (1)
Resolved since last review (1)

Comment thread braindecode/models/brainomni.py Outdated
Copilot AI balanced review requested due to automatic review settings October 7, 2026 13:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The release note overstates forward parity despite the intentional attention-dropout difference.

Review effort: Balanced
Findings: 1 Low severity

Open (1)
Resolved since last review (1)

Comment thread docs/whats_new.rst Outdated
Copilot AI balanced review requested due to automatic review settings October 7, 2026 15:35
@bruAristimunha
bruAristimunha merged commit b2a9c67 into braindecode:master Oct 7, 2026
9 of 12 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The legacy fallback can silently accept official checkpoints with all RoPE state missing.

1 open finding
1 resolved since last review

🧠 Review effort: Balanced

"tokenizer.sensor_type",
}:
remapped[key] = own_state[key]
if not any(".rope_embedding_layer." in key for key in remapped):
bruAristimunha added a commit to bruAristimunha/braindecode that referenced this pull request Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants