Этот пример показывает, как классифицировать каждый временной шаг данных последовательности с помощью общей временной сверточной сети (TCN).
В то время как задачи перехода в последовательность обычно решаются с рекуррентными архитектурами нейронной сети, Bai et al. [1] показать, что сверточные нейронные сети могут совпадать с эффективностью рекуррентных сетей при типичных задачах моделирования последовательности или даже превосходить их. Потенциальными преимуществами использования сверточных сетей могут быть лучший параллелизм, лучший контроль над размером приемного поля, лучший контроль объема памяти сети во время обучения и более стабильные градиенты. Так же, как и рекуррентные сети, сверточные сети могут работать с входными последовательностями переменной длины и могут использоваться, чтобы смоделировать задачи «последовательность в последовательность» или «последовательность в одну».
Этот пример обучает сеть TCN распознавать активность человека, носящего смартфон на теле, с учетом данных временных рядов, представляющих показания акселерометра в трех различных направлениях. Пример задает TCN как функцию и обучает модель с помощью пользовательского цикла обучения.
Для свертки во времени размерности входных данных (также известных как 1-D свертки) используйте dlconv
и задайте размерности весов используя 'WeightsFormat'
опция или задайте веса как форматированные dlarray
.
Загрузите данные распознавания активности. Данные содержат семь временных рядов данных о датчике, полученных с носимого на корпусе смартфона. Каждая последовательность имеет три функции и изменяется в длине. Три функции соответствуют показаниям акселерометра в трех разных направлениях. Шесть последовательностей используются для обучения, а одна последовательность используется для проверки после обучения.
s = load("HumanActivityTrain.mat");
Создание arrayDatastore
объекты, содержащие данные, и объединить их с помощью combine
функция.
dsXTrain = arrayDatastore(s.XTrain,'OutputType','same'); dsYTrain = arrayDatastore(s.YTrain,'OutputType','same'); dsTrain = combine(dsXTrain,dsYTrain);
Просмотрите количество наблюдений в обучающих данных.
numObservations = numel(s.XTrain)
numObservations = 6
Просмотрите количество классов в обучающих данных.
classes = categories(s.YTrain{1}); numClasses = numel(classes)
numClasses = 5
Визуализируйте одну из обучающих последовательностей на графике. Постройте график функций первой обучающей последовательности и соответствующей деятельности.
figure for i = 1:3 X = s.XTrain{1}(i,:); subplot(4,1,i) plot(X) ylabel("Feature " + i + newline + "Acceleration") end subplot(4,1,4) hold on plot(s.YTrain{1}) hold off xlabel("Time Step") ylabel("Activity") subplot(4,1,1) title("Training Sequence 1")
Основной базовый блок временной сверточной сети является расширенным слоем причинно-следственной свертки, который действует на временных шагах каждой последовательности. В этом контексте «причинно-следственная» означает, что активации, вычисленные для определенного временного шага, не могут зависеть от активации от будущих временных шагов.
В порядок создания контекста из предыдущих временных шагов несколько сверточных слоев обычно складываются друг на верхнюю часть друг друга. Для достижения больших размеров рецептивного поля коэффициент расширения последующих слоев свертки увеличивается экспоненциально, как показано на изображении ниже. Принимая коэффициент расширения k-го сверточного слоя, и шаг равен 1, тогда размер приемного поля такой сети может быть вычислен как , где - размер фильтра и количество сверточных слоев. Измените размер и количество слоев фильтра, чтобы легко настроить размер восприимчивого поля и количество или настраиваемые параметры по мере необходимости для данных и задачи.
Одним из недостатков TCN по сравнению с RNN является более высокая площадь памяти во время вывода. Вся необработанная последовательность требуется для вычисления следующего временного шага. Чтобы уменьшить время вывода и потребление памяти, особенно для предсказаний с шагом вперед, может быть выгодно обучаться с наименьшим разумным размером восприимчивого поля и выполнять только предсказание с последним временные шаги последовательности входа.
Общая архитектура TCN (как описано в [1]) состоит из нескольких остаточных блоков, каждый из которых содержит два набора расширенных слоев причинно-следственной свертки с одним и тем же фактором коэффициента расширения с последующей нормализацией, активацией ReLU и пространственными слоями отсева. Вход к каждому блоку добавлен к выходу блока (включая скручивание 1 на 1 на входе, когда количество каналов между входом и выходом не соответствует), и применена заключительная функция активации.
Задайте параметры для архитектуры TCN с четырьмя остаточными блоками, содержащими расширенные слои причинно-следственной свертки с каждым 175 фильтрами размера 3.
numBlocks = 4; numFilters = 175; filterSize = 3; dropoutFactor = 0.05;
Чтобы передать гиперпараметры модели функциям модели (количество блоков и коэффициент отсева), создайте структуру, содержащую эти значения.
hyperparameters = struct; hyperparameters.NumBlocks = numBlocks; hyperparameters.DropoutFactor = dropoutFactor;
Создайте структуру, содержащую dlarray
объекты для всех настраиваемых параметров модели на основе количества каналов входа и гиперпараметров, которые определяют архитектуру модели. Каждый остаточный блок требует весовых параметров и параметров смещения для каждой из двух операций свертки. Первый остаточный блок обычно также требует весов и смещений для дополнительной операции свертки с размером фильтра 1. Конечная полносвязная операция требует также весов и параметра смещения. Инициализируйте обучаемые веса и смещения слоев с помощью initializeGaussian
и initializeZeros
функций, соответственно. Эти функции присоединены к этому примеру как вспомогательный файл.
numInputChannels = 3; parameters = struct; numChannels = numInputChannels; mu = 0; sigma = 0.01; for k = 1:numBlocks parametersBlock = struct; blockName = "Block"+k; % 1-D Convolution. sz = [filterSize numChannels numFilters]; parametersBlock.Conv1.Weights = initializeGaussian(sz,mu,sigma); parametersBlock.Conv1.Bias = initializeZeros([numFilters 1]); % 1-D Convolution. sz = [filterSize numFilters numFilters]; parametersBlock.Conv2.Weights = initializeGaussian(sz,mu,sigma); parametersBlock.Conv2.Bias = initializeZeros([numFilters 1]); % If the input and output of the block have different numbers of % channels, then add a convolution with filter size 1. if numChannels ~= numFilters % 1-D Convolution. sz = [1 numChannels numFilters]; parametersBlock.Conv3.Weights = initializeGaussian(sz,mu,sigma); parametersBlock.Conv3.Bias = initializeZeros([numFilters 1]); end numChannels = numFilters; parameters.(blockName) = parametersBlock; end % Fully connect. sz = [numClasses numChannels]; parameters.FC.Weights = initializeGaussian(sz,mu,sigma); parameters.FC.Bias = initializeZeros([numClasses 1]);
Просмотрите параметры сети.
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
принимает входные данные, параметры усвояемой модели, гиперпараметры модели и флаг, который определяет, должна ли модель возвращать выходы для обучения или предсказания. Сеть выходов предсказаний для меток на каждом временном шаге последовательности входа.
Создайте функцию modelGradients
, приведенный в разделе Model Gradients Function в конце примера, который берёт мини-пакет входных данных, соответствующие целевые последовательности и параметры сети и возвращает градиенты потерь относительно настраиваемых параметров и соответствующих потерь.
Задайте набор опций обучения, используемых в пользовательском цикле обучения.
Train на 30 эпох с мини-пакетом размером 1.
Начните с начальной скорости обучения 0,001
Умножьте скорость обучения на 0,1 каждые 12 эпох.
Обрезка градиентов с помощью норма с порогом 1.
maxEpochs = 30; miniBatchSize = 1; initialLearnRate = 0.001; learnRateDropFactor = 0.1; learnRateDropPeriod = 12; gradientThreshold = 1;
Чтобы контролировать процесс обучения, можно построить график потерь обучения после каждой итерации. Создайте переменную plots
который содержит "training-progress"
. Если вы не хотите строить график процесса обучения, задайте это значение "none"
.
plots = "training-progress";
Обучите сеть через стохастический градиентный спуск путем закольцовывания последовательностей в обучающем наборе данных, вычисления градиентов параметров и обновления сетевых параметров через правило обновления Адама. Этот процесс повторяется несколько раз (упоминается как эпохи), пока не сходится обучение и не достигнуто максимальное количество эпох.
Использование minibatchqueue
обрабатывать и управлять мини-пакетами изображений во время обучения. Для каждого мини-пакета:
Предварительно обработайте данные с помощью пользовательской функции мини-пакетной предварительной обработки preprocessMiniBatch
(заданный в конце этого примера), который возвращает заполненные предикторы, заполненные закодированные цели с одним горячим кодированием и маску заполнения. Функция имеет три выхода, поэтому задайте три выходные переменные для minibatchqueue
объект.
Преобразуйте выходы в dlarray
объекты с форматом 'CTB'
(канал, время, пакет). The minibatchqueue
объект по умолчанию выводит данные с базовым типом single
.
Обучите на графическом процессоре, если он доступен. The minibatchqueue
объект по умолчанию преобразует каждый выход в gpuArray
при наличии графический процессор. Для использования GPU требуется Parallel Computing Toolbox™ и графический процессор с поддержкой CUDA ® NVIDIA ®. Для получения дополнительной информации смотрите Поддержку GPU by Release (Parallel Computing Toolbox).
numDatastoreOutputs = 3; mbq = minibatchqueue(dsTrain,numDatastoreOutputs,... 'MiniBatchSize',miniBatchSize,... 'MiniBatchFormat',{'CTB','CTB','CTB'},... 'MiniBatchFcn', @preprocessMiniBatch);
Для каждой эпохи перетасовывайте обучающие данные. Для каждого мини-пакета:
Оцените градиенты модели и потери с помощью dlfeval
и modelGradients
функция.
Обрезка больших градиентов с помощью функции thresholdL2Norm
, перечисленные в конце примера, и dlupdate
функция.
Обновляйте параметры сети с помощью adamupdate
функция.
Обновите график процесса обучения.
После завершения learnRateDropPeriod
эпох, уменьшите скорость обучения путем умножения текущей скорости обучения на learnRateDropFactor
.
Инициализируйте скорость обучения, которая будет умножена на LearnRateDropFactor
значение каждый LearnRateDropPeriod
эпох.
learnRate = initialLearnRate;
Инициализируйте скользящее среднее значение градиентов параметра и поэлементных квадратов градиентов, используемых оптимизатором Адама.
trailingAvg = []; trailingAvgSq = [];
Инициализируйте график, показывающий процесс обучения.
if plots == "training-progress" figure lineLossTrain = animatedline('Color',[0.85 0.325 0.098]); ylim([0 inf]) xlabel("Iteration") ylabel("Loss") grid on end
Обучите модель.
iteration = 0; start = tic; % Loop over epochs. for epoch = 1:maxEpochs % Shuffle the data. shuffle(mbq) % Loop over mini-batches. while hasdata(mbq) iteration = iteration + 1; [dlX,dlY,mask] = next(mbq); % Evaluate the model gradients and loss using dlfeval. [gradients, loss] = dlfeval(@modelGradients,parameters,hyperparameters,dlX,dlY,mask); % 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 numTimeSteps = sum(mask(1,:,:),3); loss = mean(loss ./ numTimeSteps); loss = double(gather(extractdata(loss))); loss = mean(loss); addpoints(lineLossTrain,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
Протестируйте классификационную точность модели путем сравнения предсказаний на задержанном тестовом наборе с истинными метками для каждого временного шага.
Создайте datastore, содержащий предикторы теста.
s = load("HumanActivityTest.mat"); dsXTest = arrayDatastore(s.XTest,'OutputType','same');
После обучения создание предсказаний по новым данным не требует меток. Создание minibatchqueue
объект, содержащий только предикторы тестовых данных:
Чтобы игнорировать метки для проверки, установите количество выходов мини-очереди пакетов равным 1.
Укажите тот же размер мини-пакета, что и для обучения.
Предварительно обработайте предикторы, используя preprocessMiniBatchPredictors
функции, перечисленной в конце примера.
Для одинарного выхода datastore задайте формат пакета 'CTB'
(канал, время, пакет).
numDatastoreOutputs = 1; mbqTest = minibatchqueue(dsXTest,numDatastoreOutputs,... 'MiniBatchSize',miniBatchSize,... 'MiniBatchFormat','CTB', ... 'MiniBatchFcn', @preprocessMiniBatchPredictors);
Закольцовывайте мини-пакеты и классифицируйте последовательности с помощью modelPredictions
функции, перечисленной в конце примера.
predictions = modelPredictions(parameters,hyperparameters,mbqTest,classes);
Оцените точность классификации путем сравнения предсказаний с тестовыми метками.
YTest = s.YTest{1}; accuracy = mean(predictions == YTest)
accuracy = 0.9996
Визуализируйте тестовую последовательность на графике и сравните предсказания с соответствующими тестовыми данными.
figure for i = 1:3 X = s.XTest{1}(i,:); subplot(4,1,i) plot(X) ylabel("Feature " + i + newline + "Acceleration") end subplot(4,1,4) idx = 1; plot(predictions(idx,:),'.-') hold on plot(YTest(idx,:)) hold off xlabel("Time Step") ylabel("Activity") legend(["Predicted" "Test Data"],'Location','northeast') subplot(4,1,1) title("Test Sequence")
Функция model
, описанный в разделе Define Model and Model Gradients Functions примера, принимает за вход параметры модели и гиперпараметры, входные данные dlX
, и флаг doTraining
который определяет, должна ли модель возвращать выходы для обучения или предсказания. Сеть выходов предсказаний для меток на каждом временном шаге последовательности входа. Модель состоит из нескольких остаточных блоков с экспоненциально увеличивающимися коэффициентами расширения. После последнего остаточного блока, конечное fullyconnect
операция преобразует выход в количество классов в целевых данных. Функция model создает остаточные блоки, используя residualBlock
функция, представленная в разделе «Функция остаточных блоков» примера.
function dlY = model(parameters,hyperparameters,dlX,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); % Softmax. dlY = softmax(dlY); end
Функция residualBlock
реализует основной базовый блок временной сверточной сети.
Чтобы применить 1-D причинно-следственную расширенную свертку, используйте dlconv
функция:
Для свертки в течение временной размерности установите 'WeightsFormat'
опция для 'TCU'
(время, канал, не определено),
Установите '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, ... 'WeightsFormat','TCU', ... 'DilationFactor', dilationFactor, ... 'Padding', [paddingSize; 0]); % Normalization. dim = find(dims(dlY)=='T'); mu = mean(dlY,dim); sigmaSq = var(dlY,1,dim); epsilon = 1e-5; dlY = (dlY - mu) ./ sqrt(sigmaSq + epsilon); % ReLU, spatial dropout. dlY = relu(dlY); dlY = spatialDropout(dlY,dropoutFactor,doTraining); % Convolution. weights = parametersBlock.Conv2.Weights; bias = parametersBlock.Conv2.Bias; dlY = dlconv(dlY,weights,bias, ... 'WeightsFormat','TCU', ... 'DilationFactor', dilationFactor, ... 'Padding',[paddingSize; 0]); % Normalization. dim = find(dims(dlY)=='T'); mu = mean(dlY,dim); sigmaSq = var(dlY,1,dim); epsilon = 1e-5; dlY = (dlY - mu) ./ sqrt(sigmaSq + epsilon); % ReLU, spatial dropout. dlY = relu(dlY); dlY = spatialDropout(dlY,dropoutFactor,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,'WeightsFormat','TCU'); end % Addition and ReLU dlY = relu(dlX + dlY); end
The modelGradients
функция, описанная в разделе Define Model and Model Gradients Functions примера, принимает за вход параметры модели и гиперпараметры, мини-пакет входных данных dlX
, соответствующие целевые последовательности dlT
, и последовательность заполнения маски, и возвращает градиенты потерь относительно настраиваемых параметров и соответствующих потерь. Чтобы вычислить маскированные потери перекрестной энтропии, используйте 'Mask'
опция crossentropy
функция. Чтобы вычислить градиенты, вычислите modelGradients
функция, использующая dlfeval
функция в цикле обучения.
function [gradients,loss] = modelGradients(parameters,hyperparameters,dlX,dlT,mask) doTraining = true; dlY = model(parameters,hyperparameters,dlX,doTraining); mask = stripdims(mask); loss = crossentropy(dlY,dlT, ... 'Mask',mask, ... 'Reduction','none'); loss = sum(loss,[1 3]); loss = mean(loss); gradients = dlgradient(loss,parameters); end
The modelPredictions
функция принимает за вход параметры модели и гиперпараметры, а minibatchqueue
входных данных mbq
, и сетевых классов, и вычисляет предсказания модели путем итерации по всем данным в minibatchqueue
объект. Функция использует onehotdecode
функция для поиска предсказанных классов с самым высоким счетом.
function predictions = modelPredictions(parameters,hyperparameters,mbq,classes) doTraining = false; predictions = []; while hasdata(mbq) dlX = next(mbq); dlYPred = model(parameters,hyperparameters,dlX,doTraining); YPred = onehotdecode(dlYPred,classes,1); predictions = [predictions; YPred]; end predictions = permute(predictions,[2 3 1]); end
The spatialDropout
функция выполняет пространственное выпадение [3] на входе dlX
с метками размерностей fmt
когда doTraining
флаг true
. Пространственный выпадающий канал сбрасывает весь канал входных данных. То есть все временные шаги определенного канала отбрасываются с вероятностью, заданной dropoutFactor
. Каналы выпадают независимо по размерности партии.
function dlY = spatialDropout(dlX,dropoutFactor,doTraining) fmt = dims(dlX); if doTraining maskSize = size(dlX); maskSize(ismember(fmt,'ST')) = 1; dropoutScaleFactor = single(1 - dropoutFactor); dropoutMask = (rand(maskSize,'like',dlX) > dropoutFactor) / dropoutScaleFactor; dlY = dlX .* dropoutMask; else dlY = dlX; end end
The preprocessMiniBatch
функция предварительно обрабатывает данные для обучения. Функция преобразует входные последовательности в числовой массив 1-D последовательностей с заполнением влево, а также возвращает маску заполнения.
The preprocessMiniBatch
функция предварительно обрабатывает данные с помощью следующего шага:
Предварительно обработайте предикторы, используя preprocessMiniBatchPredictors
функция/.
Однократное кодирование категориальных меток каждого временного шага в числовые массивы.
Дополните последовательности такой же длиной, как и самую длинную последовательность в мини-пакете, используя padsequences
функция
function [XTransformed,YTransformed,mask] = preprocessMiniBatch(XCell,YCell) XTransformed = preprocessMiniBatchPredictors(XCell); miniBatchSize = numel(XCell); responses = cell(1,miniBatchSize); for i = 1:miniBatchSize responses{i} = onehotencode(YCell{i},1); end [YTransformed,mask] = padsequences(responses,2,'Direction','left'); end
The preprocessMiniBatchPredictors
функция предварительно обрабатывает мини-пакет предикторов путем извлечения данных последовательности из массива входа ячеек и левой вставки их, чтобы иметь ту же длину.
function XTransformed = preprocessMiniBatchPredictors(XCell) XTransformed = padsequences(XCell,2,'Direction','left'); end
The 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] Бай, Шаоцзе, Ж. Зико Колтер, и Владлен Колтун. «Эмпирическая оценка типовых сверточных и рекуррентных сетей для моделирования последовательности». arXiv preprint arXiv:1803.01271 (2018).
[2] Van Den Oord, Aäron, et al. WaveNet: генеративная модель для необработанного звука. SSW 125 (2016).
[3] Томпсон, Джонатан и др. «Эффективная локализация объектов с использованием сверточных сетей». Материалы Конференции IEEE по компьютерному зрению и распознаванию шаблонов. 2015.
adamupdate
| crossentropy
| dlarray
| dlconv
| dlfeval
| dlgradient
| fullyconnect
| minibatchqueue
| onehotdecode
| onehotencode
| relu
| softmax