braindecode.models.VEMG2Pose#
- class braindecode.models.VEMG2Pose(n_outputs=None, n_chans=None, chs_info=None, n_times=None, input_window_seconds=None, sfreq=None, *, encoder_channels=256, feature_dim=64, hidden_size=512, lstm_layers=2, decoder='lstm', decoder_hidden_sizes=(512, 512), stem_kernel_sizes=(11, 5), stem_strides=(5, 2), tds_subsample_kernels=(17, 9), tds_subsample_strides=(4, 2), tds_kernel_widths=(9, 5), tds_blocks=2, tds_channels=16, rollout_rate=50.0, num_position_steps=500, parameterization='hybrid', output_scalar=0.01, activation=<class 'torch.nn.modules.activation.LeakyReLU'>, drop_prob=0.0)[source]#
VEMG2Pose from Salter et al (2024) [salter2024].
Convolution Recurrent
Figure 1 of Salter et al. (2024). The paper describes vemg2pose in prose (Section 3.5, Appendix C) and publishes no architecture diagram, so this is its overview figure.#
Time-depth-separable (TDS) convolutional encoder [hannun2019] feeding a state-conditioned LSTM that rolls the hand pose out at 50 Hz. The decoder emits a position and a velocity for every joint at each step: it takes the position for the first
num_position_steps, then integrates the velocity from there on. That hybrid is what the leadingvnames.Architecture Overview
(B, n_chans, T)→ two stridedConv1dblocks (kernel 11 stride 5, then 5 / 2, LayerNorm over channels) → two TDS stages, each a strided subsampling convolution followed bytds_blockspairs of a time-depth-separable 2-D convolution and a residual feed-forward block → a linear projection tofeature_dim→ linear resampling torollout_rate→ LSTM rollout conditioned on the previous pose →(B, T - left_context, n_outputs).Every convolution is valid (no padding), so the encoder consumes a
left_contextof1790samples at the default schedule. It returns predictions for the valid interval[left_context, T).Macro Components
VEMG2Pose.encoderOperations.
_Conv1dBlock×2 then_TdsStage×2. A stage is a stridedConv1d+ LayerNorm, thentds_blocks× (TDSConv2dBlock,TDSFullyConnectedBlock), both residual and each closing with a LayerNorm over the channel axis. Role. Compresses 2 kHz sEMG tofeature_dimfeatures atsfreq / prod(strides), i.e. 25 Hz for the paper’s input.VEMG2Pose.lstm/VEMG2Pose.final_layerOperations. Two-layer LSTM over
[feature ; previous pose], then LeakyReLU and a linear map to2 * n_outputs, scaled byoutput_scalar. The halves are the position and the velocity. Role. Autoregressive pose rollout with state feedback.
Temporal, Spatial, and Spectral Encoding
Time: Strided convolutions then an explicit 50 Hz rollout.
Channels/space: The first convolution mixes all electrodes; the TDS blocks then convolve within a channel/width factorisation.
Frequency: Learned from the raw waveform; nothing is filtered out.
Additional Mechanisms
Passing
y0conditions the rollout on a known initial pose (the paper’s tracking regime). Omitting it starts from zeros, which is the regression regime the releasedregression_vemg2pose.ckptwas trained under.Pre-trained weights
Meta’s released checkpoints are rehosted with these parameter names:
# vemg2pose: recurrent decoder VEMG2Pose.from_pretrained("braindecode/VEMG2Pose-emg2pose") VEMG2Pose.from_pretrained("braindecode/VEMG2Pose-emg2pose-tracking") # emg2pose: same encoder, stateless MLP decoder VEMG2Pose.from_pretrained("braindecode/EMG2Pose-emg2pose") VEMG2Pose.from_pretrained("braindecode/EMG2Pose-emg2pose-tracking")
Each config records its own
decoderandparameterization, so the right rollout is restored automatically.The rehosted weights reproduce the reference implementation exactly and remain under emg2pose’s CC BY-NC-SA 4.0 license.
Added in version 1.8.
- Parameters:
n_outputs (int) – Number of outputs of the model. This is the number of classes in the case of classification.
n_chans (int) – Number of EEG channels.
chs_info (list of dict) – Information about each individual EEG channel. This should be filled with
info["chs"]. Refer tomne.Infofor more details.n_times (int) – Number of time samples of the input window.
input_window_seconds (float) – Length of the input window in seconds.
sfreq (float) – Sampling frequency of the EEG recordings.
encoder_channels (
int) – Width of the strided stem convolutions and of the TDS stages. The default is256.feature_dim (
int) – Width the encoder projects to before the decoder. The default is64.hidden_size (
int) – LSTM hidden width. The default is512.lstm_layers (
int) – Depth of the decoder LSTM, whendecoder='lstm'. The default is2.decoder (
str) – Rollout decoder.'lstm'(the default) is the recurrent decoder thevemg2posecheckpoints use.'mlp'is the stateless normalised MLP behind emg2pose’s ownemg2posebaseline; both are state-conditioned on the previous pose either way.decoder_hidden_sizes (
tuple[int,...]) – Hidden widths of the MLP decoder, unused whendecoder='lstm'. The default is(512, 512).stem_kernel_sizes (
tuple[int,...]) – Kernel of each strided stem convolution. The default is(11, 5).stem_strides (
tuple[int,...]) – Stride of each stem convolution. The default is(5, 2).tds_subsample_kernels (
tuple[int,...]) – Subsampling kernel of each TDS stage. The default is(17, 9).tds_subsample_strides (
tuple[int,...]) – Subsampling stride of each TDS stage. The default is(4, 2).tds_kernel_widths (
tuple[int,...]) – Temporal kernel inside each stage’s TDS blocks, which must be odd. The default is(9, 5).tds_blocks (
int) – Conv/feed-forward block pairs per TDS stage. The default is2.tds_channels (
int) – Channel count of the TDS factorisation; the width isencoder_channels // tds_channels. The default is16.rollout_rate (
float) – Rate in Hz at which the decoder is unrolled. The default is50.0.num_position_steps (
int) – Input samples for which the decoder’s position output is used directly, before it switches to integrating velocity. The default is500(250 ms at 2 kHz).parameterization (
str) – How the decoder output becomes a pose.'hybrid'(the default, and whatregression_vemg2pose.ckptwas trained as) emits a position and a velocity per joint, taking the position fornum_position_stepsand integrating the velocity thereafter.'velocity'integrates a single velocity fromy0, which is thetracking_vemg2pose.ckptsetting.'position'reads the pose out directly.output_scalar (
float) – Fixed multiplier on the decoder output. The default is0.01.activation (
type[Module]) – Activation before the output projection. The default isnn.LeakyReLU.drop_prob (
float) – Dropout inside the convolution blocks. The default is0.0.
- Raises:
ValueError – If some input signal-related parameters are not specified: and can not be inferred.
Notes
If some input signal-related parameters are not specified, there will be an attempt to infer them from the other parameters.
References
[salter2024]Salter, Warren, Schlager, Spurr, Han, Bhasin, Rohin, et al. (2024). emg2pose: A Large and Diverse Benchmark for Surface Electromyographic Hand Pose Estimation. NeurIPS Datasets and Benchmarks. arXiv:2412.02725.
[hannun2019]Hannun et al. (2019). Sequence Modeling with Time-Depth Separable Convolutions. arXiv:1904.01619.
Hugging Face Hub integration
When the optional
huggingface_hubpackage is installed, all models automatically gain the ability to be pushed to and loaded from the Hugging Face Hub. Install with:pip install braindecode[hub]
Pushing a model to the Hub:
from braindecode.models import VEMG2Pose # Train your model model = VEMG2Pose(n_chans=22, n_outputs=4, n_times=1000) # ... training code ... # Push to the Hub; ``datasets`` and ``base_model`` go to the card model.push_to_hub( repo_id="username/my-vemg2pose-model", commit_message="Initial model upload", model_card_kwargs={"datasets": ["BNCI2014_001"]}, )
Loading a model from the Hub:
from braindecode.models import VEMG2Pose # Load pretrained model model = VEMG2Pose.from_pretrained("username/my-vemg2pose-model") # Data the weights were trained on, from the Hub model card print(model.card_data.datasets if model.card_data else None) # Load with a different number of outputs (head is rebuilt automatically) model = VEMG2Pose.from_pretrained("username/my-vemg2pose-model", n_outputs=4)
Extracting features and replacing the head:
import torch x = torch.randn(1, model.n_chans, model.n_times) # Extract encoder features (consistent dict across all models) out = model(x, return_features=True) features = out["features"] # Replace the classification head model.reset_head(n_outputs=10)
Saving and restoring full configuration:
import json config = model.get_config() # all __init__ params with open("config.json", "w") as f: json.dump(config, f) model2 = VEMG2Pose.from_config(config) # reconstruct (no weights)
All model parameters (both EEG-specific and model-specific such as dropout rates, activation functions, number of filters) are automatically saved to the Hub and restored when loading.
See Loading and Adapting Pretrained Foundation Models for a complete tutorial.
Methods
- reset_head(n_outputs)[source]#
Swap the output layer for a new output dimensionality.
The decoded pose feeds back as decoder input, so changing
n_outputsalso rebuilds the recurrent stack; its weights are re-initialised.Examples
>>> from braindecode.models import BENDR >>> model = BENDR(n_chans=22, n_times=1000, n_outputs=4) >>> model.reset_head(10) >>> model.n_outputs 10
Added in version 1.4.