| name | neuroscience-research-method |
| version | 1.0.0 |
| description | CNN + Adversarial Autoencoder (AAE) for EEG signal classification — from raw EEG to image representations, latent-space regularization, and robust brain-computer interface (BCI) decoding. |
| keywords | ["eeg-classification","adversarial-autoencoder","cnn-eeg","brain-computer-interface","spectrogram","topographic-map","latent-space-regularization","脑电图分类","对抗自编码器","脑机接口","深度学习","神经信号处理"] |
| related | ["signal-processing","time-series-analysis","computer-vision","generative-models","domain-adaptation","eeg-preprocessing"] |
CNN + Adversarial Autoencoder for EEG Classification
Overview
This skill covers the methodology for classifying electroencephalography (EEG) signals using a hybrid Convolutional Neural Network (CNN) + Adversarial Autoencoder (AAE) architecture. The approach transforms raw multi-channel EEG time-series into 2D image representations, encodes them with a CNN-based encoder, and regularizes the latent space via adversarial training to improve generalization and reduce overfitting — a persistent challenge in EEG/BCI research.
Why AAE for EEG?
| Challenge | AAE Solution |
|---|
| Small labeled datasets (10-100 subjects) | Adversarial regularization prevents degenerate latent codes |
| Non-stationary EEG signals | Distribution matching stabilizes feature representations |
| High inter-subject variability | Latent prior (Gaussian/mixture) enforces compact representations |
| Artifact contamination | AAE reconstruction loss acts as implicit denoiser |
1. EEG-to-Image Transformation Methods (EEG转图像变换方法)
1.1 Time-Frequency Spectrograms (时频图谱)
Convert each EEG channel into a spectrogram via Short-Time Fourier Transform (STFT) or Continuous Wavelet Transform (CWT).
import numpy as np
import scipy.signal as signal
import matplotlib.pyplot as plt
def eeg_to_spectrogram(eeg_data, fs=256, nperseg=64, noverlap=32):
"""
Transform multi-channel EEG into spectrogram images.
Args:
eeg_data: (n_channels, n_samples) ndarray
fs: sampling frequency in Hz
nperseg: window size for STFT
noverlap: overlap between windows
Returns:
spectrograms: (n_channels, freq_bins, time_bins) ndarray
"""
n_channels = eeg_data.shape[0]
freqs, times, Sxx = signal.spectrogram(
eeg_data[0], fs=fs, nperseg=nperseg, noverlap=noverlap
)
spectrograms = np.zeros((n_channels, len(freqs), len(times)))
for ch in range(n_channels):
_, _, Sxx = signal.spectrogram(
eeg_data[ch], fs=fs, nperseg=nperseg, noverlap=noverlap
)
spectrograms[ch] = np.log1p(np.abs(Sxx))
return spectrograms
def eeg_to_scalogram(eeg_data, fs=256, widths=None, wavelet='morl'):
"""CWT-based scalogram for better time-frequency resolution."""
import pywt
if widths is None:
widths = np.arange(1, 128)
n_channels = eeg_data.shape[0]
scalograms = []
for ch (n_channels):
coeffs = pywt.cwt(eeg_data[ch], widths, wavelet, sampling_period=/fs)
scalograms.append(np.log1p(np.(coeffs[])))
np.array(scalograms)
Stacking channels into a single image:
- (n_channels, freq_bins, time_bins) → treat channels as "color" channels (like RGB)
- Or concatenate along frequency axis for single-channel input to CNN
1.2 Topographic Maps (地形图)
Project electrode voltages onto a 2D scalp map using spatial interpolation. Ideal for ERP/SSVEP analysis where spatial distribution is discriminative.
import numpy as np
from scipy.interpolate import griddata
ELECTRODE_POSITIONS = {
'Fp1': (-0.35, 0.45), 'Fp2': (0.35, 0.45),
'F3': (-0.25, 0.25), 'Fz': (0.0, 0.3), 'F4': (0.25, 0.25),
'C3': (-0.35, 0.0), 'Cz': (0.0, 0.0), 'C4': (0.35, 0.0),
'P3': (-0.25, -0.25),'Pz': (0.0, -0.3), 'P4': (0.25, -0.25),
'O1': (-0.2, -0.45),'Oz': (0.0, -0.5), 'O2': (0.2, -0.45),
}
def eeg_to_topomap(eeg_values, electrode_names, grid_size=32):
"""
Convert single-timepoint EEG values to a topographic scalp map image.
Args:
eeg_values: dict or list of voltage values per electrode
electrode_names: list of electrode names (10-20 system)
grid_size: output image resolution (grid_size x grid_size)
Returns:
topomap: (grid_size, grid_size) ndarray
"""
(eeg_values, ):
values = [eeg_values[el] el electrode_names]
:
values = (eeg_values)
points = np.array([ELECTRODE_POSITIONS[el] el electrode_names])
xi = np.linspace(-, , grid_size)
yi = np.linspace(-, , grid_size)
XI, YI = np.meshgrid(xi, yi)
ZI = griddata(points, values, (XI, YI), method=, fill_value=)
ZI = np.nan_to_num(ZI, nan=)
ZI = (ZI - ZI.()) / (ZI.ptp() + )
ZI
():
n_samples = eeg_epoch.shape[]
time_window :
eeg_epoch = eeg_epoch[:, time_window[]:time_window[]].mean(axis=, keepdims=)
topomaps = []
t (eeg_epoch.shape[]):
topomaps.append(eeg_to_topomap(
eeg_epoch[:, t], electrode_names, grid_size
))
np.array(topomaps)
1.3 Combined Representations (组合表示法)
For maximum information, stack multiple representations:
def build_multi_view_input(eeg_epoch, fs=256, electrode_names=None, grid_size=32):
"""
Create multi-view tensor from a single EEG epoch.
Returns:
(n_views, H, W) tensor suitable for multi-branch CNN
"""
views = []
spec = eeg_to_spectrogram(eeg_epoch, fs=fs)
views.append(np.concatenate([spec[ch] for ch in range(spec.shape[0])], axis=0))
if electrode_names:
topo_seq = eeg_epoch_to_topomap_sequence(eeg_epoch, electrode_names, grid_size=grid_size)
views.append(topo_seq.mean(axis=0))
return np.array(views)
2. CNN Architecture for EEG (EEG卷积神经网络架构)
2.1 Core CNN Encoder
Adapted for EEG image inputs with spectrogram/topographic map characteristics:
import torch
import torch.nn as nn
import torch.nn.functional as F
class EEGCNNEncoder(nn.Module):
"""
CNN Encoder for EEG image representations.
Designed for spectrogram (freq × time) and topomap (spatial) inputs.
"""
def __init__(self, input_channels=1, latent_dim=64, dropout=0.5):
super().__init__()
self.encoder = nn.Sequential(
nn.Conv2d(input_channels, 32, kernel_size=5, stride=1, padding=2),
nn.BatchNorm2d(32),
nn.LeakyReLU(0.2),
nn.MaxPool2d(2, 2),
nn.Dropout2d(dropout),
nn.Conv2d(32, 64, kernel_size=5, stride=1, padding=2),
nn.BatchNorm2d(64),
nn.LeakyReLU(0.2),
nn.MaxPool2d(2, 2),
nn.Dropout2d(dropout),
nn.Conv2d(64, 128, kernel_size=3, stride=1, padding=1),
nn.BatchNorm2d(128),
nn.LeakyReLU(0.2),
nn.MaxPool2d(, ),
nn.Conv2d(, , kernel_size=, stride=, padding=),
nn.BatchNorm2d(),
nn.LeakyReLU(),
nn.AdaptiveAvgPool2d(),
)
.fc = nn.Sequential(
nn.Flatten(),
nn.Linear(, latent_dim),
)
():
features = .encoder(x)
latent = .fc(features)
latent, features
(nn.Module):
():
().__init__()
.output_size = output_size
.fc = nn.Sequential(
nn.Linear(input_dim, * * ),
nn.LeakyReLU(),
)
.decoder = nn.Sequential(
nn.ConvTranspose2d(, , , stride=, padding=),
nn.BatchNorm2d(),
nn.LeakyReLU(),
nn.ConvTranspose2d(, , , stride=, padding=),
nn.BatchNorm2d(),
nn.LeakyReLU(),
nn.ConvTranspose2d(, output_channels, , stride=, padding=),
nn.Sigmoid(),
)
():
x = .fc(z)
x = x.view(-, , , )
.decoder(x)
2.2 EEG-Specific CNN Variants
class MultiBranchEEGCNN(nn.Module):
"""
Multi-branch CNN that processes different EEG views separately,
then fuses them in latent space.
Branches:
1. Spectrogram branch (temporal-frequency features)
2. Topographic branch (spatial features)
3. Raw signal branch (temporal features via 1D conv)
"""
def __init__(self, latent_dim=64):
super().__init__()
self.spec_branch = nn.Sequential(
nn.Conv2d(1, 32, 5, padding=2),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1),
nn.Flatten(),
)
self.topo_branch = nn.Sequential(
nn.Conv2d(1, 32, 5, padding=2),
nn.BatchNorm2d(32),
nn.ReLU(),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 3, padding=1),
nn.BatchNorm2d(64),
nn.ReLU(),
nn.AdaptiveAvgPool2d(1),
nn.Flatten(),
)
self.fusion = nn.Sequential(
nn.Linear( + , latent_dim),
nn.BatchNorm1d(latent_dim),
nn.ReLU(),
)
():
spec_feat = .spec_branch(spec_input)
topo_feat = .topo_branch(topo_input)
fused = torch.cat([spec_feat, topo_feat], dim=)
latent = .fusion(fused)
latent
3. AAE Latent Space Regularization (对抗自编码器潜在空间正则化)
3.1 Adversarial Autoencoder (AAE) Core
The AAE adds a discriminator that enforces the aggregated posterior distribution Q(z) to match a prior P(z) (typically N(0,I) or a Gaussian Mixture Model).
class AAE(nn.Module):
"""
Adversarial Autoencoder for EEG classification.
Components:
- Encoder: EEG image → latent code z
- Decoder: latent code z → reconstructed EEG image
- Discriminator: distinguishes real samples from prior vs. encoded z
- Classifier: latent code z → class label (optional, semi-supervised)
"""
def __init__(self, input_channels=1, latent_dim=64, n_classes=4):
super().__init__()
self.encoder = EEGCNNEncoder(input_channels, latent_dim)
self.decoder = EEGCNNDecoder(latent_dim, input_channels)
self.discriminator = nn.Sequential(
nn.Linear(latent_dim, 128),
nn.LeakyReLU(0.2),
nn.Dropout(0.3),
nn.Linear(128, 64),
nn.LeakyReLU(0.2),
nn.Linear(64, 1),
nn.Sigmoid(),
)
self.classifier = nn.Sequential(
nn.Linear(latent_dim, 64),
nn.ReLU(),
nn.Dropout(0.5),
nn.Linear(64, n_classes),
)
def encode(self, x):
return self.encoder(x)
def decode(self, z):
return self.decoder(z)
():
z, features = .encode(x)
x_recon = .decode(z)
class_logits = .classifier(z)
z, x_recon, class_logits
:
():
.model = model.to(device)
.device = device
.recon_opt = torch.optim.Adam(
(model.encoder.parameters()) +
(model.decoder.parameters()), lr=, weight_decay=
)
.disc_opt = torch.optim.Adam(model.discriminator.parameters(), lr=)
.gen_opt = torch.optim.Adam(model.encoder.parameters(), lr=)
.clf_opt = torch.optim.Adam(model.classifier.parameters(), lr=)
.bce_loss = nn.BCELoss()
.mse_loss = nn.MSELoss()
.ce_loss = nn.CrossEntropyLoss()
():
eeg_images = eeg_images.to(.device)
z, x_recon, class_logits = .model(eeg_images)
recon_loss = .mse_loss(x_recon, eeg_images)
.recon_opt.zero_grad()
recon_loss.backward()
.recon_opt.step()
batch_size = eeg_images.shape[]
real_samples = torch.randn(batch_size, z.shape[]).to(.device)
real_labels = torch.ones(batch_size, ).to(.device)
fake_labels = torch.zeros(batch_size, ).to(.device)
d_real = .model.discriminator(real_samples)
d_fake = .model.discriminator(z.detach())
d_loss = .bce_loss(d_real, real_labels) + \
.bce_loss(d_fake, fake_labels)
.disc_opt.zero_grad()
d_loss.backward()
.disc_opt.step()
d_fake_for_gen = .model.discriminator(z)
g_loss = .bce_loss(d_fake_for_gen, real_labels)
.gen_opt.zero_grad()
g_loss.backward()
.gen_opt.step()
clf_loss = torch.tensor(, device=.device)
labels :
labels = labels.to(.device)
clf_loss = .ce_loss(class_logits, labels)
.clf_opt.zero_grad()
clf_loss.backward()
.clf_opt.step()
{
: recon_loss.item(),
: d_loss.item(),
: g_loss.item(),
: clf_loss.item(),
}
3.2 Gaussian Mixture Prior (高斯混合先验)
For classification tasks, a GMM prior with one component per class creates well-separated clusters:
class GMMPrior:
"""Gaussian Mixture Model prior for class-conditioned AAE."""
def __init__(self, n_classes, latent_dim, device='cuda'):
self.n_classes = n_classes
self.latent_dim = latent_dim
self.device = device
centers = torch.zeros(n_classes, latent_dim)
for i in range(n_classes):
centers[i, i % latent_dim] = 1.0
centers = F.normalize(centers, p=2, dim=1) * 2.0
self.register_buffer('centers', centers)
def sample(self, batch_size, labels=None):
"""Sample from GMM prior."""
if labels is None:
labels = torch.randint(0, self.n_classes, (batch_size,))
centers = self.centers[labels].to(self.device)
noise = torch.randn_like(centers) * 0.5
return centers + noise
3.3 Additional Regularization Techniques
def add_latent_regularization(z, reg_type='kl', beta=0.1):
"""
Additional latent space regularization beyond adversarial loss.
reg_type: 'kl' (KL divergence), 'mmd' (Maximum Mean Discrepancy),
'coral' (CORAL domain alignment)
"""
if reg_type == 'kl':
mu = z.mean(dim=0)
var = z.var(dim=0)
kl = 0.5 * torch.sum(mu**2 + var - torch.log(var) - 1)
return beta * kl
elif reg_type == 'mmd':
prior = torch.randn_like(z)
K_zz = torch.mm(z, z.t()) / z.shape[1]
K_pp = torch.mm(prior, prior.t()) / prior.shape[1]
K_zp = torch.mm(z, prior.t()) / z.shape[1]
mmd = K_zz.mean() + K_pp.mean() - 2 * K_zp.mean()
return beta * mmd
elif reg_type == 'coral':
cov_z = z - z.mean(dim=0)
cov_z = torch.mm(cov_z.t(), cov_z) / (z.shape[0] - 1)
prior = torch.randn_like(z)
cov_p = prior - prior.mean(dim=0)
cov_p = torch.mm(cov_p.t(), cov_p) / (prior.shape[0] - 1)
coral = torch.sum((cov_z - cov_p) ** 2)
return beta * coral
return torch.tensor(0.0, device=z.device)
4. Implementation Patterns (实现模式)
4.1 Data Pipeline (数据处理流水线)
from torch.utils.data import Dataset, DataLoader
import h5py
class EEGDataset(Dataset):
"""EEG dataset with on-the-fly image transformation."""
def __init__(self, data_path, transform='spectrogram',
electrode_names=None, fs=256, augment=False):
self.transform = transform
self.electrode_names = electrode_names
self.fs = fs
self.augment = augment
if data_path.endswith('.h5'):
with h5py.File(data_path, 'r') as f:
self.data = f['eeg_data'][:]
self.labels = f['labels'][:]
else:
self.data, self.labels = self._load_custom(data_path)
def _apply_augmentation(self, eeg):
"""EEG-specific augmentations."""
if np.random.rand() > 0.5:
shift = np.random.randint(-50, 50)
eeg = np.roll(eeg, shift, axis=-)
np.random.rand() > :
noise_level = np.std(eeg) *
eeg = eeg + np.random.randn(*eeg.shape) * noise_level
np.random.rand() > :
n_drop = np.random.randint(, )
channels = np.random.choice(eeg.shape[], n_drop, replace=)
eeg[channels] =
eeg
():
eeg = .data[idx].copy()
.augment:
eeg = ._apply_augmentation(eeg)
scipy.signal butter, filtfilt
b, a = butter(, [/(.fs/), /(.fs/)], btype=)
eeg = filtfilt(b, a, eeg, axis=-)
.transform == :
img = eeg_to_spectrogram(eeg, fs=.fs)
.transform == .electrode_names:
img = eeg_epoch_to_topomap_sequence(eeg, .electrode_names)
:
img = eeg_to_spectrogram(eeg, fs=.fs)
img = (img - img.mean()) / (img.std() + )
img = torch.tensor(img, dtype=torch.float32).unsqueeze()
img, torch.tensor(.labels[idx], dtype=torch.long)
():
(.data)
train_dataset = EEGDataset(, transform=, augment=)
train_loader = DataLoader(train_dataset, batch_size=, shuffle=, num_workers=)
4.2 Cross-Validation Setup (交叉验证)
from sklearn.model_selection import StratifiedKFold
def cross_validate_eeg(data_path, n_folds=5, seed=42):
"""Subject-wise or trial-wise cross-validation for EEG."""
dataset = EEGDataset(data_path, transform='spectrogram')
skf = StratifiedKFold(n_splits=n_folds, shuffle=True, random_state=seed)
fold_results = []
for fold, (train_idx, val_idx) in enumerate(skf.split(dataset.data, dataset.labels)):
print(f"--- Fold {fold+1}/{n_folds} ---")
train_data = torch.utils.data.Subset(dataset, train_idx)
val_data = torch.utils.data.Subset(dataset, val_idx)
train_loader = DataLoader(train_data, batch_size=32, shuffle=True)
val_loader = DataLoader(val_data, batch_size=32)
model = AAE(input_channels=1, latent_dim=64, n_classes=4)
trainer = AAETrainer(model)
best_acc = 0
for epoch in range(50):
model.train()
for imgs, labels in train_loader:
losses = trainer.train_step(imgs, labels)
model.eval()
correct = 0
total = 0
with torch.no_grad():
for imgs, labels val_loader:
_, _, logits = model(imgs.to(trainer.device))
preds = logits.argmax(dim=)
correct += (preds.cpu() == labels).().item()
total += labels.shape[]
acc = correct / total
best_acc = (best_acc, acc)
fold_results.append(best_acc)
()
()
fold_results
4.3 Inference & Visualization
def visualize_latent_space(model, dataloader, method='tsne'):
"""Visualize the learned latent space."""
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
model.eval()
all_z, all_labels = [], []
with torch.no_grad():
for imgs, labels in dataloader:
z, _ = model.encoder(imgs.to(model.device))
all_z.append(z.cpu().numpy())
all_labels.append(labels.numpy())
all_z = np.concatenate(all_z)
all_labels = np.concatenate(all_labels)
if method == 'tsne':
reducer = TSNE(n_components=2, perplexity=30, random_state=42)
elif method == 'umap':
import umap
reducer = umap.UMAP(n_components=2, random_state=42)
z_2d = reducer.fit_transform(all_z)
fig, ax = plt.subplots(figsize=(8, 6))
classes = np.unique(all_labels)
for cls in classes:
mask = all_labels == cls
ax.scatter(z_2d[mask, 0], z_2d[mask, 1],
label=f'Class {cls}', alpha=0.6, s=50)
ax.legend()
ax.set_title('AAE Latent Space Visualization')
plt.tight_layout()
return fig
5. Activation Keywords (激活关键词)
English Keywords
eeg-classification, adversarial-autoencoder, aae, cnn-eeg,
brain-computer-interface, bci, motor-imagery, erp, ssvep,
spectrogram, topographic-map, scalp-map, eeg-image,
latent-space, adversarial-regularization, gmm-prior,
domain-adaptation, cross-subject, transfer-learning,
mne-python, neurodsp, pytorch, time-frequency,
eeg-preprocessing, artifact-removal, ica-eeg,
few-shot-eeg, self-supervised-eeg, contrastive-learning
Chinese Keywords (中文关键词)
脑电图分类, 对抗自编码器, 卷积神经网络脑电, 脑机接口,
运动想象, 事件相关电位, 稳态视觉诱发电位,
频谱图, 地形图, 头皮地图, 脑电图像,
潜在空间, 对抗正则化, 高斯混合先验,
域适应, 跨被试, 迁移学习,
时间频率分析, 脑电预处理, 伪影去除, 独立成分分析,
少样本脑电, 自监督学习, 对比学习
6. Pitfalls & Mitigations (常见陷阱与应对策略)
6.1 Overfitting (过拟合)
| Symptom | Cause | Mitigation |
|---|
| Train acc >> Val acc | Too few trials per subject | Use AAE adversarial regularization; data augmentation |
| Model memorizes noise | High model capacity | Dropout (0.3-0.5); weight decay (1e-5); early stopping |
| Latent collapse | Weak discriminator | Increase discriminator capacity; use WGAN-GP loss |
| Spectrogram overfitting | Too large nperseg | Reduce window size; use multi-scale spectrograms |
anti_overfit = {
'dropout': 0.3,
'weight_decay': 1e-5,
'label_smoothing': 0.1,
'early_stop_patience': 10,
'data_augmentation': True,
'spectral_augmentation':
lambda x: mask_freq_bands(x, mask_ratio=0.15),
}
6.2 Domain Shift (域偏移)
| Type | Description | Solution |
|---|
| Cross-subject | Different electrode impedances, skull thickness | Domain adversarial training (DANN); CORAL alignment |
| Cross-session | Day-to-day variability, fatigue | Batch normalization statistics update; test-time adaptation |
| Cross-device | Different EEG amplifiers, sampling rates | Harmonization; style transfer in latent space |
| Cross-task | MI vs. ERP paradigms | Multi-task learning; shared encoder with task-specific heads |
class DomainAdversarialAAE(AAE):
"""AAE with domain adversarial training for cross-subject generalization."""
def __init__(self, input_channels=1, latent_dim=64, n_classes=4, n_domains=10):
super().__init__(input_channels, latent_dim, n_classes)
self.domain_classifier = nn.Sequential(
GradientReversalLayer(alpha=1.0),
nn.Linear(latent_dim, 64),
nn.ReLU(),
nn.Linear(64, n_domains),
)
def forward(self, x, domain_labels=None):
z, features = self.encode(x)
x_recon = self.decode(z)
class_logits = self.classifier(z)
domain_logits = self.domain_classifier(z)
return z, x_recon, class_logits, domain_logits
6.3 Artifact Contamination (伪影污染)
| Artifact | Source | Detection | Removal |
|---|
| Ocular (EOG) | Eye blinks, movements | High amplitude frontal channels | ICA; regression; AAE reconstruction |
| Muscular (EMG) | Jaw clenching, tension | High-frequency content (>30 Hz) | Low-pass filter; wavelet thresholding |
| Cardiac (ECG) | Heartbeat | Periodic ~1 Hz pattern | Template subtraction; ICA |
| Line noise | 50/60 Hz electrical | Sharp spectral peak | Notch filter; adaptive filtering |
| Electrode pop | Poor contact | Sudden step-change | Interpolation; channel rejection |
def detect_and_remove_artifacts(eeg_data, fs=256, eog_channels=None):
"""
Multi-stage artifact detection and removal pipeline.
Returns cleaned EEG and artifact mask.
"""
from mne.preprocessing import ICA
import mne
from scipy.signal import butter, filtfilt
b, a = butter(4, 0.5/(fs/2), btype='high')
eeg_clean = filtfilt(b, a, eeg_data, axis=-1)
b_notch, a_notch = scipy.signal.iirnotch(50.0, 30.0, fs)
eeg_clean = filtfilt(b_notch, a_notch, eeg_clean, axis=-1)
threshold = np.median(np.abs(eeg_clean)) * 5
artifact_mask = np.abs(eeg_clean) > threshold
return eeg_clean, artifact_mask
6.4 Additional Pitfalls
| Pitfall | Description | Fix |
|---|
| Information leakage | Train/test trials from same continuous recording | Strict epoch separation; subject-wise split |
| Class imbalance | Some classes have far fewer trials | Weighted loss; oversampling; focal loss |
| Spectrogram resolution | Wrong window size misses key frequency bands | Use multiple window sizes; validate on domain knowledge |
| Ignoring phase info | Spectrograms lose phase information | Use complex-valued CNN; add phase as separate channel |
| Non-stationarity | EEG statistics change over time | Sliding window normalization; adaptive batch norm |
7. Related BCI/EEG Skills (相关脑机接口/脑电图技能)
| Skill | Description | Connection |
|---|
eeg-preprocessing | MNE-based preprocessing, ICA, filtering | Prerequisite: clean data before CNN input |
signal-processing | FFT, wavelet, STFT, filter design | Core: spectrogram generation |
time-series-analysis | Temporal patterns, sequence modeling | Complementary: RNN/Transformer for raw EEG |
computer-vision | CNN architectures, data augmentation | Core: image-based EEG classification |
generative-models | VAEs, GANs, diffusion models | Core: AAE is a generative approach |
domain-adaptation | CORAL, DANN, MMD alignment | Critical: cross-subject generalization |
self-supervised-learning | Contrastive learning, masked modeling | Extension: pre-training on unlabeled EEG |
csp-features | Common Spatial Patterns for motor imagery | Alternative: handcrafted features vs. CNN |
eeg-spatial-filtering | Laplacian, surface Laplacian, source localization | Enhancement: improve signal-to-noise ratio |
transfer-learning-eeg | Pre-trained models for EEG | Extension: leverage large EEG corpora |
Quick Start Checklist
☐ 1. Preprocess EEG: filter (0.5-45Hz), remove artifacts (ICA), re-reference
☐ 2. Segment into epochs: e.g., [-0.5s, 2.5s] relative to cue
☐ 3. Transform to image: spectrogram (STFT/CWT) or topographic maps
☐ 4. Build CNN encoder/decoder: 4 conv blocks + adaptive pooling
☐ 5. Add AAE discriminator: 2-3 FC layers, BCE loss
☐ 6. Train with 3 phases: reconstruction → discriminator → generator
☐ 7. Validate: cross-subject or cross-session split (NOT random trial split!)
☐ 8. Visualize: t-SNE/UMAP of latent space; check class separation
☐ 9. Deploy: optimize for real-time (quantization, TensorRT)
☐ 10. Monitor: track BCI accuracy over time for drift detection
References
- Makhzani et al. (2015). "Adversarial Autoencoders." arXiv:1511.05644.
- Schirrmeister et al. (2017). "Deep Learning with Convolutional Neural Networks for EEG Decoding." Human Brain Mapping.
- Roy et al. (2019). "EEG-based Brain-Computer Interfaces using Deep Learning: A Review." IEEE T-NSRE.
- Craik et al. (2019). "Deep Learning-Based Electroencephalography Analysis: A Systematic Review." Journal of Neural Engineering.
- Lotte et al. (2018). "A Review of Classification Algorithms for EEG-based BCIs." Journal of Neural Engineering.