基于上一篇文章对于CNN网络结构的整理,我们将用matlab实现一维数据的CNN网络单\多输入和单\多输出。
文中字母含义详情可见上一篇文章。
一、加载数据与数据集划分
clc;clear;close all;data=load('data.csv')'; %总数据label=load('label.csv')'; %总标签[train_x,train_y,test_x,test_y,val_x,val_y]=spilt(data,label,0.8,0.1,0.1); %划分训练集、测试集和验证集
data数据格式应为M×SN;M为一维数据的长度(即一个样本有多少个点),由于是一维数据,所以宽度N为1;SN则为样本个数。
label数据格式应为P×SN;P代表输出特征个数(也就是单输出和多输出),SN为样本个数。
spilt是个自定义函数,用来随机划分训练集、测试集和验证集,当然你也可以自行划分好后分别导入。
function [trainData,trainLabel,testData,testLabel,valData,valLabel]=spilt(data,label,trainRatio,testRatio,validationRatio)% 生成随机分组索引c = cvpartition(size(data,2), 'HoldOut', testRatio);trainIdx = c.training;testIdx = c.test;% 对剩余数据再次进行随机分组c = cvpartition(sum(trainIdx), 'HoldOut', validationRatio/(1-testRatio));true_trainIdx=c.training;valIdx = c.test;% 将训练集、验证集和测试集分别保存到不同的变量中trainData = data(:,trainIdx);trainLabel = label(trainIdx);valData = trainData(:,valIdx);valLabel = trainLabel(valIdx);testData = data(:,testIdx);testLabel = label(testIdx);trainData = trainData(:,true_trainIdx);trainLabel = trainLabel(true_trainIdx);
总之,train_x、test_x和val_x数据格式应该分别为M×样本个数。
train_y、test_y和val_y数据格式应该分别为P×样本个数。
二、数据预处理
%method=@mapminmax; %归一化method=@mapstd; %标准化[train_x,train_ps]=method(train_x,0,1);test_x=method('apply',test_x,train_ps);val_x=method('apply',val_x,train_ps);[train_y,output_ps]=method(train_y,0,1);test_y=method('apply',test_y,output_ps);val_y=method('apply',val_y,output_ps);
选择对数据进行归一化或者标准化。
三、数据输入格式转换
trainD=reshape(train_x,[M,N,D,SN]);%训练集输入,[单个样本长度,单个样本宽度,输入特征个数,样本数]testD=reshape(test_x,[M,N,D,SN]);%测试集输入,[单个样本长度,单个样本宽度,输入特征个数,样本数]valD=reshape(val_x,[M,N,D,SN]);%验证集输入,[单个样本长度,单个样本宽度,输入特征个数,样本数]
其中,因为是一维数据,所以N=1,下文同。
CNN的输入数据格式应为【单个样本长度,单个样本宽度,输入特征个数,样本数】;
即【M,N,D,SN】。
D=1则代表单输入。
四、CNN网络结构建立
layers = [ imageInputLayer([M N D]) %输入层参数设置,[M,N,D]%第一层卷积层和池化层 convolution2dLayer([64,1],128,'Padding','same') %[64,1]是卷积核大小,128是个数 %对于一维数据,卷积核第二个参数为1就行了,这样就是一维卷积 reluLayer %relu激活函数 maxPooling2dLayer([32 1],'Stride',10)%第二层卷积层和池化层 convolution2dLayer([32,1],128,'Padding','same') reluLayer %relu激活函数 maxPooling2dLayer([16 1],'Stride',20)%两层全连接层 fullyConnectedLayer(20) % 20个全连接层神经元 reluLayer %relu激活函数 fullyConnectedLayer(10) % 10个全连接层神经元 fullyConnectedLayer(P) % 输出层神经元个数P regressionLayer];%添加回归层,用于计算损失值
上述代码中的卷积核大小、个数等参数不具备参考意义,应当根据自身数据结构自行调整。
convolution2dLayer代表卷积层;【64,1】是卷积核的大小,由于是一维数据,所以第二个参数为1;128是卷积核个数;'Padding','same'代表填充使得该层的神经元输入和输出个数相同。我们也可以自定义其他填充方式或者步幅大小。
详情可见官方文档:https://ww2.mathworks.cn/help/deeplearning/ref/nnet.cnn.layer.convolution2dlayer.html
maxPooling2dLayer代表最大池化层;【32,1】是池化层大小;'Stride',10代表池化步幅。
fullyConnectedLayer代表全连接层,最后一层全连接层需决定输出特征个数P;reluLayer代表激活函数的选择。
regressionLayer代表回归层,也可以换成分类层softmaxLayer和classificationLayer。
analyzeNetwork(net);
我们可以采用该指令来查看网络结构,包括层结构和每一层的输入输出大小。
有时候你的卷积核大小、个数、步幅以及池化层大小和步幅设置的不合理,导致网络出错,我们就可以通过该命令检查每一层的输入输出。比如,某一池化层的大小不能大于上一层的输出个数。
具体可见下一篇文章,CNN结合灰狼优化算法进行超参数优化。
五、模型设置
options = trainingOptions('adam', ...%优化方法:sgdm、adam等 'MaxEpochs',30, ... 'MiniBatchSize',20, ... 'InitialLearnRate',0.001, ... 'GradientThreshold',1, ... 'Verbose',false,... 'ExecutionEnvironment','multi-gpu',...% GPU训练 'Plots','training-progress',...%'none'代表不显示训练过程 'ValidationData',{valD,val_y'});%验证集
值得一提的是验证集的输入,在很多书籍中,验证集和测试集是同一个数据集。
六、模型训练与测试
net = trainNetwork(trainD,train_y',layers,options);% 预测YPred = predict(net,testD);% 结果YPred=double(YPred');%输出是n*1的single型数据,要转换为1*n的double是数据形式% 反归一化predict_value=method('reverse',YPred,output_ps);predict_value=double(predict_value);true_value=method('reverse',test_y,output_ps);true_value=double(true_value);
七、结果评估
rmse=sqrt(mean((true_value-predict_value).^2));disp(['根均方差:',num2str(rmse)])mae=mean(abs(true_value-predict_value));disp(['平均绝对误差:',num2str(mae)])maxape=max(abs((true_value-predict_value)./true_value));disp(['最大相对百分误差:',num2str(maxape*100),'%'])mape=mean(abs((true_value-predict_value)./true_value));disp(['平均相对百分误差:',num2str(mape*100),'%'])