В этом примере показано, как классифицировать жанр музыкального отрывка с использованием вейвлет-временного рассеяния и хранилища аудиоданных. При вейвлет-рассеянии данные распространяются посредством серии вейвлет-преобразований, нелинейностей и усреднения для получения представлений данных с низкой дисперсией. Эти представления с низкой дисперсией затем используются в качестве входных данных для классификатора.
Набор данных, используемый в этом примере, представляет собой коллекцию жанров GTZAN [7] [8]. Данные предоставляются в виде архивного зипированного дегтя, размер которого составляет приблизительно 1,2 ГБ. Для несжатого набора данных требуется около 3 ГБ дискового пространства. При извлечении сжатого tar-файла из ссылки, представленной в ссылках, создается папка с десятью подпапками. Каждая подпапка названа по жанру музыкальных образцов, которые она содержит. Жанры: блюз, классика, кантри, диско, хипхоп, джаз, метал, поп, регги, рок. Существует 100 примеров каждого жанра, и каждый аудиофайл состоит примерно из 30 секунд данных, дискретизированных на частоте 22050 Гц. В оригинальной статье авторы использовали ряд признаков временной и частотной областей, включая коэффициенты mel-frequency cepstral (MFC), извлеченные из каждого музыкального примера, и классификацию модели гауссовой смеси (GMM) для достижения точности 61 процент [7]. Впоследствии к этим данным были применены сети глубокого обучения. В большинстве случаев эти подходы глубокого обучения состоят из сверточных нейронных сетей (CNN) с коэффициентами MFC или спектрограммами в качестве входных данных для глубокого CNN. Эти подходы привели к производительности около 84% [4]. Подход LSTM с квантами времени спектрограмм приводил к 79% точности, а особенности временной и частотной областей в сочетании с подходом ансамблевого обучения (AdaBoost) приводили к 82% точности тестового набора [2] [3]. Недавно подход к машинному обучению с разреженным представлением достиг точности приблизительно 89% [6].
Первым шагом является загрузка коллекции жанров GTZAN [7] [8]. В данном примере предполагается, что набор данных загружается во временный каталог .tempdir, в MATLAB ®. Если вы решили загрузить данные в папку, отличную отtempdirизмените имя каталога в последующих инструкциях. Использовать gunzip для загрузки и распаковки набора данных. Затем использовать untar для извлечения содержимого файла дегтя. Папка genres создается в tempdir. Внутри genres десять подпапок, по одной для каждого музыкального жанра.
dataURL = 'http://opihi.cs.uvic.ca/sound/genres.tar.gz'; gunzip(dataURL,tempdir) % creates genres.tar in tempdir untar(fullfile(tempdir,'genres.tar'),tempdir) % creates genres folder
Единственными параметрами для задания в сети вейвлет-временного рассеяния являются длительность инвариантности времени, количество блоков вейвлет-фильтров и количество вейвлетов на октаву. Для большинства приложений достаточно каскадировать данные через два банка вейвлет-фильтров. В этом примере мы используем сеть рассеяния по умолчанию, которая использует два банка вейвлет-фильтров. Первый блок фильтров имеет 8 вейвлетов на октаву, а второй блок фильтров имеет 1 вейвлет на октаву. Для этого примера установите инвариантную шкалу равной 0,5 секунды, что соответствует чуть более 11000 выборок для заданной частоты дискретизации. Создайте сеть декомпозиции вейвлет-рассеяния.
sn = waveletScattering('SignalLength',2^19,'SamplingFrequency',22050,... 'InvarianceScale',0.5);
Чтобы понять роль шкалы инвариантности, получите и постройте график масштабного фильтра во времени вместе с действительной и мнимой частями наиболее масштабного вейвлета из первого набора фильтров. Следует отметить, что поддержка масштабного фильтра во времени составляет, по существу, 0,5 секунды, как предусмотрено. Кроме того, временная поддержка наиболее масштабного вейвлета не превышает инвариантного масштаба разложения вейвлет-рассеяния.
[fb,f,filterparams] = filterbank(sn);
phi = ifftshift(ifft(fb{1}.phift));
psiL1 = ifftshift(ifft(fb{2}.psift(:,end)));
dt = 1/22050;
time = -2^18*dt:dt:2^18*dt-dt;
scalplt = plot(time,phi,'linewidth',1.5);
hold on
grid on
ylimits = [-3e-4 3e-4];
ylim(ylimits);
plot([-0.25 -0.25],ylimits,'k--');
plot([0.25 0.25],ylimits,'k--');
xlim([-0.6 0.6]);
xlabel('Seconds'); ylabel('Amplitude');
wavplt = plot(time,[real(psiL1) imag(psiL1)]);
legend([scalplt wavplt(1) wavplt(2)],...
{'Scaling Function','Wavelet-Real Part','Wavelet-Imaginary Part'});
title({'Scaling Function';'Coarsest-Scale Wavelet First Filter Bank'})
hold off
Хранилище аудиоданных позволяет управлять коллекциями файлов аудиоданных. Для машинного или глубокого обучения хранилище аудиоданных не только управляет потоком аудиоданных из файлов и папок, хранилище аудиоданных также управляет связями меток с данными и предоставляет возможность случайного разделения данных на различные наборы для обучения, проверки и тестирования. В этом примере используйте хранилище аудиоданных для управления коллекцией жанров музыки GTZAN. Вспомним, что каждая подпапка коллекции названа по жанру, который она представляет. Установите 'IncludeSubFolders' свойство для true для указания хранилищу аудиоданных использовать подпапки и установить 'LabelSource' свойство для 'foldernames' для создания меток данных на основе имен подпапок. В этом примере предполагается, что каталог верхнего уровня находится внутри MATLAB. tempdir и называется «жанрами». Убедитесь, что location правильный путь к папке данных верхнего уровня на компьютере. Папка данных верхнего уровня на компьютере должна содержать десять подпапок, каждая из которых называется для десяти жанров, и должна содержать только аудиофайлы, соответствующие этим жанрам.
location = fullfile(tempdir,'genres'); ads = audioDatastore(location,'IncludeSubFolders',true,... 'LabelSource','foldernames');
Выполните следующие действия, чтобы получить подсчет музыкальных жанров в наборе данных.
countEachLabel(ads)
ans=10×2 table
Label Count
_________ _____
blues 100
classical 100
country 100
disco 100
hiphop 100
jazz 100
metal 100
pop 100
reggae 100
rock 100
Как говорилось ранее, существует 10 жанров по 100 файлов в каждом.
Создание обучающих и тестовых наборов для разработки и тестирования нашего классификатора. Мы используем 80% данных для обучения и удерживаем оставшиеся 20% для тестирования. shuffle функция хранилища аудиоданных произвольно тасует данные. Выполните это перед разделением данных по меткам для рандомизации данных. В этом примере мы задаем начальное число генератора случайных чисел для воспроизводимости. Использовать хранилище аудиоданных splitEachLabel для выполнения разделения 80-20. splitEachLabel обеспечивает равное представление всех классов.
rng(100) ads = shuffle(ads); [adsTrain,adsTest] = splitEachLabel(ads,0.8); countEachLabel(adsTrain)
ans=10×2 table
Label Count
_________ _____
blues 80
classical 80
country 80
disco 80
hiphop 80
jazz 80
metal 80
pop 80
reggae 80
rock 80
countEachLabel(adsTest)
ans=10×2 table
Label Count
_________ _____
blues 20
classical 20
country 20
disco 20
hiphop 20
jazz 20
metal 20
pop 20
reggae 20
rock 20
В данных обучения, как и ожидалось, имеется 800 записей, а в данных тестирования - 200 записей. Кроме того, существует 80 примеров каждого жанра в обучающем наборе и 20 примеров каждого жанра в тестовом наборе.
Для получения характеристик рассеяния используйте вспомогательную функцию, helperbatchscatfeatures, который получает натуральный логарифм признаков рассеяния для выборок каждого аудиофайла и поднабор числа окон рассеяния на 6. Исходный код для helperbatchscatfeatures перечислены в приложении. Характеристики вейвлет-рассеяния вычисляются с использованием размера партии 64 сигнала.
При наличии Toolbox™ параллельных вычислений и поддерживаемого графического процессора установите useGPU кому true в следующем коде и преобразование рассеяния будет вычислено с использованием GPU. При использовании графического процессора NVIDIA Titan V с размером партии 64 характеристики рассеяния в этом примере вычислялись приблизительно в 9 раз быстрее, чем при использовании ЦП.
N = 2^19; batchsize = 64; scTrain = []; useGPU = false; % Set to true to use the GPU while hasdata(adsTrain) sc = helperbatchscatfeatures(adsTrain,sn,N,batchsize,useGPU); scTrain = cat(3,scTrain,sc); end
Запишите количество временных окон в преобразовании рассеяния для создания метки.
numTimeWindows = size(scTrain,2);
В этом примере имеется 43 временных окна или кадра для каждого пути рассеяния.
Повторите тот же процесс извлечения элементов для данных теста.
scTest = []; while hasdata(adsTest) sc = helperbatchscatfeatures(adsTest,sn,N,batchsize,useGPU); scTest = cat(3,scTest,sc); end
Определите количество путей в сети рассеяния и преобразуйте обучающие и тестовые функции в 2-D матрицы.
[~,npaths] = sn.paths(); Npaths = sum(npaths); TrainFeatures = permute(scTrain,[2 3 1]); TrainFeatures = reshape(TrainFeatures,[],Npaths,1); TestFeatures = permute(scTest,[2 3 1]); TestFeatures = reshape(TestFeatures,[],Npaths,1);
Каждая строка TrainFeatures и TestFeatures представляет собой одно временное окно рассеяния по 334 путям в преобразовании рассеяния каждого звукового сигнала. Для каждого музыкального образца у нас 43 таких окна времени. Соответственно, матрица признаков для обучающих данных составляет 34400 на 334. Количество строк равно количеству обучающих примеров (800), умноженному на количество окон рассеяния в примере (43). Аналогично, матрица признаков рассеяния для тестовых данных составляет 8600 на 334. В каждом примере имеется 200 тестовых примеров и 43 окна. Создайте жанровую метку для каждого из 43 окон в матрице функций вейвлет-рассеяния для обучающих данных.
trainLabels = adsTrain.Labels; numTrainSignals = numel(trainLabels); trainLabels = repmat(trainLabels,1,numTimeWindows); trainLabels = reshape(trainLabels',numTrainSignals*numTimeWindows,1);
Повторите процесс для тестовых данных.
testLabels = adsTest.Labels; numTestSignals = numel(testLabels); testLabels = repmat(testLabels,1,numTimeWindows); testLabels = reshape(testLabels',numTestSignals*numTimeWindows,1);
В этом примере используйте мультиклассный классификатор вектора поддержки (SVM) с кубическим полиномиальным ядром. Подберите SVM к учебным данным.
template = templateSVM(... 'KernelFunction', 'polynomial', ... 'PolynomialOrder', 3, ... 'KernelScale', 'auto', ... 'BoxConstraint', 1, ... 'Standardize', true); Classes = {'blues','classical','country','disco','hiphop','jazz',... 'metal','pop','reggae','rock'}; classificationSVM = fitcecoc(... TrainFeatures, ... trainLabels, ... 'Learners', template, ... 'Coding', 'onevsone','ClassNames',categorical(Classes));
Используйте модель SVM, соответствующую преобразованиям рассеяния обучающих данных, чтобы предсказать музыкальные жанры для тестовых данных. Напомним, что для каждого сигнала в преобразовании рассеяния имеется 43 временных окна. Используйте простое большинство голосов, чтобы предсказать жанр. Вспомогательная функция helperMajorityVote получает режим жанровых меток по всем 43 окнам рассеяния. При отсутствии уникального режима helperMajorityVote возвращает ошибку классификации, указанную 'NoUniqueMode'. Это приводит к дополнительному столбцу в матрице путаницы. Исходный код для helperMajorityVote перечислены в приложении.
predLabels = predict(classificationSVM,TestFeatures); [TestVotes,TestCounts] = helperMajorityVote(predLabels,adsTest.Labels,categorical(Classes)); testAccuracy = sum(eq(TestVotes,adsTest.Labels))/numTestSignals*100
testAccuracy = 87.5000
Точность испытания, testAccuracy, составляет приблизительно 88 процентов. Эта точность сопоставима с современным уровнем техники набора данных GTZAN.
Отображение матрицы путаницы для проверки коэффициентов точности по жанрам. Напомним, в каждом классе по 20 примеров.
confusionchart(TestVotes,adsTest.Labels)

Диагональ матричного сюжета путаницы показывает, что классификационные точности для отдельных жанров достаточно хороши в целом. Извлечь эти жанровые точности и сюжет отдельно.
cm = confusionmat(TestVotes,adsTest.Labels); cm(:,end) = []; genreAccuracy = diag(cm)./20*100; figure; bar(genreAccuracy) set(gca,'XTickLabels',Classes); xtickangle(gca,30); title('Percentage Correct by Genre - Test Set');

Этот пример продемонстрировал использование вейвлет-временного рассеяния и хранилища аудиоданных в классификации музыкальных жанров. В этом примере вейвлет-временное рассеяние достигало точности классификации, сравнимой с современной производительностью для набора данных GTZAN. В отличие от других подходов, требующих выделения ряда признаков временной и частотной областей, вейвлет-рассеяние требовало только спецификации одного параметра, масштаба инварианта времени. Хранилище аудиоданных позволило нам эффективно управлять передачей большого набора данных с диска в MATLAB и позволило нам рандомизировать данные и точно сохранить жанровое членство в рандомизированных данных посредством процесса классификации.
Анден, Дж. и Маллат, С. 2014. Спектр глубокого рассеяния. Транзакции IEEE по обработке сигналов, том 62, 16, стр. 4114-4128.
Bergstra, J., Casagrande, N., Erhan, D., Eck, D. и Kegl, B. Агрегатные особенности и AdaBoost для музыкальной классификации. Машинное обучение, том 65, выпуск 2-3, стр. 473-484.
Ирвин, Дж., Чарток, Э., и Холландер, Н. 2016. Повторяющиеся нейронные сети с вниманием к жанровой классификации. https://www.semanticscholar.org/paper/Recurrent-Neural-Networks-with-Attention-for-Genre-Irvin/6da301817851f19107447e4c72e682e3f183ae8a
Ли, Т., Чан, А.Б., и Чун, А. 2010. Автоматическое извлечение признаков музыкального узора с использованием сверточной нейронной сети. Международная конференция по анализу и применению данных.
Маллат. S. 2012. Инвариантное рассеяние группы. Сообщения по чистой и прикладной математике, том 65, 10, стр. 1331-1398.
Panagakis, Y., Kotropoulos, C.L., and Arce, G.R. 2014. Классификация музыкальных жанров посредством совместного разреженного низкорангового представления звуковых особенностей. Транзакции IEEE по обработке звука, речи и языка, 22, 12, стр. 1905-1917.
Цанетакис, Г. и Кук, П. 2002. Классификация музыкальных жанров аудиосигналов. Транзакции IEEE по обработке речи и звука, том 10, № 5, стр. 293-302.
Коллекция жанров GTZAN. http://marsyas.info/downloads/datasets.html
helperPotingVote - эта функция возвращает режим меток классов, предсказанных для ряда векторов функций. При вейвлет-рассеянии времени получаем метку класса для каждого временного окна. Если уникальный режим не найден, метка 'NoUniqueMode' возвращается для обозначения ошибки классификации.
type helperMajorityVotefunction [ClassVotes,ClassCounts] = helperMajorityVote(predLabels,origLabels,classes)
% This function is in support of wavelet scattering examples only. It may
% change or be removed in a future release.
% Make categorical arrays if the labels are not already categorical
predLabels = categorical(predLabels);
origLabels = categorical(origLabels);
% Expects both predLabels and origLabels to be categorical vectors
Npred = numel(predLabels);
Norig = numel(origLabels);
Nwin = Npred/Norig;
predLabels = reshape(predLabels,Nwin,Norig);
ClassCounts = countcats(predLabels);
[mxcount,idx] = max(ClassCounts);
ClassVotes = classes(idx);
% Check for any ties in the maximum values and ensure they are marked as
% error if the mode occurs more than once
tmpsum = sum(ClassCounts == mxcount);
ClassVotes(tmpsum > 1) = categorical({'NoUniqueMode'});
ClassVotes = ClassVotes(:);
helperbatchscatfeatures - эта функция возвращает матрицу характеристик вейвлет-временного рассеяния для данного входного сигнала. В этом случае мы используем натуральный логарифм коэффициентов вейвлет-рассеяния. Матрица признаков рассеяния вычисляется на выборках сигнала. Признаки рассеяния субдискретизируются с коэффициентом 6. Если useGPU имеет значение trueпреобразование рассеяния вычисляется на GPU.
function sc = helperbatchscatfeatures(ds,sn,N,batchsize,useGPU) % This function is only intended to support examples in the Wavelet % Toolbox. It may be changed or removed in a future release. % Read batch of data from audio datastore batch = helperReadBatch(ds,N,batchsize); if useGPU batch = gpuArray(batch); end % Obtain scattering features S = sn.featureMatrix(batch,'transform','log'); gather(batch); S = gather(S); % Subsample the features sc = S(:,1:6:end,:); end
helperReadBatch - эта функция считывает пакеты указанного размера из хранилища данных и возвращает выходные данные с одной точностью. Каждый столбец выходных данных представляет собой отдельный сигнал из хранилища данных. Выходные данные могут содержать меньше столбцов, чем размер пакета, если в хранилище данных недостаточно записей.
function batchout = helperReadBatch(ds,N,batchsize) % This function is only in support of Wavelet Toolbox examples. It may % change or be removed in a future release. % % batchout = readReadBatch(ds,N,batchsize) where ds is the Datastore and % ds is the Datastore % batchsize is the batchsize kk = 1; while(hasdata(ds)) && kk <= batchsize tmpRead = read(ds); batchout(:,kk) = cast(tmpRead(1:N),'single'); %#ok<AGROW> kk = kk+1; end end