В этом примере показано, как обучить модель глубокого обучения, которая обнаруживает присутствие речевых команд в аудио. Пример использует Речевой Набор данных Команд [1], чтобы обучить сверточную нейронную сеть распознавать данный набор команд.
Чтобы обучить сеть с нуля, необходимо сначала загрузить набор данных. Если вы не хотите загружать набор данных или обучать сеть, то можно загрузить предварительно обученную сеть, которой предоставляют этот пример, и выполнить следующие два раздела примера: Распознайте Команды с Предварительно обученной сетью и Обнаружьте Команды Используя Передачу потокового аудио от Микрофона.
Перед входом в учебный процесс подробно, вы будете использовать предварительно обученную сеть распознавания речи, чтобы идентифицировать речевые команды.
Загрузите предварительно обученную сеть.
load('commandNet.mat')
Сеть обучена, чтобы распознать следующие речевые команды:
"да"
"нет"
"вниз"
"левый"
"право"
on
off
остановка
"пойти"
Загрузите короткий речевой сигнал, где человек говорит "остановку".
[x,fs] = audioread('stop_command.flac');
Слушайте команду.
sound(x,fs)
Предварительно обученная сеть берет слуховые спектрограммы в качестве входных параметров. Вы сначала преобразуете речевую форму волны в слуховую спектрограмму.
Используйте функциональный extractAuditoryFeature
вычислить слуховую спектрограмму. Вы пройдете детали извлечения признаков позже в примере.
auditorySpect = helperExtractAuditoryFeatures(x,fs);
Классифицируйте команду на основе ее слуховой спектрограммы.
command = classify(trainedNet,auditorySpect)
command = categorical stop
Сеть обучена, чтобы классифицировать слова, не принадлежащие этому набору как "неизвестные".
Вы теперь классифицируете слово ("игра"), которая не была включена в список команды, чтобы идентифицировать.
Загрузите речь, сигнализируют и слушают его.
x = audioread('play_command.flac');
sound(x,fs)
Вычислите слуховую спектрограмму.
auditorySpect = helperExtractAuditoryFeatures(x,fs);
Классифицируйте сигнал.
command = classify(trainedNet,auditorySpect)
command = categorical unknown
Сеть обучена, чтобы классифицировать фоновый шум как "фон".
Создайте один второй сигнал, состоящий из случайного шума.
x = pinknoise(16e3);
Вычислите слуховую спектрограмму.
auditorySpect = helperExtractAuditoryFeatures(x,fs);
Классифицируйте фоновый шум.
command = classify(trainedNet,auditorySpect)
command = categorical background
Протестируйте свою предварительно обученную сеть обнаружения команды на передаче потокового аудио от вашего микрофона. Попытайтесь говорить одну из команд, например, да, нет, или остановку. Затем попытайтесь говорить одно из неизвестных слов, таких как Марвин, Шейла, кровать, дом, кошка, птица или любой номер от нуля до девять.
Задайте уровень классификации в Гц и создайте читателя аудио устройства, который может считать аудио из вашего микрофона.
classificationRate = 20; adr = audioDeviceReader('SampleRate',fs,'SamplesPerFrame',floor(fs/classificationRate));
Инициализируйте буфер для аудио. Извлеките метки классификации сети. Инициализируйте буферы половины секунды для меток и вероятностей классификации передачи потокового аудио. Используйте эти буферы, чтобы сравнить результаты классификации за более длительный промежуток времени и той сборкой 'соглашение', когда команда будет обнаружена. Задайте пороги для логики решения.
audioBuffer = dsp.AsyncBuffer(fs);
labels = trainedNet.Layers(end).Classes;
YBuffer(1:classificationRate/2) = categorical("background");
probBuffer = zeros([numel(labels),classificationRate/2]);
countThreshold = ceil(classificationRate*0.2);
probThreshold = 0.7;
Создайте фигуру и обнаружьте команды, пока созданная фигура существует. Чтобы запустить цикл неопределенно, установите timeLimit
к Inf
. Чтобы остановить живое обнаружение, просто закройте фигуру.
h = figure('Units','normalized','Position',[0.2 0.1 0.6 0.8]); timeLimit = 20; tic while ishandle(h) && toc < timeLimit % Extract audio samples from the audio device and add the samples to % the buffer. x = adr(); write(audioBuffer,x); y = read(audioBuffer,fs,fs-adr.SamplesPerFrame); spec = helperExtractAuditoryFeatures(y,fs); % Classify the current spectrogram, save the label to the label buffer, % and save the predicted probabilities to the probability buffer. [YPredicted,probs] = classify(trainedNet,spec,'ExecutionEnvironment','cpu'); YBuffer = [YBuffer(2:end),YPredicted]; probBuffer = [probBuffer(:,2:end),probs(:)]; % Plot the current waveform and spectrogram. subplot(2,1,1) plot(y) axis tight ylim([-1,1]) subplot(2,1,2) pcolor(spec') caxis([-4 2.6445]) shading flat % Now do the actual command detection by performing a very simple % thresholding operation. Declare a detection and display it in the % figure title if all of the following hold: 1) The most common label % is not background. 2) At least countThreshold of the latest frame % labels agree. 3) The maximum probability of the predicted label is at % least probThreshold. Otherwise, do not declare a detection. [YMode,count] = mode(YBuffer); maxProb = max(probBuffer(labels == YMode,:)); subplot(2,1,1) if YMode == "background" || count < countThreshold || maxProb < probThreshold title(" ") else title(string(YMode),'FontSize',20) end drawnow end
Этот пример использует Google Speech Commands Dataset [1]. Загрузите набор данных и untar загруженный файл. Установите PathToDatabase на местоположение данных.
url = 'https://ssd.mathworks.com/supportfiles/audio/google_speech.zip'; downloadFolder = tempdir; dataFolder = fullfile(downloadFolder,'google_speech'); if ~exist(dataFolder,'dir') disp('Downloading data set (1.4 GB) ...') unzip(url,downloadFolder) end
Создайте audioDatastore
это указывает на обучающий набор данных.
ads = audioDatastore(fullfile(dataFolder, 'train'), ... 'IncludeSubfolders',true, ... 'FileExtensions','.wav', ... 'LabelSource','foldernames')
ads = audioDatastore with properties: Files: { ' ...\AppData\Local\Temp\google_speech\train\bed\00176480_nohash_0.wav'; ' ...\AppData\Local\Temp\google_speech\train\bed\004ae714_nohash_0.wav'; ' ...\AppData\Local\Temp\google_speech\train\bed\004ae714_nohash_1.wav' ... and 51085 more } Folders: { 'C:\Users\jibrahim\AppData\Local\Temp\google_speech\train' } Labels: [bed; bed; bed ... and 51085 more categorical] AlternateFileSystemRoots: {} OutputDataType: 'double' SupportedOutputFormats: ["wav" "flac" "ogg" "mp4" "m4a"] DefaultOutputFormat: "wav"
Задайте слова, которые вы хотите, чтобы ваша модель распознала командами. Пометьте все слова, которые не являются командами как unknown
. Маркировка слов, которые не являются командами как unknown
создает группу слов, которая аппроксимирует распределение всех слов кроме команд. Сеть использует эту группу, чтобы изучить различие между командами и всеми другими словами.
Чтобы уменьшать неустойчивость класса между известными и неизвестными словами и ускорить обработку, только включайте часть неизвестных слов в наборе обучающих данных.
Используйте subset
создать datastore, который содержит только команды и подмножество неизвестных слов. Считайте количество примеров, принадлежащих каждой категории.
commands = categorical(["yes","no","up","down","left","right","on","off","stop","go"]); isCommand = ismember(ads.Labels,commands); isUnknown = ~isCommand; includeFraction = 0.2; mask = rand(numel(ads.Labels),1) < includeFraction; isUnknown = isUnknown & mask; ads.Labels(isUnknown) = categorical("unknown"); adsTrain = subset(ads,isCommand|isUnknown); countEachLabel(adsTrain)
ans = 11×2 table Label Count _______ _____ down 1842 go 1861 left 1839 no 1853 off 1839 on 1864 right 1852 stop 1885 unknown 6483 up 1843 yes 1860
Создайте audioDatastore
это указывает на набор данных валидации. Выполните те же шаги, используемые, чтобы создать учебный datastore.
ads = audioDatastore(fullfile(dataFolder, 'validation'), ... 'IncludeSubfolders',true, ... 'FileExtensions','.wav', ... 'LabelSource','foldernames') isCommand = ismember(ads.Labels,commands); isUnknown = ~isCommand; includeFraction = 0.2; mask = rand(numel(ads.Labels),1) < includeFraction; isUnknown = isUnknown & mask; ads.Labels(isUnknown) = categorical("unknown"); adsValidation = subset(ads,isCommand|isUnknown); countEachLabel(adsValidation)
ads = audioDatastore with properties: Files: { ' ...\AppData\Local\Temp\google_speech\validation\bed\026290a7_nohash_0.wav'; ' ...\AppData\Local\Temp\google_speech\validation\bed\060cd039_nohash_0.wav'; ' ...\AppData\Local\Temp\google_speech\validation\bed\060cd039_nohash_1.wav' ... and 6795 more } Folders: { 'C:\Users\jibrahim\AppData\Local\Temp\google_speech\validation' } Labels: [bed; bed; bed ... and 6795 more categorical] AlternateFileSystemRoots: {} OutputDataType: 'double' SupportedOutputFormats: ["wav" "flac" "ogg" "mp4" "m4a"] DefaultOutputFormat: "wav" ans = 11×2 table Label Count _______ _____ down 264 go 260 left 247 no 270 off 256 on 257 right 256 stop 246 unknown 850 up 260 yes 261
Чтобы обучить сеть с набором данных в целом и достигнуть максимально возможной точности, установите reduceDataset
к false
. Чтобы запустить этот пример быстро, установите reduceDataset
к true
.
reduceDataset = false; if reduceDataset numUniqueLabels = numel(unique(adsTrain.Labels)); % Reduce the dataset by a factor of 20 adsTrain = splitEachLabel(adsTrain,round(numel(adsTrain.Files) / numUniqueLabels / 20)); adsValidation = splitEachLabel(adsValidation,round(numel(adsValidation.Files) / numUniqueLabels / 20)); end
Чтобы подготовить данные к эффективному обучению сверточной нейронной сети, преобразуйте речевые формы волны в слуховые спектрограммы.
Задайте параметры извлечения признаков. segmentDuration
длительность каждого речевого клипа (в секундах). frameDuration
длительность каждой системы координат для вычисления спектра. hopDuration
временной шаг между каждым спектром. numBands
количество, просачивается слуховая спектрограмма.
Создайте audioFeatureExtractor
объект выполнить извлечение признаков.
fs = 16e3; % Known sample rate of the data set. segmentDuration = 1; frameDuration = 0.025; hopDuration = 0.010; segmentSamples = round(segmentDuration*fs); frameSamples = round(frameDuration*fs); hopSamples = round(hopDuration*fs); overlapSamples = frameSamples - hopSamples; FFTLength = 512; numBands = 50; afe = audioFeatureExtractor( ... 'SampleRate',fs, ... 'FFTLength',FFTLength, ... 'Window',hann(frameSamples,'periodic'), ... 'OverlapLength',overlapSamples, ... 'barkSpectrum',true); setExtractorParams(afe,'barkSpectrum','NumBands',numBands,'WindowNormalization',false);
Считайте файл из набора данных. Обучение сверточная нейронная сеть требует, чтобы вход был сопоставимым размером. Некоторые файлы в наборе данных меньше 1 секунды долго. Примените дополнение нуля к передней и задней части звукового сигнала так, чтобы это имел длину segmentSamples
.
x = read(adsTrain); numSamples = size(x,1); numToPadFront = floor( (segmentSamples - numSamples)/2 ); numToPadBack = ceil( (segmentSamples - numSamples)/2 ); xPadded = [zeros(numToPadFront,1,'like',x);x;zeros(numToPadBack,1,'like',x)];
Чтобы извлечь функции аудио, вызовите extract
. Выход является спектром Коры со временем через строки.
features = extract(afe,xPadded); [numHops,numFeatures] = size(features)
numHops = 98 numFeatures = 50
В этом примере вы постобрабатываете слуховую спектрограмму путем применения логарифма. Взятие журнала небольших чисел может привести к ошибке округления.
Чтобы ускорить обработку, можно распределить извлечение признаков на нескольких рабочих, использующих parfor
.
Во-первых, определите количество разделов для набора данных. Если у вас нет Parallel Computing Toolbox™, используйте один раздел.
if ~isempty(ver('parallel')) && ~reduceDataset pool = gcp; numPar = numpartitions(adsTrain,pool); else numPar = 1; end
Для каждого раздела читайте из datastore, нулевая клавиатура сигнал, и затем извлеките функции.
parfor ii = 1:numPar subds = partition(adsTrain,numPar,ii); XTrain = zeros(numHops,numBands,1,numel(subds.Files)); for idx = 1:numel(subds.Files) x = read(subds); xPadded = [zeros(floor((segmentSamples-size(x,1))/2),1);x;zeros(ceil((segmentSamples-size(x,1))/2),1)]; XTrain(:,:,:,idx) = extract(afe,xPadded); end XTrainC{ii} = XTrain; end
Преобразуйте выход в 4-мерный массив со слуховыми спектрограммами по четвертому измерению.
XTrain = cat(4,XTrainC{:}); [numHops,numBands,numChannels,numSpec] = size(XTrain)
numHops = 98 numBands = 50 numChannels = 1 numSpec = 25021
Масштабируйте функции степенью окна и затем возьмите журнал. Чтобы получить данные с более сглаженным распределением, возьмите логарифм спектрограмм с помощью маленького смещения.
epsil = 1e-6; XTrain = log10(XTrain + epsil);
Выполните шаги извлечения признаков, описанные выше к набору валидации.
if ~isempty(ver('parallel')) pool = gcp; numPar = numpartitions(adsValidation,pool); else numPar = 1; end parfor ii = 1:numPar subds = partition(adsValidation,numPar,ii); XValidation = zeros(numHops,numBands,1,numel(subds.Files)); for idx = 1:numel(subds.Files) x = read(subds); xPadded = [zeros(floor((segmentSamples-size(x,1))/2),1);x;zeros(ceil((segmentSamples-size(x,1))/2),1)]; XValidation(:,:,:,idx) = extract(afe,xPadded); end XValidationC{ii} = XValidation; end XValidation = cat(4,XValidationC{:}); XValidation = log10(XValidation + epsil);
Изолируйте метки обучения и валидации. Удалите пустые категории.
YTrain = removecats(adsTrain.Labels); YValidation = removecats(adsValidation.Labels);
Постройте формы волны и слуховые спектрограммы нескольких обучающих выборок. Проигрывайте соответствующие аудиоклипы.
specMin = min(XTrain,[],'all'); specMax = max(XTrain,[],'all'); idx = randperm(numel(adsTrain.Files),3); figure('Units','normalized','Position',[0.2 0.2 0.6 0.6]); for i = 1:3 [x,fs] = audioread(adsTrain.Files{idx(i)}); subplot(2,3,i) plot(x) axis tight title(string(adsTrain.Labels(idx(i)))) subplot(2,3,i+3) spect = (XTrain(:,:,1,idx(i))'); pcolor(spect) caxis([specMin specMax]) shading flat sound(x,fs) pause(2) end
Сеть должна смочь не только распознать различные произносимые слова, но также и обнаружить, если вход содержит тишину или фоновый шум.
Используйте звуковые файлы в _background
_ папка, чтобы создать выборки вторых клипов фонового шума. Создайте равное количество роликов фона из каждого файла фонового шума. Можно также создать собственные записи фонового шума и добавить их в _background
_ папка. Прежде, чем вычислить спектрограммы, функция перемасштабирует каждый аудиоклип с фактором, произведенным от логарифмического равномерного распределения в области значений, данной volumeRange
.
adsBkg = audioDatastore(fullfile(dataFolder, 'background')) numBkgClips = 4000; if reduceDataset numBkgClips = numBkgClips/20; end volumeRange = log10([1e-4,1]); numBkgFiles = numel(adsBkg.Files); numClipsPerFile = histcounts(1:numBkgClips,linspace(1,numBkgClips,numBkgFiles+1)); Xbkg = zeros(size(XTrain,1),size(XTrain,2),1,numBkgClips,'single'); bkgAll = readall(adsBkg); ind = 1; for count = 1:numBkgFiles bkg = bkgAll{count}; idxStart = randi(numel(bkg)-fs,numClipsPerFile(count),1); idxEnd = idxStart+fs-1; gain = 10.^((volumeRange(2)-volumeRange(1))*rand(numClipsPerFile(count),1) + volumeRange(1)); for j = 1:numClipsPerFile(count) x = bkg(idxStart(j):idxEnd(j))*gain(j); x = max(min(x,1),-1); Xbkg(:,:,:,ind) = extract(afe,x); if mod(ind,1000)==0 disp("Processed " + string(ind) + " background clips out of " + string(numBkgClips)) end ind = ind + 1; end end Xbkg = log10(Xbkg + epsil);
adsBkg = audioDatastore with properties: Files: { ' ...\AppData\Local\Temp\google_speech\background\doing_the_dishes.wav'; ' ...\AppData\Local\Temp\google_speech\background\dude_miaowing.wav'; ' ...\AppData\Local\Temp\google_speech\background\exercise_bike.wav' ... and 3 more } Folders: { 'C:\Users\jibrahim\AppData\Local\Temp\google_speech\background' } AlternateFileSystemRoots: {} OutputDataType: 'double' Labels: {} SupportedOutputFormats: ["wav" "flac" "ogg" "mp4" "m4a"] DefaultOutputFormat: "wav" Processed 1000 background clips out of 4000 Processed 2000 background clips out of 4000 Processed 3000 background clips out of 4000 Processed 4000 background clips out of 4000
Разделите спектрограммы фонового шума между обучением, валидацией и наборами тестов. Поскольку _background_noise
_ папка содержит только приблизительно пять с половиной минут фонового шума, фоновые выборки в различных наборах данных высоко коррелируются. Чтобы увеличить изменение в фоновом режиме шум, можно создать собственные фоновые файлы и добавить их в папку. Чтобы увеличить робастность сети к шуму, можно также попытаться смешать фоновый шум в речевые файлы.
numTrainBkg = floor(0.85*numBkgClips); numValidationBkg = floor(0.15*numBkgClips); XTrain(:,:,:,end+1:end+numTrainBkg) = Xbkg(:,:,:,1:numTrainBkg); YTrain(end+1:end+numTrainBkg) = "background"; XValidation(:,:,:,end+1:end+numValidationBkg) = Xbkg(:,:,:,numTrainBkg+1:end); YValidation(end+1:end+numValidationBkg) = "background";
Постройте распределение различных меток класса в наборах обучения и валидации.
figure('Units','normalized','Position',[0.2 0.2 0.5 0.5]) subplot(2,1,1) histogram(YTrain) title("Training Label Distribution") subplot(2,1,2) histogram(YValidation) title("Validation Label Distribution")
Создайте простую сетевую архитектуру как массив слоев. Используйте сверточный и слои нормализации партии. и проредите карты функции "пространственно" (то есть, вовремя и частота) использование макс. слоев объединения. Добавьте финал макс. объединение слоя, который объединяет входную карту функции глобально в зависимости от времени. Это осуществляет (аппроксимируют) инвариантность перевода времени во входных спектрограммах, позволяя сети выполнить ту же классификацию, независимую от точного положения речи вовремя. Глобальное объединение также значительно сокращает количество параметров в итоговом полносвязном слое. Чтобы уменьшать возможность сети, запоминая определенные функции обучающих данных, добавьте небольшое количество уволенного к входу к последнему полносвязному слою.
Сеть мала, когда она имеет только пять сверточных слоев с немногими фильтрами. numF
управляет количеством, просачивается сверточные слои. Чтобы увеличить точность сети, попытайтесь увеличить сетевую глубину путем добавления идентичных блоков сверточных, нормализации партии. и слоев ReLU. Можно также попытаться увеличить число сверточных фильтров путем увеличения numF
.
Используйте взвешенную перекрестную энтропийную потерю классификации. weightedClassificationLayer(classWeights)
создает пользовательский слой классификации, который вычисляет потерю перекрестной энтропии с наблюдениями, взвешенными classWeights
. Задайте веса класса в том же порядке, как классы появляются в categories(YTrain)
. Чтобы дать каждому классу равную общую массу в потере, используйте веса класса, которые обратно пропорциональны количеству учебных примеров в каждом классе. При использовании оптимизатора Адама, чтобы обучить сеть, алгоритм настройки независим от полной нормализации весов класса.
classWeights = 1./countcats(YTrain); classWeights = classWeights'/mean(classWeights); numClasses = numel(categories(YTrain)); timePoolSize = ceil(numHops/8); dropoutProb = 0.2; numF = 12; layers = [ imageInputLayer([numHops numBands]) convolution2dLayer(3,numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer(3,'Stride',2,'Padding','same') convolution2dLayer(3,2*numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer(3,'Stride',2,'Padding','same') convolution2dLayer(3,4*numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer(3,'Stride',2,'Padding','same') convolution2dLayer(3,4*numF,'Padding','same') batchNormalizationLayer reluLayer convolution2dLayer(3,4*numF,'Padding','same') batchNormalizationLayer reluLayer maxPooling2dLayer([timePoolSize,1]) dropoutLayer(dropoutProb) fullyConnectedLayer(numClasses) softmaxLayer weightedClassificationLayer(classWeights)];
Задайте опции обучения. Используйте оптимизатор Адама с мини-пакетным размером 128. Обучайтесь в течение 25 эпох и уменьшайте скорость обучения на коэффициент 10 после 20 эпох.
miniBatchSize = 128; validationFrequency = floor(numel(YTrain)/miniBatchSize); options = trainingOptions('adam', ... 'InitialLearnRate',3e-4, ... 'MaxEpochs',25, ... 'MiniBatchSize',miniBatchSize, ... 'Shuffle','every-epoch', ... 'Plots','training-progress', ... 'Verbose',false, ... 'ValidationData',{XValidation,YValidation}, ... 'ValidationFrequency',validationFrequency, ... 'LearnRateSchedule','piecewise', ... 'LearnRateDropFactor',0.1, ... 'LearnRateDropPeriod',20);
Обучите сеть. Если у вас нет графического процессора, то обучение сети может занять время.
trainedNet = trainNetwork(XTrain,YTrain,layers,options);
Вычислите итоговую точность сети на наборе обучающих данных (без увеличения данных) и набор валидации. Сеть очень точна на этом наборе данных. Однако обучение, валидация и тестовые данные, у всех есть подобные распределения, которые не обязательно отражают реальные среды. Это ограничение особенно применяется к unknown
категория, которая содержит произнесение только небольшого количества слов.
if reduceDataset load('commandNet.mat','trainedNet'); end YValPred = classify(trainedNet,XValidation); validationError = mean(YValPred ~= YValidation); YTrainPred = classify(trainedNet,XTrain); trainError = mean(YTrainPred ~= YTrain); disp("Training error: " + trainError*100 + "%") disp("Validation error: " + validationError*100 + "%")
Training error: 1.907% Validation error: 5.5376%
Постройте матрицу беспорядка. Отобразите точность и отзыв для каждого класса при помощи сводных данных строки и столбца. Сортировка классов матрицы беспорядка. Самый большой беспорядок между неизвестными словами и командами, и прочь, вниз и не, и пойдите и нет.
figure('Units','normalized','Position',[0.2 0.2 0.5 0.5]); cm = confusionchart(YValidation,YValPred); cm.Title = 'Confusion Matrix for Validation Data'; cm.ColumnSummary = 'column-normalized'; cm.RowSummary = 'row-normalized'; sortClasses(cm, [commands,"unknown","background"])
При работе над приложениями с ограниченными аппаратными ресурсами, такими как мобильные приложения, рассмотрите ограничения на доступную память и вычислительные ресурсы. Вычислите общий размер сети в килобайтах и протестируйте ее скорость предсказания при использовании центрального процессора. Время предсказания является временем для классификации одного входного изображения. Если вы вводите повторные изображения к сети, они могут быть классифицированы одновременно, ведя к более коротким временам предсказания на изображение. При классификации передачи потокового аудио, однако, время предсказания одно изображения является самым релевантным.
info = whos('trainedNet'); disp("Network size: " + info.bytes/1024 + " kB") for i = 1:100 x = randn([numHops,numBands]); tic [YPredicted,probs] = classify(trainedNet,x,"ExecutionEnvironment",'cpu'); time(i) = toc; end disp("Single-image prediction time on CPU: " + mean(time(11:end))*1000 + " ms")
Network size: 286.7402 kB Single-image prediction time on CPU: 2.5119 ms
[1] Начальник П. "Речевые Команды: общедоступный набор данных для распознавания речи однословного", 2017. Доступный от https://storage.googleapis.com/download.tensorflow.org/data/speech_commands_v0.01.tar.gz. Авторское право Google 2017. Речевой Набор данных Команд лицензируется при Приписывании Creative Commons 4,0 лицензии, доступные здесь: https://creativecommons.org/licenses/by/4.0/legalcode.