基于OpenBMI的运动想象分类

数据集

在本次实验中,我们用到了OpenBMI 运动想象脑电数据集,在这个用于通用BCI研究的开放数据集中,EEG信号被记录与大量受试者(54名参与者)多个测试(在不同的被试者进行两次session),以及使用多种范式(MI,ERP和SSVEP)。因此,数据集可以支持广泛的BCI研究,例如依赖于受试者或独立的BCI,会话到会话的转移,以及用户BCI性能的预测,等等。本次实验只使用MI范式,数据集中包含54个被试,每个被试中包含两个session,其中session1是前一天的BCI测试,session2是后一天的测试结果,每一个session中包含train和test两个文件,其中每个文件包含100个trial(50次左脑测试,50次右脑测试)。
数据集下载地址:openBMI
数据说明见论文 Lee MH, Kwon OY, Kim YJ, Kim HK, Lee YE,Williamson J, Fazli S, Lee SW. EEG dataset and OpenBMI toolbox for three BCI
paradigms: an investigation into BCI illiteracy. Gigascience. 2019 May 1;8(5):giz002. doi: 10.1093/gigascience/giz002. PMID: 30698704; PMCID: PMC6501944.
论文链接:gigascience

数据预处理及特征提取

脑电信号是一种 5-100μv和低频的生物电信号,需放大后才能显示和处理。在脑电信号处理与模式识别系统中,为了正确的识别 EEG 信号,信号的处理应该包括以下三个部分:预处理、特征选择与提取以及特征分类。信号预处理主要为了去除低频噪声干扰,如利用空间滤波器(CAR)滤除眼电、肌电等低频噪声干扰。特征提取与选择主要是为了降低脑电数据的维数和提取出与分类相关的特征。对于采集到的EEG信号而言,空域滤波(spatial filter)很适合处理这种多维信号和数据,能够同步利用EEG信号的空间相关性,可以对信号的噪声的消除,并且可以实现局部皮层神经活动的定位。空域滤波结合时域和频域特征进行有效结合,往往能够获取更好的处理效果。EEG-BCI研究中使用最广泛的空域滤波技术是CSP。共空间模式(CSP)是一种对两分类任务下的空域滤波特征提取算法,能够从多通道的脑机接口数据里面提取出每一类的空间分布成分。公共空间模式算法的基本原理是利用矩阵的对角化,找到一组最优空间滤波器进行投影,使得两类信号的方差值差异最大化,从而得到具有较高区分度的特征向量。
在本次实验中,我们将数据集中MI范式的数据集传入matlab中,利用train中的数据首先进行通道选择,然后将数据通过一个8-30Hz的空间滤波器进行滤波,然后以10ms为时间片进行数据分段,将预处理好的数据进行CSP特征处理,至此数据预处理和特征提取结束。将提取好特征的数据放入LDA二元分类器进行训练,最后使用不同的验证方式将分类好的数据和test数据进行对比得出结论。

样例(LDA分类器,不跨被试)

数据导入

        [CNT_tr,CNT_te] = Load_MAT(fullfile('D:\OpenBMI-master\DATA\OpenBMI',sprintf('sub%d',i),[sprintf('sess%02d_',session),sprintf('subj%02d',i),'_EEG_MI.mat']));

数据预处理

脑电图数据(CNT)在8至30 Hz的频率范围内进行滤波,并选择与电机相关的通道。然后,连续的脑电图数据在预定义的时间间隔内被分割(SMT)。空间滤波器CSP_W和分类器参数(CF_PARAM)被计算出来,这些参数用于从测试数据生成分类器输出。

		%数据预处理
		CNT_tr = prep_selectChannels(CNT_tr,{'Index',1:20});%划分电极1到20
        CNT_tr = prep_filter(CNT_tr,{'frequency',[8,30]});%划分频率8到30赫兹
        SMT_tr = prep_segmentation(CNT_tr,{'interval',[1000,3500]});%数据分段,10ms分一段,为250X20X100(trials)
        %CSP训练
        [CSP_tr,CSP_W] = func_csp(SMT_tr,{'nPattern',[2]});%空间滤波器
        FT_tr=func_featureExtraction(CSP_tr,{'feature','logvar'});%特征提取
        CF_PARAW=func_train(FT_tr,{'classifier','LDA'});%计算分类器参数

绩效评估

测试数据使用与训练数据相同的函数和参数进行预处理。将投影矩阵CSP_W应用于测试数据,并提取对数方差特征。然后通过比较分类器输出cf_out和真实类标签.y_dec测试数据来计算解码精度。

        CNT_te = prep_selectChannels(CNT_te,{'Index',1:20});
        CNT_te = prep_filter(CNT_te,{'frequency',[8,30]});
        SMT_te = prep_segmentation(CNT_te,{'interval',[1000,3500]});
        CSP_te = func_projection(SMT_te,CSP_W);
        FT_te = func_featureExtraction(CSP_te,{'feature','logvar'});
        cf_out = func_predict(FT_te,CF_PARAW); 
        loss = eval_calLoss(FT_te.y_dec,cf_out);

可视化

vis_plotController(averaged_SMT,rval_SMT,{'Interval',var_ival;'Channels',{'C3','C4'};
    'Class',{'right','left'};'TimePlot','on';'TopPlot','on'})

相关函数可查阅openBMI工具箱

分类器

LDA线性判别器

简述

LDA(线性判别器)是一种经典的监督线性降维方法,能有效提取反映不同类别之间差异的特征。 LDA的思想非常朴素:给定训练样例集,设法将样例投影到一条直线上,使得同类样例的投影点尽可能接近、异类样例点的投影点尽可能远离;在对新样本进行分类时,将其投影到同样的这条直线上,再根据投影点的位置来确定新样本的类别。

算法流程

  1. 计算来自X1,X2两类的均值向量和协方差矩阵。
  2. 计算类内总离散度矩阵 S w = ∑ 0 + ∑ 1 S_w=\sum_0+\sum_1 Sw=0+1
  3. W = S w − 1 ( u 0 − u 1 ) W=S^{-1}_w(u_0-u_1) W=Sw1(u0u1)求解W。
  4. 计算阈值y0。
  5. 如果 y = W > y 0 y=W>y_0 y=W>y0,则x∈x0,否则x∈x1。
    算法流程图如下:
    K分类器的算法流程

matlab实现(样例)

clear all;close all;clc;
x=[0.697,0.774,0.634,0.608,0.556,0.403,0.481,0.437,0.666,0.243,0.245,0.343,0.639,0.657,0.360,0.593,0.719];
y=[0.460,0.376,0.264,0.318,0.215,0.237,0.149,0.211,0.091,0.267,0.057,0.099,0.161,0.198,0.370,0.042,0.103];
z=[1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0];
%data=[x;y;z]';
data=[4,2,2,3,4,9,6,9,8,10
    2,4,3,6,4,10,8,5,7,8
    1,1,1,1,1,0,0,0,0,0]';
[m,n]=size(data);
for i=1:m
    if data(i,3)==1
        plot(data(i,1),data(i,2),'r.','MarkerSize', 12);hold on;
    end
    if data(i,3)==0
        plot(data(i,1),data(i,2),'b.','MarkerSize', 12);
    end
end
new_data=zeros(m,n-1);
cen1=zeros(1,2);cen0=zeros(1,2);%定义类中心
sum1=zeros(1,2);sum0=zeros(1,2);
num1=0;num0=0;
%计算类中心
for i=1:m
    if data(i,3)==1
        sum1(1,1)=sum1(1,1)+data(i,1);
        sum1(1,2)=sum1(1,1)+data(i,2);
        num1=num1+1;
    end
    if data(i,3)==0
        sum0(1,1)=sum0(1,1)+data(i,1);
        sum0(1,2)=sum0(1,2)+data(i,2);
        num0=num0+1;
    end
end
cen0=sum0/num0;cen1=sum1/num1;
%计算类内散度矩阵Sw
Sw=zeros(2,2);
for i=1:m
    if data(i,3)==1
        Sw=Sw+(data(i,[1 2])-cen1(1,:))'*(data(i,[1 2])-cen1(1,:));
    end
    if data(i,3)==0
        Sw=Sw+(data(i,[1 2])-cen0(1,:))'*(data(i,[1 2])-cen0(1,:));
    end
end
%计算类间散度矩阵Sb;
Sb=(cen0-cen1)'*(cen0-cen1);
[L,D]=eigs(Sw\Sb',1);%计算最大特征值和特征向量
%显示投影线
k=L(1)/L(2);
b=0;
xx=0:10;
yy=k*xx;
plot(xx,yy)
%计算投影点并显示
new_data(:,1)=(k*data(:,2)+data(:,1))/(k*k+1);
new_data(:,2)=k*new_data(:,1);
new_data(:,3)=data(:,3);
for i=1:m
    if new_data(i,3)==1
        plot(new_data(i,1),new_data(i,2),'r+','MarkerSize', 5);
    end
    if new_data(i,3)==0
        plot(new_data(i,1),new_data(i,2),'b+','MarkerSize', 5);
    end
end
%axis([0 1 0 1])
axis([0 15 0 15])
hold on;

SVM(支持向量机)

简述

支持向量机(support vector machine)是一种二分类模型,它的目的是寻找一个平面来对样本进行分割,分割的原则是间隔最大化,最终转化为一个凸二次规划问题来求解。由简至繁的模型包括:
1>当训练样本线性可分时,通过硬间隔最大化,学习一个线性可分支持向量机;
2>当训练样本近似线性可分时,通过软间隔最大化,学习一个线性支持向量机;
3>当训练样本线性不可分时,通过核技巧和软间隔最大化,学习一个非线性支持向量机。

算法流程

  1. 读取训练数据集;
  2. 主成分分析法降维并去除数据之间的相关性;
  3. 数据规格化;
  4. SVM训练(选取径向基和函数)得到分类函数;
  5. 读取测试数据、降维、规格化;
  6. 用4、5产生的分类函数进行分类(多分类问题,采用一对一投票策略,归位得票最多的一类);
  7. 计算正确率.

matlab实现(样例)

%------------主函数----------------
clear all;
close all;
C = 10;  %成本约束参数
kertype = 'linear';  %线性核

%①------数据准备
n = 30;
%randn('state',6);   %指定状态,一般可以不用
x1 = randn(2,n);    %2行N列矩阵,元素服从正态分布
y1 = ones(1,n);       %1*N个1
x2 = 4+randn(2,n);   %2*N矩阵,元素服从正态分布且均值为5,测试高斯核可x2 = 3+randn(2,n); 
y2 = -ones(1,n);      %1*N个-1
 
figure;  %创建一个用来显示图形输出的一个窗口对象
plot(x1(1,:),x1(2,:),'bs',x2(1,:),x2(2,:),'k+');  %画图,两堆点
axis([-3 8 -3 8]);  %设置坐标轴范围
hold on;    %在同一个figure中画几幅图时,用此句

%②-------------训练样本
X = [x1,x2];        %训练样本2*n矩阵,n为样本个数,d为特征向量个数
Y = [y1,y2];        %训练目标1*n矩阵,n为样本个数,值为+1或-1
svm = svmTrain(X,Y,kertype,C);  %训练样本
plot(svm.Xsv(1,:),svm.Xsv(2,:),'ro');   %把支持向量标出来

%③-------------测试
[x1,x2] = meshgrid(-2:0.05:7,-2:0.05:7);  %x1和x2都是181*181的矩阵
[rows,cols] = size(x1);  
nt = rows*cols;                  
Xt = [reshape(x1,1,nt);reshape(x2,1,nt)];
%前半句reshape(x1,1,nt)是将x1转成1*(181*181)的矩阵,所以xt是2*(181*181)的矩阵
%reshape函数重新调整矩阵的行、列、维数
Yt = ones(1,nt);

result = svmTest(svm, Xt, Yt, kertype);

%④--------------画曲线的等高线图
Yd = reshape(result.Y,rows,cols);
contour(x1,x2,Yd,[0,0],'ShowText','on');%画等高线
title('svm分类结果图');   
x1=xlabel('X轴');  
x2=ylabel('Y轴'); 

%-----------训练样本的函数---------
function svm = svmTrain(X,Y,kertype,C)

% Options是用来控制算法的选项参数的向量,optimset无参时,创建一个选项结构所有字段为默认值的选项
options = optimset;    
options.LargeScale = 'off';%LargeScale指大规模搜索,off表示在规模搜索模式关闭
options.Display = 'off';    %表示无输出

%二次规划来求解问题,可输入命令help quadprog查看详情
n = length(Y);  %返回Y最长维数
H = (Y'*Y).*kernel(X,X,kertype);    
f = -ones(n,1); %f为1*n个-1,f相当于Quadprog函数中的c
A = [];
b = [];
Aeq = Y; %相当于Quadprog函数中的A1,b1
beq = 0;
lb = zeros(n,1); %相当于Quadprog函数中的LB,UB
ub = C*ones(n,1);
a0 = zeros(n,1);  % a0是解的初始近似值
[a,fval,eXitflag,output,lambda]  = quadprog(H,f,A,b,Aeq,beq,lb,ub,a0,options);
%a是输出变量,问题的解
%fval是目标函数在解a处的值
%eXitflag>0,则程序收敛于解x;=0则函数的计算达到了最大次数;<0则问题无可行解,或程序运行失败
%output输出程序运行的某些信息
%lambda为在解a处的值Lagrange乘子

epsilon = 1e-8;  
 %0<a<a(max)则认为x为支持向量,find返回一个包含数组X中每个非零元素的线性索引的向量。 
sv_label = find(abs(a)>epsilon);     
svm.a = a(sv_label);
svm.Xsv = X(:,sv_label);
svm.Ysv = Y(sv_label);
svm.svnum = length(sv_label);
%svm.label = sv_label;
end

%---------------测试的函数-------------
function result = svmTest(svm, Xt, Yt, kertype)
temp = (svm.a'.*svm.Ysv)*kernel(svm.Xsv,svm.Xsv,kertype);
%total_b = svm.Ysv-temp;
b = mean(svm.Ysv-temp);  %b取均值
w = (svm.a'.*svm.Ysv)*kernel(svm.Xsv,Xt,kertype);
result.score = w + b;
Y = sign(w+b);  %f(x)
result.Y = Y;
result.accuracy = size(find(Y==Yt))/size(Yt);
end

%---------------核函数---------------
function K = kernel(X,Y,type)
%X 维数*个数
switch type
case 'linear'   %此时代表线性核
    K = X'*Y;
case 'rbf'      %此时代表高斯核
    delta = 5;
    delta = delta*delta;
    XX = sum(X'.*X',2);%2表示将矩阵中的按行为单位进行求和
    YY = sum(Y'.*Y',2);
    XY = X'*Y;
    K = abs(repmat(XX,[1 size(YY,1)]) + repmat(YY',[size(XX,1) 1]) - 2*XY);
    K = exp(-K./delta);
end
end

结果分析

在不跨被试的验证中,由于只跑了一轮,模型预测精度在0.4到0.9之间浮动,54个被试的平均精度为0.65。在之后的跨被试的留一验证中,54个被试的平均预测精度为0.56,因为不同的被试对BMI测试的经验不同,并且不同被试测试相同信号的脑电波差异较大。

参考文献

LDA判别器:小淮槐-MATLAB实现线性判别分析
SVM分类器:小果果学长-【人工智能实验】SVM分类器的设计与应用
数据处理样例: EEG dataset and OpenBMI toolbox for three BCI
paradigms: an investigation into BCI illiteracy

创作不易,还望多多支持,请勿抄袭搬运,谢谢!

猜你喜欢

转载自blog.csdn.net/Yoker_/article/details/122495289