主要内容

利用深度学习训练三维声音事件定位和检测(SELD)

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

简介

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

您将为声音事件检测任务和定位任务训练两个单独的模型。这两个模型都是基于卷积递归神经网络架构描述[1].声音事件检测任务被定义为分类任务。该声音事件定位任务估计声源的笛卡尔坐标,并将其表述为回归任务。您使用L3DAS21数据集[2]训练和验证网络。要了解本例中训练的模型,请参见训练有素的循环卷积神经网络的三维声音事件定位与检测(音频工具箱)

下载和准备数据

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

下载数据集。

downloadFolder = matlab.internal.examples.download金宝appSupportFile(“音频”“L3DAS21_ov1.zip”);dataFolder = tempdir;unzip(下载文件夹,数据文件夹)dataset = fullfile(数据文件夹,数据文件夹)“L3DAS21_ov1”);

可选减少数据集

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

speedupExample =

创建数据存储

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

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

如果需要,减少数据集。

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

检查数据

预览双音域录音并绘制数据图。

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

听一段数据。

麦克风=1;频道=1;时间=10;Fs = 32e3;数据的已知采样率。s = [micA,micB];数据= s(1:轮(时长*fs),通道+(麦克风-1)*4);声音(数据、fs)

创建目标

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

keySet = [“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(keySet,valueSet);

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

[folder,fn] = fileparts(adsTrainA.Files);targetPath = fullfile(strrep(文件夹,filesep+“数据”filesep +“标签”),“label_”+ strrep (fn,“_A”"") +. csv”);ttdsTrain = tabularTextDatastore(targetPath);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的目标

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

SEDTargets = extractSEDTargets(labelTable,params);[numframes,numclasses] = size(SEDTargets{1})
Numframes = 600
Numclasses = 14

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

dsTTrain = transform(ttdsTrain,@(x)extractSEDTargets(x,params));sedTTrain = readall(dsTTrain);[folder,fn] = fileparts(adsValidationA.Files);targetPath = fullfile(strrep(文件夹,filesep+“数据”filesep +“标签”),“label_”+ strrep (fn,“_A”"") +. csv”);ttdsValidation = tabularTextDatastore(targetPath);dsTValidation = transform(ttdsValidation,@(x)extractSEDTargets(x,params));sedTValidation = readall(dsTValidation);

DOA的目标

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

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

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

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

dsTTrain = transform(ttdsTrain,@(x)extractDOATargets(x,params));doaTTrain = readall(dsTTrain);[folder,fn] = fileparts(adsValidationA.Files);targetPath = fullfile(strrep(文件夹,filesep+“数据”filesep +“标签”),“label_”+ strrep (fn,“_A”"") +. csv”);ttdsValidation = tabularTextDatastore(targetPath);dsTValidation = transform(ttdsValidation,@(x)extractDOATargets(x,params));doaTValidation = readall(dsTValidation);

声音事件检测(SED)

特征提取

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

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

支撑函数金宝app,extractSTFT,获取麦克风读数的单元阵列,并提取半边居中对数幅stft。两个麦克风对应的STFT特征沿第三维叠加。

stftfeat = extractSTFT({micA,micB},params);[numfeatered, numframered, numchannelsed] = size(stftfeat)
numfeatuured = 256
numframered = 4800
numchannelsed = 8

绘制一个通道的STFT特征。

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

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

pFlag = ~isempty(ver(“平行”) && ~speedupExample;trainDS = combine(adsTrainA,adsTrainB);trainDS_T = transform(trainDS,@(x){extractSTFT(x,params)},IncludeInfo=false);XTrain = readall(trainDS_T,UseParallel=pFlag);valDS = combine(adsValidationA,adsValidationB);valDS_T = transform(valDS,@(x){extractSTFT(x,params)},IncludeInfo=false);XValidation = readall(valDS_T,UseParallel=pFlag);

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

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

培训方案

定义Adam优化的训练参数。

trainoptionsed = struct(...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对象从训练和验证数据存储中读取小批。

trainseddmbq = 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([numfeatered, numframered,numchannelsSED],Normalization= . seldnetCNNLayers = [imageInputLayer([numfeatered, numframered, numchannelsed],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。美国有线电视新闻网;sedModel。RNN = netRNN;

SED培训网络

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

迭代= 0;averageGrad = [];averageSqGrad = [];Epoch = 0;bestLoss = Inf;badEpochs = 0;learnRate = trainOptionsSED.InitialLearnRate;

要显示训练进度,请初始化支持对象金宝appprogresPlotterSELD.支撑对象金宝app,progressPlotterSELD,在打开此示例时放置在当前文件夹中。

pp = progressPlotterSELD();

进行循环训练。

rng (0)epoch < trainOptionsSED。MaxEpochs && badEpochs < trainoptionsed .;ValidationPatience epoch = epoch + 1;打乱迷你批处理队列。洗牌(trainSEDmbq)hasdata (trainSEDmbq)更新迭代计数器。迭代=迭代+ 1;读取小批数据。[X,T] = next(trainSEDmbq);使用dlfeval和modelLoss函数评估模型梯度和损失。[loss,grad,state] = dlfeval(@modelLoss,sedModel,X,T);损耗=损耗/尺寸(T,2);%更新状态。sedModel.CNN.State = state.CNN;sedModel.RNN.State = state.RNN;使用Adam优化器更新网络参数。。[sedModel,averageGrad, averageqgrad] = adamupdate(sedModel,grad,averageGrad,...迭代,averageSqGrad learnRate、trainOptionsSED.GradientDecayFactor trainOptionsSED.SquaredGradientDecayFactor);更新培训进度图。updateTrainingProgress (pp、时代=时代,LearnRate = LearnRate迭代=迭代,损失=损失);结束%在每个epoch之后执行验证。loss = predictBatch(sedModel,validationSEDmbq);用验证结果更新训练进度图。。updateValidation (pp、损失=损失、迭代=迭代)如果验证损失得到改善,则创建检查点。如果验证%损失没有改善,增加了坏epoch的数量。如果损失< bestLoss bestLoss =损失;badEpochs = 0;文件名=“SED-BestModel”;保存(文件名,“sedModel”);其他的badEpochs = badEpochs + 1;结束更新学习率如果rem(epoch, trainoptionssed . learnratdroppperiod)==0 learnRate = learnRate* trainoptionssed . learnratdroppfactor;结束结束

到达方向(DOA)

特征提取

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

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

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

gccphatfeat = extractGCCPHAT({micA,micB},params);[numfeaturesDOA,timestepsDOA,numchannelsDOA] = size(gccphatfeat)
numfeaturesDOA = 96
timestepsDOA = 4800
numchannelsDOA = 28

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

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

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

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

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

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

培训方案

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

trainOptionsDOA = trainoptionsed;

为列车和验证集创建迷你批处理队列。

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],Stride=[3,2],Padding=“相同”、名称=“maxpool1”);netCNN = dlnetwork(layerGraph(seldnetCNNLayers));seldnetGRULayers(11) = fullyConnectedLayer(3,Name=“fc4”);seldnetGRULayers(12) = tanhLayer(Name=“输出”);netRNN = dlnetwork(layerGraph(seldnetGRULayers));

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

doaModel。美国有线电视新闻网;doaModel。RNN = netRNN;

列车DOA网络

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

迭代= 0;averageGrad = [];averageSqGrad = [];Epoch = 0;bestLoss = Inf;badEpochs = 0;learnRate = trainOptionsDOA.InitialLearnRate;

要显示训练进度,请初始化支持对象金宝appprogressPlotterSELD.支撑对象金宝app,progressPlotterSELD,在打开此示例时放置在当前文件夹中。

pp = progressPlotterSELD();

进行循环训练。

rng (0)epoch < trainOptionsDOA。MaxEpochs && badEpochs < trainOptionsDOA。ValidationPatience epoch = epoch + 1;打乱迷你批处理队列。洗牌(trainDOAmbq)hasdata (trainDOAmbq)更新迭代计数器。迭代=迭代+ 1;读取小批数据。[X,T] = next(trainDOAmbq);使用dlfeval和modelLoss函数评估模型梯度和损失。[loss,grad,state] = dlfeval(@modelLoss,doaModel,X,T);损耗=损耗/尺寸(T,2);%更新状态。domodel . cnn . state = state.CNN;dommodell . rnn . state = state.RNN;使用Adam优化器更新网络参数。。[doaModel,averageGrad,averageSqGrad] = adamupdate(doaModel,grad,averageGrad,...迭代,averageSqGrad learnRate、trainOptionsDOA.GradientDecayFactor trainOptionsDOA.SquaredGradientDecayFactor);更新培训进度图updateTrainingProgress (pp、时代=时代,LearnRate = LearnRate迭代=迭代,损失=损失);结束%在每个epoch之后执行验证loss = predictBatch(doaModel,validationDOAmbq);用验证结果更新训练进度图。。updateValidation (pp、损失=损失、迭代=迭代)如果验证损失得到改善,则创建检查点。如果验证%损失没有改善,增加了坏epoch的数量。如果损失< bestLoss bestLoss =损失;badEpochs = 0;文件名=“DOA-BestModel”;保存(文件名,“doaModel”);其他的badEpochs = badEpochs + 1;结束更新学习率如果rem(epoch, trainoptionsdoa . learnratdroppperiod)==0 learnRate = learnRate* trainoptionsdoa . learnratdroppfactor;结束结束

评估系统性能

中定义的位置敏感检测错误可评估系统性能[4].加载性能最佳的模型。

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

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

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

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

results = computeMetrics(sedModel,doaModel,validationSEDmbq,validationDOAmbq,params);结果
结果=带字段的结构:精度:0.4246召回率:0.4275 f1Score: 0.4261平均gerr: 0.1861

computeMetrics金宝app支持功能可以在评估系统之前,随时间推移选择性地平滑决策。此选项需要统计和机器学习工具箱™。再次评估系统,这一次包括平滑。

[results,cm] = computeMetrics(sedModel,doaModel,validationSEDmbq,validationDOAmbq,params,ApplySmoothing=true);结果
结果=带字段的结构:精度:0.5077召回率:0.5084 f1Score: 0.5080平均gerr: 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, pp. 34-48, 2019。

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

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

[4] Mesaros, Annamaria, Sharath Adavanne, Archontis Politis, Toni Heittola和Tuomas Virtanen。“声音事件定位和探测的联合测量”2019 IEEE信号处理在音频和声学中的应用研讨会, 2019年。https://doi.org/10.1109/waspaa.2019.8937220。

金宝app支持功能

提取到达方向目标

函数T = extractDOATargets(csvFile,params)提取到达方向(DOA)目标% T = extractDOATargets(fileName,params)解析CSV文件返回一个矩阵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);对于每个声源,填充目标矩阵声源位置为%适当的帧数。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的边界)激活层)。将目标包装在单元格数组中以方便批处理%处理。T = {T/params.DOA.ScaleFactor};结束

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

函数T = extractSEDTargets(csvFile,params)提取声音事件检测(SED)目标% T = extractSEDTargets(fileName,params)解析CSV文件返回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(ii,1):startendFrame(ii,2)-1,classID) = 1;结束将目标包装在单元格数组中,以便于批处理。T = {T};结束

短时傅里叶变换(STFT)

函数X = extractSTFT(s,params)提取居中STFT的对数大小% X = extractSTFT({s1,s2},params)连接s1和s2,然后%提取单边对数量级STFT。信号是预先填充的% STFT,使第一个窗口以第一个样本为中心。的%输出被修剪以去除第一个(DC)系数和最后一个%频谱。输入参数定义STFT。沿着第二个(信道)维度连接信号。音频= cat(2,s{:});提取居中的STFT。N = number (params.SED.Window);overlapLength = N - params.SED.HopLength;S = centeredSTFT(音频,params.SED.Window,overlapLength,N);从所有频谱中修剪第一个系数,并修剪最后一个频谱。S = S(2:end,1:end-1,:);%转换为对数大小。使用偏移量来防止log为零。mag = log(abs(S) + eps);%铸造输出单精度。X = single(mag);结束

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

函数X = extractGCCPHAT(s,params)提取广义互相关相位变换(GCC-PHAT)特征% X = extractGCCPHAT({s1,s2},params)连接s1和s2,然后%为所有通道对提取GCC-PHAT。连接两个麦克风对应的信号。音频= cat(2,s{:});统计输入通道总数。nChan = size(音频,2);计算输出通道总数。numOutputChannels = nchoosek(nChan,2);预先分配一个NumFeatures-by-NumFrames-by-NumChannels特性(预测器)%的矩阵。numFrames = size(audio,1)/params.DOA.HopLength;X = 0 (params.DOA.NumBands,numFrames,numOutputChannels);% -----------------------------------计算每对通道的GCC-PHAT。为每个通道预计算STFT。N = number (params.DOA.Window);overlapLength = N - params.DOA.HopLength;micAB_stft = centeredSTFT(音频,params.DOA.Window,overlapLength,N);conmicab_stft = conj(micAB_stft(:,:,2:end));Idx = 1;2 = 1: nChan - 1 R = micAB_stft (:,:, ii)。* conjmicAB_stft (:,:, ii:结束);R = exp(1i .*角度(R));R = padarray(R, N/2 - 1,“职位”);gcc = fftshift(ifft(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(音频,win,overlapLength,fftLength)居中STFT% s = centeredSTFT(audioIn,win,overlapLength,fftLength)计算一个STFT%,第一个窗口以第一个示例为中心。两端是%填充反射音频信号。%输入信号的前面和后面。firstR = flip(音频(1:fftLength/2,:),1);lastR = flip(audio(end - fftLength/2 + 1:end,:),1);sig = cat(1,firstR,audio,lastR);%执行STFT。s = stft(sig,Window=win,OverlapLength= OverlapLength,FFTLength= FFTLength,FrequencyRange=“单向的”);结束

将时间戳转换为帧号

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

通过CNN和RNN网络向前传递

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

全模型预测

函数[损失,Y3] = predictAll(模型,X,T)通过CNN和RNN网络进行模型预测% [loss,prediction] = predictAll(model,X,T)传递预测因子X%的模型,并返回损失和模型预测。这个模型是结构,包含CNN网络和RNN网络。%通过CNN传递预测器。Y1 = predict(model.CNN,X);标记从CNN输出的尺寸供RNN使用。Y2 = darray (Y1,“TCUB”);%通过RNN传递预测器。Y3 = predict(model.RNN,Y2);计算损失。损失= seldNetLoss(Y3,T);结束

预测批

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

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

函数[loss,gradients,state] = modelLoss(model,X,T)计算模型丢失、梯度和网络状态% [loss,gradients,state] = modelLoss(model,X,T)通过%预测因子X通过模型,并返回损失,梯度和%模型中网络的状态。模型是一个包含CNN网络和RNN网络。通过模型传递预测因子。[loss,cnnState,rnnState] = forwardAll(model,X,T);把可学习的东西分离出来。allGrad。CNN = model.CNN.Learnables;allGrad。RNN =模型。RNN. learnables;状态。CNN = cnnState;状态。RNN = rnnState;计算梯度。gradients = dlgradient(loss,allGrad);结束

SELDnet的损失函数

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

计算性能指标

函数[r,cm] = computeMetrics(sedModel,doaModel,sedMBQ,doaMBQ,params,nvargs)计算性能指标% [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;Err = 0;sedYAll = [];sedTAll = [];遍历所有数据。重置(sedMBQ)重置(doaMBQ)hasdata (sedMBQ)获取SED模型的预测器、目标和预测。。[sedXb,sedTb] = next(sedMBQ);[~,sedYb] = predictAll(sedModel,sedXb,sedTb);sedTb = extractdata(gather(sedTb));sedYb = extractdata(收集(sedYb));获取DOA模型的预测器、目标和预测。。[doaXb,doaTb] = next(doaMBQ);[~,doaYb] = predictAll(doaModel,doaXb,doaTb);doaTb = extractdata(gather(doaTb));doaYb = extractdata(gather(doaYb));doaYb = doaYb*params.DOA.ScaleFactor;doaTb = doaTb*params.DOA.ScaleFactor;遍历小批。batch = 1:size(sedYb,2)隔离当前数据点的预测因子和目标。sedY = squeeze(sedYb(:,batch,:));sedT = squeeze(sedTb(:,batch,:));doaY = squeeze(doaYb(:,batch,:));doaT = squeeze(doaTb(:,batch,:));%如果一个帧的SED预测都是用low%置信度(低于阈值),假设没有声音%源当前。isActive = ~(sum(double(sedY将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,doaT,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。精度+ r.recall + eps);r.avgErr = err/ct;计算混淆矩阵。confmat = confusionmat(sedTAll,single(sedYAll),Order=0:14);Cm = confmat(2:end,2:end);从混乱矩阵中移除沉默。结束

位置敏感检测

函数[TP,FP,FN,totErr,ct] = locationSensitiveDetection(sedY,sedT,doaY,doaT,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 = sum(dist.*isReferenceActive);%活动目标总数。ct = sum(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)平滑DOA和SED随时间的预测% [doaYSmooth,sedYSmooth] = smoothOutputs(doaY,sedY,params)平滑DOA%和SED预测随时间的变化。预分配平滑输出。doaYSmooth = doaY;sedYSmooth = sedY;聚类DOA预测。clusters = clusterdata(doaY',Criterion=“距离”,截止= params.SpatialThreshold);STT = 1;Enn = 1;enn <= params.Targets.NumFrames如果集群(stt) ==集群(enn) enn = enn + 1;其他的doaYSmooth(:,stt:enn-1) = smoothDOA(doaY(:,stt:enn-1));sedYSmooth(:,stt:enn-1) = smoothSED(sedY(:,stt:enn-1));STT = enn;结束结束doaYSmooth(:,stt:enn-1) = smoothDOA(doaY(:,stt:enn-1));sedYSmooth(:,stt:enn-1) = smoothSED(sedY(:,stt:enn-1));sedYSmooth = round(movmedian(sedYSmooth,5));结束

平滑DOA预测

函数平滑=平滑doa(块)平滑DOA平滑DOA预测平滑DOA(chunk)平滑DOA预测每个轴的%值与该轴在数据块中的平均值。均值是丢弃数据的上下四分之一后计算的%。确定块的长度,然后将中间的索引剪掉%一半的数据。Chlen = size(chunk,2);St = max(round(chlen*1/4),1);En = max(round(chlen*3/4),1);对空间轴(列)进行排序。Dim = sort(chunk,2);取内半部分的均值。Smoothed = repmat(mean(dim(:,st:en),2),1,chlen);结束

平滑SED预测

函数光滑的(块)平滑SED预测% smooened = smoothSED(chunk)使用模式平滑SED预测。smoothened = repmat(mode(chunk),1,size(chunk,2));结束