В этом примере показано, как классифицировать ультраширокополосный (UWB) импульсные радарные данные сигнала с помощью нескольких - вход, сверточная нейронная сеть (CNN) одно выхода.
Основанные на перемещении данные сигнала, полученные с помощью датчиков, как импульсные радары UWB, содержат шаблоны, характерные для различных жестов. Сопоставление данных о движении с перемещением приносит пользу нескольким путям работы. Например, ручное распознавание жеста важно для бесконтактного человеко-машинного взаимодействия. Этот пример стремится использовать решение для глубокого обучения автоматизировать извлечение признаков от шаблонов в ручном наборе данных жеста и обеспечить метку для каждой выборки сигнала.
UWB-жесты являются общедоступным набором данных динамических ручных жестов [1]. Это содержит в общей сложности 9 600 выборок, собранных от 8 различных волонтеров - людей. Чтобы получить каждую запись, ревизоры поместили отдельный импульсный радар UWB слева, верхнюю часть и правые стороны их экспериментальной настройки, приводящей к 3 полученным радарным матрицам данных сигнала. Волонтеры выполнили ручные жесты из словаря жеста, состоящего из 12 динамических движений рук:
лево-правильный сильный удар (сильный удар L-R)
оставленный праву сильный удар (сильный удар R-L)
вниз сильный удар (сильный удар U-D)
вниз сильный удар (сильный удар D-U)
диагональ, оставленная право вниз, сильно ударяет (сильный удар Diag-LR-UD)
диагональ, оставленная право вниз, сильно ударяет (сильный удар Diag-LR-DU)
диагональное право, оставленное вниз, сильно ударяет (Сильный удар Diag-RL-UD)
диагональное право, оставленное вниз, сильно ударяет (Сильный удар Diag-RL-DU)
по часовой стрелке вращение
против часовой стрелки вращение
внутрь продвиньте
пустой жест
Когда каждое ручное движение жеста получено 3 независимыми импульсными радарами UWB, мы будем использовать архитектуру CNN, которая принимает 3 сигнала как отдельные входные параметры. Модель CNN извлечет информацию о функции из каждого сигнала прежде, чем объединить его, чтобы сделать итоговое предсказание метки жеста. По сути, несколько - вход, CNN одно выхода будет использовать минимально предварительно обработанные радарные матрицы данных сигнала, чтобы классифицировать различные жесты.
Каждая радарная матрица данных сигнала помечена как ручной жест, который сгенерировал ее. 8 различных волонтеров - людей выполнили 12 отдельных ручных жестов для в общей сложности 96 испытаний, сохраненных в 96 MAT-файлах. Каждый MAT-файл содержит 3 радарных матрицы данных, соответствуя этим 3 радарам, используемым в экспериментальной настройке. Их называют Left
, Top
, и Right
. Файлы доступны в следующем местоположении:
https://ssd.mathworks.com/supportfiles/SPT/data/uwb-gestures.zip
Загрузите файлы данных в свою директорию MATLAB Examples.
datasetZipFolder = matlab.internal.examples.downloadSupportFile("SPT","data/uwb-gestures.zip"); datasetFolder = erase(datasetZipFolder,".zip"); if ~exist(datasetFolder,"dir") downloadLocation = fileparts(datasetZipFolder); unzip(datasetZipFolder,downloadLocation); end
Можно также принять решение загрузить отдельный файл, который включает предварительно обученную сеть, misoNet
, сохраненный в MAT-файле под названием pretrainedNetwork.mat
. Это доступно в следующем местоположении:
https://ssd.mathworks.com/supportfiles/SPT/data/uwb-gestures-network.zip
Можно пропустить учебные шаги и использовать предварительно обученную сеть для классификации установкой doTraining
к false
. Если doTraining
установлен в false
, предварительно обученная сеть будет загружена позже в этом примере. Если вы хотите обучить сеть, когда пример запускается, убедитесь, что установили doTraining
к true
.
doTraining = true;
Создайте datastore сигнала, чтобы получить доступ к данным в файлах. Задайте имена переменной сигнала, которые вы хотите считать из каждого файла с помощью SignalVariableNames
параметр. Этот пример принимает, что набор данных хранился в вашей директории MATLAB Examples под папкой uwb-жестов. Если дело обстоит не так, изменение путь к данным в datasetFolder
переменная.
sds = signalDatastore(datasetFolder,... "IncludeSubfolders",true,... "SignalVariableNames",["Left","Top","Right"],... "FileExtensions",".mat",... "ReadOutputOrientation","row")
sds = signalDatastore with properties: Files:{ ' .../R2021b/supportfiles/SPT/data/uwb-gestures/HV_01/HV_01_G10_RawData.mat'; ' .../R2021b/supportfiles/SPT/data/uwb-gestures/HV_01/HV_01_G11_RawData.mat'; ' .../R2021b/supportfiles/SPT/data/uwb-gestures/HV_01/HV_01_G12_RawData.mat' ... and 93 more } Folders: {'/home/ejoshi/Documents/MATLAB/Examples/R2021b/supportfiles/SPT/data/uwb-gestures'} AlternateFileSystemRoots: [0×0 string] ReadSize: 1 SignalVariableNames: ["Left" "Top" "Right"] ReadOutputOrientation: "row"
Datastore возвращает трехэлементный массив ячеек, содержащий радарные матрицы сигнала для левых, верхней части и правильных радаров, в том порядке.
preview(sds)
ans=1×3 cell array
{9000×189 double} {9000×189 double} {9000×189 double}
Строки и столбцы в каждой радарной матрице сигнала представляют длительность ручного (медленно-разового) жеста и расстояние руки от (быстро-разового) радара, соответственно. Во время сбора данных ревизоры записали предмет, повторяющий конкретный ручной жест в течение 450 секунд, соответствуя 9 000 (медленно-разовых) строк. В 90 медленно-разовых системах координат существует 1 полное движение жеста. По сути, каждая радарная матрица сигнала содержит 100 полных ручных движений жеста samples.The, область значений каждого радара UWB составляет 1,2 метра, соответствуя 189 быстро-разовым интервалам.
slowTimeFrames = 90; recordedTimePerSample = 4.5; radarRange = 1.2;
Чтобы визуализировать ручное движение жеста, задайте радарное местоположение UWB, жест и выборку жеста (между 1 и 100).
radarToPlot = 1; gestureToPlot = "_G1_"; gestureSample = 50;
Получите радарную матрицу сигнала для выбранного ручного жеста и радарного местоположения.
sdssubset = subset(sds,contains(sds.Files,gestureToPlot)); radarDataMatrix = read(sdssubset); radarDataMatrix = radarDataMatrix{radarToPlot};
Используйте normalize
преобразовать данные сигнала жеста, чтобы расположиться между 0 и 1 и использовать imagesc
визуализировать ручную выборку движения жеста.
normalizedRadarData = normalize(radarDataMatrix,2,"range",[0 1]); imagesc([0 radarRange],... [0 recordedTimePerSample],... normalizedRadarData(slowTimeFrames*(gestureSample-1)+1:slowTimeFrames*gestureSample,:),... [0 1]); set(gca,"YDir","normal") title("Raw Signal") xlabel("Distance of Hand from the Radar (m)") ylabel("Duration of Hand Gesture (s)")
Как вы видите, это затрудняет, чтобы различить шаблон движения.
Необработанный сигнал содержит экологические отражения от частей тела или другого подарка статических объектов в радаре "s область значений. Эти нежелательные отражения известны как "помеху" и могут быть удалены с помощью импульсного компенсатора, который выполняет экспоненциальное скользящее среднее. Передаточная функция для этой операции
таким образом, что значение это управляет объемом усреднения [2]. Используйте filter
с коэффициентами числителя и содействующим набором знаменателя как [1 - 1] и [1 - 0.9], соответственно, чтобы удалить помеху из необработанного сигнала.
clutterRemovedSignal = filter([1 -1],[1 -0.9],radarDataMatrix,[],1);
Визуализируйте удаленный с помехой сигнал видеть различие.
normalizedClutterRemovedSignal = normalize(clutterRemovedSignal,2,"range",[0 1]); imagesc([0 radarRange],... [0 recordedTimePerSample],... normalizedClutterRemovedSignal(slowTimeFrames*(gestureSample-1)+1:slowTimeFrames*gestureSample,:),... [0 1]); set(gca,"YDir","normal") title("Clutter-Removed Signal") xlabel("Distance of Hand from the Radar (m)") ylabel("Duration of Hand Gesture (s)")
Обратите внимание на то, что шаблон движения намного больше отображается теперь. Например, если вы примете решение визуализировать лево-правильный сильный удар с точки зрения левого радара, вы будете видеть, что расстояние руки от радара увеличивается по длительности ручного жеста.
Имена MAT-файла содержат коды жеста (G1, G2..., G12) соответствие меткам для каждой радарной матрицы сигнала. Преобразуйте эти коды в метки в рамках словаря жеста, с помощью категориального массива.
[~,filenames] = fileparts(sds.Files); gestureLabels = extract(filenames,"G"+digitsPattern); gestureCodes = ["G1","G2","G3","G4",... "G5","G6","G7","G8",... "G9","G10","G11","G12"]; gestureVocabulary = ["L-R swipe", "R-L swipe", "U-D swipe", "D-U swipe",... "Diag-LR-UD swipe","Diag-LR-DU swipe","Diag-RL-UD swipe","Diag-RL-DU swipe",... "clockwise", "counterclockwise","inward push", "empty"]; gestureLabels = categorical(gestureLabels,gestureCodes,gestureVocabulary);
Соберите метки в datastore массивов.
labelDs = arrayDatastore(gestureLabels,"OutputType","cell");
Объедините datastore сигнала и datastore массивов, чтобы получить один datastore, который содержит данные сигнала от каждого радара и категориальной метки. Переставьте получившийся datastore, чтобы рандомизировать порядок, в котором он хранит MAT-файлы.
allDataDs = combine(sds,labelDs); allDataDs = shuffle(allDataDs); preview(allDataDs)
ans=1×4 cell array
{9000×189 double} {9000×189 double} {9000×189 double} {[Diag-LR-UD swipe]}
Функция преобразования позволяет функцию помощника, processData
, быть примененным к данным, когда это читается datastore. processData
выполняет нормализацию и фильтрацию, которая была описана в вышеупомянутом разделе, чтобы стандартизировать данные и удалить помеху. Кроме того, это делит радарную матрицу сигнала на отдельные ручные выборки движения жеста.
allDataDs = transform(allDataDs,@processData); preview(allDataDs)
ans=8×4 cell array
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
{90×189 double} {90×189 double} {90×189 double} {[Diag-LR-UD swipe]}
Обучение нейронной сети является итеративным. В каждой итерации datastore считывает данные из файлов и преобразовывает данные прежде, чем обновить сетевые коэффициенты. Поскольку данные считываются из отдельных выборок, данные должны будут быть считаны в память, прежде чем быть переставленным и вставлены в другой datastore для обучения.
Поскольку целый обучающий набор данных умещается в памяти, возможно преобразовать данные параллельно, если Parallel Computing Toolbox доступен, и затем соберите его в рабочую область. Используйте readall
с UseParallel
отметьте набор к истине, чтобы использовать параллельный пул, чтобы считать все данные сигнала и метки в рабочую область. Если совпадения данных в память о вашем компьютере, импортируя данные в рабочую область включают более быстрое обучение, потому что данные считаны и преобразованы только однажды. Обратите внимание на то, что, если данные не умещаются в памяти, вы должны, чтобы передать datastore в учебную функцию, и преобразования выполняются в каждую учебную эпоху.
allData = readall(allDataDs,"UseParallel",true);
Starting parallel pool (parpool) using the 'local' profile ... Connected to the parallel pool (number of workers: 8).
Метки возвращены как последний столбец в allData
. Используйте countlabels
получить пропорции значений метки в наборе данных. Обратите внимание на то, что жесты сбалансированы и хорошо представлены через набор данных.
countlabels(allData(:,4))
ans=12×3 table
Label Count Percent
________________ _____ _______
D-U swipe 793 8.2664
Diag-LR-DU swipe 800 8.3394
Diag-LR-UD swipe 800 8.3394
Diag-RL-DU swipe 800 8.3394
Diag-RL-UD swipe 800 8.3394
L-R swipe 800 8.3394
R-L swipe 800 8.3394
U-D swipe 800 8.3394
clockwise 800 8.3394
counterclockwise 800 8.3394
empty 800 8.3394
inward push 800 8.3394
Разделите данные случайным образом на наборы обучения и валидации, убеждаясь на потом оставить данные о тестировании. В этом примере обучение, валидация и тестирующие разделения составят 70%, 15% и 15%, соответственно. Используйте splitlabels
разделять данные в обучение, валидацию и тестирующие наборы, которые обеспечивают те же пропорции метки как исходный набор данных. Задайте randomized
опция, чтобы переставить данные случайным образом через три набора.
idxs = splitlabels(allData(:,4),[0.7 0.15],"randomized");
trainIdx = idxs{1}; valIdx = idxs{2}; testIdx = idxs{3};
Постарайтесь не выбирать выборки из того же испытания путем рандомизации индексов еще раз. Храните данные об обучении и валидации в оперативной памяти в хранилищах данных массивов так, чтобы они могли использоваться, чтобы обучить мультивходную сеть.
trainData = allData(trainIdx(randperm(length(trainIdx))),:); valData = allData(valIdx(randperm(length(valIdx))),:); trainDataDs = arrayDatastore(trainData,"OutputType","same"); valDataDs = arrayDatastore(valData,"OutputType","same");
Задайте сетевую архитектуру перед обучением. Поскольку каждое ручное движение жеста получено 3 независимыми импульсными радарами UWB, мы будем использовать архитектуру CNN, которая принимает 3 сигнала как отдельные входные параметры. Результаты, достигнутые после обучения, это предложило несколько - вход, CNN одно выхода, значительно лучше, чем достигнутые с альтернативным одно входом, CNN одно выхода, вход которого является 90 x 189 x 3 стека радарных матриц данных.
repeatBranch
содержит операции, которые будут выполняться отдельно на трех радарных матрицах сигнала данных. Модель CNN должна объединить извлеченную информацию о функции от каждого сигнала сделать итоговое предсказание метки жеста. mainBranch
содержит операции, которые конкатенируют 3 repeatBranch
выходные параметры и оценочные метки. Задайте imageInputLayer
из размера 90 x 189, чтобы принять ручные выборки движения жеста. Задайте additionLayer
с количеством входного набора к 3, чтобы собрать выходные параметры 3 ветвей и передать их в раздел классификации модели. Задайте fullyConnectedLayer
с выходным размером 12, один для каждого из ручных жестов. Добавьте softmaxLayer
и classificationLayer
выводить предполагаемые метки.
repeatBranch = [ imageInputLayer([90 189 1],"Normalization", "none") convolution2dLayer(3,8,"Padding",1) batchNormalizationLayer reluLayer maxPooling2dLayer(2,"Stride",2) convolution2dLayer(3,16,"Padding",1) batchNormalizationLayer reluLayer maxPooling2dLayer(2,"Stride",2) convolution2dLayer(3,32,"Padding",1) batchNormalizationLayer reluLayer maxPooling2dLayer(2,"Stride",2) convolution2dLayer(3,64,"Padding",1) batchNormalizationLayer reluLayer maxPooling2dLayer(2,"Stride",2)]; mainBranch = [ additionLayer(3) fullyConnectedLayer(12) softmaxLayer classificationLayer];
Задайте layerGraph
к которому repeatBranch
добавляется 3 раза и mainBranch
добавляется однажды. Соедините выходные параметры итогового maxPooling2dLayer
в каждом repeatBranch
к входным параметрам additionLayer
.
misoCNN = layerGraph(); misoCNN = addLayers(misoCNN, repeatBranch); misoCNN = addLayers(misoCNN, repeatBranch); misoCNN = addLayers(misoCNN, repeatBranch); misoCNN = addLayers(misoCNN, mainBranch); misoCNN = connectLayers(misoCNN, "maxpool_4", "addition/in1"); misoCNN = connectLayers(misoCNN, "maxpool_8", "addition/in2"); misoCNN = connectLayers(misoCNN, "maxpool_12", "addition/in3");
Визуализируйте несколько - вход, CNN одно выхода.
plot(misoCNN);
Выберите опции для учебного процесса, которые гарантируют хорошую производительность сети. Убедитесь, что задали valDataDs
как ValidationData
, так, чтобы это использовалось для валидации во время обучения. Обратитесь к trainingOptions
(Deep Learning Toolbox) документация для описания каждого параметра.
options = trainingOptions("adam", ... "InitialLearnRate",1e-3, ... "MaxEpochs",3,... "MiniBatchSize",32, ... "ValidationData",valDataDs,... "ValidationFrequency",40,... "Verbose",false,... "Plots","training-progress");
Используйте trainNetwork
команда, чтобы обучить CNN.
if doTraining == true misoNet = trainNetwork(trainDataDs,misoCNN,options); else pretrainedNetworkZipFolder = matlab.internal.examples.downloadSupportFile("SPT","data/uwb-gestures-network.zip"); pretrainedNetworkFolder = erase(pretrainedNetworkZipFolder,".zip"); if ~exist(pretrainedNetworkFolder,"dir") downloadLocation = fileparts(pretrainedNetworkZipFolder); unzip(pretrainedNetworkZipFolder,downloadLocation); end load(fullfile(pretrainedNetworkFolder,"pretrainedNetwork.mat"),"misoNet"); end
Классифицируйте данные о тестировании с помощью обученного CNN и classify
команда.
testData = allData(testIdx,:);
testData = {cat(4,testData{:,1}),cat(4,testData{:,2}),cat(4,testData{:,3}),cat(1,testData{:,4})};
actualLabels = testData{4};
predictedLabels = classify(misoNet,testData{1},testData{2},testData{3});
accuracy = mean(predictedLabels==actualLabels);
fprintf("Accuracy on the test set is %0.2f%%",100*accuracy)
Accuracy on the test set is 96.04%
Визуализируйте эффективность классификации с помощью матрицы беспорядка.
confusionchart(predictedLabels,actualLabels);
Самый большой беспорядок между против часовой стрелки и по часовой стрелке перемещения и входящее нажатие и пустые перемещения.
Можно получить баллы из итоговых макс. объединяющих слоев в каждой входной ветви, чтобы получить лучшее понимание как данные из каждого радара, внесенного итоговому сетевому доверию. Функция помощника, getActivationData
, возвращает softmax-нормированные баллы (вероятности для членства в классе) и индексы, соответствующие 3 самым высоким баллам.
gestureToPlot = "L-R swipe"; gestureToPlotIndices = находят (соответствия (строка (actualLabels), gestureToPlot)); gestureSelection = randsample (gestureToPlotIndices, 1); actualLabel = actualLabels (gestureSelection); predictedLabel = predictedLabels (gestureSelection); allLabels = категории (actualLabels); [leftScores, leftClassIds] = getActivationData (misoNet, данные тестирования, gestureSelection,"maxpool_4"); [topScores, topClassIds] = getActivationData (misoNet, данные тестирования, gestureSelection,"maxpool_8"); [rightScores, rightClassIds] = getActivationData (misoNet, данные тестирования, gestureSelection,"maxpool_12");
Используйте функцию помощника plotActivationData
чтобы визуализировать данные из каждого радара и наложить метки, соответствующие 3 самым высоким баллам после, операции в каждой входной ветви завершаются.
t = tiledlayout(1,3); plotActivationData(testData, allLabels, leftScores, leftClassIds,... gestureSelection, [0 radarRange],[0 recordedTimePerSample], "Left"); plotActivationData(testData, allLabels, topScores, topClassIds,... gestureSelection, [0 radarRange],[0 recordedTimePerSample], "Top"); plotActivationData(testData, allLabels, rightScores, rightClassIds,... gestureSelection, [0 radarRange],[0 recordedTimePerSample], "Right"); title(t,["Actual Label : "+string(actualLabel);"Predicted Label : "+string(predictedLabel)],"FontSize",12); xlabel(t,"Distance of Hand from the Radar (m)","FontSize",11) ylabel(t,"Duration of Hand Gesture (s)","FontSize",11)
В этом примере вы изучили, как использовать несколько - вход CNN, чтобы извлечь информацию из 3 независимых радарных матриц данных и классифицировать ручные перемещения жеста. Несколько - входная архитектура позволила нам использовать в своих интересах данные, сгенерированные несколькими датчиками, записывающими то же событие.
function dataOut = processData(dataIn) label = dataIn(end); dataIn(end) = []; dataOut = cellfun(@(x) filter([1 -1],[1 -0.9],x,[],1),dataIn,"UniformOutput",false); dataOut = cellfun(@(x) normalize(x,2,"range",[0 1]),dataOut,"UniformOutput",false); dataOut = cat(1,dataOut{:}); dataOut = mat2cell(dataOut,90*ones(1,size(dataOut,1)/90)); dataOut = reshape(dataOut,[],3); dataOut(:,4) = label; end function [scores, classIds] = getActivationData(net, testData, index, layer) activation = activations(net,testData{1}(:,:,index),testData{2}(:,:,index),testData{3}(:,:,index),layer,"OutputAs","columns"); fcWeights = net.Layers(end-2).Weights; fcBias = net.Layers(end-2).Bias; scores = fcWeights*activation + fcBias; scores = exp(scores)/sum(exp(scores)); [~,classIds] = maxk(scores,3); end function plotActivationData(testData, labels, scores, ids, sampleIdx, xlimits, ylimits, plotTitle) if plotTitle == "Left" gesture = 1; elseif plotTitle == "Top" gesture = 2; elseif plotTitle == "Right" gesture = 3; end nexttile; imagesc(xlimits,ylimits,testData{gesture}(:,:,sampleIdx),[0 1]) text(0.3,4,plotTitle,"Color","red","FontWeight","bold","HorizontalAlignment","center") set(gca,"YDir","normal") title(string(labels(ids)) + ": " + string(round(scores(ids),3)),"FontSize",8); end
[1] Ахмед, S., Ван, D., Парк, J. и др. UWB-жесты, общедоступный набор данных динамических ручных жестов, полученных с помощью импульсных радарных датчиков. Научные Данные 8, 102 (2021). https://doi.org/10.1038/s41597-021-00876-0
[2] Lazaro A, Girbau D, Виллэрино Р. Текникс для подавления помехи в присутствии движений тела во время обнаружения дыхательного действия через радары UWB. Датчики (Базель, Швейцария). 2 014 февралей; 14 (2):2595-2618. DOI: 10.3390/s140202595.
arrayDatastore
| signalDatastore
(Signal Processing Toolbox) | splitlabels
(Signal Processing Toolbox) | trainNetwork