DEAP数据集是一个基于主要基于脑电信号的可用于做情绪分析的数据集。数据集是由data和label两类数据组成的。data的结构是40*40*8064(刺激* 通道*数据),值得说明的是通道中只有前32个通道是脑电信号。label的结构是40*40*4,前两个40分别是刺激源和通道,后一个4是valence,arousal,dominance,liking。
我在data数据中选择了前22个人的全部40种刺激的32个脑电通道数据,label数据中,提取前22个人的全部40种刺激对应的vlaence和arousal的label,并且依据V_A两维高低的两两组合,以4为阀值,将其分成四个类。
下面介绍我使用的K-means算法。
设原始数据为{x1,x2,...,xn},四类的中心位置是{u1,u2,u3,u4},xn和uk都是向量。
迭代公式是:
公式一:求出所有数据和初始化的随机数据的距离,然后找出距离每个初始数据最近的数据。在我的算法中,距离的定义是对应项作差后取绝对值求和。
公式二:求出所有和这个初始数据最近原始数据的距离的均值。
迭代结束是按次数来的
下面是源码
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% 使用DEAP基于K-means的情感计算 %%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
clear all;
close all;
clc;
% % % %载入数据
raw(1) = load('s01.mat');
raw(2) = load('s02.mat');
raw(3) = load('s03.mat');
raw(4) = load('s04.mat');
raw(5) = load('s05.mat');
raw(6) = load('s06.mat');
raw(7) = load('s07.mat');
raw(8) = load('s08.mat');
raw(9) = load('s09.mat');
raw(10) = load('s10.mat');
raw(11) = load('s11.mat');
raw(12) = load('s12.mat');
raw(13) = load('s13.mat');
raw(14) = load('s14.mat');
raw(15) = load('s15.mat');
raw(16) = load('s16.mat');
raw(17) = load('s17.mat');
raw(18) = load('s18.mat');
raw(19) = load('s19.mat');
raw(20) = load('s20.mat');
raw(21) = load('s21.mat');
raw(22) = load('s22.mat');
% % %对标签进行处理
lab = [];
for i = 1:22
for j = 1:40
if raw(i).labels(j,1) >= 4 && raw(i).labels(j,2) >= 4
lab = [lab;1];
elseif raw(i).labels(j,1) >= 4 && raw(i).labels(j,2) < 4
lab = [lab;2];
elseif raw(i).labels(j,1) < 4 && raw(i).labels(j,2) < 4
lab = [lab;3];
else
lab = [lab;4];
end
end
end
dlmwrite('label.txt',lab,'delimiter','n');
% % %对数据整形进行处理
data = [];
for i = 1:22
for j = 1:40
for k = 1:32
pre(k,:) = raw(i).data(j,k,:);
end
[sx sy] = size(pre);
xy = sx * sy;
data = [data;reshape(pre,1,xy)];
end
end
[m n]=size(data);
% % %k-means聚类
[u re]=KMeans(data,4);
[m n]=size(re);
dlmwrite('KM.txt',re,'delimiter','t');
% % %判断对错
sxy = xy + 1;
right = zeros(1,24);
wrong = zeros(1,24);
ji = 1;
for z = 1:4
for x = 1:4
for c = 1:4
for v = 1:4
if z == x || z == c || z == v || x == c || x == v || c == v
continue;
else
for i = 1:m
if (re(i,sxy) == 1 && lab(i) == z) || (re(i,sxy) == 2 && lab(i) == x) || (re(i,sxy) == 3 && lab(i) == c) || (re(i,sxy) == 4 && lab(i) == v)
right(ji) = right(ji) + 1;
else
wrong(ji) = wrong(ji) + 1;
end
end
ji = ji + 1;
end
end
end
end
end
dlmwrite('right.txt',right,'delimiter','n');
dlmwrite('wrong.txt',wrong,'delimiter','n');
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% K-means %%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%N是数据一共分多少类
%data是输入的不带分类标号的数据
%u是每一类的中心
%re是返回的带分类标号的数据
function [u re]=KMeans(data,N)
%%%数据相关信息获取
[m n]=size(data); %m是数据个数,n是数据维数
ma=zeros(n,1); %每一维最大的数
mi=zeros(n,1); %每一维最小的数
u=zeros(N,n); %随机初始化,最终迭代到每一类的中心位置
for i = 1:n
ma(i)=max(data(:,i));
mi(i)=min(data(:,i));
end
%%%为保证初始随机点不是太糟糕
count = 0;
quan = zeros(m,N);
while sum(quan(:,1)) == 0 || sum(quan(:,2)) == 0 || sum(quan(:,3)) == 0 || sum(quan(:,4)) == 0
for i=1:n
for j=1:N
u(j,i)=ma(i)+(mi(i)-ma(i))*rand(); %随机初始化
end
end
for i=1:N
tmp{i}=[];
for j=1:m
tmp{i}=[tmp{i};data(j,:)-u(i,:)];
end
end
for i=1:m
c=[];
for j=1:N
c=[c sum(abs(tmp{j}(i,:)))];
end
[junk index]=min(c);
quan(i,index)=sum(abs(tmp{index}(i,:)));
end
count
count = count + 1;
for i = 1:4
sum(quan(:,i))
end
end
loop = 1;
while 1
pre_u=u;
for i=1:N
tmp{i}=[];
for j=1:m
tmp{i}=[tmp{i};data(j,:)-u(i,:)];
end
end
%%%公式一的实现
quan = zeros(m,N);
for i=1:m
c=[];
for j=1:N
c=[c sum(abs(tmp{j}(i,:)))]; %两种距离定义方式
% c=[c norm(tmp{j}(i,:))];
end
[junk index]=min(c);
quan(i,index)=sum(abs(tmp{index}(i,:)));
% quan(i,index)=norm(tmp{index}(i,:));
end
%%%公式二的实现
for i=1:N
for j=1:n
u(i,j)=sum(quan(:,i).*data(:,j))/(sum(quan(:,i) + eps));
end
end
loop
loop = loop + 1;
for i = 1:4
u(i)
end
if loop > 0
break;
end
end
re=[];
for i=1:m
tmp=[];
for j=1:N
tmp=[tmp norm(data(i,:)-u(j,:))];
end
[junk index]=min(tmp);
re=[re;data(i,:) index];
end
end
跑完下来总结:
在代码编写技巧方面:
1、在Matlab中,有分母存在时,在分母项加上eps。
2、在代码中要让中间结果,和迭代进度输出,以备及时发现问题,避免浪费时间。
在算法评估方面:
1、如果以准确率作为评价标准,那么这次的准确率在54%,值得高兴。但是如果仔细分析这个结果会发现并不乐观,因为其错分率在46%,原因如下。
原来880个数据样本在4类数据的分布是:464,161,78,171;
而K-means跑下来给出的4类数据的分布是:875,2,1,2;
因为K-means只是按照距离分四类,而不是按照V_A两维高低来分四类,因此如果按照最佳的准确率的配对,则是上述结果。其实也最有可能是是上述结果。原因在于数据源本身不平衡,而现在所有的统计学习方法中对解决数据不平衡问题尚无很好的方法,可能解决这个问题的比较火热的方法是强化学习和对抗生成网络。
2、在本次实验中,所用的距离的定义可以继续考究。在本次实验中我所使用的是最为简单的,可以说是欧式距离的方法,甚至由于一开始发现如果对距离取平方再开放,那么在258048维的数据的背景下,数据量太大了。所以后来改用作差取绝对值求和。在这一点上可以考虑一下降维后再做处理。而在降维的方法中,我觉得可以考虑流型的降维方法。
3、因为这个代码前后跑了五次,前后跑了两个星期,除了由于我自己之前没有让输出中间过程与迭代进度导致我没有及时发现算法有问题而浪费了时间之外,算法本身也存在几个问题值得总结教训,
a.首先是初始点的选取,初始点的选取真的是极其重要,首先是点不能全数域取,而是要在各维的最值范围内取值,其次是要检验初始的四个点是不是都有属于各自的最近点。因为,我发现随机取四点,设为a,b,c,d,很可能发生在880个点中没有1个点距离d点最近,那么在后面的迭代中就会导致分母为零的情况发生(这也是我前面总结分母项要加上eps的原因),如果不及时制止就会导致这次的运算结果无效的后果。
而会导致这个这个原因我推测有两个方面,一个是由于数据集本身不平衡导致的,另一个可能是由于我按V_A高低组合分成四类不是很合理导致,当然这两个方面的原因也有相互交合的地方。
b.我所选取的这个数据体量在我的i5-3470(3.2GHZ),16G内存的配置下跑得并不快,24个小时大体上能迭代30次左右。而后来我发现,其实迭代10次之后,就没有变化不大了。