脑电模型实战系列(三):基于 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实战

相关推荐
言之。3 分钟前
大模型 API 中的 Token Log Probabilities(logprobs)
人工智能·算法·机器学习
Cigaretter79 分钟前
Day 38 早停策略和模型权重的保存
python·深度学习·机器学习
小鸡吃米…25 分钟前
机器学习中的随机森林算法
算法·随机森林·机器学习
啊巴矲1 小时前
小白从零开始勇闯人工智能:机器学习初级篇(TF-IDF)
人工智能·机器学习·tf-idf
dulu~dulu1 小时前
机器学习---计算题总结
人工智能·机器学习·支持向量机·集成学习·贝叶斯分类器
Das11 小时前
【机器学习】03_贝叶斯决策
人工智能·机器学习
大厂技术总监下海1 小时前
你的个人AI工作站已就绪:Ollama开源框架,支持多模态、可定制、一键部署
人工智能·机器学习·开源
YukiMori232 小时前
基于Paddle微调ERNIE的中文情感分析实战教程
深度学习·机器学习
墨&白.2 小时前
机器学习速成笔记week9:决策树ID3、C4.5和CART的底层逻辑
笔记·决策树·机器学习
武子康2 小时前
大数据-207 如何应对多重共线性:使用线性回归中的最小二乘法时常见问题与解决方案
大数据·后端·机器学习