SEED-VII数据集实战:用Python快速搭建情感脑电图分析环境(附代码)

深入探索 SEED-VII:情感脑电图研究的新利器 SEED-VII是上海交通大学最新发布的多模态情感脑电图数据集,在传统EEG数据基础上增加了眼动、生理信号等多维信息,支持更精细的情感识别研究。该数据集提供了丰富的情感维度标注(效价和唤醒度),可用于算法基准测试、跨模态情感融合及个性化模型开发等研究场景。在应用层面,SEED-VII可推动人机交互、心理健康监测、教育娱乐等领域的情感计算技术发展。文章还提供了PyTorch加载该数据集的代码示例,为研究者使用该数据集提供了技术参考。 阅读详情

SEED-VII数据集实战:用Python快速搭建情感脑电图分析环境(附代码)

最近几年,身边不少做情感计算和脑机接口的朋友,都开始把目光投向了一个叫SEED-VII的数据集。说实话,我第一次接触它的时候,也被它庞大的数据量和丰富的模态吓了一跳。这不仅仅是一个简单的脑电图(EEG)数据集合,它更像是一个为情感识别研究量身定制的“游乐场”,里面不仅有经典的EEG信号,还整合了眼动、生理信号等多种信息,让研究者能从更多维度去窥探情绪的奥秘。对于开发者、学生和刚入行的研究人员来说,直接上手这样一个复杂的数据集,难免会感到无从下手——环境怎么配?数据怎么读?模型怎么搭?别急,这篇文章就是为你准备的。我会带你从零开始,用Python一步步搭建起一个可运行、可实验的分析环境,把SEED-VII这个“利器”真正用起来。

1. 环境搭建与数据获取:迈出第一步

在开始任何数据分析之前,一个稳定、可复现的Python环境是基石。对于脑电信号处理,我们需要的工具链相对固定,但版本兼容性是个需要特别注意的坑。

1.1 创建并配置Python虚拟环境

我强烈建议你为这个项目创建一个独立的虚拟环境。这能避免与你系统上其他项目的依赖发生冲突。使用conda或venv都可以,这里以conda为例,因为它能更好地管理一些科学计算包的二进制依赖。

# 创建一个名为seed_vii_env的Python 3.9环境
conda create -n seed_vii_env python=3.9 -y

# 激活环境
conda activate seed_vii_env

环境激活后,我们开始安装核心包。脑电处理离不开numpy、scipy和pandas进行数值运算和数据操作,而mne(MNE-Python)是处理脑电数据的行业标准工具,提供了从读取、预处理到可视化的全套功能。

# 安装核心数据处理和科学计算库
pip install numpy scipy pandas matplotlib seaborn

# 安装脑电处理专业库MNE
pip install mne

# 安装机器学习框架,这里以PyTorch为例(根据你的CUDA版本选择安装命令)
# 例如,对于CUDA 11.8
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu118

# 安装scikit-learn用于传统机器学习模型和评估
pip install scikit-learn

注意:mne库在Windows系统上安装时,可能会遇到一些底层C库的编译问题。如果安装失败,可以尝试使用conda install -c conda-forge mne命令通过conda安装,conda-forge频道通常提供了预编译好的二进制包。

1.2 获取与初步探索SEED-VII数据集

SEED-VII数据集通常需要从官方渠道申请获取。假设你已经成功下载了数据集,并解压到了本地目录。数据集的文件结构可能比较复杂,常见的是按被试者(Subject)和实验会话(Session)进行组织,数据格式可能是.mat(MATLAB文件)或.npy(NumPy文件)。

我们先写一个简单的脚本来探索数据目录结构,做到心中有数。

import os
from pathlib import Path

# 假设你的数据集根目录路径
data_root = Path('./SEED-VII')
if not data_root.exists():
    print(f"警告:数据目录 {data_root} 不存在,请检查路径。")
else:
    print(f"正在探索数据目录: {data_root}")
    for root, dirs, files in os.walk(data_root):
        level = root.replace(str(data_root), '').count(os.sep)
        indent = ' ' * 2 * level
        print(f'{indent}{os.path.basename(root)}/')
        sub_indent = ' ' * 2 * (level + 1)
        for file in files[:5]: # 只打印前5个文件,避免输出过长
            print(f'{sub_indent}{file}')
        if len(files) > 5:
            print(f'{sub_indent}... 以及另外 {len(files)-5} 个文件')

一个典型的数据结构可能如下表所示,了解它有助于后续编写数据加载器:

目录/文件层级描述示例
SEED-VII/数据集根目录
├── Subject_01/1号被试者数据目录
│ ├── Session_1/第1次实验会话
│ │ ├── eeg_data.matEEG原始数据文件
│ │ ├── eye_tracking.csv眼动数据文件
│ │ └── labels.npy情感标签文件(如效价、唤醒度)
│ └── Session_2/第2次实验会话
└── Subject_02/2号被试者数据目录

2. 数据加载与预处理:从原始信号到可用特征

拿到原始数据后,我们很少能直接将其扔进模型。EEG信号非常微弱,容易受到眼电、肌电、工频干扰等噪声污染,必须经过一系列预处理步骤来“提纯”。

2.1 使用MNE-Python加载与可视化原始EEG

假设我们的EEG数据是.mat格式。我们可以使用scipy读取,并利用mne创建仿真的原始数据结构,以便使用其强大的预处理流水线。

import scipy.io as sio
import mne
import numpy as np
import matplotlib.pyplot as plt

def load_eeg_mat(file_path, sfreq=200):
    """
    加载.mat格式的EEG数据,并转换为MNE Raw对象。
    参数:
        file_path: .mat文件路径。
        sfreq: 采样频率(Hz),SEED-VII通常为200Hz或更高。
    返回:
        raw: MNE Raw对象。
    """
    try:
        mat_data = sio.loadmat(file_path)
        # 关键:需要根据实际.mat文件的结构访问EEG数据
        # 例如,数据可能在键'eeg_data'下,形状为(通道数, 时间点数)
        eeg_data = mat_data['eeg_data']  # 请根据实际情况修改键名

        # 创建MNE的Info对象,描述数据
        n_channels, n_times = eeg_data.shape
        # 创建通道名称列表,假设是标准62导联
        ch_names = [f'EEG{i:03d}' for i in range(n_channels)]
        ch_types = ['eeg'] * n_channels
        info = mne.create_info(ch_names=ch_names, sfreq=sfreq, ch_types=ch_types)

        # 创建RawArray对象。注意:数据单位需要是伏特(V),通常原始数据是微伏(uV),需转换
        # 假设数据是uV,转换为V
        eeg_data_v = eeg_data * 1e-6
        raw = mne.io.RawArray(eeg_data_v, info)

        # 设置标准10-20系统电极位置(近似)。对于精确分析,需要真实的电极位置文件。
        montage = mne.channels.make_standard_montage('standard_1020')
        # 由于我们的通道名是自定义的,需要手动映射或使用通用位置,这里仅作示例
        # raw.set_montage(montage) # 如果通道名能匹配,可以直接设置

        print(f"成功加载数据: {file_path}")
        print(f"  通道数: {n_channels}, 时间点数: {n_times}, 时长: {n_times/sfreq:.2f}秒")
        return raw
    except Exception as e:
        print(f"加载文件 {file_path} 时出错: {e}")
        return None

# 示例:加载一个文件并绘制原始信号片段
sample_file = './SEED-VII/Subject_01/Session_1/eeg_data.mat'
raw = load_eeg_mat(sample_file, sfreq=200)
if raw:
    # 绘制前5秒的原始信号(仅显示前10个通道避免过于拥挤)
    fig = raw.plot(duration=5.0, n_channels=10, scalings='auto', show=False)
    fig.suptitle('原始EEG信号示例 (前10个通道,前5秒)')
    plt.show()

2.2 构建标准预处理流水线

预处理步骤的顺序和参数设置需要根据具体研究和数据质量调整。下面是一个典型的流水线:

  1. 滤波:去除高频噪声和低频漂移。通常保留1-45Hz的频率成分用于情感分析。
  2. 坏道检测与插值:识别并修复信号质量极差的电极通道。
  3. 重参考:将参考电极转换为平均参考,以减少参考电极的影响。
  4. 独立成分分析:识别并去除眼电、心电等伪迹成分。
def preprocess_raw(raw, l_freq=1.0, h_freq=45.0):
    """
    对Raw对象进行预处理。
    """
    raw_copy = raw.copy() # 在副本上操作,避免修改原始数据

    # 1. 带通滤波
    print("应用带通滤波 (1-45 Hz)...")
    raw_copy.filter(l_freq=l_freq, h_freq=h_freq, fir_design='firwin')

    # 2. 坏道检测(简单阈值法示例)
    print("检测坏道...")
    # 计算每个通道的峰峰值幅度
    data, times = raw_copy[:, :]
    peak_to_peak = np.ptp(data, axis=1)
    median_ptp = np.median(peak_to_peak)
    std_ptp = np.std(peak_to_peak)
    # 将幅度超过中位数3个标准差的通道标记为坏道
    bad_ch_idx = np.where(peak_to_peak > median_ptp + 3 * std_ptp)[0]
    bad_chs = [raw_copy.ch_names[i] for i in bad_ch_idx]
    if bad_chs:
        raw_copy.info['bads'] = bad_chs
        print(f"  发现坏道: {bad_chs}")
        # 在实际应用中,可以考虑插值坏道: raw_copy.interpolate_bads()
    else:
        print("  未发现明显坏道。")

    # 3. 重参考(设置为平均参考)
    print("应用平均参考...")
    raw_copy.set_eeg_reference(ref_channels='average', projection=False)

    # 注意:完整的ICA去除伪迹步骤较耗时,通常需要根据眼电等标记进行成分识别和剔除。
    # 此处为简化流程,暂不包含。实际项目中强烈建议执行此步骤。
    # print("进行ICA去除眼电伪迹...")
    # ica = mne.preprocessing.ICA(n_components=20, random_state=97)
    # ica.fit(raw_copy)
    # ica.exclude = [...] # 需要手动或自动识别眼电成分的索引
    # ica.apply(raw_copy)

    print("预处理完成。")
    return raw_copy

# 应用预处理
if raw:
    raw_processed = preprocess_raw(raw)
    # 可以再次绘图对比预处理效果
    fig = raw_processed.plot(duration=5.0, n_channels=10, scalings='auto', show=False)
    fig.suptitle('预处理后EEG信号示例')
    plt.show()

3. 特征工程:从时域到频域,捕捉情感指纹

经过预处理的干净EEG信号,数据量依然庞大。直接使用高维时间序列作为模型输入,不仅计算成本高,而且容易过拟合。因此,我们需要从中提取能够有效表征情感状态的特征。情感EEG分析中,微分熵(Differential Entropy, DE)特征因其在情绪识别中的良好表现而被广泛使用,特别是在SEED系列数据集的基线方法中。

3.1 理解微分熵特征

微分熵是香农熵在连续概率分布上的推广,可以粗略理解为信号在特定频带内复杂度的度量。对于一段近似服从高斯分布的脑电信号,其在某个频带(如Theta: 4-8Hz, Alpha: 8-14Hz, Beta: 14-31Hz, Gamma: 31-45Hz)内的微分熵有一个简化的计算公式,等价于对该频带信号功率谱的对数运算。

提取DE特征的一般步骤是:

  • 将每个通道的EEG信号进行短时傅里叶变换(STFT)或使用带通滤波器组。
  • 计算每个频带内的信号功率。
  • 根据公式计算该频带的微分熵值。

3.2 使用Python批量提取DE特征

下面我们实现一个函数,用于从单个试次(trial)的EEG数据中提取多频带DE特征。

import numpy as np
from scipy import signal, integrate

def extract_de_features(eeg_segment, sfreq, bands, window_length=1.0, overlap=0.5):
    """
    从一段多通道EEG数据中提取微分熵特征。
    参数:
        eeg_segment: numpy数组,形状为 (通道数, 时间点数)。
        sfreq: 采样频率。
        bands: 字典,键为频带名,值为(低频, 高频)元组,如{'theta': (4,8), 'alpha': (8,14)}。
        window_length: 滑动窗口长度(秒)。
        overlap: 滑动窗口重叠比例(0到1之间)。
    返回:
        de_features: numpy数组,形状为 (通道数, 频带数)。
    """
    n_channels, n_points = eeg_segment.shape
    n_bands = len(bands)
    band_names = list(bands.keys())

    # 初始化特征矩阵
    de_features = np.zeros((n_channels, n_bands))

    # 计算每个通道、每个频带的DE
    for ch_idx in range(n_channels):
        channel_data = eeg_segment[ch_idx, :]
        for band_idx, (band_name, (low_freq, high_freq)) in enumerate(bands.items()):
            # 设计带通滤波器
            nyquist = sfreq / 2.0
            low = low_freq / nyquist
            high = high_freq / nyquist
            b, a = signal.butter(5, [low, high], btype='band')

            # 应用滤波器,得到该频带信号
            band_signal = signal.filtfilt(b, a, channel_data)

            # 计算该频带信号的方差,假设信号服从高斯分布 N(0, sigma^2)
            # 对于高斯分布,微分熵 h(X) = 0.5 * log(2 * pi * e * sigma^2)
            sigma2 = np.var(band_signal)
            # 防止方差为0导致对数计算错误
            if sigma2 <= 0:
                sigma2 = 1e-10
            de = 0.5 * np.log(2 * np.pi * np.e * sigma2)
            de_features[ch_idx, band_idx] = de

    return de_features, band_names

# 定义常用的情感EEG频带
EEG_BANDS = {
    'delta': (1, 4),
    'theta': (4, 8),
    'alpha': (8, 14),
    'beta': (14, 31),
    'gamma': (31, 45)
}

# 示例:从一段预处理后的数据中提取特征
if 'raw_processed' in locals():
    # 获取一段5秒的数据(假设从第10秒开始)
    start_sample = int(10 * raw_processed.info['sfreq'])
    end_sample = int(15 * raw_processed.info['sfreq'])
    data_segment, times = raw_processed[:, start_sample:end_sample]

    de_feat, bands_used = extract_de_features(data_segment, sfreq=raw_processed.info['sfreq'], bands=EEG_BANDS)
    print(f"特征提取完成。形状: {de_feat.shape} (通道数 x 频带数)")
    print(f"使用的频带: {bands_used}")

    # 可视化某个通道在不同频带的DE值
    ch_to_plot = 0
    plt.figure(figsize=(10, 5))
    plt.bar(bands_used, de_feat[ch_to_plot, :])
    plt.xlabel('频带')
    plt.ylabel('微分熵 (DE)')
    plt.title(f'通道 {raw_processed.ch_names[ch_to_plot]} 在各频带的微分熵特征')
    plt.grid(True, axis='y', linestyle='--', alpha=0.7)
    plt.show()

提取出的DE特征矩阵,其维度为(通道数 × 频带数)。对于62个通道、5个频带的情况,每个试次就得到了一个310维的特征向量。这个向量相比原始数万维的时间点数据,维度大大降低,且包含了与情感相关的频域能量信息。

4. 构建情感识别模型:从传统机器学习到深度学习

特征准备好之后,我们就可以构建分类器来识别情感了。根据任务的不同(如二分类:积极/消极;三分类:积极/中性/消极;或维度回归:预测效价、唤醒度分值),我们可以选择不同的模型。

4.1 基于Scikit-learn的传统机器学习流程

对于刚入门的研究者,从逻辑回归、支持向量机(SVM)、随机森林等传统模型开始是一个稳妥的选择。它们训练速度快,可解释性相对较强,能快速建立基线性能。

假设我们已经将多个被试者、多个试次的数据处理成了特征矩阵X(形状:[n_samples, n_features])和标签向量y(形状:[n_samples,])。

from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
import seaborn as sns

def train_evaluate_sklearn_model(X, y, model_type='svm', test_size=0.2, random_state=42):
    """
    使用传统机器学习模型进行训练和评估。
    """
    # 1. 划分训练集和测试集
    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=test_size, stratify=y, random_state=random_state
    )
    print(f"数据划分: 训练集 {X_train.shape}, 测试集 {X_test.shape}")

    # 2. 标准化特征(对SVM等基于距离的模型很重要)
    scaler = StandardScaler()
    X_train_scaled = scaler.fit_transform(X_train)
    X_test_scaled = scaler.transform(X_test)

    # 3. 定义模型
    if model_type == 'svm':
        # 可以先使用默认参数快速测试
        model = SVC(kernel='rbf', C=1.0, gamma='scale', random_state=random_state)
        # 也可以进行网格搜索寻找最优参数(耗时)
        # param_grid = {'C': [0.1, 1, 10], 'gamma': ['scale', 'auto', 0.01, 0.1]}
        # grid_search = GridSearchCV(SVC(random_state=random_state), param_grid, cv=3)
        # grid_search.fit(X_train_scaled, y_train)
        # model = grid_search.best_estimator_
    elif model_type == 'random_forest':
        model = RandomForestClassifier(n_estimators=100, max_depth=10, random_state=random_state)
    else:
        raise ValueError(f"不支持的模型类型: {model_type}")

    # 4. 训练模型
    print(f"训练 {model_type.upper()} 模型...")
    model.fit(X_train_scaled, y_train)

    # 5. 在测试集上评估
    y_pred = model.predict(X_test_scaled)
    accuracy = accuracy_score(y_test, y_pred)
    print(f"测试集准确率: {accuracy:.4f}")
    print("\n详细分类报告:")
    print(classification_report(y_test, y_pred))

    # 6. 绘制混淆矩阵
    cm = confusion_matrix(y_test, y_pred)
    plt.figure(figsize=(8, 6))
    sns.heatmap(cm, annot=True, fmt='d', cmap='Blues', xticklabels=model.classes_, yticklabels=model.classes_)
    plt.xlabel('预测标签')
    plt.ylabel('真实标签')
    plt.title(f'{model_type.upper()} 混淆矩阵 (准确率: {accuracy:.3f})')
    plt.show()

    return model, scaler, accuracy

# 注意:此处X, y需要你从所有提取的特征和标签中整合而来。
# 以下是一个模拟数据的示例,你需要替换为真实数据
# 假设我们有100个样本,每个样本有310个特征(62通道*5频带),3类情感标签
# X_simulated = np.random.randn(100, 310)
# y_simulated = np.random.randint(0, 3, 100)
# _ = train_evaluate_sklearn_model(X_simulated, y_simulated, model_type='svm')

4.2 构建一个简单的深度学习模型(PyTorch)

对于更复杂的模式或当数据量足够大时,深度学习模型,特别是卷积神经网络(CNN)和长短时记忆网络(LSTM),在情感EEG识别中展现出了强大的潜力。CNN可以捕捉脑电信号在空间(通道)和频率上的局部相关性,而LSTM则擅长处理信号的时间动态特性。

这里我们构建一个结合了CNN和LSTM的混合模型,它先将多通道的DE特征(可以重塑为“通道×频带”的2D图像)进行空间卷积,再用LSTM捕捉时间序列上的变化。

import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader, TensorDataset

class EEGNet_LSTM(nn.Module):
    """
    一个用于情感EEG分类的简单CNN-LSTM混合模型。
    输入形状: (batch_size, seq_len, channels, features)
    例如: (batch, 时间步长, 62, 5)
    """
    def __init__(self, num_channels=62, num_features=5, num_classes=3, lstm_hidden=128):
        super(EEGNet_LSTM, self).__init__()
        # 第一部分:CNN处理空间-频域特征
        self.conv1 = nn.Conv2d(in_channels=1, out_channels=16, kernel_size=(3, 3), padding=1)
        self.bn1 = nn.BatchNorm2d(16)
        self.pool1 = nn.MaxPool2d(kernel_size=(2, 2))
        self.conv2 = nn.Conv2d(16, 32, kernel_size=(3, 3), padding=1)
        self.bn2 = nn.BatchNorm2d(32)
        self.pool2 = nn.MaxPool2d(kernel_size=(2, 2))
        self.dropout1 = nn.Dropout(0.5)

        # 动态计算CNN输出展平后的尺寸
        # 这里简化计算,实际需要根据输入尺寸推导或使用自适应池化
        self.cnn_output_size = 32 * (num_channels // 4) * (num_features // 4) # 假设两次池化后尺寸

        # 第二部分:LSTM处理时间序列
        self.lstm = nn.LSTM(input_size=self.cnn_output_size, hidden_size=lstm_hidden,
                            batch_first=True, bidirectional=True)
        self.dropout2 = nn.Dropout(0.5)
        # 双向LSTM,输出维度为 hidden_size * 2
        self.fc = nn.Linear(lstm_hidden * 2, num_classes)

    def forward(self, x):
        # x shape: (batch, seq_len, channels, features)
        batch_size, seq_len, C, F = x.size()
        # 将时间步和批次合并,以便CNN独立处理每个时间步的“图像”
        x = x.view(batch_size * seq_len, 1, C, F)

        # CNN部分
        x = torch.relu(self.bn1(self.conv1(x)))
        x = self.pool1(x)
        x = torch.relu(self.bn2(self.conv2(x)))
        x = self.pool2(x)
        x = self.dropout1(x)
        # 展平
        x = x.view(batch_size * seq_len, -1)

        # 恢复时间序列维度
        x = x.view(batch_size, seq_len, -1)

        # LSTM部分
        lstm_out, (h_n, c_n) = self.lstm(x)
        # 取最后一个时间步的输出
        x = lstm_out[:, -1, :]
        x = self.dropout2(x)
        # 全连接分类层
        out = self.fc(x)
        return out

def train_deep_model(model, train_loader, val_loader, num_epochs=50, lr=0.001):
    """
    训练深度学习模型。
    """
    device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
    print(f"使用设备: {device}")
    model.to(device)

    criterion = nn.CrossEntropyLoss()
    optimizer = optim.Adam(model.parameters(), lr=lr)
    scheduler = optim.lr_scheduler.ReduceLROnPlateau(optimizer, mode='min', patience=5, factor=0.5)

    train_losses, val_losses, val_accs = [], [], []

    for epoch in range(num_epochs):
        model.train()
        running_loss = 0.0
        for batch_X, batch_y in train_loader:
            batch_X, batch_y = batch_X.to(device), batch_y.to(device)
            optimizer.zero_grad()
            outputs = model(batch_X)
            loss = criterion(outputs, batch_y)
            loss.backward()
            optimizer.step()
            running_loss += loss.item() * batch_X.size(0)

        epoch_train_loss = running_loss / len(train_loader.dataset)
        train_losses.append(epoch_train_loss)

        # 验证阶段
        model.eval()
        val_loss = 0.0
        correct = 0
        total = 0
        with torch.no_grad():
            for batch_X, batch_y in val_loader:
                batch_X, batch_y = batch_X.to(device), batch_y.to(device)
                outputs = model(batch_X)
                loss = criterion(outputs, batch_y)
                val_loss += loss.item() * batch_X.size(0)
                _, predicted = torch.max(outputs.data, 1)
                total += batch_y.size(0)
                correct += (predicted == batch_y).sum().item()

        epoch_val_loss = val_loss / len(val_loader.dataset)
        epoch_val_acc = correct / total
        val_losses.append(epoch_val_loss)
        val_accs.append(epoch_val_acc)

        scheduler.step(epoch_val_loss)

        if (epoch + 1) % 10 == 0:
            print(f'Epoch [{epoch+1:03d}/{num_epochs}], '
                  f'Train Loss: {epoch_train_loss:.4f}, '
                  f'Val Loss: {epoch_val_loss:.4f}, '
                  f'Val Acc: {epoch_val_acc:.4f}')

    print('训练完成。')
    # 绘制训练曲线
    plt.figure(figsize=(12, 4))
    plt.subplot(1, 2, 1)
    plt.plot(train_losses, label='Train Loss')
    plt.plot(val_losses, label='Val Loss')
    plt.xlabel('Epoch')
    plt.ylabel('Loss')
    plt.legend()
    plt.title('训练和验证损失')

    plt.subplot(1, 2, 2)
    plt.plot(val_accs)
    plt.xlabel('Epoch')
    plt.ylabel('Accuracy')
    plt.title('验证集准确率')
    plt.tight_layout()
    plt.show()

    return model

# 注意:以下代码需要真实的、按时间序列组织的数据来运行。
# 假设我们已经将数据组织成了 (样本数, 时间步长seq_len, 通道数, 频带数) 的numpy数组 X_sequence
# 以及对应的标签 y
# 然后转换为PyTorch张量并创建DataLoader
# X_tensor = torch.FloatTensor(X_sequence)
# y_tensor = torch.LongTensor(y)
# dataset = TensorDataset(X_tensor, y_tensor)
# train_size = int(0.8 * len(dataset))
# val_size = len(dataset) - train_size
# train_dataset, val_dataset = torch.utils.data.random_split(dataset, [train_size, val_size])
# train_loader = DataLoader(train_dataset, batch_size=32, shuffle=True)
# val_loader = DataLoader(val_dataset, batch_size=32, shuffle=False)
# model = EEGNet_LSTM(num_channels=62, num_features=5, num_classes=3)
# trained_model = train_deep_model(model, train_loader, val_loader, num_epochs=30)

5. 实验设计与性能提升技巧

搭建好基础流程后,要想获得可靠且具有竞争力的结果,还需要在实验设计上下功夫。脑电情感识别有几个独特的挑战,比如被试者间差异大、数据量相对较小、信号噪声高等。

  • 被试者独立与依赖实验:这是最重要的设计决策之一。在被试者依赖实验中,同一个被试者的数据会同时出现在训练集和测试集中,模型容易学习到个体特异性特征,准确率往往虚高,但泛化能力存疑。而在被试者独立实验中,测试集的数据来自训练集未出现过的被试者,这对模型的泛化能力是真正的考验,也是更贴近实际应用的场景。在SEED-VII上做研究,我建议至少报告被试者独立的实验结果。

  • 交叉验证策略:对于被试者独立实验,可以采用“留出一个被试者”(Leave-One-Subject-Out, LOSO)的交叉验证。即每次选择一个被试者作为测试集,其余所有被试者作为训练集,循环直到每个被试者都被测试过一次,最后取平均性能。这种方法能充分利用有限的数据,并给出模型泛化能力的稳健估计。

  • 数据增强:针对EEG数据量小的问题,数据增强是有效的缓解过拟合手段。除了经典的添加高斯噪声、随机缩放、时间偏移外,针对EEG的增强方法还有:

    • 通道丢弃:随机屏蔽一部分通道,模拟电极接触不良。
    • 频带随机增强:随机增强或减弱某个特定频带的能量。
    • 混合样本:在特征层面或原始信号层面混合两个样本,生成新样本。
  • 多模态融合:SEED-VII的优势在于多模态。不要只盯着EEG数据。可以尝试将眼动特征(如注视点、瞳孔直径变化)或生理信号特征(如皮电、心电)与EEG特征进行融合。融合可以在特征层(早期融合)、模型层(中期融合)或决策层(晚期融合)进行。例如,可以分别用两个神经网络处理EEG和眼动特征,然后在全连接层之前进行拼接。

# 一个简单的晚期融合示例(决策层平均)
def late_fusion(pred_eeg, pred_eye, method='average'):
    """
    pred_eeg, pred_eye: 来自EEG模型和眼动模型的预测概率向量(softmax输出)。
    method: 'average' 或 'weighted'
    """
    if method == 'average':
        final_pred = (pred_eeg + pred_eye) / 2
    elif method == 'weighted':
        weight_eeg = 0.7  # 假设我们更信任EEG模型
        weight_eye = 0.3
        final_pred = weight_eeg * pred_eeg + weight_eye * pred_eye
    else:
        raise ValueError("融合方法必须是 'average' 或 'weighted'")
    return final_pred
  • 可视化与可解释性:模型为什么做出某个决策?我们可以通过绘制地形图来可视化不同情感状态下,各个脑区或频带的激活差异。对于深度学习模型,可以使用梯度加权类激活映射等方法,来观察输入EEG信号的哪些部分对模型的决策贡献最大。这不仅能增加研究的可信度,也能帮助我们理解情感处理的大脑机制。

整个流程走下来,你会发现处理SEED-VII这样的数据集,更像是在完成一个系统工程。从环境配置、数据清洗、特征提取到模型构建与评估,每一步都需要耐心和细致的调优。我自己的经验是,在特征工程和实验设计上多花时间,其回报往往比盲目尝试更复杂的模型架构要大得多。刚开始结果不理想很正常,可能是预处理参数需要调整,也可能是特征选择不当,或者模型不适合当前的数据分布。多看看领域内顶会论文中大家是如何处理类似数据的,复现他们的基线方法,然后再进行自己的创新,这是一个比较高效的学习路径。

SEED-VII 实战指南:如何利用多模态脑电图数据提升情感识别精度 本文提供了SEED-VII多模态脑电图数据集的实战指南,详细介绍了如何利用该数据集提升情感识别精度。内容涵盖数据获取、预处理、特征提取,以及构建多模态融合神经网络模型的关键步骤,并强调了被试者独立评估的重要性,为情感计算研究者提供了从数据到模型的全流程解决方案。 阅读详情

相关推荐

SEED-VII数据集实战:5步搞定情感脑电图分析(附Python代码)

本文详细介绍了如何使用SEED-VII数据集进行情感脑电图分析,包括环境配置、数据预处理、特征提取、深度学习模型构建及结果可视化。通过5个步骤和附带的Python代码,帮助研究者高效处理EEG信号并实现情感分类,特别适合情感计算领域的新手和从业者。

weixin_42530458的博客 350

SEED、SEED-IV、SEED-V

每个mat文件中包含-----15个数组(每个数组包含一项实验中15个试验的分段预处理脑电数据,形状为[eeg_1-eeg_15,通道数×数据(与电影时长和频率有关)])总共45个.mat文件,每个实验1个,每个被试进行三次实验,间隔一周。15人×3次=45。:每个session被切成4s的非重叠片段,在模型训练过程中,每个段被视为1个数据样本。每次实验有15个试验trials(15个中国电影剪辑,每个电影片段4分钟。:3次(session,每个session包括24次试验):15名(男性7名,女性8名)

Einstein·Jun 3137

SEED-VII情感脑电数据集实战:从预处理到深度学习建模全流程解析

情感脑电是情绪识别与人机交互研究的重要方向,脑电信号凭借毫秒级时间分辨率,能够客观反映情绪状态,但实际应用中常面临数据获取困难、标注复杂等挑战。公开数据集如SEED系列为研究者提供了标准化基准,其中SEED-VII在情绪类别细粒度、多模态同步和连续维度标注上做了重要升级,支持更精细的情感建模与跨被试泛化评估。该数据集可支撑情绪七分类、唤醒度回归、多模态融合等任务,在智能心理评估、情感交互系统等场景中具有关键应用价值。本文基于SEED-VII的完整实践,系统介绍了从数据格式解析、预处理链路、特征工程到深度学习

weixin_33226548的博客 244

SEED-VII 数据集介绍:面向七类情绪识别的 EEG 与眼动多模态数据集

SEED-VII数据集是由上海交通大学BCMI实验室发布的多模态情绪识别数据集,包含62通道EEG信号和眼动信号,覆盖快乐、悲伤、恐惧、厌恶、惊讶、愤怒和中性七类情绪。该数据集创新性地采用视频诱发情绪,包含20名被试的80个视频片段(总时长超14000秒),并提供离散情绪类别和连续强度双标签。相比同类数据集,SEED-VII具有更完整的情绪类别覆盖,支持EEG与眼动的多模态融合研究,其连续强度标签为情绪动态分析提供了可能。基准实验显示多模态融合效果最佳(71.28%准确率),但跨被试识别仍具挑战性(40.9

Gigavision的博客 622

一文详细介绍情绪识别常用的数据集

一文详细介绍情绪识别常用的数据集SEED 本文详细介绍了脑机接口情绪识别常用的数据集,主要有SEED, SEED-IV, SEED-V, DEAP, CIAIC。 SEED SEED数据集下载地址 SEED数据集不开放给个人使用,若想使用必须写申请。 ...

丶夜未央丶的博客 2万+

SEED数据集

SEED数据集中ExtractedFeatures_4s与ExtractedFeatures_1s文件夹中一些数据的解释(这里大多以1_20131027.mat文件为例)1、数组文件名称(试验者名字 + 电影片段序列)以1_20131027.mat文件为例,这是第一个数据,采集于2013年10月27日。2、单个数组名称(大多采用“预处理方法 + 过滤方法 + 影片序号”)(1)psd:提取功率谱密度(2)asm:不对称(3)Dasm:微分不对称(4)Dcau:微分因果性(5)Rasm:有理不对称。

Hvhvhvjvhvhv的博客 3366

基于SEED、DEAP和DREAMER数据集构建情绪识别诊断系统的可行性分析报告

本报告分析了利用 SEED、DEAP 和 DREAMER 数据集开发情绪识别诊断系统的可行性。技术上,单数据集研究已实现较高准确率(部分超 90%),但转化为临床诊断系统面临多重挑战。多模态数据(EEG、ECG 等)虽能增强鲁棒性,却因格式、采集参数等差异带来融合难题。跨受试者 / 语料库泛化、伦理隐私及监管合规(如 FDA 准则)是主要瓶颈。 报告指出,需解决数据异构性、泛化能力、临床验证等问题,建议分阶段开发,组建多学科团队,优化数据策略,聚焦模型泛化,重视伦理与监管,以推动系统从研究走向临床应用。

qq_32516809的博客 1375

振动分析中相位的确定

更是定位不平衡量角度的关键。是确定振动信号与转子旋转位置之间时间关系的核心步骤,而。以内,以确保校正配重安装角度的有效性。在实际动平衡中,相位角的误差需控制在。在振动分析和动平衡中,

轩辕霸天L 2735

脑电情感识别SEED代码数据 下载方式

SEED数据集 脑电 情感 下载方式

Gigavision的博客 2278

Rope DFM一键整合包AI脸部替换教程:使用DeepFaceLab模型实现专业级视频和直播脸部替换

在AI脸部替换工具中,Rope凭借其出色的自动遮罩处理拔得头筹,结合DFM模型中的DFL XSeg遮罩处理,效果更是提升了一个台阶。因为在训练模型时,它已经通过大量的侧脸图片进行训练,达到了单张图片无法企及的效果。然而,想要通过DeepFaceLab获得专业级的效果,不仅难度大,时间成本也相当高,训练模型也是个不小的挑战。举个简单的例子,使用图片脸部替换时,如果佩戴眼镜,脸部替换后的可能会显得很假。另外,在视频中,比如歌手演唱时,话筒遮挡住脸部,这种场景也可以通过遮罩训练后的模型来处理,呈现出更逼真的效果。

m0_72580657的博客 8166

LM Studio中文版零基础入门:从安装到模型下载的保姆级教程(附国内镜像源配置)

本文提供LM Studio中文版的零基础入门教程,涵盖从软件安装、中文界面切换到国内镜像源配置的全流程。特别针对模型下载速度慢的问题,详细介绍了如何通过修改配置文件使用国内镜像站,并推荐适合新手的入门模型及硬件配置建议,帮助用户快速上手本地大模型运行。

pz890123456的博客 2611

情感计算新突破:SEED-VII数据集在心理健康监测中的5个实际应用案例

SEED-VII数据集作为情感计算领域的重要突破,通过整合情感脑电图、眼动及生理信号,为心理健康监测提供了高质量多模态数据。本文探讨了其在抑郁情绪早期筛查、心理治疗辅助评估、特殊教育适应性学习、职场压力风险管理及睡眠情绪干预等五个实际应用案例,展示了技术如何转化为可落地的产品价值。

ipfs8storage的博客 323

使用STM32微控制器实现光电传感器的接口和数据处理

本文将介绍如何使用STM32微控制器实现光电传感器的接口和数据处理的方案,包括硬件设计、引脚配置、数据采集、滤波和阈值判断等关键步骤,并给出相应的代码示例。本文将介绍如何使用STM32微控制器实现光电传感器的接口和数据处理的方案,包括硬件设计、引脚配置、数据采集、滤波和阈值判断等关键步骤,并给出相应的代码示例。在光电传感器的接口和数据处理的实现过程中,要关注系统的性能。在使用STM32微控制器实现光电传感器的接口和数据处理之前,需要设计一个合适的硬件平台。

weixin_66608063的博客 3206

SEED:基于SEED数据集的理解

研究影响情绪关键频段和信道,选择了4、6、9、12个通道进行实验。1.与积极、中性和消极情绪相关的神经特征确实存在,并且他们在不同个体之间存在共性。2.30次实验,即在两次不同的时间进行实验。研究情绪识别中的脑电图稳定性。1.调查了三种情绪的稳定神经模式。2.在研究情绪识别任务中,还应该考虑性别、年龄和种族等重要因素。

IN year 4866

Apache-Plc4x-Modbus-Tcp学习记录(一)

Apache-Plc4x-Modbus-Tcp学习记录(一)

weixin_45325423的博客 6989

OpenClaw 开机自启配置:Windows/macOS/Linux 全平台技术指南

OpenClaw 开机自启全平台配置指南 本文提供了在Windows、macOS和Linux系统上配置OpenClaw开机自启的完整技术方案。主要内容包括: Windows配置:提供三种方法(Windows服务、任务计划程序、启动文件夹),详细说明服务脚本创建、服务注册和测试流程 macOS配置:使用LaunchAgent实现开机自启,包含配置文件创建和加载方法 Linux配置:通过systemd服务实现,包括服务单元文件创建和启用步骤 系统要求:明确硬件(64位CPU/4GB内存)和软件(各平台操作系统版

一键难忘的博客 2995

【第84期】过拟合欠拟合与复杂度对照的实验与排查

系列:《从小白到 AI 大模型开发工程师的进阶之路》 技术点:AI-0306 过拟合与欠拟合 主人公:小蓝伞 前置:AI-0302 本期产出:学习曲线小蓝伞用越来越高次的多项式拟合一条带噪曲线,训练误差继续下降,但这不能说明他更接近真实关系。本期固定训练样本与噪声,只改变多项式次数,比较对训练点和无噪参考曲线的误差,并区分验证曲线与学习曲线。教学情境用来组织问题,下面的执行数据来自构造实验。

qq_36085846的博客 171

Idea插件Plugins无法联网下载 解决方法(终极版必解决!)

插件联网的Bug疑难杂症,网络上不一样的解决方式以及独特的彩蛋!包解决

码农研究僧的博客 5518

从认识人工智能到理解机器学习:我的 AI 入门第一课

正式开始学习人工智能之前,我对 AI 的认识其实比较模糊:知道 ChatGPT、知道大模型,也知道现在很多岗位都在谈 AI,但如果有人问我“人工智能到底是什么”“机器是怎么学会识别一只猫的”,我未必能从头到尾讲清楚。上午的课程更偏向行业全景:人工智能的发展、研究方向、NLP、岗位分工、学习路线和 AI Coding 工具。下午的课程更偏向技术基础:机器学习的原理、有监督学习、深度学习、模型训练和评估方法。

chh563的博客 533

4、接收天线:接收电压、功率和传输系数

本文详细探讨了接收天线的性能参数,包括接收电压、功率和传输系数,通过建立等效电路模型分析接收偶极子的电流和开路电压,并定义电压和功率传输函数。研究揭示了天线长度、工作频率及极化失配对传输性能的影响,并提出了优化接收天线性能的策略,为天线系统的设计与优化提供了理论支持。

ll5678的博客 235

贝叶斯统计 [韦来生编]

贝叶斯(Bayes)统计是近几十年来迅速发展起来的数理统计的一个重要分支。贝叶斯方法与经典统计方法的主要不同之处是进行统计推断时除了利用样本信息外,还要利用参数的先验信息,因此可以提高统计推断或统计决策的效果。它在经济、金融、生物、医学、自然科学和社会科学等许多领域具有广泛的应用。贝叶斯方法的研究已渗透到了统计学的几乎所有领域。作者在给中国科学技术大学概率论与数理统计专业本科生开设的“数理统计”课程中有一章专门讲授“贝叶斯方法和统计决策理论”,近十年来也给中国科学技术大学概率统计专业研究生开设过几次“贝叶斯分析”课。本书是在对过去讲稿的内容作了适当的增减和调整的基础上完成的。  本书共分六章。第1章是绪论,介绍了贝叶斯统计的若干基本概念,同时对必要的数理统计的基础知识有重点地作了回顾。第二章介绍了确定先验分布的若干可供选择的方法。第三章介绍了几类常见统计模型参数的后验分布的主要结果和计算方法。第四章和第五章分别介绍了“贝叶斯统计推断”和“贝叶斯统计决策”的内容。第六章介绍了贝叶斯统计计算的若干方法,包括蒙特卡洛方法和马尔可夫链蒙特卡洛(MCMC)方法以及相关统计软件的简介。本书的每一章附右大量的例题和习题,书末附有常用的几个表格和部分习题解答供读者查用。  大约可在54学时内讲授本书的第1章至第六章的主要内容,教师可根据学时需要选讲本书第1章至第六章的部分内容。书中标“*”号的章节可略去不讲,留给读者作为阅读材料。第六章例题中的R代码和部分例题的数据文件等可从作者个人主页下载。  作者在编写本书的过程中参考了J.O.Berger教授的《统计决策论及贝叶斯分析》和茆诗松教授编写的《贝叶斯统计》等书中的一些内容,以及《贝叶斯分析》一书中由张伟平博士编写的有关贝叶斯统计计算的一些内容。在此表示衷心感谢。  本书编写准备过程中,中国科学技术大学统计与金融系的几个研究生帮助完成了本书部分中文的录入和排版,作者对他们的辛勤工作表示真诚的感谢。高等教育出版社为本书的出版给予了大力支持。在此一并致谢。

小批量训练的JAX机器学习

本文介绍基于BFGS的小批量训练算法,针对全批量方法在高样本量下计算复杂度高的问题,结合JAX的自动微分与高效计算,实现快速收敛。通过optimtool库中的改进的bfgs优化器与小批量策略,支持分类与回归任务,具备早停机制与灵活监控选项,显著提升大规模机器学习训练效率。

林景的博客 133

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

低温ECR-MOCVD生长(002)GaN:椭偏仪测厚与电学性能

本研究用ECR-MOCVD在Si(111)和蓝宝石衬底上低温生长GaN薄膜,蓝宝石1.0SCCM、2h样品呈完全(002)择优取向,椭偏仪测厚32.81nm,迁移率10.42cm²/(V·s)。文章梳理了生长温度、TMGa流量与生长时间对结晶取向、膜厚、粗糙度、光学带隙和电学性能的影响。

Flexfilm_的博客 396

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

上一篇: 如何用CiteSpace高效分析CNKI文献?手把手教你从数据导出到生成可视化图谱
下一篇: 麒麟V10 SP1桌面版安装svmspro deb包保姆级教程(含服务状态检查)
sql99
博客等级 码龄19年 168粉丝 1905原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值