主要内容

使用深度学习训练3-D声音事件定位和检测(SELD)

在这个例子中,你训练了一个深度学习模型,从双音速数据中执行声音定位和事件检测。该模型由两个独立训练的卷积循环神经网络(CRNN)组成[1]:一个用于声音事件检测(SED),一个用于到达方向(DOA)估计。要探索本例中训练的模型,请参见利用训练过的循环卷积神经网络进行三维声音事件定位与检测(音频工具箱)

介绍

Ambisonics是一种流行的3d声音格式,在声源定位、语音增强和声源分离等任务中显示出了前景。Ambisonics是一种全球面环绕声格式,包含与说话者无关的声场表示(b格式)。一阶b格式双声录音包含与全向麦克风(W)捕捉到的声压相对应的成分,以及与沿三个空间轴方向的八字胶囊捕捉到的前/后、左/右和上/下对应的声压梯度X、Y和Z。3-D SELD在虚拟现实、机器人、智能家居和国防等领域都有应用。

你将为声音事件检测任务和定位任务训练两个独立的模型。这两个模型都基于中描述的卷积递归神经网络体系结构[1].将声音事件检测任务表述为分类任务。。声音事件定位任务估计声源的笛卡尔坐标,并表述为回归任务。使用L3DAS21数据集[2]训练和验证网络。要探索本例中训练的模型,请参见利用训练过的循环卷积神经网络进行三维声音事件定位与检测(音频工具箱)

下载和准备数据

本例使用L3DAS21 Task 2挑战数据集的一个子集[2].数据集包含多源多视角(MSMP) b格式双声子音频记录,采样率为32 kHz。数据集提供了序列和验证分割。每段录音一分钟长,包含一个模拟的3- d音频环境,其中最多可同时激活3个同时发生的声学事件。在本例中,您只使用包含非重叠声音的数据。声音事件属于14个声音类。标签以csv文件的形式提供,其中包含声音类、声源的笛卡尔坐标以及起始和偏移时间戳。

下载数据集。

downloadFolder = matlab.internal.examples.downloadSupportFile (“音频”,“L3DAS21_ov1.zip”);dataFolder = tempdir;unzip(downloadFolder,dataFolder) dataset = fullfile(dataFolder,“L3DAS21_ov1”);

可选地减少数据集

用整个数据集训练网络,并达到合理的性能,集speedupExample.要快速运行此示例,请设置speedupExample真正的

speedupExample =;

创建数据存储

创建audioDatastore(音频工具箱)对象来摄取数据。数据集中的每个数据点由两个B格式的双声子录音组成,对应于两个麦克风(A和B)。对于每个数据文件夹(训练和验证),使用子集(音频工具箱)创建两个与两个麦克风对应的子集。

adsTrain = audioDatastore (fullfile(数据集,“训练”,“数据”));adsTrainA =子集(adsTrain cellfun (@ (c) endsWith (c,“A.wav”), adsTrain.Files));adsTrainB =子集(adsTrain cellfun (@ (c) endsWith (c,“B.wav”), adsTrain.Files));adsValidation = audioDatastore (fullfile(数据集,“确认”,“数据”));adsValidationA =子集(adsValidation cellfun (@ (c) endsWith (c,“A.wav”), adsValidation.Files));adsValidationB =子集(adsValidation cellfun (@ (c) endsWith (c,“B.wav”), adsValidation.Files));

如有请求,减少数据集。

如果speedupExample adsTrainA =子集(adsTrainA,1:2);adsTrainB =子集(adsTrainB 1:2);结束

检查数据

预览双声录音并绘制数据。

云母=预览(adsTrainA);micB =预览(adsTrainB);tiledlayout (4,2 TileSpacing =“紧”) nexttile plot(micA(:,1)) title(“麦克风”) ylabel (“W”) nexttile plot(micB(:,1)) title(B“麦克风”nexttile plot(micA(:,2)) ylabel(“X”) nexttile plot(micB(:,2)) nexttile plot(micA(:,3)) ylabel(“Y”) nexttile plot(micB(:,3)) nexttile plot(micB(:,4)) ylabel(“Z”) nexttile情节(micB (:,)

听一段数据。

麦克风=1;频道=1;时间=10;fs = 32 e3;%数据的已知采样率。s =(云母、micB);Data = s(1:round(duration*fs),channel +(麦克风-1)*4);声音(数据、fs)

创建目标

数据集中的每个数据点都有一个对应的CSV文件,其中包含声音事件类、声音的开始和结束时间以及声音的位置。创建一个容器,在声音类和整数之间进行映射。

键盘= [“Chink_and_clink”,“Computer_keyboard”,“Cupboard_open_or_close”,“Drawer_open_or_close”,“Female_speech_and_woman_speaking”,“Finger_snapping”,“Keys_jangling”,“敲门”,“笑”,“Male_speech_and_man_speaking”,“打印机”,“剪刀”,“电话”,“写”];valueSet ={1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12日,13日,14日};参数个数。SoundClasses = containers.Map(键盘、valueSet);

创建一个tabularTextDatastore来摄取列车文件标签。确保标签文件与数据文件的顺序一致。预览数据存储中的标签文件。

(文件夹,fn) = fileparts (adsTrainA.Files);定位路径= fullfile (strrep(文件夹,filesep +“数据”filesep +“标签”),“label_”+ strrep (fn,“_A”,”“) +. csv”);ttdsTrain = tabularTextDatastore(定位路径);labelTable =预览(ttdsTrain)
labelTable =8×7表文件开始结束类X Y Z  ____ _______ ______ ____________________________________ ____ ____ ____ 0 0.54784 9.6651{‘写’}0 0.5 -1.5 0.3 11.521 - 12.534{‘Finger_snapping} 0.75 1.25 1 0 14.255 16.064{‘Keys_jangling} 0 0.5 -1.5 0.3 17.728 - 18.878 0.5{‘Chink_and_clink} 1 0 0 19.95 20.4{“打印机”}0 -1.5 -1.5 -0.6 20.994 23.477{‘Cupboard_open_or_close} -0.5 - 0.75 0 0 25.032 25.723{‘Chink_and_clink} 2 -0.5 - -0.3 0 26.547 -1.5 27.491{‘Female_speech_and_woman_speaking} 1 0

数据集中的标签以秒为单位提供时间戳。为了创建目标和训练网络,需要将时间戳映射到帧。每个文件的总持续时间为60秒。你将为目标将每个文件划分为600帧,这意味着模型将每0.1秒做出一次预测。

params.Targets.TotalDuration = 60;params.Targets.NumFrames = 600;

SED的目标

支持函数,extractSEDTargets,使用标签数据创建SED目标。目标是一个单热编码的大小矩阵numframes——- - - - - -numclasses.没有声音的帧被编码为全零向量。

SEDTargets = extractSEDTargets (labelTable params);[numframes, numclasses] =大小(SEDTargets {1})
numframes = 600
numclasses = 14

从训练集和验证集中提取SED目标。

dsTTrain =变换(ttdsTrain @ (x) extractSEDTargets (x, params));sedTTrain = readall (dsTTrain);(文件夹,fn) = fileparts (adsValidationA.Files);定位路径= fullfile (strrep(文件夹,filesep +“数据”filesep +“标签”),“label_”+ strrep (fn,“_A”,”“) +. csv”);ttdsValidation = tabularTextDatastore(定位路径);dsTValidation =变换(ttdsValidation @ (x) extractSEDTargets (x, params));sedTValidation = readall (dsTValidation);

DOA的目标

支持函数,extractDOATargets,使用标签数据创建一个DOA目标。目标是一个大小矩阵numframes——- - - - - -numaxis.轴值对应的是声源在三维空间中的位置。没有声音的帧被编码为全零向量。

首先,定义一个参数来缩放目标轴值,使其在-1到1之间。这种缩放是必要的,因为您稍后定义的DOA网络使用tanh激活作为它的最后一层。

params.DOA.ScaleFactor = 2;DOATargets = extractDOATargets (labelTable params);[numframes, numaxis] =大小(DOATargets {1})
numframes = 600
numaxis = 3

从训练集和验证集中提取DOA目标。

dsTTrain =变换(ttdsTrain @ (x) extractDOATargets (x, params));doaTTrain = readall (dsTTrain);(文件夹,fn) = fileparts (adsValidationA.Files);定位路径= fullfile (strrep(文件夹,filesep +“数据”filesep +“标签”),“label_”+ strrep (fn,“_A”,”“) +. csv”);ttdsValidation = tabularTextDatastore(定位路径);dsTValidation =变换(ttdsValidation @ (x) extractDOATargets (x, params));doaTValidation = readall (dsTValidation);

声音事件检测(SED)

特征提取

声音事件检测模型使用对数量级短时傅里叶变换(STFT)作为系统的预测器。指定512点周期汉明窗和400个样本的跳长。

params.SED.SampleRate = 32 e3;params.SED.HopLength = 400;params.SED.Window =汉明(512“周期”);

支持函数,extractSTFT,取麦克风读数的单元阵列并提取半边心对数量级STFTs。与两个麦克风对应的STFT特征沿着第三维度堆叠。

stftFeats = extractSTFT({云母,micB}, params);[numfeaturesSED, numframesSED numchannelsSED] =大小(stftFeats)
numfeaturesSED = 256
numframesSED = 4800
numchannelsSED = 8

绘制一个通道的STFT特征。

频道=7;figure imagesc(stftfeat (:,:,channel)) colorbar xlabel(“帧”) ylabel (“频率(本)”)设置(gca YDir =“正常”)

从整个序列和验证集中提取特征。首先,结合麦克风A和麦克风b对应的数据存储,然后,定义A变换(音频工具箱)在数据存储上,因此从它读取将返回STFT。如果您有并行计算工具箱™,您可以使用UseParallelreadall(音频工具箱)

= ~ isempty(版本(pFlag“平行”) & & ~ speedupExample;trainDS =结合(adsTrainA adsTrainB);trainDS_T =变换(trainDS @ (x) {extractSTFT (x, params)}, IncludeInfo = false);XTrain = readall (pFlag trainDS_T UseParallel =);valDS =结合(adsValidationA adsValidationB);valDS_T =变换(valDS @ (x) {extractSTFT (x, params)}, IncludeInfo = false);XValidation = readall (pFlag valDS_T UseParallel =);

将预测器阵列与之前计算的SED目标阵列结合。

trainSedDS =结合(arrayDatastore (XTrain OutputType =“相同”), arrayDatastore (sedTTrain OutputType =“相同”));valSedDS =结合(arrayDatastore (XValidation OutputType =“相同”), arrayDatastore (sedTValidation OutputType =“相同”));

培训方案

为Adam优化定义训练参数。

trainOptionsSED =结构(MaxEpochs = 300,MiniBatchSize = 4,InitialLearnRate e-5 = 1,GradientDecayFactor = 0.01,SquaredGradientDecayFactor = 0.0,ValidationPatience = 25,LearnRateDropPeriod = 100,LearnRateDropFactor = 1);如果speedupExample trainOptionsSED。MaxEpochs = 1;结束

创建minibatchqueue对象,从训练和验证数据存储中读取小批。

trainSEDmbq = minibatchqueue (trainSedDS,MiniBatchSize = trainOptionsSED。MiniBatchSize,OutputAsDlarray = [1],MiniBatchFormat = [“SSCB”,“TCB”),OutputEnvironment = [“汽车”,“汽车”]);validationSEDmbq = minibatchqueue (valSedDS,MiniBatchSize = trainOptionsSED。MiniBatchSize,OutputAsDlarray = [1],MiniBatchFormat = [“SSCB”,“TCB”),OutputEnvironment = [“汽车”,“汽车”]);

定义声音事件检测(SED)网络

该网络分两个阶段实现——卷积神经网络(CNN)和门控循环网络(GRU)。您将使用一个自定义的重塑层来将CNN模型的输出重铸为一个序列,并将其作为RNN模型的输入传递。当您打开此示例时,自定义重塑层被放置在当前文件夹中。最后的输出层使用sigmoid激活。

为SED模型定义CNN层。

seldnetCNNLayers = [imageInputLayer([numfeatuered, numframered,numchannelsSED],Normalization= .“没有”、名称=“输入”) convolution2dLayer([3, 3], 64填充=“相同”、名称=“conv1”) batchNormalizationLayer (Name =“batchnorm1”) reluLayer (Name =“relu1”) maxPooling2dLayer([8 2],跨步=[8 2],填充=“相同”、名称=“maxpool1”) convolution2dLayer([3, 3], 128填充=“相同”、名称=“conv2”) batchNormalizationLayer (Name =“batchnorm2”) reluLayer (Name =“relu2”) maxPooling2dLayer([8 2],跨步=[8 2],填充=“相同”、名称=“maxpool2”) convolution2dLayer([3, 3], 256填充=“相同”、名称=“conv3”) batchNormalizationLayer (Name =“batchnorm3”) reluLayer (Name =“relu3”) maxPooling2dLayer(2, 2,步=(2,2)填充=“相同”、名称=“maxpool3”) convolution2dLayer([3, 3], 512填充=“相同”、名称=“conv4”) batchNormalizationLayer (Name =“batchnorm4”) reluLayer (Name =“relu4”) maxPooling2dLayer([1],跨步=[1],填充=“相同”、名称=“maxpool4”) reshapeLayer (“重塑”));netCNN = dlnetwork (layerGraph (seldnetCNNLayers));

为SED模型定义RNN层。

seldnetGRULayers = [sequenceInputLayer(1024,Name= .“sequenceInputLayer”) bigruLayer(1024、256、Name =“gru1”) bigruLayer(512256年,Name =“gru2”) bigruLayer(512256年,Name =“gru3”) fullyConnectedLayer(1024年,Name =“fc1”) reluLayer (Name =“relu1”) fullyConnectedLayer(1024年,Name =“取得”) reluLayer (Name =“relu2”) fullyConnectedLayer(1024年,Name =“一个fc3”文件) reluLayer (Name =“relu3”) fullyConnectedLayer (params.SoundClasses.Count Name =“fc4”) sigmoidLayer (Name =“输出”));netRNN = dlnetwork (layerGraph (seldnetGRULayers));

创建一个结构来包含完整模型的CNN和RNN部分。

sedModel。CNN = netCNN;sedModel。RNN = netRNN;

火车SED网络

初始化变量来跟踪训练的进度。

迭代= 0;averageGrad = [];averageSqGrad = [];时代= 0;bestLoss =正;badEpochs = 0;learnRate = trainOptionsSED.InitialLearnRate;

要显示训练进度,请初始化辅助对象progresPlotterSELD.支持对象,progressPlotterSELD,在您打开此示例时放在您的当前文件夹中。

页= progressPlotterSELD ();

运行训练循环。

rng (0)时代< trainOptionsSED。MaxEpochs && badEpochs < trainOptionsSED. MaxEpochs && badEpochs < trainOptionsSED.ValidationPatience epoch = epoch + 1;% Shuffle迷你批处理队列。洗牌(trainSEDmbq)hasdata (trainSEDmbq)%更新迭代计数器。迭代=迭代+ 1;%读取小批数据。[X, T] =下一个(trainSEDmbq);%评估模型的梯度和损失使用dlfeval和modelLoss函数。(损失、毕业生、国家)= dlfeval (@modelLoss sedModel X T);损失=损失/大小(T, 2);%更新状态。sedModel.CNN.State = state.CNN;sedModel.RNN.State = state.RNN;%使用Adam优化器更新网络参数。[sedModel, averageGrad averageSqGrad] = adamupdate (averageGrad sedModel,研究生,迭代,averageSqGrad learnRate、trainOptionsSED.GradientDecayFactor trainOptionsSED.SquaredGradientDecayFactor);更新训练进度图。。updateTrainingProgress (pp、时代=时代,LearnRate = LearnRate迭代=迭代,损失=损失);结束%在每个纪元之后执行验证。损失= predictBatch (sedModel validationSEDmbq);%用验证结果更新训练进度图。updateValidation (pp、损失=损失、迭代=迭代)%如果验证损失改善,创建检查点。如果验证%的损失没有改善,增加了糟糕时期的数量。如果loss < bestLoss bestLoss = loss;badEpochs = 0;文件名=“SED-BestModel”;保存(文件名,“sedModel”);其他的badEpochs = badEpochs + 1;结束%更新学习率如果rem(epoch,trainOptionsSED.LearnRateDropPeriod)==0 learnRate = learnRate*trainOptionsSED.LearnRateDropFactor;结束结束

到达方向(DOA)

特征提取

到达方向估计模型使用广义互相关相位变换(GCC-PHAT)作为对系统的预测器。指定1024点汉恩窗,跳长为400个样本,频带数为96。

params.DOA.SampleRate = 32 e3;params.DOA.Window =损害(1024);params.DOA.NumBands = 96;params.DOA.HopLength = 400;

提取gc - phat特征,用作声音定位网络的输入预测器。GCC-PHAT算法测量每对信道之间的相互关系。输入信号总共有8个通道,因此输出总共有28个测量值。

gccPhatFeats = extractGCCPHAT({云母,micB}, params);[numfeaturesDOA, timestepsDOA numchannelsDOA] =大小(gccPhatFeats)
numfeaturesDOA = 96
timestepsDOA = 4800
numchannelsDOA = 28

绘制通道对的GCC-PHAT特征。

channelpair =1;figure imagesc(gccphatfeat (:,:,channelpair)) colorbar xlabel(“帧”) ylabel (“乐队”)设置(gca YDir =“正常”)

从整个序列和验证集中提取特征。如果您有并行计算工具箱™,您可以使用UseParallelreadall

= ~ isempty(版本(pFlag“平行”) & & ~ speedupExample;trainDS =结合(adsTrainA adsTrainB);trainDS_T =变换(trainDS @ (x) {extractGCCPHAT (x, params)}, IncludeInfo = false);XTrain = readall (pFlag trainDS_T UseParallel =);
使用'local'配置文件启动并行池(parpool)…连接到平行池(工人人数:6)。
valDS =结合(adsValidationA adsValidationB);valDS_T =变换(valDS @ (x) {extractGCCPHAT (x, params)}, IncludeInfo = false);XValidation = readall (pFlag valDS_T UseParallel =);

将预测器数组与前面计算的DOA目标数组结合起来。

trainDOA =结合(arrayDatastore (XTrain OutputType =“相同”), arrayDatastore (doaTTrain OutputType =“相同”));validationDOA =结合(arrayDatastore (XValidation OutputType =“相同”), arrayDatastore (doaTValidation OutputType =“相同”));

培训方案

使用训练SED网络时定义的相同训练选项。

trainOptionsDOA = trainOptionsSED;

为列车和验证集创建小批队列。

trainDOAmbq = minibatchqueue (trainDOA,MiniBatchSize = trainOptionsDOA。MiniBatchSize,OutputAsDlarray = [1],MiniBatchFormat = [“SSCB”,“TCB”),OutputEnvironment = [“汽车”,“汽车”]);validationDOAmbq = minibatchqueue (validationDOA,MiniBatchSize = trainOptionsDOA。MiniBatchSize,OutputAsDlarray = [1],MiniBatchFormat = [“SSCB”,“TCB”),OutputEnvironment = [“汽车”,“汽车”]);

定义到达方向(DOA)网络

DOA网络与前面定义的SED网络非常相似。关键的区别在于输入层和最终激活层的大小。

更新用于SED网络的SELDnet架构,以便与DOA估计一起使用。

seldnetCNNLayers (1) = imageInputLayer ([numfeaturesDOA、timestepsDOA numchannelsDOA],标准化=“没有”、名称=“输入”);seldnetCNNLayers (5) = maxPooling2dLayer(3 2,步=(3 2),填充=“相同”、名称=“maxpool1”);netCNN = dlnetwork (layerGraph (seldnetCNNLayers));seldnetGRULayers (11) = fullyConnectedLayer (Name =“fc4”);seldnetGRULayers (12) = tanhLayer (Name =“输出”);netRNN = dlnetwork (layerGraph (seldnetGRULayers));

创建一个结构来包含完整模型的CNN和RNN部分。

doaModel。CNN = netCNN;doaModel。RNN = netRNN;

火车DOA网络

初始化训练循环中使用的变量。

迭代= 0;averageGrad = [];averageSqGrad = [];时代= 0;bestLoss =正;badEpochs = 0;learnRate = trainOptionsDOA.InitialLearnRate;

要显示训练进度,请初始化辅助对象progressPlotterSELD.支持对象,progressPlotterSELD,在您打开此示例时放在您的当前文件夹中。

页= progressPlotterSELD ();

运行训练循环。

rng (0)时代< trainOptionsDOA。MaxEpochs && badEpochs < trainOptionsDOA。ValidationPatience epoch = epoch + 1;% Shuffle迷你批处理队列。洗牌(trainDOAmbq)hasdata (trainDOAmbq)%更新迭代计数器。迭代=迭代+ 1;%读取小批数据。[X, T] =下一个(trainDOAmbq);%评估模型的梯度和损失使用dlfeval和modelLoss函数。(损失、毕业生、国家)= dlfeval (@modelLoss doaModel X T);损失=损失/大小(T, 2);%更新状态。doaModel.CNN.State = state.CNN;doModel.RNN.State = state.RNN;%使用Adam优化器更新网络参数。[doaModel, averageGrad averageSqGrad] = adamupdate (averageGrad doaModel,研究生,迭代,averageSqGrad learnRate、trainOptionsDOA.GradientDecayFactor trainOptionsDOA.SquaredGradientDecayFactor);%更新训练进度图updateTrainingProgress (pp、时代=时代,LearnRate = LearnRate迭代=迭代,损失=损失);结束%在每个epoch之后执行验证损失= predictBatch (doaModel validationDOAmbq);%用验证结果更新训练进度图。updateValidation (pp、损失=损失、迭代=迭代)%如果验证损失改善,创建检查点。如果验证%的损失没有改善,增加了糟糕时期的数量。如果loss < bestLoss bestLoss = loss;badEpochs = 0;文件名=“DOA-BestModel”;保存(文件名,“doaModel”);其他的badEpochs = badEpochs + 1;结束%更新学习率如果rem(epoch,trainOptionsDOA.LearnRateDropPeriod)==0 learnRate = learnRate* trainoptionsdoa . learnratdropfactor;结束结束

评估系统的性能

评估您的系统的性能,使用中定义的位置敏感检测误差[4].加载性能最好的模型。

sedModel = importdata (“SED-BestModel.mat”);doaModel = importdata (“DOA-BestModel.mat”);

位置敏感检测是一种联合度量,用于评估声音事件检测和声音事件定位任务的结果。在这种类型的评估中,只有当预测的标签是正确的,并且预测的位置在真实位置的预定义阈值内时,才会出现真阳性。本例中使用的阈值为0.2,约为最大可能误差的~3%。为了确定预测中的沉默区域,在SED决策上设置一个置信度阈值。如果SED预测低于该阈值,则认为该帧为静默。

参数个数。SpatialThreshold = 0.2;参数个数。SilenceThreshold = 0.1;

的方法计算验证数据集的度量computeMetrics支持功能。

结果= computeMetrics (sedModel doaModel、validationSEDmbq validationDOAmbq, params);结果
结果=结构体字段:精度:0.4246召回率:0.4275 f1Score: 0.4261 avgErr: 0.1861

computeMetrics支持函数可以选择性地在评估系统之前随时间平滑决策。这个选项需要统计和机器学习工具箱™。再次评估系统,这次包括平滑。

(结果,cm) = computeMetrics (sedModel、doaModel validationSEDmbq, validationDOAmbq, params, ApplySmoothing = true);结果
结果=结构体字段:精度:0.5077召回率:0.5084 f1Score: 0.5080 avgErr: 0.1659

您可以检查SED预测的混淆矩阵,以获得关于预测误差的更多见解。混淆矩阵只在有有源声源的区域上计算。

figure(Position=[100 100 800 800]);confusionchart(厘米,密匙(params.SoundClasses))

结论

下一步,你可以从这个例子中下载并尝试预训练的模型,在第二个例子中显示推理:利用训练过的循环卷积神经网络进行三维声音事件定位与检测(音频工具箱)

参考文献

[1] Sharath Adavanne, Archontis Politis, Joonas Nikunen,和Tuomas Virtanen,“使用卷积递归神经网络的声音事件定位和重叠源检测”,IEEE J. Sel。上面。信号的过程。,第13卷,no。第1页,2019年第34-48页。

[2] Eric Guizzo, Riccardo F. Gramaccioni, Saeid Jamili, Christian Marinoni, Edoardo Massaro, Claudia Medaglia, Giuseppe Nachira, Leonardo Nucciarelli, Ludovica Paglialunga, Marco Pennese, Sveva Pepe, Enrico Rocchi, Aurelio Uncini,和Danilo Comminiello《L3DAS21挑战:3D音频信号处理的机器学习》,2021年。

[3]曹尹,孔秋强,Turab Iqbal,安凤燕,王文武,和Mark D. Plumbley,“使用两阶段策略的复音声音事件检测和定位”,arXiv预印本:arXiv:1905.00268v4, 2019。

[4]梅萨罗斯、安娜玛利亚、萨拉特·阿达万尼、阿孔蒂斯·波利蒂斯、托尼·海托拉和图马斯·维尔塔宁。“声音事件的定位和探测联合测量。”2019 IEEE信号处理在音频和声学中的应用研讨会(WASPAA), 2019年。https://doi.org/10.1109/waspaa.2019.8937220。

支持功能

提取到达方向(DOA)目标

函数T = extractDOATargets (csvFile params)%EXTRACTDOATARGETS提取到达方向(DOA)目标% T = extractDOATargets(fileName,params)解析CSV文件% fileName,并返回一个矩阵t。目标矩阵是一个n × 3矩阵%矩阵,其中N对应帧数,3对应3个轴描述三维空间中的位置。。%预分配目标矩阵。全0的帧对应无声音%的来源。T = 0 (params.Targets.NumFrames 3);%将声源的时间戳量化为帧。startendTime = [csvFile.Start, csvFile.End];startendFrame = time2frame (startendTime params.Targets.TotalDuration params.Targets.NumFrames);%对于每个声源,填充目标矩阵声源位置为%适当的帧数。ii = 1:size(startendFrame,1) idx = startendFrame(ii,1):startendFrame(ii,2)-1;T (idx:) = repmat ([csvFile.X (ii), csvFile.Y (ii), csvFile.Z (ii)],元素个数(idx), 1);结束%缩放目标,使其在-1和1之间(tanh的边界%活化层)。将目标包裹在cell数组中,方便批量%处理。T = {T / params.DOA.ScaleFactor};结束

提取声音事件检测(SED)目标

函数T = extractSEDTargets (csvFile params)%EXTRACTSEDTARGETS提取声音事件检测(SED)目标% T = extractSEDTargets(fileName,params)解析CSV文件% fileName,并返回SED目标t的矩阵。目标矩阵是一个n × k矩阵%矩阵,其中N对应帧数,K对应%声音类的数量。%预分配目标矩阵。全0的帧对应无声音%的来源。T = 0 (params.Targets.NumFrames params.SoundClasses.Count);%将声源的时间戳量化为帧。startendTime = [csvFile.Start, csvFile.End];startendFrame = time2frame (startendTime params.Targets.TotalDuration params.Targets.NumFrames);%对于每个声源,填充目标矩阵的相应列%,带1,表示该帧中存在声音类。ii = 1:size(startendFrame,1) classID = params.SoundClasses(csvFile.Class{ii});T (startendFrame (2, 1): startendFrame (2, 2) 1, classID) = 1;结束%将目标包装在单元格数组中,以方便批处理。T = {T};结束

短时傅里叶变换(STFT)

函数X = extractSTFT(年代,params)%EXTRACTSTFT提取log-量级的居中STFT% X = extractSTFT({s1,s2},params)连接s1和s2,然后%提取单边对数量级STFT。之前对信号进行填充%的STFT,使第一个窗口以第一个样本为中心。的% output被修剪,以去除第一个(DC)系数和最后一个系数%频谱。输入参数定义了STFT。沿着第二个(通道)维度连接信号。。音频=猫(2 s {:});%提取居中的STFT。N =元素个数(params.SED.Window);overlapLength = N - params.SED.HopLength;S = centeredSTFT(音频、params.SED.Window overlapLength N);%修剪第一个系数从所有频谱和修剪最后一个频谱。S = S(2:结束,1:end-1,:);%转换为对数量级。使用偏移量来防止log为零。mag = log(abs(S) + eps);%投射输出到单精度。X =单(mag);结束

相位变换广义交叉相关(GCC-PHAT)

函数X = extractGCCPHAT(年代,params)%EXTRACTGCCPHAT提取广义互相关相位变换(GCC-PHAT)特征% X = extractGCCPHAT({s1,s2},params)连接s1和s2,然后%提取所有通道对的GCC-PHAT。%连接两个麦克风对应的信号。音频=猫(2 s {:});%统计输入通道的总数。nChan =大小(音频,2);%计算输出通道总数。numOutputChannels = nchoosek (nChan, 2);%预分配一个NumFeatures-by-NumFrames-by-NumChannels特性(预测器)%的矩阵。numFrames =大小(音频,1)/ params.DOA.HopLength;X = 0 (params.DOA.NumBands numFrames numOutputChannels);% -----------------------------------%计算每一对渠道的GCC-PHAT。%预计算每个通道的STFT。N =元素个数(params.DOA.Window);overlapLength = N - params.DOA.HopLength;micAB_stft = centeredSTFT(音频、params.DOA.Window overlapLength N);conjmicAB_stft =连词(micAB_stft(:,:, 2:结束));idx = 1;2 = 1: nChan - 1 R = micAB_stft (:,:, ii)。* conjmicAB_stft (:,:, ii:结束);R = exp(1i .* angle(R));R = padarray(R, N/2 - 1,“职位”);gcc = fftshift(传输线(R, [], 1,“对称”), 1);X (:,:, idx: idx +大小(R, 3) 1) = gcc(地板(N / 2 + 1 - (params.DOA.NumBands-1) / 2):地板(N / 2 + 1 + (params.DOA.NumBands-1) / 2), 1: end-1,:);idx = idx + size(R,3);结束% -----------------------------------%投射输出到单精度。X =单(X);结束

居中短时傅里叶变换(STFT)

函数s = centeredSTFT(音频、赢得overlapLength fftLength)% CENTEREDSTFT集中STFT% s = centeredSTFT(audioIn,win,overlapLength,fftLength)计算一个STFT%,第一个窗口以第一个样本为中心。两端分别为%填充的反射音频信号。% Pad前面和后面的输入信号。firstR =翻转(音频(1:fftLength / 2,:), 1);lastR = flip(audio(end - fftLength/2 + 1:end,:),1);sig =猫(1 firstR音频,昨天深);% STFT执行。s = stft(团体、窗口=赢,OverlapLength = OverlapLength FFTLength = FFTLength FrequencyRange =“单向的”);结束

将时间戳转换为帧数

函数fnum = time2frame (t,杜尔numFrames)%TIME2FRAME将时间戳转换为帧号% fnum = time2frame(t,dur,numFrames)映射存在于dur中的时间t,%到帧号,如果dur被划分为numFrames。stp =大调的/ numFrames;qt = (t / stp)。* stp;fnum = floor(qt*(numFrames - 1)/dur) + 1;结束

通过CNN和RNN网络转发

函数(损失,cnnState rnnState, Y3) = forwardAll(模型、X T)%FORWARDALL模型通过CNN和RNN网络的正向传递% [loss,cnnState,rnnState] = forwardAll(模型,X,T)传递预测器X%通过模型,并返回损失和网络的状态%的模型。该模型是一个包含CNN网络和RNN的结构%网络。%% [loss,cnnState,rnnState,Y] = forwardAll(model,X,T)也返回最终结果模型Y. %的预测。%通过CNN传递预测器。向前(cnnState日元)= (model.CNN X);%标记从CNN输出的尺寸,以供RNN消费。Y2 = dlarray(日元“TCUB”);%通过RNN传递预测器。向前(Y3, rnnState) = (model.RNN, Y2);%计算损失。损失= seldNetLoss (Y3、T);结束

完整的模型预测

函数(损失、Y3) = predictAll(模型、X T)%PREDICTALL通过CNN和RNN网络的模型预测%[损耗,预测]= predictAll(模型,X,T)将预测器X通过%的模型,并返回损失和模型预测。模型是一个%结构包含一个CNN网络和一个RNN网络。%通过CNN传递预测器。日元=预测(model.CNN X);%标记从CNN输出的尺寸,以供RNN消费。Y2 = dlarray(日元“TCUB”);%通过RNN传递预测器。Y3 =预测(model.RNN, Y2);%计算损失。损失= seldNetLoss (Y3、T);结束

预测批

函数损失= predictBatch(模型,兆贝可)%PREDICTBATCH计算小批处理队列的丢失% loss = predictBatch(model,mbq)返回由。计算的总损失%通过模型传递小批处理队列的全部内容。%重置小批队列并初始化计数器。复位(mbq)损耗= 0;n = 0;hasdata(兆贝可)%从小批队列中读取预测器和目标。[X, T] =下一个(兆贝可);将小批量产品通过模型并计算损失。lss = predictAll(模型、X T);lss = lss /大小(T, 2);%更新总损失。损失=损失+ lss;%数据点总数。N = N + 1;结束%用累计的总损失除以小批量的数量。损失= / n损失;结束

计算模型损失,梯度和网络状态

函数(损失、渐变、状态)= modelLoss(模型、X T)%MODELLOSS计算模型损耗、梯度和网络状态%[损耗,梯度,状态]= modelLoss(模型,X,T)通过%预测X通过模型,并返回损失,梯度,和%模型中网络的状态。该模型是一个包含CNN网络和RNN网络。%通过模型传递预测器。(损失、cnnState rnnState] = forwardAll(模型、X T);%隔离可学习对象。allGrad。CNN = model.CNN.Learnables;allGrad。RNN = model.RNN.Learnables;状态。CNN = cnnState;状态。RNN = rnnState;%计算梯度。梯度= dlgradient(损失、allGrad);结束

SELDnet的损失函数

函数损失= seldNetLoss (Y, T)%SELDNETLOSS计算DOA或SED模型的SELDnet损失函数% loss = seldNetLoss(Y,T)返回给定预测Y和的SELDnet损失%目标t损失函数取决于网络(DOA或SED)。的%网络由目标的维数推断。对于DOA网络,%损失函数为均方误差。对于SED网络,损失%函数是crossentropy。%确定目标是否对应于DOA网络或SED%网络。isDOAModel =大小(T, find (dim (T) = =“C”) = = 3;如果isDOAModel%计算MSE损失。doaLoss = mse (Y, T);doaLossFactor = 2 / (size(Y,1) * size(Y,3));loss = doaLoss * doaLossFactor;%与原始实现保持一致其他的%计算交叉熵损失。损失= crossentropy (Y, T, TargetCategories =“独立”NormalizationFactor =“所有元素”);结束loss = loss * size(T,2);结束

计算性能指标

函数(r cm) = computeMetrics (sedModel、doaModel sedMBQ, doaMBQ, params, nvargs)%COMPUTEMETRICS计算性能指标% [r,cm] = computeMetrics(sedModel,doaModel,sedMBQ,doaMBW,params)返回根据SED和DOA计算的性能指标结构%验证小批队列,和混淆矩阵cm有效SED%的地区。参数sedModel doaModel sedMBQ doaMBQ params nvargs.;ApplySmoothing = false;结束%初始化计数器。TP = 0;FP = 0;FN = 0;= 0;ct = 0;呃= 0;sedYAll = [];sedTAll = [];%遍历所有数据。重置(sedMBQ)重置(doaMBQ)hasdata (sedMBQ)%获取SED模型的预测器、目标和预测。[sedXb, sedTb] =下一个(sedMBQ);[~, sedYb] = predictAll (sedModel、sedXb sedTb);sedTb = extractdata(收集(sedTb));sedYb = extractdata(收集(sedYb));%获取DOA模型的预测器、目标和预测。[doaXb, doaTb] =下一个(doaMBQ);[~, doaYb] = predictAll (doaModel、doaXb doaTb);doaTb = extractdata(收集(doaTb));doaYb = extractdata(收集(doaYb));doaYb = doaYb * params.DOA.ScaleFactor;doaTb = doaTb * params.DOA.ScaleFactor;%对小批进行循环。批= 1:尺寸(sedYb, 2)%隔离当前数据点的预测器和目标。sedY =挤压(sedYb(:,批:));sedT =挤压(sedTb(:,批:));doaY =挤压(doaYb(:,批:));衰老=挤压(doaTb(:,批:));%如果一个帧的SED预测都是用low做出的%置信度(低于阈值),假设没有声音%的来源。isActive = ~(总和(双(sedY < params.SilenceThreshold), 1) = =大小(sedY,1));%将SED预测器和目标从单一热点向量转换为%标量。[~, sedY] = max (sedY [], 1);sedY = sedY。* isActive;[isActive, sedT] = max (sedT [], 1);sedT = sedT。* isActive;%平稳输出。如果nvargs。ApplySmoothing [doaY,sedY] = smoothOutputs(doaY,sedY,params);结束%执行位置敏感检测。[tp, fp, fn、e、c] = locationSensitiveDetection (sedY、sedT doaY,衰老,params);%累积性能指标。TP = TP + TP;FP = FP + FP;FN = FN + FN;Err = Err + e;Ct = Ct + c;sedYAll = [sedYAll sedY.*isActive];% #好< AGROW >sedTAll = [sedTAll sedT.*isActive];% #好< AGROW >结束It = It + 1;结束%计算性能指标。r.precision = TP/(TP + FP + eps);r.recall = TP / (TP + FN + eps);r.f1Score = 2 * (r.precision * r.recall) / (r。Precision + r.recall + eps);r.avgErr =呃/ ct;%计算混淆矩阵。confmat = confusionmat (sedTAll、单(sedYAll),订单= 0:14);厘米= confmat(2:结束,2:结束);%从混淆矩阵中去除沉默。结束

位置敏感的检测

函数[TP, FP, FN、totErr ct) = locationSensitiveDetection (sedY、sedT doaY,衰老,params)%LOCATIONSENSITIVEDETECTION位置敏感检测% (TP、FP FN、totErr ct) =% locationSensitiveDetection(sedY,sedT,doaY,doaT,params)计算%真阳性,假阳性,假阴性,DOA总错误,和活动目标的%数量。每个指标的定义在%[4]。%计算距离。dist = vecnorm (doaY-doaT);%判断声音是否活跃,以供参考和预测。isReferenceActive = sedT ~ = 0;isPredictedActive = sedY ~ = 0;%计算引用活动段的总DOA误差。totErr = (dist。* isReferenceActive)之和;%统计活动目标的总数。ct =总和(isReferenceActive);%确定DOA是否在每帧阈值内。isDOAnear = dist < params.SpatialThreshold;%真阳性:TP = sum(isDOAnear & isReferenceActive & isPredictedActive & (sedT==sedY));%假阳性:FP1 = sum(~isReferenceActive & isPredictedActive);FP2 = sum(isReferenceActive & isPredictedActive & (sedT~=sedY | ~isDOAnear));Fp = fp1 + fp2;%假阴性:FN1 = sum(isReferenceActive & ~isPredictedActive);FN2 = sum(isReferenceActive & (sedT~=sedY | ~isDOAnear));Fn = fn1 + fn2;结束

平滑的输出

函数[doaYSmooth, sedYSmooth] = smoothOutputs (doaY、sedY params)%SMOOTHOUTPUTS随时间的平滑DOA和SED预测% [doaYSmooth,sedYSmooth] = smoothOutputs(doaY,sedY,params)平滑DOA%和SED随时间的预测。%预分配平滑输出。doaYSmooth = doaY;sedYSmooth = sedY;%聚类DOA预测。集群= = clusterdata (doaY”标准“距离”,截止= params.SpatialThreshold);stt = 1;新奥集团= 1;新奥集团< = params.Targets.NumFrames如果Clusters (stt) == Clusters (enn) enn = enn + 1;其他的doaYSmooth (: stt: enn-1) = smoothDOA (doaY (:, stt: enn-1));sedYSmooth (: stt: enn-1) =平滑(sedY (:, stt: enn-1));stt =新奥集团;结束结束doaYSmooth (: stt: enn-1) = smoothDOA (doaY (:, stt: enn-1));sedYSmooth (: stt: enn-1) =平滑(sedY (:, stt: enn-1));sedYSmooth =圆(movmedian (sedYSmooth 5));结束

光滑的DOA预测

函数平滑= smoothDOA(块)SMOOTHDOA平滑的DOA预测% smoothened = smoothDOA(chunk)通过替换%的值,每个轴的平均值在块。的意思是丢弃数据的上下四分之一后计算的%。%确定块的长度,然后索引切出中间%一半的数据。chlen =大小(块,2);圣= max(圆(chlen * 1/4), 1);en = max(圆(chlen * 3/4), 1);%对空间轴(列)进行排序。昏暗的=排序(块,2);%取内半部的平均值。平滑= repmat(意思是(暗(:,圣:en), 2), 1, chlen);结束

光滑的SED预测

函数平滑=平滑(块)%SMOOTHSED SED平滑预测% smoothened = smoothSED(chunk)使用模式平滑SED预测。平滑= repmat(模式(块),1,大小(块,2));结束
Baidu
map