В этом примере показано, как классифицировать каждый временной шаг данных о последовательности с помощью типовой временной сверточной сети (TCN).
В то время как задачи от последовательности к последовательности обычно решаются с архитектурой рекуррентной нейронной сети, Бай и др. [1] показывают, что сверточные нейронные сети могут совпадать с производительностью текущих сетей на типичных задачах моделирования последовательности или даже превзойти их по характеристикам. Потенциальные выгоды использования сверточных сетей могут быть лучшим параллелизмом, лучше управлять по восприимчивому размеру поля, лучшему управлению объема потребляемой памяти сети во время обучения и другие устойчивые градиенты. Точно так же, как текущие сети сверточные сети могут работать с входными последовательностями переменной длины и могут использоваться к от последовательности к последовательности модели или sequence-one задачам.
Этот пример использует данные о датчике, полученные из смартфона, который изнашивают на теле, и обучает временную сверточную сеть как функцию с помощью пользовательского учебного цикла и автоматического дифференцирования, чтобы распознать действие владельца, данного данные временных рядов, представляющие показания акселерометра в трех различных направлениях.
Загрузите данные о распознавании деятельности человека. Данные содержат семь временных рядов данных о датчике, полученных из смартфона, который изнашивают на теле. Каждая последовательность имеет три функции и варьируется по длине. Три функции соответствуют показаниям акселерометра в трех различных направлениях. Шесть последовательностей используются в обучении, и одна последовательность используется в тестировании после обучения.
s = load("HumanActivityTrain.mat");
XTrain = s.XTrain;
YTrain = s.YTrain;
numObservations = numel(XTrain);
classes = categories(YTrain{1});
numClasses = numel(classes);
Визуализируйте одну обучающую последовательность в графике. Постройте первую функцию первой обучающей последовательности и окрасьте график согласно соответствующему действию.
X = XTrain{1}(1,:); figure for j = 1:numel(classes) label = classes(j); idx = find(YTrain{1} == label); hold on plot(idx,X(idx)) end hold off xlabel("Time Step") ylabel("Acceleration") title("Training Sequence 1, Feature 1") legend(classes,'Location','northwest')
Основной базовый блок временной сверточной сети является расширенным причинным слоем свертки, который действует по временным шагам каждой последовательности. В этом контексте, "причинном", означает, что активации, вычисленные для шага определенного времени, не могут зависеть от активаций от будущих временных шагов.
Для того, чтобы создать контекст от предыдущих временных шагов, несколько сверточных слоев обычно складываются друг на друге. Чтобы достигнуть больших восприимчивых размеров поля, фактор расширения последующих слоев свертки увеличен экспоненциально как показано в изображении ниже. Принятие фактора расширения k-th сверточного слоя и шаг равняется 1, затем восприимчивый размер поля такой сети может быть вычислен как , где размер фильтра и количество сверточных слоев. Измените размер фильтра и количество слоев, чтобы легко настроить восприимчивый размер поля и номер или learnable параметры по мере необходимости для данных и задачи под рукой.
Один из недостатков TCNs по сравнению с RNNs является более высоким объемом потребляемой памяти во время вывода. Целая необработанная последовательность требуется, чтобы вычислять следующий временной шаг. Чтобы уменьшать время вывода и потребление памяти, специально для неродной вперед прогнозов, это может быть выгодно, чтобы обучаться с самым маленьким разумным восприимчивым размером поля и только выполните прогноз с последним временные шаги входной последовательности.
Общая архитектура TCN (как описано в [1]) состоит из нескольких остаточных блоков, каждый содержащий два набора расширенных причинных слоев свертки с тем же фактором расширения, сопровождаемым нормализацией, активацией ReLU и пространственными слоями уволенного. Вход с каждым блоком добавляется к выходу блока (включая свертку 1 на 1 на входе, когда количество каналов между вводом и выводом не соответствует), и применяется итоговая функция активации.
Укажите, что параметры для архитектуры TCN с четырьмя остаточными блоками, содержащими, расширили причинные слои свертки с каждым 175 фильтров размера 3.
numBlocks = 4; numFilters = 175; filterSize = 3; dropoutFactor = 0.05;
Чтобы передать гиперпараметры модели функциям модели (количество блоков и фактора уволенного), создайте struct, содержащий эти значения.
hyperparameters = struct; hyperparameters.NumBlocks = numBlocks; hyperparameters.DropoutFactor = dropoutFactor;
Создайте struct, содержащий dlarray
объекты для всех learnable параметров основанного на модели на количестве входа образовывают канал и гиперпараметры, которые задают архитектуру модели. Каждый остаточный блок требует параметров весов и параметров смещения для каждой из двух операций свертки. Первый остаточный блок обычно также требует весов и смещений для дополнительной операции свертки с размером фильтра 1. Финал полностью соединился, операция требует весов и параметра смещения также. Инициализируйте learnable веса слоя с помощью функционального initializeGaussian
, перечисленный в конце примера. Инициализируйте learnable смещения слоя с нулями.
numInputChannels = 3; parameters = struct; numChannels = numInputChannels; for k = 1:numBlocks parametersBlock = struct; blockName = "Block"+k; weights = initializeGaussian([filterSize, numChannels, numFilters]); bias = zeros(numFilters, 1, 'single'); parametersBlock.Conv1.Weights = dlarray(weights); parametersBlock.Conv1.Bias = dlarray(bias); weights = initializeGaussian([filterSize, numFilters, numFilters]); bias = zeros(numFilters, 1, 'single'); parametersBlock.Conv2.Weights = dlarray(weights); parametersBlock.Conv2.Bias = dlarray(bias); % If the input and output of the block have different numbers of % channels, then add a convolution with filter size 1. if numChannels ~= numFilters weights = initializeGaussian([1, numChannels, numFilters]); bias = zeros(numFilters, 1, 'single'); parametersBlock.Conv3.Weights = dlarray(weights); parametersBlock.Conv3.Bias = dlarray(bias); end numChannels = numFilters; parameters.(blockName) = parametersBlock; end weights = initializeGaussian([numClasses,numChannels]); bias = zeros(numClasses,1,'single'); parameters.FC.Weights = dlarray(weights); parameters.FC.Bias = dlarray(bias);
Просмотрите сетевые параметры.
parameters
parameters = struct with fields:
Block1: [1×1 struct]
Block2: [1×1 struct]
Block3: [1×1 struct]
Block4: [1×1 struct]
FC: [1×1 struct]
Просмотрите параметры первого блока.
parameters.Block1
ans = struct with fields:
Conv1: [1×1 struct]
Conv2: [1×1 struct]
Conv3: [1×1 struct]
Просмотрите параметры первой сверточной операции первого блока.
parameters.Block1.Conv1
ans = struct with fields:
Weights: [3×3×175 dlarray]
Bias: [175×1 dlarray]
Создайте функциональный model
, перечисленный в разделе Model Function в конце примера, который вычисляет выходные параметры модели глубокого обучения. Функциональный model
берет входные данные, learnable параметры модели, гиперпараметры модели и флаг, который задает, должна ли модель возвратить выходные параметры для обучения или прогноза. Сетевые выходные параметры прогнозы для меток на каждом временном шаге входной последовательности.
Создайте функциональный modelGradients
, перечисленный в разделе Model Gradients Function в конце примера, который берет мини-пакет входных данных, соответствующих целевых последовательностей и параметров сети, и возвращает градиенты потери относительно learnable параметров и соответствующей потери.
Задайте набор опций обучения, используемых в пользовательском учебном цикле.
Обучайтесь в течение 30 эпох с мини-пакетным размером 1.
Запустите с начальной буквы, изучают уровень 0,001
Умножьте изучить уровень к 0,1 каждым 12 эпохам.
Отсеките градиенты с помощью норма с порогом 1.
maxEpochs = 30; miniBatchSize = 1; initialLearnRate = 0.001; learnRateDropFactor = 0.1; learnRateDropPeriod = 12; gradientThreshold = 1;
Чтобы обучаться на графическом процессоре, если вы доступны, задайте среду выполнения "auto"
. Чтобы явным образом обучаться на центральном процессоре или графическом процессоре, задайте "cpu"
или "gpu"
соответственно. Используя графический процессор требует Parallel Computing Toolbox™, и CUDA® включил NVIDIA®, графический процессор с вычисляет возможность 3.0 или выше.
executionEnvironment = "auto";
Чтобы контролировать процесс обучения, можно построить учебную потерю после каждой итерации. Создайте переменную plots
это содержит "training-progress"
. Если вы не хотите строить процесс обучения, то установленный это значение к "none"
.
plots = "training-progress";
Обучите сеть через стохастический градиентный спуск цикличным выполнением по последовательностям в обучающем наборе данных, вычислительных градиентах параметра и обновлении сетевых параметров через правило обновления Адама. Этот процесс повторяется многократно (называемый эпохами), пока обучение не сходится, и максимальное количество эпох достигнуто.
В течение каждой эпохи переставьте обучающие данные.
Для каждого мини-пакета:
Преобразуйте метки в фиктивные переменные.
Предварительно обработайте данные о последовательности с помощью transformSequences
функция, перечисленная в конце примера.
Преобразуйте данные в dlarray
объекты с базовым одним типом
Для обучения графического процессора преобразуйте данные в gpuArray
.
Оцените градиенты модели и потерю с помощью dlfeval
и modelGradients
функция.
Отсеките градиенты, если они являются слишком большими, с помощью функционального thresholdL2Norm
, перечисленный в конце примера и dlupdate
функция.
Обновите сетевые параметры с помощью adamupdate
функция.
Обновите график процесса обучения.
После завершения learnRateDropPeriod
эпохи, уменьшайте изучить уровень путем умножения текущего темпа обучения на learnRateDropFactor
.
Инициализируйте темп обучения, который будет умножен на LearnRateDropFactor
оцените каждый LearnRateDropPeriod
эпохи.
learnRate = initialLearnRate;
Инициализируйте скользящее среднее значение градиентов параметра и поэлементные квадраты градиентов, используемых оптимизатором Адама.
trailingAvg = []; trailingAvgSq = [];
Инициализируйте график, показывающий процесс обучения.
if plots == "training-progress" figure ylabel("Loss") xlabel("Iteration") lineLoss = animatedline; end
Обучите модель.
iteration = 0; numIterationsPerEpoch = floor(numObservations./miniBatchSize); start = tic; % Loop over epochs. for epoch = 1:maxEpochs % Shuffle the data. idx = randperm(numObservations); XTrain = XTrain(idx); YTrain = YTrain(idx); % Loop over mini-batches. for i = 1:numIterationsPerEpoch iteration = iteration + 1; % Read mini-batch of data and apply the transformSequences % preprocessing function. idx = (i-1)*miniBatchSize+1:i*miniBatchSize; [X,Y,numTimeSteps] = transformSequences(XTrain(idx),YTrain(idx)); % Convert to dlarray. dlX = dlarray(X); % If training on a GPU, convert data to gpuArray. if (executionEnvironment == "auto" && canUseGPU) || executionEnvironment == "gpu" dlX = gpuArray(dlX); end % Evaluate the model gradients and loss using dlfeval. [gradients, loss] = dlfeval(@modelGradients,dlX,Y,parameters,hyperparameters,numTimeSteps); % Clip the gradients. gradients = dlupdate(@(g) thresholdL2Norm(g,gradientThreshold),gradients); % Update the network parameters using the Adam optimizer. [parameters,trailingAvg,trailingAvgSq] = adamupdate(parameters,gradients, ... trailingAvg, trailingAvgSq, iteration, learnRate); if plots == "training-progress" % Plot training progress. D = duration(0,0,toc(start),'Format','hh:mm:ss'); % Normalize the loss over the sequence lengths loss = mean(loss ./ numTimeSteps); loss = double(gather(extractdata(loss))); addpoints(lineLoss,iteration,loss); title("Epoch: " + epoch + ", Elapsed: " + string(D)) drawnow end end % Reduce the learning rate after LearnRateDropPeriod epochs if mod(epoch,learnRateDropPeriod) == 0 learnRate = learnRate*learnRateDropFactor; end end
Протестируйте точность классификации модели путем сравнения прогнозов на протянутом наборе тестов с истинными метками для каждого временного шага.
s = load("HumanActivityTest.mat");
XTest = s.XTest;
YTest = s.YTest;
numObservationsTest = numel(XTest);
Предварительно обработайте тестовые данные с помощью той же функции, используемой в обучении, и преобразуйте данные в dlarray
.
[X,Y] = transformSequences(XTest,YTest); dlXTest = dlarray(X);
Чтобы предсказать метки тестовых данных, используйте функцию модели обученными параметрами, гиперпараметрами и doTraining
набор опции ко лжи.
doTraining = false; dlYPred = model(dlXTest,parameters,hyperparameters,doTraining);
Чтобы получить категориальные метки прогнозов, найдите метку класса с самым высоким счетом к каждому временному шагу. Вычислите точность путем оценки средней точности классификации для последовательностей.
YPred = gather(extractdata(dlYPred)); labelsPred = categorical(zeros(numObservationsTest,size(dlYPred,3))); accuracy = zeros(1,numObservationsTest); for i = 1:numObservationsTest [~,idxPred] = max(YPred(:,i,:),[],1); [~,idxTest] = max(Y(:,i,:),[],1); labelsPred(i,:) = classes(idxPred)'; accuracy(i) = mean(idxPred == idxTest); end
Просмотрите среднюю точность по набору тестов.
mean(accuracy)
ans = 0.9996
Сравните прогнозы для одной последовательности с соответствующими тестовыми данными при помощи графика.
figure idx = 1; plot(categorical(labelsPred(idx,:)),'.-') hold on plot(YTest{1}) hold off xlabel("Time Step") ylabel("Activity") title("Predicted Activities") legend(["Predicted" "Test Data"])
Функциональный model
берет входные данные dlX
, learnable параметры модели, гиперпараметры модели и флаг doTraining
который задает, должна ли модель возвратить выходные параметры для обучения или прогноза. Сетевые выходные параметры прогнозы для меток на каждом временном шаге входной последовательности. Модель состоит из нескольких остаточных блоков с экспоненциально увеличивающимися факторами расширения. После последнего остаточного блока, итогового fullyconnect
операция сопоставляет выход с количеством классов в целевых данных.
function dlY = model(dlX,parameters,hyperparameters,doTraining) numBlocks = hyperparameters.NumBlocks; dropoutFactor = hyperparameters.DropoutFactor; dlY = dlX; % Residual blocks. for k = 1:numBlocks dilationFactor = 2^(k-1); parametersBlock = parameters.("Block"+k); dlY = residualBlock(dlY,dilationFactor,dropoutFactor,parametersBlock,doTraining); end % Fully connect. weights = parameters.FC.Weights; bias = parameters.FC.Bias; dlY = fullyconnect(dlY,weights,bias,'DataFormat','CBT'); end
Функциональный residualBlock
реализует базовый базовый блок временной сверточной сети.
Чтобы применить 1D причинную расширенную свертку, используйте dlconv
функция:
Чтобы применить операцию свертки по пространственным размерностям, установите 'DataFormat'
опция к 'CBS'
(используйте метку 'S'
размерности вместо
'T'
),
Установите 'DilationFactor'
опция согласно фактору расширения остаточного блока.
Чтобы гарантировать только прошлые временные шаги используются, применяют дополнение только в начале последовательности.
function dlY = residualBlock(dlX,dilationFactor,dropoutFactor,parametersBlock,doTraining) % Convolution options. filterSize = size(parametersBlock.Conv1.Weights,1); paddingSize = (filterSize - 1) * dilationFactor; % Convolution. weights = parametersBlock.Conv1.Weights; bias = parametersBlock.Conv1.Bias; dlY = dlconv(dlX,weights,bias, ... 'DataFormat','CBS', ... 'Stride', 1, ... 'DilationFactor', dilationFactor, ... 'Padding', [paddingSize; 0] ); % Instance normalization, ReLU, spatial dropout. dlY = instanceNormalization(dlY,'CBS'); dlY = relu(dlY); dlY = spatialDropout(dlY,dropoutFactor,'CBS',doTraining); % Convolution. weights = parametersBlock.Conv2.Weights; bias = parametersBlock.Conv2.Bias; dlY = dlconv(dlY,weights,bias, ... 'DataFormat','CBS', ... 'Stride', 1, ... 'DilationFactor', dilationFactor, ... 'Padding',[paddingSize; 0] ); % Instance normalization, ReLU, spatial dropout. dlY = instanceNormalization(dlY,'CBS'); dlY = relu(dlY); dlY = spatialDropout(dlY,dropoutFactor,'CBS',doTraining); % Optional 1-by-1 convolution. if ~isequal(size(dlX),size(dlY)) weights = parametersBlock.Conv3.Weights; bias = parametersBlock.Conv3.Bias; dlX = dlconv(dlX,weights,bias,'DataFormat','CBS'); end % Addition and ReLU dlY = relu(dlX+dlY); end
modelGradients
функционируйте берет мини-пакет входных данных dlX
, соответствующие целевые последовательности T
, learnable параметры и гиперпараметры, и возвращают градиенты потери относительно learnable параметров и соответствующей потери. Чтобы вычислить градиенты, оцените modelGradients
функция с помощью dlfeval
функция в учебном цикле.
function [gradients,loss] = modelGradients(dlX,T,parameters,hyperparameters,numTimeSteps) dlY = model(dlX,parameters,hyperparameters,true); dlY = softmax(dlY,'DataFormat','CBT'); dlT = dlarray(T,'CBT'); loss = maskedCrossEntropyLoss(dlY, dlT, numTimeSteps); gradients = dlgradient(loss,parameters); end
maskedCrossEntropyLoss
функция вычисляет перекрестную энтропийную потерю для мини-пакетов последовательностей, где последовательности являются различными длинами.
function loss = maskedCrossEntropyLoss(dlY, dlT, numTimeSteps) numObservations = size(dlY,2); for i = 1:numObservations idx = 1:numTimeSteps(i); loss(i) = crossentropy(dlY(:,i,idx),dlT(:,i,idx),'DataFormat','CBT'); end end
instanceNormalization
функция нормирует вход dlX
первым вычислением среднего значения и отклонение для каждого наблюдения по каждому входному каналу. Затем это вычисляет нормированные активации как
По сравнению с пакетной нормализацией, средним значением и отклонением отличается для каждого наблюдения в мини-пакете. Используйте нормализацию, такую как нормализация экземпляра, между сверточными слоями и нелинейностью, чтобы ускорить обучение сверточных нейронных сетей и улучшить сходимость.
function dlY = instanceNormalization(dlX,fmt) reductionDims = find(fmt == 'S'); mu = mean(dlX,reductionDims); sigmaSq = var(dlX,1,reductionDims); epsilon = 1e-5; dlY = (dlX-mu) ./ sqrt(sigmaSq+epsilon); end
spatialDropout
функция выполняет пространственного уволенного [3] на входе dlX
с размерностью маркирует fmt
когда doTraining
флагом является true
. Пространственный уволенный пропускает целый канал входных данных. Таким образом, все временные шаги определенного канала пропущены с вероятностью, заданной dropoutFactor
. Каналы опущены независимо в пакетной размерности.
function dlY = spatialDropout(dlX,dropoutFactor,fmt,doTraining) if doTraining maskSize = size(dlX); maskSize(fmt=='S') = 1; dropoutScaleFactor = single(1 - dropoutFactor); dropoutMask = (rand(maskSize,'like',dlX) > dropoutFactor) / dropoutScaleFactor; dlY = dlX .* dropoutMask; else dlY = dlX; end end
initializeGaussian
функциональные демонстрационные веса от Распределения Гаусса со средним значением 0 и стандартным отклонением 0.01.
function parameter = initializeGaussian(sz) parameter = randn(sz,'single') .* 0.01; end
sequenceTransform
функционируйте берет массив ячеек последовательностей N и возвращает C N S числовым массивом лево-заполненных 1D последовательностей и количеством временных шагов в каждой последовательности, где C соответствует количеству функций последовательностей, и S соответствует количеству временных шагов самой длинной последовательности.
function [XTransformed, YTransformed, numTimeSteps] = transformSequences(X,Y) numTimeSteps = cellfun(@(sequence) size(sequence,2),X); miniBatchSize = numel(X); numFeatures = size(X{1},1); sequenceLength = max(cellfun(@(sequence) size(sequence,2),X)); classes = categories(Y{1}); numClasses = numel(classes); sz = [numFeatures miniBatchSize sequenceLength]; XTransformed = zeros(sz,'single'); sz = [numClasses miniBatchSize sequenceLength]; YTransformed = zeros(sz,'single'); for i = 1:miniBatchSize predictors = X{i}; % Create dummy labels. numTimeSteps = size(predictors,2); responses = zeros(numClasses, numTimeSteps, 'single'); for c = 1:numClasses responses(c,Y{i}==classes(c)) = 1; end % Left pad. XTransformed(:,i,:) = leftPad(predictors,sequenceLength); YTransformed(:,i,:) = leftPad(responses,sequenceLength); end end
leftPad
функционируйте берет последовательность и лево-заполняет его нулями, чтобы иметь заданную длину последовательности.
function sequencePadded = leftPad(sequence,sequenceLength) [numFeatures,numTimeSteps] = size(sequence); paddingSize = sequenceLength - numTimeSteps; padding = zeros(numFeatures,paddingSize); sequencePadded = [padding sequence]; end
thresholdL2Norm
функционируйте масштабирует градиент g
так, чтобы норма равняется gradientThreshold
когда норма градиента больше, чем gradientThreshold
.
function g = thresholdL2Norm(g,gradientThreshold) gradientNorm = sqrt(sum(g.^2,'all')); if gradientNorm > gradientThreshold g = g * (gradientThreshold / gradientNorm); end end
[1] Бай, Shaojie, Х. Зико Кольтер и Владлен Кольтун. "Эмпирическая оценка типовых сверточных и текущих сетей для моделирования последовательности". arXiv предварительно распечатывают arXiv:1803.01271 (2018).
[2] Ван ден Урд, Aäron, и др. "WaveNet: порождающая модель для необработанного аудио". SSW 125 (2016).
[3] Томпсон, Джонатан, и др. "Эффективная объектная локализация с помощью сверточных сетей". Продолжения Конференции по IEEE по Компьютерному зрению и Распознаванию образов. 2015.
adamupdate
| crossentropy
| dlarray
| dlconv
| dlfeval
| dlgradient
| fullyconnect
| relu
| softmax