OSCR

A Supervised Contrastive Variational Autoencoder with Probabilistic Latent Alignment for Cross-Domain EEG Emotion Recognition.

Code ↔ Paper

1 match between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 1 match
  1. [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

  1. import numpy as np
  2. import torch
  3. import os
  4. from torch import nn
  5. import scipy
  6. from torch.utils.data import Dataset, DataLoader
  7. import random
  8. from scipy.ndimage import zoom
  9. import bisect
  10. import torch.nn.functional as F
  11. import torch.optim as optim
  12. from torch.optim import RAdam
  13. from itertools import cycle
  14. # from pre_model import *
  15. # from pre_train import *
  16. from torch.nn import init
  17. from metrics import *
  18. from utils import *
  19. # 设置随机数种子
  20. def seed_torch(seed=12):
  21. random.seed(seed)
  22. np.random.seed(seed)
  23. torch.manual_seed(seed)
  24. torch.cuda.manual_seed(seed)
  25. torch.cuda.manual_seed_all(seed)
  26. torch.backends.cudnn.benchmark = False # 关闭自动选择最快算法的选项
  27. torch.backends.cudnn.deterministic = True # 让 CuDNN 以确定性的方式执行
  28. seed_torch()
  29. from data_input import getloader, SEED_dataset_train
  30. # 整个训练脚本产生的结果都将存入这个脚本中,模型,log等等
  31. def cre_prolog():
  32. # 获取当前脚本的绝对路径
  33. current_script_path = os.path.abspath(__file__)
  34. # 获取当前脚本的所在目录
  35. parent_dir = os.path.dirname(current_script_path)
  36. # 定义要创建的Pro_log目录的路径
  37. pro_log_dir_path = os.path.join(parent_dir, 'Pro_log')
  38. # 检查Pro_log目录是否存在,如果不存在则创建
  39. if not os.path.exists(pro_log_dir_path):
  40. os.makedirs(pro_log_dir_path)
  41. print(f"Directory '{pro_log_dir_path}' created.")
  42. return pro_log_dir_path
  43. data_dir = "../../../datasets/SEED/seed_4s/"
  44. device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
  45. print(device)
  46. exp_dir = cre_prolog()
  47. log_file = f"{exp_dir}/log.txt"
  48. # 重构损失
  49. def reconstruction_loss(x, x_recon):
  50. return F.l1_loss(x_recon, x)
  51. # 有监督对比损失
  52. class SupConLoss(nn.Module):
  53. """Supervised Contrastive Learning: https://arxiv.org/pdf/2004.11362.pdf.
  54. It also supports the unsupervised contrastive loss in SimCLR"""
  55. def __init__(self, temperature=0.07, contrast_mode='all',
  56. base_temperature=0.07):
  57. super(SupConLoss, self).__init__()
  58. self.temperature = temperature
  59. self.contrast_mode = contrast_mode
  60. self.base_temperature = base_temperature
  61. def forward(self, features, labels=None, mask=None):
  62. device = (torch.device('cuda')
  63. if features.is_cuda
  64. else torch.device('cpu'))
  65. if len(features.shape) < 3:
  66. raise ValueError('`features` needs to be [bsz, n_views, ...],'
  67. 'at least 3 dimensions are required')
  68. if len(features.shape) > 3:
  69. features = features.view(features.shape[0], features.shape[1], -1)
  70. batch_size = features.shape[0]
  71. if labels is not None and mask is not None:
  72. raise ValueError('Cannot define both `labels` and `mask`')
  73. elif labels is None and mask is None:
  74. mask = torch.eye(batch_size, dtype=torch.float32).to(device)
  75. elif labels is not None:
  76. labels = labels.contiguous().view(-1, 1)
  77. if labels.shape[0] != batch_size:
  78. raise ValueError('Num of labels does not match num of features')
  79. mask = torch.eq(labels, labels.T).float().to(device)
  80. else:
  81. mask = mask.float().to(device)
  82. contrast_count = features.shape[1]
  83. contrast_feature = torch.cat(torch.unbind(features, dim=1), dim=0)
  84. if self.contrast_mode == 'one':
  85. anchor_feature = features[:, 0]
  86. anchor_count = 1
  87. elif self.contrast_mode == 'all':
  88. anchor_feature = contrast_feature
  89. anchor_count = contrast_count
  90. else:
  91. raise ValueError('Unknown mode: {}'.format(self.contrast_mode))
  92. # compute logits
  93. anchor_dot_contrast = torch.div(
  94. torch.matmul(anchor_feature, contrast_feature.T),
  95. self.temperature)
  96. # for numerical stability
  97. logits_max, _ = torch.max(anchor_dot_contrast, dim=1, keepdim=True)
  98. logits = anchor_dot_contrast - logits_max.detach()
  99. # tile mask
  100. mask = mask.repeat(anchor_count, contrast_count)
  101. # mask-out self-contrast cases
  102. logits_mask = torch.scatter(
  103. torch.ones_like(mask),
  104. 1,
  105. torch.arange(batch_size * anchor_count).view(-1, 1).to(device),
  106. 0
  107. )
  108. mask = mask * logits_mask
  109. # compute log_prob
  110. exp_logits = torch.exp(logits) * logits_mask
  111. log_prob = logits - torch.log(exp_logits.sum(1, keepdim=True))
  112. mask_pos_pairs = mask.sum(1)
  113. mask_pos_pairs = torch.where(mask_pos_pairs < 1e-6, 1, mask_pos_pairs)
  114. mean_log_prob_pos = (mask * log_prob).sum(1) / mask_pos_pairs
  115. # loss
  116. loss = - (self.temperature / self.base_temperature) * mean_log_prob_pos
  117. loss = loss.view(anchor_count, batch_size).mean()
  118. return loss
  119. class SE_Block(nn.Module):
  120. """Squeeze-and-Excitation 通道注意力模块"""
  121. def __init__(self, channels, reduction=8):
  122. super().__init__()
  123. self.avg_pool = nn.AdaptiveAvgPool1d(1)
  124. self.fc = nn.Sequential(
  125. nn.Linear(channels, channels // reduction, bias=False),
  126. nn.ReLU(inplace=True),
  127. nn.Linear(channels // reduction, channels, bias=False),
  128. nn.Sigmoid()
  129. )
  130. def forward(self, x):
  131. B, C, T = x.size()
  132. y = self.avg_pool(x).view(B, C)
  133. y = self.fc(y).view(B, C, 1)
  134. return x * y.expand_as(x)
  135. # VAE
  136. class autocoder(nn.Module):
  137. """
  138. True VAE:
  139. Encoder: Conv → SE → Conv → flatten → μ & logσ²
  140. Decoder: Linear → reshape → Conv
  141. """
  142. def __init__(self, in_channels=62, latent_dim=512): # ★ 从 256 改为 512
  143. super().__init__()
  144. self.in_channels = in_channels
  145. self.time_len = 5 # [B, 62, 5]
  146. self.latent_dim = latent_dim
  147. # -------- Encoder --------
  148. self.conv1 = nn.Conv1d(in_channels, 128, kernel_size=1)#[B,128,5]
  149. self.se1 = SE_Block(128, reduction=8) #[B,128,5]
  150. self.conv2 = nn.Conv1d(128, 128, kernel_size=1) #[B,128,5]
  151. self.relu = nn.ReLU(inplace=True)
  152. encoder_out_dim = 128 * self.time_len # 640
  153. self.fc_mu = nn.Linear(encoder_out_dim, latent_dim) ##[B,512]
  154. self.fc_logvar = nn.Linear(encoder_out_dim, latent_dim) ##[B,512]
  155. # -------- Decoder --------
  156. self.fc_decoder = nn.Linear(latent_dim, encoder_out_dim) #[B,640]
  157. self.decoder = nn.Conv1d(128, in_channels, kernel_size=1)
  158. def reparameterize(self, mu, logvar):
  159. std = torch.exp(0.5 * logvar)
  160. eps = torch.randn_like(std)
  161. return mu + eps * std # [B, latent_dim]
  162. def forward(self, x):
  163. """
  164. x: [B, 62, 5]
  165. return:
  166. z: latent vector for cls/MMD
  167. x_rec: reconstructed output
  168. mu, logvar: VAE parameters
  169. """
  170. B = x.size(0)
  171. # ----------- Encoder ------------
  172. h = self.relu(self.conv1(x)) # [B,128,5]
  173. h = h + self.se1(h)
  174. h = self.relu(self.conv2(h)) # [B,128,5]
  175. h_flat = h.view(B, -1) # [B, 128*5 = 640]
  176. mu = self.fc_mu(h_flat) # [B, 512]
  177. logvar = self.fc_logvar(h_flat) # [B, 512]
  178. z = self.reparameterize(mu, logvar) # [B, 512]
  179. # ----------- Decoder ------------
  180. dec_h = self.fc_decoder(z) # [B, 128*5]
  181. dec_h = dec_h.view(B, 128, self.time_len)
  182. x_rec = self.decoder(dec_h) # [B,62,5]
  183. return z, x_rec, mu, logvar
  184. # 迁移自动编码器+分类(去掉对比loss,MMD改为目标域vs每个源域)
  185. class model(nn.Module):
  186. def __init__(self, feature_dim=5, hidden_dim=5, source_num=14, classs_num=3):
  187. super(model, self).__init__()
  188. self.autocoder = autocoder(62, 256)
  189. self.supcon = SupConLoss()
  190. self.mmd_loss = MMDLoss(kernel_type='rbf',
  191. kernel_mul=2.0,
  192. kernel_num=5,
  193. fix_sigma=None)
  194. self.feature_extractor1 = nn.Sequential(
  195. nn.Linear(256, 128), # 第一层线性层
  196. # nn.BatchNorm1d(256),
  197. nn.ReLU(inplace=True)
  198. )
  199. self.feature_extractor2 = nn.Sequential(
  200. nn.Linear(256, 128), # 第一层线性层
  201. # nn.BatchNorm1d(256),
  202. nn.ReLU(inplace=True)
  203. )
  204. # 分类器
  205. self.classifier = nn.Sequential(
  206. nn.Linear(256, 256),
  207. nn.BatchNorm1d(256),
  208. nn.ReLU(inplace=True),
  209. nn.Dropout(p=0.5),
  210. nn.Linear(256, classs_num)
  211. )
  212. #分类预测准确率
  213. def predict_class(self, x):
  214. z,x_rec,mu,logvar= self.autocoder(x) # z: [B,256,5]
  215. logits = self.classifier(mu)
  216. return logits
  217. def extract_feature(self, x):
  218. """
  219. 用于 t-SNE / 测试阶段
  220. """
  221. z, x_rec, mu, logvar = self.autocoder(x)
  222. return mu # 或者 return torch.cat([mu, torch.exp(0.5 * logvar)], dim=1)
  223. def forward(self, data_source, data_target, label_source=None):
  224. if self.training:
  225. z_s,x_s,mu_s,logvar_s = self.autocoder(data_source) # [B,256,5]
  226. z_t,x_t,mu_t,logvar_t = self.autocoder(data_target) # [B,256,5]
  227. # ------------- Reconstruction Loss -------------
  228. reconloss = reconstruction_loss(data_source, x_s) + reconstruction_loss(data_target, x_t)
  229. # ------------- KL Loss -------------
  230. kl_s = 0.5 * torch.mean(torch.sum(mu_s.pow(2) + logvar_s.exp() - logvar_s - 1, dim=1))
  231. kl_t = 0.5 * torch.mean(torch.sum(mu_t.pow(2) + logvar_t.exp() - logvar_t - 1, dim=1))
  232. klloss = kl_s + kl_t
  233. # -------------分类Loss -------------
  234. logits = self.classifier(mu_s)
  235. classloss = F.cross_entropy(logits, label_source)
  236. # -------------mmd Loss -------------
  237. mmdloss = self.mmd_loss(mu_s, mu_t)
  238. # -------------多视图对比Loss -------------
  239. mu1_s = self.feature_extractor1(mu_s)
  240. mu2_s = self.feature_extractor2(mu_s)
  241. features = torch.stack([mu1_s, mu2_s], dim=1)
  242. contrastloss = self.supcon(features, labels=label_source)
  243. return reconloss,klloss,mmdloss,contrastloss,classloss,logits
  244. #测试 / 推理阶段
  245. else:
  246. z_t,x_t,mu_t,logvar_t = self.autocoder(data_target) # [B,256,5]
  247. logits = self.classifier(mu_t)
  248. return logits

Main_model.py at commit 2877117, no license · at the source

Overview

Authors: Linna Wu1, Yong Yang2, Wenhao Wang1, Yuanlun Xie1, Nan Zhou1, Kaibo Shi1
  1. School of Electronic Information and Electrical Engineering, Chengdu University, Chengdu 610106, China; (L.W.); (Y.X.); (N.Z.); (K.S.)
  2. College of Computer Science, Chengdu University, Chengdu 610106, China
Institutions: Chengdu University (China)
Journal: Sensors (Basel, Switzerland), volume 26, issue 10, article 3217
Dates: received 9 April 2026; accepted 14 May 2026; published online 19 May 2026
Type: Research article · Language: English
License: CC BY
Identifiers: DOI 10.3390/s26103217 · PMID 42198025 · PMCID PMC13210999 · OpenAlex W7161643768
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: EEG (modality), human (organism), cognitive (subfield)
Methods: Spectral & time-frequency, Preprocessing, Connectivity, Statistics, Machine learning
Keywords: EEG-based emotion recognition, domain adaption, variational autoencoder, multi-view supervised contrastive learning
MeSH: Electroencephalography*, Emotions*, Algorithms, Autoencoder, Humans, Signal Processing, Computer-Assisted (* major topic)
Topic: Emotion and Mood Recognition (Experimental and Cognitive Psychology, Psychology), according to OpenAlex
Funding: Science and Technology Department of Sichuan Province (No. 2024NSFSC2056)
Citations: not cited yet (Europe PMC); 48 references in the paper

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%/96.84% on SEED and 74.94%/79.44% on SEED-IV, respectively. These experimental results demonstrate the effectiveness of the proposed method in cross-domain EEG emotion recognition.

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

License: none: the authors keep all their rights
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 28771172757e3c93d81ee38ab04bb0492ebdf1f9, 8 June 2026
Languages: Python (20)
Size: 170 files, 20 scripts
Software Heritage: not archived
Found in: the text, “3.6.2. Implementation Details”
Holds: README
Not found: license file, CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (20 files), PyTorch (20 files), SciPy (12 files)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
21 files

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://bcmi.sjtu.edu.cn/home/seed/seed.html (accessed on 8 May 2015), and the SEED-IV dataset is available at https://bcmi.sjtu.edu.cn/home/seed/seed-iv.html (accessed on 8 February 2018).

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://doi.org/10.3390/s26103217

BibTeX

@article{wu2026supervised,
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/s26103217},
url = {https://doi.org/10.3390/s26103217},
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/05/19
VL - 26
IS - 10
SP - 3217
SN - 1424-8220
PB - Multidisciplinary Digital Publishing Institute (MDPI)
DO - 10.3390/s26103217
UR - https://doi.org/10.3390/s26103217
LA - en
ER -

CSL-JSON

{
"id": "10.3390/s26103217",
"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": "Sensors (Basel)",
"volume": "26",
"issue": "10",
"page": "3217",
"DOI": "10.3390/s26103217",
"PMID": "42198025",
"PMCID": "PMC13210999",
"ISSN": "1424-8220",
"publisher": "Multidisciplinary Digital Publishing Institute (MDPI)",
"URL": "https://doi.org/10.3390/s26103217",
"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: Biosensors
In 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 neuroscience
In 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 one
In 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 mapping
In 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 psychology
In 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 systems
In 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 data
In 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 reports
In 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.

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.