Классификация последовательности в последовательности с использованием 1-D сверток

Этот пример показывает, как классифицировать каждый временной шаг данных последовательности с помощью общей временной сверточной сети (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-го сверточного слоя, 2(k-1) и шаг равен 1, тогда размер приемного поля такой сети может быть вычислен как R=(f-1)(2K-1)+1, где f - размер фильтра и K количество сверточных слоев. Измените размер и количество слоев фильтра, чтобы легко настроить размер восприимчивого поля и количество или настраиваемые параметры по мере необходимости для данных и задачи.

Одним из недостатков TCN по сравнению с RNN является более высокая площадь памяти во время вывода. Вся необработанная последовательность требуется для вычисления следующего временного шага. Чтобы уменьшить время вывода и потребление памяти, особенно для предсказаний с шагом вперед, может быть выгодно обучаться с наименьшим разумным размером восприимчивого поля R и выполнять только предсказание с последним R временные шаги последовательности входа.

Общая архитектура 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 эпох.

  • Обрезка градиентов с помощью L2 норма с порогом 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 функция предварительно обрабатывает данные с помощью следующего шага:

  1. Предварительно обработайте предикторы, используя preprocessMiniBatchPredictors функция/.

  2. Однократное кодирование категориальных меток каждого временного шага в числовые массивы.

  3. Дополните последовательности такой же длиной, как и самую длинную последовательность в мини-пакете, используя 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 так, чтобы его L2 норма равна gradientThreshold когда L2 норма градиента больше 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.

См. также

| | | | | | | | | | |

Похожие темы