Этот пример показов, как классифицировать жанр музыкального отрывка с помощью вейвлета рассеяния времени и audio datastore. При вейвлет данные распространяются через ряд вейвлет, нелинейностей и усреднения, чтобы получить низкодисперсные представления данных. Эти представления с низкой дисперсией затем используются в качестве входов для классификатора.
В этом примере используется набор данных GTZAN Ganre Collection [7] [8]. Данные предоставляются в виде архива zipped tar, который составляет приблизительно 1,2 ГБ. Несжатый набор данных требует около 3 ГБ дискового пространства. Извлечение сжатого файла tar из ссылки, представленной в ссылках, создает папку с десятью подпапками. Каждая подпапка названа по содержащемуся в ней жанру музыкальных выборок. Жанры: блюз, классика, кантри, диско, хипхоп, джаз, метал, поп, регги и рок. Существует 100 примеров каждого жанра, и каждый аудио файла состоит примерно из 30 секунд данных, дискретизированных с частотой дискретизации 22050 Гц. В оригинальной статье авторы использовали ряд функций временной области и частотного диапазона, включая коэффициенты mel-frequency (MFC), извлеченные из каждого музыкального примера, и классификацию Гауссовой смешанной модели (GMM), чтобы достичь точности 61 процент [7]. Впоследствии к этим данным были применены нейронные сети для глубокого обучения. В большинстве случаев эти подходы к глубокому обучению состоят из сверточных нейронных сетей (CNN) с коэффициентами MFC или спектрограммами в качестве входов для глубокого CNN. Эти подходы привели к эффективности около 84% [4]. Подход LSTM со срезов времени спектрограммы привел к точности 79% и временной области и частотного диапазона функций в сочетании с подходом обучения ансамбля (AdaBoost) привел к точности 82% на тестовом наборе [2] [3]. Недавно подход разреженного машинного обучения с представлением достиг приблизительно 89% точности [6].
Первым шагом является загрузка GTZAN Ganre Набора [7] [8]. Инструкции в этом примере предполагают, что вы загружаете набор данных на свою временную директорию, tempdir
, в MATLAB ®. Если вы принимаете решение загрузить данные в папку, отличную от tempdir
измените имя директории в последующих инструкциях. Использование gunzip
для загрузки и распаковки набора данных. Затем используйте untar
для извлечения содержимого файла tar. Папка 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
audio datastore позволяет управлять наборы аудио файлов данных. Для машинного или глубокого обучения audio datastore не только управляет потоком аудиоданных из файлов и папок, audio datastore также управляет ассоциацией меток с данными и предоставляет возможность случайным образом разбить ваши данные на различные наборы для обучения, валидации и проверки. В этом примере используйте audio datastore для управления набором жанра музыки GTZAN. Напомним, каждая подпапка набора названа в честь жанра, который она представляет. Установите 'IncludeSubFolders'
свойство к true
чтобы дать команду audio datastore использовать подпапки и задать 'LabelSource'
свойство к 'foldernames'
создание меток данных на основе имен подпапок. Этот пример предполагает, что директория верхнего уровня находится внутри вашего MATLAB tempdir
директория и называется 'genres'. Убедитесь, что 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% для проверки. The shuffle
функция audio datastore случайным образом тасует данные. Сделайте это до разделения данных по меткам, чтобы рандомизировать данные. В этом примере мы устанавливаем seed генератора случайных чисел для воспроизводимости. Используйте audio datastore 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 сигнала.
Если у вас есть Parallel Computing Toolbox™ и поддерживаемый графический процессор, установите useGPU
на true
в следующем коде и преобразование рассеяния будет вычислено с помощью графический процессор. Используя графический процессор 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');
Этот пример продемонстрировал использование вейвлет рассеяния и audio datastore в классификации жанра музыки. В этом примере рассеяние вейвлета времени достигло точности классификации, сопоставимой с состоянием из эффективности уровня техники для набора данных GTZAN. В отличие от других подходов, требующих извлечения ряда функций временной области и частотного диапазона, вейвлет требовало только спецификации одного параметра, шкалы инварианта времени. Audio datastore позволил нам эффективно управлять передачей большого набора данных с диска в MATLAB и позволил нам рандомизировать данные и точно сохранить жанровую принадлежность рандомизированных данных через рабочий процесс классификации.
Анден, Дж. И Маллат, С. 2014. Спектр глубокого рассеяния. Транзакции IEEE по обработке сигналов, том 62, 16, стр. 4114-4128.
Bergstra, J., Casagrande, N., Erhan, D., Eck, D., and Kegl, B. Aggregate features and AdaBoost for music classification. Машинное обучение, том 65, выпуск 2-3, стр. 473-484.
Irvin, J., Chartock, E., and Hollander, N. 2016. Рекуррентные нейронные сети с вниманием к жанровой классификации. https://www.semanticscholar.org/paper/Recurrent-Neural-Networks-with-Attention-for-Genre-Irvin/6da301817851f19107447e4c72e682e3f183ae8a
Li, T., Chan, A.B., and Chun, A. 2010. Автоматическая редукция данных музыкального шаблона с использованием сверточной нейронной сети. Международная конференция по майнингу данных и применениям.
Маллат. S. 2012. Групповое инвариантное рассеяние. Сообщения по чистой и прикладной математике, том 65, 10, стр. 1331-1398.
Panagakis, Y., Kotropoulos, C.L., and Arce, G.R. 2014. Классификация жанра музыки через совместное разреженное низкоранговое представление аудио функций. Транзакции IEEE по обработке звука, речи и языка, 22, 12, стр. 1905-1917.
Tzanetakis, G. and Cook, P. 2002. Музыкальная жанровая классификация аудиосигналов. Транзакции IEEE по обработке речи и аудио, том 10, № 5, стр. 293-302.
GTZAN Жанровый набор. http://marsyas.info/downloads/datasets.html
helperMightageVote - Эта функция возвращает режим меток классов, предсказанных для ряда векторов признаков. При вейвлет рассеянии мы получаем метку класса для каждого временного окна. Если уникальный режим не найден, метка 'NoUniqueMode'
возвращается для обозначения ошибки классификации.
type helperMajorityVote
function [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
, преобразование рассеяния вычисляется на графическом процессоре.
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 - эта функция считывает пакеты заданного размера из datastore и возвращает выход с одинарной точностью. Каждый столбец выхода является отдельным сигналом от datastore. Если у datastore недостаточно записей, у выход может быть меньше столбцов, чем у batchsize.
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