目录
一、前言
二、数据集.mat文件
三、数据集的处理
四、数据信号图示
五、创建时频图
六、GoogLeNet训练
6.1 加载训练模型
6.2 修改网络参数
6.3 设置训练选项并训练 GoogLeNet
6.4评估 GoogLeNet 准确度
七、SqueezeNet训练
7.1 加载训练模型
7.2修改网络参数
7.3准备数据
7.4设置训练选项并训练 SqueezeNet
7.5评估 SqueezeNet 准确度
八、AlexNet训练
8.1加载训练模型
8.2修改网络参数
8.3准备数据
8.4设置训练选项并训练 AlexNet
8.5评估 AlexNet 准确度
深水船水声音数据集由265艘真实的深水船水下声音记录组成,时长47小时和4min,属于四类。这些类别包括油轮、拖船、客船和货船,如图所示。此数据集包括全年不同海州和噪音水平的记录。除了管道信号外,记录的信号还具有自然背景噪声、海洋哺乳动物噪音和任何其他人类启动活动的噪音。
这个数据集可在https://github.com/irfankamboh/DeepShip存储库上下载,考虑到github目录文件大小和空间的限制,只有一部分数据集上传到这个存储库。通过在同一个存储库网页上提供的信息,可以下载完整的数据集
数据集被安排在四个文件夹中,以每个船舶类命名,如图所示。在每个文件夹中都有两种类型的文件,首先是包含音频数据的.wav文件,第二种类型的文件是类名文件,如车库文件、拖轮文件、油轮文件和乘客文件。此文件包含该文件夹中有关.wav文件的信息。每个类名文件包含信息,如(i)id类、(ii)记录id、(iii)船舶名称、(iii)记录日期和时间、(iv)每次记录的秒持续时间、(v)船舶距离传感器的距离。
由于此数据集过于庞大,我从老师处获得一个较为简单的数据集,老师已经帮我处理成Matlab容易识别的.mat文件(感谢老师~~),名为WaterData.mat,该文件内包括4个.mat文件,分别为Xtest.mat,Xtrain.mat,Ytest.mat,Ytrain.mat
其中,Xtest.mat和Xtrain.mat为水声数据,前者为测试集,共1080条;后者为训练集,共2520条。
此外,Ytest.mat和Ytrain.mat为标签数据,以one-hot编码(或称为“N中取1”编码)呈现,即第一类表示为1000,第二类表示为0100,第三类表示为0010,第四类表示为0001。前者为测试集,共1080条;后者为训练集,共2520条。
(由于博主本人学识水平有限,编程水平较菜,以下数据集处理方式依博主自身喜好而来,代码存在不少冗余情况,不喜勿喷,狗头保命[doge])
Xtrain.mat为水声数据,一列表示一条水声信号,共64000个数据,总共2520条。Xtrain.mat同理。由于竖着看数据我不太习惯,所以还是先转个置,方便后面使用
由于标签数据是以one-hot编码(独热编码)方式呈现,博主较菜,之前没接触过使用Matlab进行深度学习的相关编程方法(但接触过一丢丢Python的,深度学习的原理无论哪种编程语言都是相通的,所以还是能够看懂个大概)所以还是将数据集处理为自己熟悉的文本标签。。。
- size():获取矩阵的行数和列数
[r,c]=size(A) 当有两个输出参数时,size函数将矩阵的行数返回到第一个输出变量r,将矩阵的列数返回到第二个输出变量c
num2str():把数值转换成字符串
str = num2str(A) 把数组A中的数转换成字符串表示形式strcat():横向连接字符串
combinedStr= strcat(s1, s2, ..., sN) 将数组 s1,s2,...,sN 水平地连接成单个字符串,并保存于变量combinedStr中。如果任一参数是元胞数组,那么结果 combinedStr 是一个元胞数组,否则,combinedStr是一个字符数组
%% 处理标签
%转置
Ytrain_zhuang =Ytrain.';
Ytest_zhuang =Ytest.';
%数值转为字符串
[row_train,column_train] = size(Ytrain_zhuang);
[row_test,column_test] = size(Ytest_zhuang);
%Ytrain_zhuang_char = cell(row_train,column_train);
for i = 1:row_train
for j = 1:column_train
Ytrain_zhuang_char(i,j) = num2str(Ytrain_zhuang(i,j));
end
end
for i = 1:row_test
for j = 1:column_test
Ytest_zhuang_char(i,j) = num2str(Ytest_zhuang(i,j));
end
end
%字符拼接成字符串,将独热编码转为字母标签
%Ytrain_zhuang_string = ones(row_train,1);
for i = 1:row_train
a=strcat(Ytrain_zhuang_char(i,1),Ytrain_zhuang_char(i,2),Ytrain_zhuang_char(i,3),Ytrain_zhuang_char(i,4));
switch a
case '0001'
Ytrain_zhuang_string(i,1) = 'D';
case '0010'
Ytrain_zhuang_string(i,1) = 'C';
case '0100'
Ytrain_zhuang_string(i,1) = 'B';
case '1000'
Ytrain_zhuang_string(i,1) = 'A';
end
end
%Ytest_zhuang_string = ones(row_test,1);
for i = 1:row_test
a=strcat(Ytest_zhuang_char(i,1),Ytest_zhuang_char(i,2),Ytest_zhuang_char(i,3),Ytest_zhuang_char(i,4));
switch a
case '0001'
Ytest_zhuang_string(i,1) = 'D';
case '0010'
Ytest_zhuang_string(i,1) = 'C';
case '0100'
Ytest_zhuang_string(i,1) = 'B';
case '1000'
Ytest_zhuang_string(i,1) = 'A';
end
end
由于一个个数值很难进行分类识别,因此需要将数值转化为一张张图片作为一个整体然后塞入神经网络进行识别。这么多种图片都放在一处容易产生标签混乱,为了识别方便,要使用文件目录存储每个类别的预处理数据
以训练集为例,首先在 'E:\project\'
(当前目录)内创建一个数据目录data_train
。然后在 'data_train'
文件夹中创建三个子目录,以每个类别A、B、C、D命名。测试集同理
- fullfile():做文件目录的拼接
f = fullfile('D:','A','fafg','*.raw') fullfile构成地址字符串,输入了一个字符串‘D:\A\fafg\.*raw’,中间自动插入了\- mkdir():用于新建文件夹
- unique():去掉矩阵中重复的元素
C = unique(A):返回的是和A中一样的值,但是没有重复元素。产生的结果向量按升序排序- numel():用于计算数组中满足指定条件的元素个数
n = numel(A); 返回数组A中元素个数
n = numel(A, index1, index2, ... indexn); 返回A(index1, index2, ... indexn)中元素的个数,其中indexi可以是切片运算、算术表达式、逻辑表达式等
%train
parentDir = 'E:\project\'; %当前所在目录
dataDir_train = 'data_train'; %新建文件夹,名为data_train
mkdir(fullfile(parentDir,dataDir_train))
folderLabels = unique(Ytrain_zhuang_string);
for i = 1:numel(folderLabels)
mkdir(fullfile(parentDir,dataDir_train,folderLabels(i)));
end
%test
parentDir = 'E:\project\'; %当前所在目录
dataDir_test = 'data_test'; %新建文件夹,名为data_test
mkdir(fullfile(parentDir,dataDir_test))
folderLabels = unique(Ytest_zhuang_string);
for i = 1:numel(folderLabels)
mkdir(fullfile(parentDir,dataDir_test,folderLabels(i)));
end
光看数值可能想象不出信号的模样,我们绘制每个类别的表示图来瞧瞧区别。以下代码绘制 信号的每个类表示的前10000个样本。
- ismember():主要是看矩阵A中的数据是不是矩阵B中的成员,是的话返回一个包含逻辑1(ture)的数组,不是返回0
ismember(a,3) 返回逻辑矩阵,如果3在哪个位置上,则哪个位置返回为1,其他位置返回为0
ismember(3,a) 如果a中有3这个元素则返回1,没有这个元素的话就返回0find():返回素有非零元素的位置,注:竖着数!!!
subplot():创建子图
subplot(m,n,p) m表示是图排成m行,n表示图排成n列,也就是整个figure中有n个图是排成一行的,一共m行。p表示图所在的位置,p=1表示从左到右从上到下的第一个位置
%% 信号表示
%train
Xtrain_zhuang = Xtrain.';
for k=1:4
dataType = folderLabels(k);
ind = find(ismember(Ytrain_zhuang_string,dataType));
subplot(4,1,k)
plot(Xtrain_zhuang(ind(1),1:10000));
grid on
title(dataType)
end
%test
Xtest_zhuang = Xtest.';
for k=1:4
dataType = folderLabels(k);
ind = find(ismember(Ytest_zhuang_string,dataType));
subplot(4,1,k)
plot(Xtest_zhuang(ind(1),1:10000));
grid on
title(dataType)
end
图示情况如下
在第三步创建各个数据类别的文件夹后,我们就要往文件夹内存放时频图啦~
时频图是信号的 连续小波变换(CWT) 系数的绝对值。要创建尺度图,需要预先计算一个 CWT 滤波器组。当要使用相同的参数获取众多信号的 CWT 时,建议预先计算 CWT 滤波器组。
这时候就要展现Matlab的强大功能啦~~使用 cwtfilterbank (Wavelet Toolbox) 为具有 10000 个样本的信号创建一个 CWT 滤波器组。使用滤波器组获取信号的前 1000 个样本的 CWT,并基于系数获得尺度图。我们先生成一个尺度图瞧瞧
- pcolor():绘制伪彩色图
pcolor(X,Y,C) 绘制指定颜色C和指定网格线间间距的伪彩色图。参量X和Y是指定网格间间距的向量或者矩阵。若X,Y为矩阵,则X和Y,C维数相同;若X,Y为向量,则X,Y的长度分别等于矩阵C的列数和行数- shading interp:对曲面或图形对象的颜色着色进行色彩的插值处理,使色彩平滑过渡
- axis tight:设置坐标轴的范围为数据的范围
%% 时频图示例
Fs = 128;
fb = cwtfilterbank('SignalLength',10000,...
'SamplingFrequency',Fs,...
'VoicesPerOctave',12);
sig = Xtrain_zhuang(1,1:10000);
[cfs,frq] = wt(fb,sig);
t = (0:9999)/Fs;
figure;
pcolor(t,frq,abs(cfs))
set(gca,'yscale','log');
shading interp;
axis tight;
title('Scalogram');xlabel('Time (s)');ylabel('Frequency (Hz)')
以训练集为例,使用下列代码将时频图创建为 RGB 图像,并将其写入 dataDir_train
中的d对应子目录。为了与 后面训练的GoogLeNet 架构兼容,每个 RGB 图像是大小为 224×224×3 的数组。
- abs():数值的绝对值和复数的幅值
y=abs(x) 对数组元素进行绝对值处理的函数。函数的定义域包括复数。对于复数x=a+b*i,有abs(x)=a2+b2
im2uint8(): 将图像数组转换成unit8类型
imresize():改变图像的大小
B = imresize(A, m) 返回的图像B的长宽是图像A的长宽的m倍,即缩放图像。 m大于1, 则放大图像; m小于1, 缩小图像
B = imresize(A, [numrows numcols]) numrows和numcols分别指定目标图像的高度和宽度。 显而易见,由于这种格式允许图像缩放后长宽比例和源图像长宽比例相同,因此所产生的图像有可能发生畸变imwrite():将图像写入图形文件
imwrite(A,filename)
将图像数据A
写入filename
指定的文件,并从扩展名推断出文件格式。imwrite
在当前文件夹中创建新文件。输出图像的位深度取决于A
的数据类型和文件格式
%% 创建时频表示
%train
imageRoot = fullfile(parentDir,dataDir_train);
data = Xtrain_zhuang;
labels = Ytrain_zhuang_string;
[~,signalLength] = size(data);
fb = cwtfilterbank('SignalLength',signalLength,'VoicesPerOctave',12);
r = size(data,1);
for ii = 1:r
cfs = abs(fb.wt(data(ii,:)));
im = ind2rgb(im2uint8(rescale(cfs)),jet(128));
imgLoc = fullfile(imageRoot,char(labels(ii)));
imFileName = strcat(char(labels(ii)),'_',num2str(ii),'.jpg');
imwrite(imresize(im,[224 224]),fullfile(imgLoc,imFileName));
end
%test
imageRoot1 = fullfile(parentDir,dataDir_test);
data1 = Xtest_zhuang;
labels1 = Ytest_zhuang_string;
[~,signalLength1] = size(data1);
fb = cwtfilterbank('SignalLength',signalLength1,'VoicesPerOctave',12);
r = size(data1,1);
for ii = 1:r
cfs = abs(fb.wt(data1(ii,:)));
im = ind2rgb(im2uint8(rescale(cfs)),jet(128));
imgLoc = fullfile(imageRoot1,char(labels1(ii)));
imFileName = strcat(char(labels1(ii)),'_',num2str(ii),'.jpg');
imwrite(imresize(im,[224 224]),fullfile(imgLoc,imFileName));
end
将时频图图像加载为图像数据存储。imageDatastore 函数自动根据文件夹名称对图像加标签,并将数据存储为 ImageDatastore 对象。通过图像数据存储可以存储大图像数据,包括无法放入内存的数据,并在 CNN 的训练过程中高效分批读取图像。
- imageDatastore():构建指定路径下的某一文件夹所有图像
imds = imageDatastore('./images', 'IncludeSubfolders', true, 'labelsource', 'foldernames')
第一个参数./images
表示文件所在的路径;后续参数都是键值对(key-value)的形式 ;labelsource
:图像 label 的来源是什么;includesubfolders
:是否继续读取子文件夹中的图像数据- disp():直接将内容输出在Matlab命令窗口中
%train
imgsTrain = imageDatastore(fullfile(parentDir,dataDir_train),...
'IncludeSubfolders',true,...
'LabelSource','foldernames');
disp(['Number of training images: ',num2str(numel(imgsTrain.Files))]);
%test
imgsValidation = imageDatastore(fullfile(parentDir,dataDir_test),...
'IncludeSubfolders',true,...
'LabelSource','foldernames');
disp(['Number of validation images: ',num2str(numel(imgsValidation.Files))]);
加载预训练的 GoogLeNet 神经网络。如果未安装 Deep Learning Toolbox™ Model for GoogLeNet Network 支持包,软件将在附加功能资源管理器中提供所需支持包的链接。点击链接安装支持包,然后点击 Install。
net = googlenet;
从网络中提取并显示层次图。
lgraph = layerGraph(net);
numberOfLayers = numel(lgraph.Layers);
figure('Units','normalized','Position',[0.1 0.1 0.8 0.8]);
plot(lgraph)
title(['GoogLeNet Layer Graph: ',num2str(numberOfLayers),' Layers']);
检查网络层属性的第一个元素。确认 GoogLeNet 需要大小为 224×224×3 的 RGB 图像。
net.Layers(1)
网络架构中的每层都可以视为一个滤波器。较浅的层识别图像的更常见特征,如斑点、边缘和颜色。后续层侧重于更具体的特征,以便区分类别。GoogLeNet 经训练可将图像分类至 1000 个目标类别。对于此分类问题,必须重新训练 GoogLeNet。
为防止过拟合,使用了丢弃层。丢弃层以给定的概率将输入元素随机设置为零。默认概率为 0.5。将网络中的最终丢弃层 'pool5-drop_7x7_s1'
替换为概率为 0.6 的丢弃层。
newDropoutLayer = dropoutLayer(0.6,'Name','new_Dropout');
lgraph = replaceLayer(lgraph,'pool5-drop_7x7_s1',newDropoutLayer);
网络的卷积层会提取最后一个可学习层和最终分类层用来对输入图像进行分类的图像特征。GoogLeNet 中的 'loss3-classifier'
和 'output'
这两个层包含有关如何将网络提取的特征合并为类概率、损失值和预测标签的信息。要重新训练 GoogLeNet 以对 RGB 图像进行分类,需将这两个层替换为适合数据的新层。
将全连接层 'loss3-classifier'
替换为新的全连接层,其中滤波器的数量等于类的数量。若要使新层中的学习速度快于迁移的层,请增大全连接层的学习率因子。
numClasses = numel(categories(imgsTrain.Labels));
newConnectedLayer = fullyConnectedLayer(numClasses,'Name','new_fc',...
'WeightLearnRateFactor',5,'BiasLearnRateFactor',5);
lgraph = replaceLayer(lgraph,'loss3-classifier',newConnectedLayer);
分类层指定网络的输出类。将分类层替换为没有类标签的新分类层。trainNetwork
会在训练时自动设置层的输出类。
newClassLayer = classificationLayer('Name','new_classoutput');
lgraph = replaceLayer(lgraph,'output',newClassLayer);
训练神经网络是一个使损失函数最小的迭代过程。要使损失函数最小,使用梯度下降算法。在每次迭代中,会评估损失函数的梯度并更新下降算法权重。
可以通过设置各种选项来调整训练。InitialLearnRate
指定损失函数负梯度方向的初始步长大小。MiniBatchSize
指定在每次迭代中使用的训练集子集的大小。一轮指对整个训练集完整运行一遍训练算法。MaxEpochs
指定用于训练的最大轮数。选择正确的轮数至关重要。减少轮数会导致模型欠拟合,而增加轮数会导致过拟合。
使用 trainingOptions 函数指定训练选项。将 MiniBatchSize
设置为 15,MaxEpochs
置为 20,InitialLearnRate
置为 0.0001。通过将 Plots
设置为 training-progress
来可视化训练进度。使用带动量的随机梯度下降优化器。将随机种子设置为默认值
%% 设置训练选项并训练 GoogLeNet
options = trainingOptions('sgdm',...
'MiniBatchSize',15,...
'MaxEpochs',20,...
'InitialLearnRate',1e-4,...
'ValidationData',imgsValidation,...
'ValidationFrequency',10,...
'Verbose',1,...
'ExecutionEnvironment','cpu',...
'Plots','training-progress');
rng default
开始训练网络。命令行窗口显示运行期间的训练信息。结果包括验证数据的轮数、迭代次数、经过的时间、小批量准确度、验证准确度和损失函数值。
trainedGN = trainNetwork(imgsTrain,lgraph,options);
查看经过训练的网络的最后一层。确认分类输出层包括四个类。
trainedGN.Layers(end);
使用验证数据评估网络。精确度与训练可视化图上报告的验证精确度相同。时频图分成训练集合和验证集合。这两个集合都用于训练 GoogLeNet。评估训练结果的理想方法是让网络对它没有见过的数据进行分类。
[YPred,probs] = classify(trainedGN,imgsValidation);
accuracy = mean(YPred==imgsValidation.Labels);
disp(['GoogLeNet Accuracy: ',num2str(100*accuracy),'%']);
SqueezeNet 是一个深度 CNN,其架构支持大小为 227×227×3 的图像。即使与 GoogLeNet 的图像大小不同,也不必以 SqueezeNet 大小生成新 RGB 图像,可以使用原始的 RGB 图像
加载预训练的 SqueezeNet 神经网络。如果未安装 Deep Learning Toolbox™ Model for SqueezeNet Network 支持包,软件将在附加功能资源管理器中提供所需支持包的链接。点击链接安装支持包,,然后点击 Install。
sqz = squeezenet;
从网络中提取层次图。确认 SqueezeNet 的层数少于 GoogLeNet。还要确认 SqueezeNet 是针对大小为 227×227×3 的图像配置的
lgraphSqz = layerGraph(sqz);
disp(['Number of Layers: ',num2str(numel(lgraphSqz.Layers))])
disp(lgraphSqz.Layers(1).InputSize)
要重新训练 SqueezeNet 对新图像进行分类,进行与对 GoogLeNet 类似的更改。检查最后六个网络层
lgraphSqz.Layers(end-5:end)
将网络中的最后一个丢弃层 'drop9'
替换为概率为 0.6 的丢弃层
tmpLayer = lgraphSqz.Layers(end-5);
newDropoutLayer = dropoutLayer(0.6,'Name','new_dropout');
lgraphSqz = replaceLayer(lgraphSqz,tmpLayer.Name,newDropoutLayer);
与 GoogLeNet 不同,SqueezeNet 中最后一个可学习层是 1×1 卷积层 'conv10'
,而不是全连接层。将 'conv10'
层替换为新的卷积层,其中滤波器的数量等于类的数量。与对 GoogLeNet 执行的操作一样,增大新层的学习率因子
numClasses = numel(categories(imgsTrain.Labels));
tmpLayer = lgraphSqz.Layers(end-4);
newLearnableLayer = convolution2dLayer(1,numClasses, ...
'Name','new_conv', ...
'WeightLearnRateFactor',10, ...
'BiasLearnRateFactor',10);
lgraphSqz = replaceLayer(lgraphSqz,tmpLayer.Name,newLearnableLayer);
将分类层替换为没有类标签的新分类层
tmpLayer = lgraphSqz.Layers(end);
newClassLayer = classificationLayer('Name','new_classoutput');
lgraphSqz = replaceLayer(lgraphSqz,tmpLayer.Name,newClassLayer);
检查网络的最后六层。确认丢弃层、卷积层和输出层已更改
lgraphSqz.Layers(63:68)
之前的RGB 图像具有适合 GoogLeNet 架构的大小,因此需要创建增强的图像数据存储,这些数据存储会自动为 SqueezeNet 架构调整现有 RGB 图像的大小
augimgsTrain = augmentedImageDatastore([227 227],imgsTrain);
augimgsValidation = augmentedImageDatastore([227 227],imgsValidation);
创建一组新的用于 SqueezeNet 的训练选项。将随机种子设置为默认值并训练网络。
ilr = 3e-4;
miniBatchSize = 10;
maxEpochs = 15;
valFreq = floor(numel(augimgsTrain.Files)/miniBatchSize);
opts = trainingOptions('sgdm',...
'MiniBatchSize',miniBatchSize,...
'MaxEpochs',maxEpochs,...
'InitialLearnRate',ilr,...
'ValidationData',augimgsValidation,...
'ValidationFrequency',valFreq,...
'Verbose',1,...
'ExecutionEnvironment','cpu',...
'Plots','training-progress');
rng default
trainedSN = trainNetwork(augimgsTrain,lgraphSqz,opts);
检查网络的最后一层。确认分类输出层包括四个类
trainedSN.Layers(end)
使用验证数据评估网络。
[YPred,probs] = classify(trainedSN,augimgsValidation);
accuracy = mean(YPred==imgsValidation.Labels);
disp(['SqueezeNet Accuracy: ',num2str(100*accuracy),'%'])
AlexNet是一个深度CNN,其架构支持尺寸为227 × 227 × 3的图像。
加载预先训练的AlexNet神经网络。如果未安装用于AlexNet网络支持包的深度学习工具箱™模型,该软件将在Add-On Explorer中提供到所需支持包的链接。单击链接安装支持包,然后单击 Install
alex = alexnet;
查看一下网络架构。注意,第一层指定图像输入大小为227 × 227 × 3,并且AlexNet的层数比GoogLeNet少
layers = alex.Layers
AlexNet的后三层为1000个类别配置。这些层必须与分类问题相匹配。第23层,即全连接层,必须设置为与类别数量相同的大小。第24层不需要改变。Softmax对输入端应用Softmax功能。第25层是分类输出层,包含用于训练网络的损失函数的名称和类标签。由于有四个类别,设第23层为大小为4的全连接层,设第25层为分类输出层。
layers(23) = fullyConnectedLayer(4);
layers(25) = classificationLayer;
从第一个AlexNet层获得AlexNet使用的图像尺寸。使用这些维度创建增强的图像数据存储,将自动调整AlexNet体系结构的现有RGB图像的大小。
inputSize = alex.Layers(1).InputSize;
augimgsTrain = augmentedImageDatastore(inputSize(1:2),imgsTrain);
augimgsValidation = augmentedImageDatastore(inputSize(1:2),imgsValidation);
创建一组新的用于AlexNet 的训练选项。将随机种子设置为默认值并训练网络。
rng default
mbSize = 10;
mxEpochs = 3;
ilr = 1e-4;
plt = 'training-progress';
opts = trainingOptions('sgdm',...
'InitialLearnRate',ilr, ...
'MaxEpochs',mxEpochs ,...
'MiniBatchSize',mbSize, ...
'ValidationData',augimgsValidation,...
'ExecutionEnvironment','cpu',...
'Plots',plt);
trainedAN = trainNetwork(augimgsTrain,layers,opts);
检查训练过的AlexNet网络的最后三层。观察分类输出层提到的四个标签。
trainedAN.Layers(end-2:end)
检查训练过的AlexNet网络的最后一层。观察分类输出层提到的四个标签。
trainedAN.Layers(end)
使用验证数据评估网络。
[YPred,probs] = classify(trainedAN,augimgsValidation);
accuracy = mean(YPred==imgsValidation.Labels);
disp(['AlexNet Accuracy: ',num2str(100*accuracy),'%'])