A Supervised Contrastive Variational Autoencoder with Probabilistic Latent Alignment for Cross-Domain EEG Emotion Recognition.
The 1 match
- [1] § 3. Method › 3.5. All Loss Functions ↔ Main_model.py, lines 233–321 · score 0.58 · cross entropy, reconstruction loss, classifier, MMD
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 322 lines · 11 KB · no license · 1 match
- import numpy as np
- import torch
- import os
- from torch import nn
- import scipy
- from torch.utils.data import Dataset, DataLoader
- import random
- from scipy.ndimage import zoom
- import bisect
- import torch.nn.functional as F
- import torch.optim as optim
- from torch.optim import RAdam
- from itertools import cycle
- # from pre_model import *
- # from pre_train import *
- from torch.nn import init
- from metrics import *
- from utils import *
- # 设置随机数种子
- def seed_torch(seed=12):
- random.seed(seed)
- np.random.seed(seed)
- torch.manual_seed(seed)
- torch.cuda.manual_seed(seed)
- torch.cuda.manual_seed_all(seed)
- torch.backends.cudnn.benchmark = False # 关闭自动选择最快算法的选项
- torch.backends.cudnn.deterministic = True # 让 CuDNN 以确定性的方式执行
- seed_torch()
- from data_input import getloader, SEED_dataset_train
- # 整个训练脚本产生的结果都将存入这个脚本中,模型,log等等
- def cre_prolog():
- # 获取当前脚本的绝对路径
- current_script_path = os.path.abspath(__file__)
- # 获取当前脚本的所在目录
- parent_dir = os.path.dirname(current_script_path)
- # 定义要创建的Pro_log目录的路径
- pro_log_dir_path = os.path.join(parent_dir, 'Pro_log')
- # 检查Pro_log目录是否存在,如果不存在则创建
- if not os.path.exists(pro_log_dir_path):
- os.makedirs(pro_log_dir_path)
- print(f"Directory '{pro_log_dir_path}' created.")
- return pro_log_dir_path
- data_dir = "../../../datasets/SEED/seed_4s/"
- device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
- print(device)
- exp_dir = cre_prolog()
- log_file = f"{exp_dir}/log.txt"
- # 重构损失
- def reconstruction_loss(x, x_recon):
- return F.l1_loss(x_recon, x)
- # 有监督对比损失
- class SupConLoss(nn.Module):
- """Supervised Contrastive Learning: https://arxiv.org/pdf/2004.11362.pdf.
- It also supports the unsupervised contrastive loss in SimCLR"""
- def __init__(self, temperature=0.07, contrast_mode='all',
- base_temperature=0.07):
- super(SupConLoss, self).__init__()
- self.temperature = temperature
- self.contrast_mode = contrast_mode
- self.base_temperature = base_temperature
- def forward(self, features, labels=None, mask=None):
- device = (torch.device('cuda')
- if features.is_cuda
- else torch.device('cpu'))
- if len(features.shape) < 3:
- raise ValueError('`features` needs to be [bsz, n_views, ...],'
- 'at least 3 dimensions are required')
- if len(features.shape) > 3:
- features = features.view(features.shape[0], features.shape[1], -1)
- batch_size = features.shape[0]
- if labels is not None and mask is not None:
- raise ValueError('Cannot define both `labels` and `mask`')
- elif labels is None and mask is None:
- mask = torch.eye(batch_size, dtype=torch.float32).to(device)
- elif labels is not None:
- labels = labels.contiguous().view(-1, 1)
- if labels.shape[0] != batch_size:
- raise ValueError('Num of labels does not match num of features')
- mask = torch.eq(labels, labels.T).float().to(device)
- else:
- mask = mask.float().to(device)
- contrast_count = features.shape[1]
- contrast_feature = torch.cat(torch.unbind(features, dim=1), dim=0)
- if self.contrast_mode == 'one':
- anchor_feature = features[:, 0]
- anchor_count = 1
- elif self.contrast_mode == 'all':
- anchor_feature = contrast_feature
- anchor_count = contrast_count
- else:
- raise ValueError('Unknown mode: {}'.format(self.contrast_mode))
- # compute logits
- anchor_dot_contrast = torch.div(
- torch.matmul(anchor_feature, contrast_feature.T),
- self.temperature)
- # for numerical stability
- logits_max, _ = torch.max(anchor_dot_contrast, dim=1, keepdim=True)
- logits = anchor_dot_contrast - logits_max.detach()
- # tile mask
- mask = mask.repeat(anchor_count, contrast_count)
- # mask-out self-contrast cases
- logits_mask = torch.scatter(
- torch.ones_like(mask),
- 1,
- torch.arange(batch_size * anchor_count).view(-1, 1).to(device),
- 0
- )
- mask = mask * logits_mask
- # compute log_prob
- exp_logits = torch.exp(logits) * logits_mask
- log_prob = logits - torch.log(exp_logits.sum(1, keepdim=True))
- mask_pos_pairs = mask.sum(1)
- mask_pos_pairs = torch.where(mask_pos_pairs < 1e-6, 1, mask_pos_pairs)
- mean_log_prob_pos = (mask * log_prob).sum(1) / mask_pos_pairs
- # loss
- loss = - (self.temperature / self.base_temperature) * mean_log_prob_pos
- loss = loss.view(anchor_count, batch_size).mean()
- return loss
- class SE_Block(nn.Module):
- """Squeeze-and-Excitation 通道注意力模块"""
- def __init__(self, channels, reduction=8):
- super().__init__()
- self.avg_pool = nn.AdaptiveAvgPool1d(1)
- self.fc = nn.Sequential(
- nn.Linear(channels, channels // reduction, bias=False),
- nn.ReLU(inplace=True),
- nn.Linear(channels // reduction, channels, bias=False),
- nn.Sigmoid()
- )
- def forward(self, x):
- B, C, T = x.size()
- y = self.avg_pool(x).view(B, C)
- y = self.fc(y).view(B, C, 1)
- return x * y.expand_as(x)
- # VAE
- class autocoder(nn.Module):
- """
- True VAE:
- Encoder: Conv → SE → Conv → flatten → μ & logσ²
- Decoder: Linear → reshape → Conv
- """
- def __init__(self, in_channels=62, latent_dim=512): # ★ 从 256 改为 512
- super().__init__()
- self.in_channels = in_channels
- self.time_len = 5 # [B, 62, 5]
- self.latent_dim = latent_dim
- # -------- Encoder --------
- self.conv1 = nn.Conv1d(in_channels, 128, kernel_size=1)#[B,128,5]
- self.se1 = SE_Block(128, reduction=8) #[B,128,5]
- self.conv2 = nn.Conv1d(128, 128, kernel_size=1) #[B,128,5]
- self.relu = nn.ReLU(inplace=True)
- encoder_out_dim = 128 * self.time_len # 640
- self.fc_mu = nn.Linear(encoder_out_dim, latent_dim) ##[B,512]
- self.fc_logvar = nn.Linear(encoder_out_dim, latent_dim) ##[B,512]
- # -------- Decoder --------
- self.fc_decoder = nn.Linear(latent_dim, encoder_out_dim) #[B,640]
- self.decoder = nn.Conv1d(128, in_channels, kernel_size=1)
- def reparameterize(self, mu, logvar):
- std = torch.exp(0.5 * logvar)
- eps = torch.randn_like(std)
- return mu + eps * std # [B, latent_dim]
- def forward(self, x):
- """
- x: [B, 62, 5]
- return:
- z: latent vector for cls/MMD
- x_rec: reconstructed output
- mu, logvar: VAE parameters
- """
- B = x.size(0)
- # ----------- Encoder ------------
- h = self.relu(self.conv1(x)) # [B,128,5]
- h = h + self.se1(h)
- h = self.relu(self.conv2(h)) # [B,128,5]
- h_flat = h.view(B, -1) # [B, 128*5 = 640]
- mu = self.fc_mu(h_flat) # [B, 512]
- logvar = self.fc_logvar(h_flat) # [B, 512]
- z = self.reparameterize(mu, logvar) # [B, 512]
- # ----------- Decoder ------------
- dec_h = self.fc_decoder(z) # [B, 128*5]
- dec_h = dec_h.view(B, 128, self.time_len)
- x_rec = self.decoder(dec_h) # [B,62,5]
- return z, x_rec, mu, logvar
- # 迁移自动编码器+分类(去掉对比loss,MMD改为目标域vs每个源域)
- class model(nn.Module):
- def __init__(self, feature_dim=5, hidden_dim=5, source_num=14, classs_num=3):
- super(model, self).__init__()
- self.autocoder = autocoder(62, 256)
- self.supcon = SupConLoss()
- self.mmd_loss = MMDLoss(kernel_type='rbf',
- kernel_mul=2.0,
- kernel_num=5,
- fix_sigma=None)
- self.feature_extractor1 = nn.Sequential(
- nn.Linear(256, 128), # 第一层线性层
- # nn.BatchNorm1d(256),
- nn.ReLU(inplace=True)
- )
- self.feature_extractor2 = nn.Sequential(
- nn.Linear(256, 128), # 第一层线性层
- # nn.BatchNorm1d(256),
- nn.ReLU(inplace=True)
- )
- # 分类器
- self.classifier = nn.Sequential(
- nn.Linear(256, 256),
- nn.BatchNorm1d(256),
- nn.ReLU(inplace=True),
- nn.Dropout(p=0.5),
- nn.Linear(256, classs_num)
- )
- #分类预测准确率
- def predict_class(self, x):
- z,x_rec,mu,logvar= self.autocoder(x) # z: [B,256,5]
- logits = self.classifier(mu)
- return logits
- def extract_feature(self, x):
- """
- 用于 t-SNE / 测试阶段
- """
- z, x_rec, mu, logvar = self.autocoder(x)
- return mu # 或者 return torch.cat([mu, torch.exp(0.5 * logvar)], dim=1)
- def forward(self, data_source, data_target, label_source=None):
- if self.training:
- z_s,x_s,mu_s,logvar_s = self.autocoder(data_source) # [B,256,5]
- z_t,x_t,mu_t,logvar_t = self.autocoder(data_target) # [B,256,5]
- # ------------- Reconstruction Loss -------------
- reconloss = reconstruction_loss(data_source, x_s) + reconstruction_loss(data_target, x_t)
- # ------------- KL Loss -------------
- kl_s = 0.5 * torch.mean(torch.sum(mu_s.pow(2) + logvar_s.exp() - logvar_s - 1, dim=1))
- kl_t = 0.5 * torch.mean(torch.sum(mu_t.pow(2) + logvar_t.exp() - logvar_t - 1, dim=1))
- klloss = kl_s + kl_t
- # -------------分类Loss -------------
- logits = self.classifier(mu_s)
- classloss = F.cross_entropy(logits, label_source)
- # -------------mmd Loss -------------
- mmdloss = self.mmd_loss(mu_s, mu_t)
- # -------------多视图对比Loss -------------
- mu1_s = self.feature_extractor1(mu_s)
- mu2_s = self.feature_extractor2(mu_s)
- features = torch.stack([mu1_s, mu2_s], dim=1)
- contrastloss = self.supcon(features, labels=label_source)
- return reconloss,klloss,mmdloss,contrastloss,classloss,logits
- #测试 / 推理阶段
- else:
- z_t,x_t,mu_t,logvar_t = self.autocoder(data_target) # [B,256,5]
- logits = self.classifier(mu_t)
- return logits
Main_model.py at commit 2877117, no license · at the source
Overview
- School of Electronic Information and Electrical Engineering, Chengdu University, Chengdu 610106, China; (L.W.); (Y.X.); (N.Z.); (K.S.)
- College of Computer Science, Chengdu University, Chengdu 610106, China
Abstract
Cross-domain emotion recognition based on electroencephalogram (EEG) is a challenging task, as EEG signals collected from different subjects or at different moments exhibit significant differences in distribution. How to enable deep learning model to learn the common feature space and reduce the distribution differences between the source and target domains is an important research direction. For this problem, we propose a Supervised Contrastive Variational AutoEncoder Network (SCVAE-Net), which possesses enhanced abilities for extracting consistent features across source and target domains, thereby improving cross-domain EEG emotion recognition performance. Specifically, this method utilizes the reconstruction mechanism and latent space probabilization of VAE to obtain intermediate features that are more consistent and transferable. Furthermore, the maximum mean discrepancy loss is employed to further reduce the distribution discrepancy of these features. To alleviate the degradation of discriminative ability during domain alignment, we introduce multi-view supervised contrastive learning in multi-source domains to enhance the intra-class consistency and inter-class separability of latent features. Under the cross-subject and cross-session settings, SCVAE-Net achieves accuracies of 95.01%/
Reproduced under the paper's license (CC BY), from the paper cited above.
Repository
Its files are read in the Code ↔ Paper reader above, with 1 match between paragraphs and lines of code.
braverSheep/SCVAE-Net
28771172757e3c93d81ee38ab04bb0492ebdf1f9, 8 June 2026Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
21 files
- Main_model.py, Python, 322 lines, 1 match
- Main_train.py, Python, 197 lines
- cross-subject--SEED--95.
00/ , Python, 322 linessession1--0.9631/ Main_model.py - cross-subject--SEED--95.
00/ , Python, 197 linessession1--0.9631/ Main_train.py - cross-subject--SEED--95.
00/ , Python, 93 linessession1--0.9631/ data_gen_seed.py - cross-subject--SEED--95.
00/ , Python, 196 linessession1--0.9631/ data_input.py - cross-subject--SEED--95.
00/ , Python, 145 linessession1--0.9631/ utils.py - cross-subject--SEED--95.
00/ , Python, 322 linessession2--0.9350/ Main_model.py - cross-subject--SEED--95.
00/ , Python, 197 linessession2--0.9350/ Main_train.py - cross-subject--SEED--95.
00/ , Python, 93 linessession2--0.9350/ data_gen_seed.py - cross-subject--SEED--95.
00/ , Python, 196 linessession2--0.9350/ data_input.py - cross-subject--SEED--95.
00/ , Python, 145 linessession2--0.9350/ utils.py - cross-subject--SEED--95.
00/ , Python, 322 linessession3--0.9520/ Main_model.py - cross-subject--SEED--95.
00/ , Python, 197 linessession3--0.9520/ Main_train.py - cross-subject--SEED--95.
00/ , Python, 93 linessession3--0.9520/ data_gen_seed.py - cross-subject--SEED--95.
00/ , Python, 196 linessession3--0.9520/ data_input.py - cross-subject--SEED--95.
00/ , Python, 145 linessession3--0.9520/ utils.py - data_gen_seed.py, Python, 93 lines
- data_input.py, Python, 143 lines
- utils.py, Python, 145 lines
- README.md, Text, 2 lines
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 1 repository of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 20 scripts, each with its path and the digest of its content;
- 1 match between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Data Availability Statement
This study uses publicly available, established datasets (SEED, SEED-IV). The SEED dataset is available at https://
Reproduced under the paper's license (CC BY), from the paper cited above.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 6 authors, 4 keywords, 6 MeSH terms, 1 funder, 42 references.
Cite
This paper
Wu, L., Yang, Y., Wang, W., Xie, Y., Zhou, N., & Shi, K. (2026). A Supervised Contrastive Variational Autoencoder with Probabilistic Latent Alignment for Cross-Domain EEG Emotion Recognition. Sensors (Basel, Switzerland), 26(10), 3217. https://
BibTeX
@article{wu2026supervise
author = {Wu, Linna and Yang, Yong and Wang, Wenhao and Xie, Yuanlun and Zhou, Nan and Shi, Kaibo},
title = {{A Supervised Contrastive Variational Autoencoder with Probabilistic Latent Alignment for Cross-Domain EEG Emotion Recognition}},
journal = {Sensors (Basel, Switzerland)},
year = {2026},
month = may,
volume = {26},
number = {10},
pages = {3217},
publisher = {Multidisciplinary Digital Publishing Institute (MDPI)},
issn = {1424-8220},
doi = {10.3390/
url = {https://
pmid = {42198025},
pmcid = {PMC13210999}
}
RIS
TY - JOUR
AU - Wu, Linna
AU - Yang, Yong
AU - Wang, Wenhao
AU - Xie, Yuanlun
AU - Zhou, Nan
AU - Shi, Kaibo
TI - A Supervised Contrastive Variational Autoencoder with Probabilistic Latent Alignment for Cross-Domain EEG Emotion Recognition
T2 - Sensors (Basel, Switzerland)
J2 - Sensors (Basel)
PY - 2026
DA - 2026/
VL - 26
IS - 10
SP - 3217
SN - 1424-8220
PB - Multidisciplinary Digital Publishing Institute (MDPI)
DO - 10.3390/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.3390/
"type": "article-journal",
"title": "A Supervised Contrastive Variational Autoencoder with Probabilistic Latent Alignment for Cross-Domain EEG Emotion Recognition",
"container-title": "Sensors (Basel, Switzerland)",
"author": [
{
"family": "Wu",
"given": "Linna"
},
{
"family": "Yang",
"given": "Yong"
},
{
"family": "Wang",
"given": "Wenhao"
},
{
"family": "Xie",
"given": "Yuanlun"
},
{
"family": "Zhou",
"given": "Nan"
},
{
"family": "Shi",
"given": "Kaibo"
}
],
"container-title-short":
"volume": "26",
"issue": "10",
"page": "3217",
"DOI": "10.3390/
"PMID": "42198025",
"PMCID": "PMC13210999",
"ISSN": "1424-8220",
"publisher": "Multidisciplinary Digital Publishing Institute (MDPI)",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
19
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.3390/bios16080400
- EEG and ECG Wearable Biosensor-Based Affective State Analysis Using Deep Learning over 6G IoT Healthcare Networks.Journal: BiosensorsIn common: EEG, 4 references
- [2] doi:10.3389/fnins.2026.1810609
- A dual-branch network with brain region-constrained attention for EEG emotion recognition.Journal: Frontiers in neuroscienceIn common: EEG, cognitive, 4 references
- [3] doi:10.1371/journal.pone.0347671 [code]
- RMETNet: A cross-subject motor imagery EEG signal classification model based on TSLANet and riemannian geometry features.Journal: PloS oneIn common: PyTorch, SciPy, NumPy, EEG, 2 references
- [4] doi:10.3390/s26175327 [code]
- Subject Identity Confounds qEEG Emotion Recognition on DEAP and DREAMER.Journal: Sensors (Basel, Switzerland)In common: SciPy, NumPy, EEG, cognitive, 2 references
- [5] doi:10.1002/hbm.70528 [code]
- Explainable AI Insights Into EEG Classification and Its Alignment to Neural Correlates.Journal: Human brain mappingIn common: PyTorch, SciPy, NumPy, EEG, cognitive, 1 reference
- [6] doi:10.3389/fpsyg.2026.1774068 [code]
- Analysis of cognitive mechanisms in phoneme perception and pronunciation errors among Korean language learners.Journal: Frontiers in psychologyIn common: PyTorch, SciPy, NumPy, EEG, cognitive, 1 reference
- [7] doi:10.1007/s10916-026-02374-5 [code]
- Attention-Enhanced U-Net for Sensor-Efficient High-Density EEG Reconstruction in Wearable Brain Monitoring Systems.Journal: Journal of medical systemsIn common: PyTorch, SciPy, NumPy, EEG, 1 reference
- [8] doi:10.3390/s26103065 [code]
- Subject-Wise Depression Screening from Eight-Channel Resting-State EEG Using Asymmetry-Aware Spectral Features and Connectivity Ablation.Journal: Sensors (Basel, Switzerland)In common: PyTorch, SciPy, NumPy, EEG, 1 reference
- [9] doi:10.1038/s41597-026-07244-w [code]
- An Open Non-Invasive EEG Dataset for Spontaneous Auditory Attention Switch Decoding.Journal: Scientific dataIn common: PyTorch, SciPy, NumPy, EEG, 1 reference
- [10] doi:10.1038/s41598-026-68186-2 [code]
- NeuroStream: spectral-spatio-temporal
deep learning for visual stimulus classification from EEG. Journal: Scientific reportsIn common: PyTorch, SciPy, NumPy, EEG, 1 reference
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 1 repository of the authors' code, each at its verified commit and with its license, 20 scripts, and 1 match between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:9419daf924e6945f…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
