脑电模型实战系列(三):基于 KNN 的 DEAP 脑电情绪识别 KNN 算法与 Canberra 距离深度剖析(三)

🌟 引言:让机器学会"读心术"

在前两篇中,我们从 DEAP 数据集的脑波海洋起步,通过 FFT 和频带提取,炼出了 160 维的"情绪指纹"(频带标准差特征)。现在,是时候揭开最后一步的神秘面纱了:分类与预测

今天的主角是 K-近邻(K-Nearest Neighbors, KNN) 。在脑电(EEG)研究领域,KNN 因其无需参数假设、对非线性特征敏感而广受欢迎。本篇将深度剖析 knn_predict.py 的核心逻辑,重点探讨为什么在 EEG 识别中 Canberra 距离 比传统的欧氏距离更胜一筹。


1. KNN 算法:邻里智慧的"懒惰"艺术

KNN 是一种典型的**懒惰学习(Lazy Learning)**算法。它并不在训练阶段构建复杂的数学模型,而是将所有训练样本存储在内存中,直到预测时才进行计算。

1.1 核心逻辑

  1. 特征匹配:将当前测试样本的 160 维特征向量,与数据库中已有的 40 个试验特征逐一对比。

  2. 寻找邻居 :计算相似度(距离),找出距离最近的 个样本。

  3. 多数表决 :这 个邻居中哪类标签最多,新样本就属于哪类。

1.2 为什么 KNN 适合 EEG?

  • 非线性分布:EEG 信号受到个体差异、电极阻抗等多种非线性因素影响。KNN 不需要像线性回归那样预设数据分布,能直接捕捉局部的非线性模式。

  • 小样本友好:DEAP 数据集每个被试只有 40 个试验,这对于深度神经网络来说太少了,但对 KNN 却是"黄金规模"。


2. 距离度量:Canberra 距离的独特魅力

距离度量(Distance Metric)是 KNN 的灵魂。虽然欧氏距离(Euclidean Distance)最广为人知,但在处理 EEG 频域特征时,Canberra 距离(兰氏距离) 展现出了惊人的鲁棒性。

2.1 数学公式对比

  • 缺点:容易被数值较大的维度主导,对接近 0 的微小差异不敏感。

  • Canberra 距离:

    优点:它是一个加权的曼哈顿距离。分母的出现起到了归一化的作用。

2.2 为什么 Canberra 更好?

EEG 的标准差(STD)特征通常分布在很小的数值区间(如 0.01 到 0.5 之间)。

  1. 零值敏感性:如果一个频带的功率从 0 变为 0.01(意味着该频带从静默转为激活),欧氏距离认为温差仅为 0.01,微乎其微;而 Canberra 距离会将这一变化视为显著差异(贡献度趋近 1)。

  2. 抗噪能力:它能有效减弱由于个别通道干扰产生的大数值异常值对整体距离的影响。


3. 核心代码实现:EmotionPredictor

为了方便大家直接使用,我将项目中的预测逻辑封装成了一个标准的 Python 类。

Python

复制代码
import numpy as np
import scipy.spatial as ss
import scipy.stats as sst
import csv

class EmotionPredictor:
    def __init__(self, train_data_path, train_label_path):
        self.train_features = self._load_csv(train_data_path)
        self.train_labels = self._load_csv(train_label_path)
        self.k = 3

    def _load_csv(self, path):
        with open(path, 'r') as f:
            reader = csv.reader(f)
            return np.array(list(reader), dtype=np.float64)

    def predict(self, test_feature):
        """
        基于Canberra距离的KNN预测
        """
        # 1. 计算所有训练样本与测试样本的Canberra距离
        distances = [ss.distance.canberra(x, test_feature) for x in self.train_features]
        
        # 2. 获取距离最近的K个邻居的索引
        nearest_indices = np.argsort(distances)[:self.k]
        nearest_distances = np.sort(distances)[:self.k]
        
        # 3. 智能投票逻辑:比率阈值法 (Ratio Trick)
        # 如果最近邻与次近邻的距离差异极大 (ratio <= 0.7),则极度信任最近邻
        ratio = nearest_distances[0] / nearest_distances[1]
        
        if ratio <= 0.7:
            prediction = self.train_labels[0, nearest_indices[0]]
        else:
            # 否则采用众数投票(民主表决)
            prediction = sst.mode(self.train_labels[0, nearest_indices])[0][0]
            
        return prediction

4. 智能投票机制:为什么是 0.7?

项目中引入了一个非常有趣的优化:比率阈值(Ratio Threshold)

在预测时,我们不仅看谁是最近邻,还看它到底有多"近"。

这种策略有效地提升了模型在面对噪声数据时的分类准确率,将单被试准确率稳定在 70% 左右。


5. 情绪映射:回归 Russell 情绪环

KNN 输出的是 Arousal(唤醒度)和 Valence(效价)的离散级别(1-低,2-中,3-高)。最后一步是将其映射回具体的心理学定义:

Python

复制代码
def get_emotion_description(arousal, valence):
    """
    根据Arousal和Valence级别判断情绪类别
    """
    if arousal == 2.0 or valence == 2.0:
        return "Neutral (中性)"
    
    mapping = {
        (3.0, 1.0): "Fear/Stress (恐惧/压力 - 高唤醒低效价)",
        (3.0, 3.0): "Happy/Excited (快乐/兴奋 - 高唤醒高效价)",
        (1.0, 3.0): "Relax/Calm (放松/平静 - 低唤醒高效价)",
        (1.0, 1.0): "Sad/Depressed (悲伤/抑郁 - 低唤醒低效价)"
    }
    
    return mapping.get((arousal, valence), "Unknown")

🚀 结语:从特征到情绪的飞跃

到此为止,我们已经完成了从脑波原始信号到情绪预测结果的全流程。KNN 虽然简单,但通过 Canberra 距离 的加持和 智能投票 的优化,它成为了脑电识别入门的最佳切入点。

下一篇预告:

我们将跑通全链路实战,结合 OpenCV 实现预测结果的实时可视化------当算法识别出你很快乐时,屏幕上会自动弹出一个笑脸。

💡 思考题:

如果我们将 K 值从 3 增加到 10,准确率会发生什么变化?(提示:考虑过拟合与欠拟合的平衡)。欢迎在评论区留下你的见解!


系列回顾:

文章标签: #脑机接口 #机器学习 #KNN #Canberra距离 #情绪识别 #Python实战

相关推荐
一个没有感情的程序猿2 小时前
前端实现人体骨架检测与姿态对比:基于 MediaPipe 的完整方案
机器学习·计算机视觉·前端框架·开源
Dev7z2 小时前
基于Stanley算法的自动驾驶车辆路径跟踪控制研究
人工智能·机器学习·自动驾驶
_Li.2 小时前
机器学习-线性判别函数
人工智能·算法·机器学习
老蒋新思维3 小时前
创客匠人推演:当知识IP成为“数字心智”的架构师——论下一代认知服务的形态
网络·人工智能·网络协议·tcp/ip·机器学习·创始人ip·创客匠人
whaosoft-1434 小时前
51c自动驾驶~合集62
人工智能·机器学习·自动驾驶
OpenBayes4 小时前
Open-AutoGLM 实现手机端自主操作;PhysDrive 数据集采集真实驾驶生理信号
人工智能·深度学习·机器学习·数据集·文档转换·图片生成·蛋白质设计
哆啦叮当4 小时前
VADv2 基于概率规划的端到端自动驾驶模型
人工智能·机器学习·自动驾驶
_Li.6 小时前
机器学习-贝叶斯公式
人工智能·机器学习·概率论