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

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

Один из недостатков TCNs по сравнению с RNNs является более высоким объемом потребляемой памяти во время вывода. Целая необработанная последовательность требуется, чтобы вычислять следующий временной шаг. Чтобы уменьшать время вывода и потребление памяти, специально для неродной вперед прогнозов, это может быть выгодно, чтобы обучаться с самым маленьким разумным восприимчивым размером поля R и только выполните прогноз с последним R временные шаги входной последовательности.

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

Модель Define и функции градиентов модели

Создайте функциональный model, перечисленный в разделе Model Function в конце примера, который вычисляет выходные параметры модели глубокого обучения. Функциональный model берет входные данные, learnable параметры модели, гиперпараметры модели и флаг, который задает, должна ли модель возвратить выходные параметры для обучения или прогноза. Сетевые выходные параметры прогнозы для меток на каждом временном шаге входной последовательности.

Создайте функциональный modelGradients, перечисленный в разделе Model Gradients Function в конце примера, который берет мини-пакет входных данных, соответствующих целевых последовательностей и параметров сети, и возвращает градиенты потери относительно learnable параметров и соответствующей потери.

Задайте опции обучения

Задайте набор опций обучения, используемых в пользовательском учебном цикле.

  • Обучайтесь в течение 30 эпох с мини-пакетным размером 1.

  • Запустите с начальной буквы, изучают уровень 0,001

  • Умножьте изучить уровень к 0,1 каждым 12 эпохам.

  • Отсеките градиенты с помощью L2 норма с порогом 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 первым вычислением среднего значения μ и отклонение σ2 для каждого наблюдения по каждому входному каналу. Затем это вычисляет нормированные активации как

Xˆ=X-μσ2+ϵ.

По сравнению с пакетной нормализацией, средним значением и отклонением отличается для каждого наблюдения в мини-пакете. Используйте нормализацию, такую как нормализация экземпляра, между сверточными слоями и нелинейностью, чтобы ускорить обучение сверточных нейронных сетей и улучшить сходимость.

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 так, чтобы 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] Бай, Shaojie, Х. Зико Кольтер и Владлен Кольтун. "Эмпирическая оценка типовых сверточных и текущих сетей для моделирования последовательности". arXiv предварительно распечатывают arXiv:1803.01271 (2018).

[2] Ван ден Урд, Aäron, и др. "WaveNet: порождающая модель для необработанного аудио". SSW 125 (2016).

[3] Томпсон, Джонатан, и др. "Эффективная объектная локализация с помощью сверточных сетей". Продолжения Конференции по IEEE по Компьютерному зрению и Распознаванию образов. 2015.

Смотрите также

| | | | | | | |

Похожие темы