%% ECG检测代码
%%时间:2019年7月26日
%%华中科技大学_光学与电子信息学院_生产实习
%%Aiwiscal
%% *****************代码说明***************************
%* 心电数据(采样频率360hz,单位mv)
%* 本代码可以检测5种ECG图反应的情况。
% 0 No TQRS
% 1 N Normal beat 正常搏动
% 4 L Left bundle branch block beat 左束支传导阻滞
% 3 R Right bundle branch block beat 右束支传导阻滞
% 2 V Premature ventricular contraction 室性早搏
% 5 A Atrial premature beat 房性早搏
%% ******************工具箱注意事项*******************
%*本代码使用了一维CNN工具箱实现,工具箱(1DCNN)说明如下:
%* (1)只支持卷积层和池化层互相交错放置。
%* (2)卷积层不会对特征图外围进行零填充,也就是每次卷积后特征图长度会减少kernelsize-1.
%* (3)池化层的每一步池化区域不重叠,即池化区域长度等于步长。
%* (4)池化层的输入特征图长度必须能被池化步长整除。
%* 注意:参数的调整必须考虑和满足以上的限制,否则就会报错。
%*****************使用说明**********************************
%* 利用cnn.layers定义CNN架构
%* (1)网络层的类型由‘type’指定,后接‘i’,‘c’,‘s’分别指输入层,卷积层和池化层
%* (2)“outputmaps”设定该层有多少输出特征图,从而决定卷积核的数目
%* (3)“kernelsize”后的数字指定1维卷积核的大小
%* (4)“scale”后数字指定池化步长
%* (5)“pool”后的字符串指定池化类型,“mean”:平均池化,“max”:最大池化
%* (6)“actv”后的字符串指定激活函数类型,“relu”:ReLU,“sigm”:Sigmoid,“tanh”:Tanh
%* (7)cnn.output="softmax"指后面的全连接层要进行softmax操作,另外还可以指定为“sigm”:使用sigmoid函数映射,“linear”:线性映射。
%************************************************************
clear;clc;
%% 载入数据
fprintf('Loading data...\n');
tic; %tic用来保存当前时间,使用toc来记录程序完成时间
%方案1:
load('C:\Users\pc\Desktop\学习与工作\集创赛资料\matlab文件\matlab截取的用于分类的心拍数据\Nb.mat'); % 正常搏动
load('C:\Users\pc\Desktop\学习与工作\集创赛资料\matlab文件\matlab截取的用于分类的心拍数据\Lb.mat'); %左束支传导阻滿
load('C:\Users\pc\Desktop\学习与工作\集创赛资料\matlab文件\matlab截取的用于分类的心拍数据\Rb.mat'); %右束支传导阻滿
load('C:\Users\pc\Desktop\学习与工作\集创赛资料\matlab文件\matlab截取的用于分类的心拍数据\Vb.mat'); %室性早搏
load('C:\Users\pc\Desktop\学习与工作\集创赛资料\matlab文件\matlab截取的用于分类的心拍数据\Ab.mat'); %房性早搏
fprintf('Finished!\n');
toc;
fprintf('=============================================================\n');
%% 控制使用数据量,并生成标签
fprintf('Data preprocessing...\n');
tic;
Nb=Nb(1:2500,:);Label1=repmat([1;0;0;0;0],1,2500); %repmat 将矩阵复制M*N矩阵 其中1000表示正常搏动 5
Vb=Vb(1:2500,:);Label2=repmat([0;1;0;0;0],1,2500); %01000表示室性早搏
Rb=Rb(1:2500,:);Label3=repmat([0;0;1;0;0],1,2500); %00100表示右束支传导阻滿
Lb=Lb(1:2500,:);Label4=repmat([0;0;0;1;0],1,2500); %00010表示左束支传导阻滿
Ab=Ab(1:2500,:);Label5=repmat([0;0;0;0;1],1,2500); %00001表示房性早搏
Data=[Nb;Vb;Rb;Lb;Ab]; %五组数据集
Label=[Label1,Label2,Label3,Label4,Label5]; %label标记疾病名称
clear Nb;clear Label1;
clear Rb;clear Label2;
clear Lb;clear Label3;
clear Vb;clear Label4;
clear Ab;clear Label5;
Data=Data-repmat(mean(Data,2),1,250); %使信号的均值为0,去掉基线的影响;mean(x,2)返回每行平均值
fprintf('Finished!\n');
toc;
fprintf('=============================================================\n');
%% 数据划分与模型训练测试;
fprintf('Model training and testing...\n');
Nums=randperm(12500); %随机打乱样本顺序,达到随机训练测试样本的目的
train_x=Data(Nums(1:10000),:); %随机划分割训练集和测试集
test_x=Data(Nums(10001:end),:); %train_x为训练数据集
train_y=Label(:,Nums(1:10000)); %test_x测试数据集
test_y=Label(:,Nums(10001:end)); %train_y训练label
train_x=train_x'; %转置操作
test_x=test_x';
%% 利用1维CNN工具箱建立CNN(工具箱说明请见代码开头)
cnn.layers = {
struct('type', 'i') %输入屿
struct('type', 'c', 'outputmaps', 4, 'kernelsize', 31,'actv','relu') %卷积屿卷积核大小31,输出4张特征图片,激活函数ReLu
struct('type', 's', 'scale', 5,'pool','max') %池化层,池化步长5,采用最大值池化
struct('type', 'c', 'outputmaps', 8, 'kernelsize', 6,'actv','relu') %卷积层,卷积核大8,激活函数ReLu
struct('type', 's', 'scale', 3,'pool','max') %池化层,池化步长3,采用最大值池化
};
cnn.output = 'softmax'; %确定cnn结构,使用SOFTMAX进行分类
%% 确定超参数;
opts.alpha = 0.01; %学习率;
opts.batchsize = 16; %batch块大小;
opts.numepochs = 30; %迭代次数:反向传播迭代学习次数
%% 建立、训练、测试1维CNN
cnn = cnnsetup1d(cnn, train_x, train_y); %建立1维CNN;
cnn = cnntrain1d(cnn, train_x, train_y,opts); %训练1维CNN;
[er,bad,out] = cnntest1d(cnn, test_x, test_y); %测试1维CNN;
%% 输出结果
[~,ptest]=max(out,[],1); %[Y,I]=max(M,[],1), 在第1维方向上取最大值,每列最大值结果存在Y里,I里存的是每列最大值的行位置
[~,test_yt]=max(test_y,[],1); %~代表忽略输出参数 只需要第二个参数使获取最大值行号
Correct_Predict=zeros(1,5); %统计各类准确率;
Class_Num=zeros(1,5); %并得到混淆矩阵;
Conf_Mat=zeros(5);
for i=1:2500
Class_Num(test_yt(i))=Class_Num(test_yt(i))+1;
Conf_Mat(test_yt(i),ptest(i))=Conf_Mat(test_yt(i),ptest(i))+1;
if ptest(i)==test_yt(i) %预测值行号与LABEL行号吻合,意味着预测正确
Correct_Predict(test_yt(i))= Correct_Predict(test_yt(i))+1;
end
end
%% 打印输出结果
ACCs=Correct_Predict./Class_Num; %获得各类别正确率
fprintf('Accuracy = %.2f%%\n',(1-er)*100);
fprintf('Accuracy_N = %.2f%%\n',ACCs(1)*100);
fprintf('Accuracy_V = %.2f%%\n',ACCs(2)*100);
fprintf('Accuracy_R = %.2f%%\n',ACCs(3)*100);
fprintf('Accuracy_L = %.2f%%\n',ACCs(4)*100);
fprintf('Accuracy_A = %.2f%%\n',ACCs(5)*100);
%% 混淆矩阵输出
mat = Conf_Mat;
imagesc(mat); %绘制混淆矩阵
%% 混淆矩阵颜色设置
map = [0.968627451 0.984313725 1
0.937254902 0.960784314 0.988
0.780392157 0.858823529 0.937254902
0.603921569 0.784313725 0.898039216
0.137254902 0.450980392 0.713725490
0.0313725490 0.188235294 0.419607843];
%colormap(flipud(gray)); %改变混淆矩阵方式为GRAY(灰度方式),黑色表示高
colormap(map);
%% LABEL、TITLE设置
title('Confusion Matrix of ECG CNN');
textStrings = num2str(mat(:),'%0.02f'); %从矩阵值创建字符串
textStrings = strtrim(cellstr(textStrings)); %删除任合空间填充
xlabel('******训练结果******')
ylabel('******真实结果******')
%% 坐标轴设置
[x,y] = meshgrid(1:5); %为字符串创建x和y坐标
hStrings = text(x(:),y(:),textStrings(:),... %显示字符串
'HorizontalAlignment','center');
midValue = mean(get(gca,'CLim')); %获取颜色范围中间值
textColors = repmat(mat(:) > midValue,1,3); %选择文本颜色
%设置文本颜色和背景颜色不同以便能够分辨字符串
set(hStrings,{'Color'},num2cell(textColors,2)); %改变字符串颜色
set(gca,'XTick',1:5,... %设置轴标记
'XTickLabel',{'N','V','R','L','A'},...
'YTick',1:5,...
'YTickLabel',{'N','V','R','L','A'},...
'TickLength',[0 0]);