Классифицируйте видео Используя глубокое обучение

В этом примере показано, как создать сеть для видео классификации путем объединения предварительно обученной модели классификации изображений и сети LSTM.

Создать нейронную сеть для глубокого обучения для видео классификации:

  1. Преобразуйте видео в последовательности характеристических векторов с помощью предварительно обученной сверточной нейронной сети, такие как GoogLeNet, чтобы извлечь функции из каждой системы координат.

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

  3. Соберите сеть, которая классифицирует видео непосредственно путем объединения слоев от обеих сетей.

Следующая схема иллюстрирует сетевую архитектуру.

  • Чтобы ввести последовательности изображений к сети, используйте входной слой последовательности.

  • Чтобы использовать сверточные слои, чтобы извлечь функции, то есть, применить сверточные операции к каждой системе координат видео независимо, используют слой сворачивания последовательности, сопровождаемый сверточными слоями.

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

  • Чтобы классифицировать последовательности итогового вектора, включайте слои LSTM, сопровождаемые выходными слоями.

Загрузите предварительно обученную сверточную сеть

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

Загрузите предварительно обученную модель GoogLeNet с помощью googlenet функция. Эта функция требует Модели Deep Learning Toolbox™ для пакета Сетевой поддержки GoogLeNet. Если этот пакет поддержки не установлен, то функция обеспечивает ссылку на загрузку.

netCNN = googlenet;

Загрузка данных

Загрузите набор данных HMBD51 с HMDB: большая человеческая база данных движения и извлечение файл RAR в папку под названием "hmdb51_org". Набор данных содержит приблизительно 2 Гбайт видеоданных для 7 000 клипов более чем 51 класс, такие как "drink"запущенный, и "shake_hands".

После извлечения файлов RAR используйте функцию поддержки hmdb51Files получить имена файлов и метки видео.

dataFolder = "hmdb51_org";
[files,labels] = hmdb51Files(dataFolder);

Считайте первое видео с помощью readVideo функция помощника, заданная в конце этого примера и представления размер видео. Видео является H W C S массивом, где H, W, C, и S являются высотой, шириной, количеством каналов и количеством кадров видео, соответственно.

idx = 1;
filename = files(idx);
video = readVideo(filename);
size(video)
ans = 1×4

   240   320     3   409

Просмотрите соответствующую метку.

labels(idx)
ans = categorical
     brush_hair 

Чтобы смотреть видео, используйте implay функция (требует Image Processing Toolbox™). Эта функция ожидает данные в области значений [0,1], таким образом, необходимо будет сначала разделить данные на 255. В качестве альтернативы можно циклично выполниться по отдельным системам координат и использовать imshow функция.

numFrames = size(video,4);
figure
for i = 1:numFrames
    frame = video(:,:,:,i);
    imshow(frame/255);
    drawnow
end

Преобразуйте системы координат в характеристические векторы

Используйте сверточную сеть в качестве экстрактора функции путем получения активаций при введении видеокадров к сети. Преобразуйте видео в последовательности характеристических векторов, где характеристическими векторами является выход activations функция на последнем слое объединения сети GoogLeNet ("pool5-7x7_s1").

Эта схема иллюстрирует поток данных через сеть.

Чтобы считать видеоданные и изменить размер их, чтобы совпадать с входным размером сети GoogLeNet, используйте readVideo и centerCrop помощник функционирует, заданный в конце этого примера. Этот шаг может занять много времени, чтобы запуститься. После преобразования видео к последовательностям сохраните последовательности в MAT-файле в tempdir папка. Если файл MAT уже существует, то загрузите последовательности из MAT-файла, не повторно преобразовывая их.

inputSize = netCNN.Layers(1).InputSize(1:2);
layerName = "pool5-7x7_s1";

tempFile = fullfile(tempdir,"hmdb51_org.mat");

if exist(tempFile,'file')
    load(tempFile,"sequences")
else
    numFiles = numel(files);
    sequences = cell(numFiles,1);
    
    for i = 1:numFiles
        fprintf("Reading file %d of %d...\n", i, numFiles)
        
        video = readVideo(files(i));
        video = centerCrop(video,inputSize);
        
        sequences{i,1} = activations(netCNN,video,layerName,'OutputAs','columns');
    end
    
    save(tempFile,"sequences","-v7.3");
end

Просмотрите размеры первых нескольких последовательностей. Каждая последовательность является D-by-S массивом, где D является количеством функций (выходной размер слоя объединения), и S является количеством кадров видео.

sequences(1:10)
ans = 10×1 cell array
    {1024×409 single}
    {1024×395 single}
    {1024×323 single}
    {1024×246 single}
    {1024×159 single}
    {1024×137 single}
    {1024×359 single}
    {1024×191 single}
    {1024×439 single}
    {1024×528 single}

Подготовьте обучающие данные

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

Создайте разделы обучения и валидации

Разделите данные. Присвойте 90% данных к учебному разделу и 10% к разделу валидации.

numObservations = numel(sequences);
idx = randperm(numObservations);
N = floor(0.9 * numObservations);

idxTrain = idx(1:N);
sequencesTrain = sequences(idxTrain);
labelsTrain = labels(idxTrain);

idxValidation = idx(N+1:end);
sequencesValidation = sequences(idxValidation);
labelsValidation = labels(idxValidation);

Удалите длинные последовательности

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

Получите длины последовательности обучающих данных и визуализируйте их в гистограмме обучающих данных.

numObservationsTrain = numel(sequencesTrain);
sequenceLengths = zeros(1,numObservationsTrain);

for i = 1:numObservationsTrain
    sequence = sequencesTrain{i};
    sequenceLengths(i) = size(sequence,2);
end

figure
histogram(sequenceLengths)
title("Sequence Lengths")
xlabel("Sequence Length")
ylabel("Frequency")

Только несколько последовательностей имеют больше чем 400 временных шагов. Чтобы улучшить точность классификации, удалите обучающие последовательности, которые имеют больше чем 400 временных шагов наряду с их соответствующими метками.

maxLength = 400;
idx = sequenceLengths > maxLength;
sequencesTrain(idx) = [];
labelsTrain(idx) = [];

Создайте сеть LSTM

Затем создайте сеть LSTM, которая может классифицировать последовательности характеристических векторов, представляющих видео.

Задайте архитектуру сети LSTM. Задайте следующие слоя сети.

  • Последовательность ввела слой с входным размером, соответствующим размерности признаков характеристических векторов

  • Слой BiLSTM с 2 000 скрытых модулей со слоем уволенного впоследствии. Выводить только одну метку для каждой последовательности путем установки 'OutputMode' опция слоя BiLSTM к 'last'

  • Полносвязный слой с выходным размером, соответствующим количеству классов, softmax слоя и слоя классификации.

numFeatures = size(sequencesTrain{1},1);
numClasses = numel(categories(labelsTrain));

layers = [
    sequenceInputLayer(numFeatures,'Name','sequence')
    bilstmLayer(2000,'OutputMode','last','Name','bilstm')
    dropoutLayer(0.5,'Name','drop')
    fullyConnectedLayer(numClasses,'Name','fc')
    softmaxLayer('Name','softmax')
    classificationLayer('Name','classification')];

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

Задайте опции обучения с помощью trainingOptions функция.

  • Установите мини-пакетный размер 16, начальная скорость обучения 0,0001 и порог градиента 2 (чтобы препятствовать тому, чтобы градиенты взорвались).

  • Переставьте данные каждая эпоха.

  • Проверьте сеть однажды в эпоху.

  • Отобразите прогресс обучения в графике и подавите многословный выход.

miniBatchSize = 16;
numObservations = numel(sequencesTrain);
numIterationsPerEpoch = floor(numObservations / miniBatchSize);

options = trainingOptions('adam', ...
    'MiniBatchSize',miniBatchSize, ...
    'InitialLearnRate',1e-4, ...
    'GradientThreshold',2, ...
    'Shuffle','every-epoch', ...
    'ValidationData',{sequencesValidation,labelsValidation}, ...
    'ValidationFrequency',numIterationsPerEpoch, ...
    'Plots','training-progress', ...
    'Verbose',false);

Обучите сеть LSTM

Обучите сеть с помощью trainNetwork функция. Это может занять много времени, чтобы запуститься.

[netLSTM,info] = trainNetwork(sequencesTrain,labelsTrain,layers,options);

Вычислите точность классификации сети на наборе валидации. Используйте тот же мини-пакетный размер что касается опций обучения.

YPred = classify(netLSTM,sequencesValidation,'MiniBatchSize',miniBatchSize);
YValidation = labelsValidation;
accuracy = mean(YPred == YValidation)
accuracy = 0.6647

Соберите видео сеть классификации

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

Следующая схема иллюстрирует сетевую архитектуру.

  • Чтобы ввести последовательности изображений к сети, используйте входной слой последовательности.

  • Чтобы использовать сверточные слои, чтобы извлечь функции, то есть, применить сверточные операции к каждой системе координат видео независимо, используют слой сворачивания последовательности, сопровождаемый сверточными слоями.

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

  • Чтобы классифицировать последовательности итогового вектора, включайте слои LSTM, сопровождаемые выходными слоями.

Добавьте сверточные слои

Во-первых, создайте график слоев сети GoogLeNet.

cnnLayers = layerGraph(netCNN);

Удалите входной слой ("data") и слои после слоя объединения, используемого для активаций ("pool5-drop_7x7_s1", "loss3-classifier", "prob", и "output").

layerNames = ["data" "pool5-drop_7x7_s1" "loss3-classifier" "prob" "output"];
cnnLayers = removeLayers(cnnLayers,layerNames);

Добавьте входной слой последовательности

Создайте входной слой последовательности, который принимает последовательности изображений, содержащие изображения того же входного размера как сеть GoogLeNet. Чтобы нормировать изображения с помощью того же среднего изображения в качестве сети GoogLeNet, установите 'Normalization' опция последовательности ввела слой к 'zerocenter' и 'Mean' опция к среднему изображению входного слоя GoogLeNet.

inputSize = netCNN.Layers(1).InputSize(1:2);
averageImage = netCNN.Layers(1).Mean;

inputLayer = sequenceInputLayer([inputSize 3], ...
    'Normalization','zerocenter', ...
    'Mean',averageImage, ...
    'Name','input');

Добавьте входной слой последовательности в график слоев. Чтобы применить сверточные слои к изображениям последовательностей независимо, удалите структуру последовательности последовательностей изображений включением слоя сворачивания последовательности между входным слоем последовательности и сверточными слоями. Соедините выход слоя сворачивания последовательности к входу первого сверточного слоя ("conv1-7x7_s2").

layers = [
    inputLayer
    sequenceFoldingLayer('Name','fold')];

lgraph = addLayers(cnnLayers,layers);
lgraph = connectLayers(lgraph,"fold/out","conv1-7x7_s2");

Добавьте слои LSTM

Добавьте слои LSTM в график слоев путем удаления входного слоя последовательности сети LSTM. Чтобы восстановить структуру последовательности, удаленную слоем сворачивания последовательности, включайте слой разворачивания последовательности после слоев свертки. Слои LSTM ожидают последовательности векторов. Чтобы изменить выход слоя разворачивания последовательности к векторным последовательностям, включайте сглаживать слой после слоя разворачивания последовательности.

Возьмите слои из сети LSTM и удалите входной слой последовательности.

lstmLayers = netLSTM.Layers;
lstmLayers(1) = [];

Добавьте слой разворачивания последовательности, сглаживать слой и слои LSTM к графику слоев. Соедините последний сверточный слой ("pool5-7x7_s1") к входу слоя разворачивания последовательности ("unfold/in").

layers = [
    sequenceUnfoldingLayer('Name','unfold')
    flattenLayer('Name','flatten')
    lstmLayers];

lgraph = addLayers(lgraph,layers);
lgraph = connectLayers(lgraph,"pool5-7x7_s1","unfold/in");

Чтобы позволить разворачивающемуся слою восстановить структуру последовательности, соедините "miniBatchSize" выход слоя сворачивания последовательности к соответствующему входу слоя разворачивания последовательности.

lgraph = connectLayers(lgraph,"fold/miniBatchSize","unfold/miniBatchSize");

Соберите сеть

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

analyzeNetwork(lgraph)

Соберите сеть так, чтобы это было готово к предсказанию с помощью assembleNetwork функция.

net = assembleNetwork(lgraph)
net = 
  DAGNetwork with properties:

         Layers: [148×1 nnet.cnn.layer.Layer]
    Connections: [175×2 table]

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

Читайте и центральная обрезка видео "pushup.mp4" использование тех же шагов как прежде.

filename = "pushup.mp4";
video = readVideo(filename);

Чтобы смотреть видео, используйте implay функция (требует Image Processing Toolbox). Эта функция ожидает данные в области значений [0,1], таким образом, необходимо будет сначала разделить данные на 255. В качестве альтернативы можно циклично выполниться по отдельным системам координат и использовать imshow функция.

numFrames = size(video,4);
figure
for i = 1:numFrames
    frame = video(:,:,:,i);
    imshow(frame/255);
    drawnow
end

Классифицируйте видео с помощью собранной сети. classify функция ожидает массив ячеек, содержащий входные видео, таким образом, необходимо будет ввести массив ячеек 1 на 1, содержащий видео.

video = centerCrop(video,inputSize);
YPred = classify(net,{video})
YPred = categorical
     pushup 

Функции помощника

readVideo функционируйте читает видео в filename и возвращает H- W- C-- S массив, где HWC, и S высота, ширина, количество каналов и количество кадров видео, соответственно.

function video = readVideo(filename)

vr = VideoReader(filename);
H = vr.Height;
W = vr.Width;
C = 3;

% Preallocate video array
numFrames = floor(vr.Duration * vr.FrameRate);
video = zeros(H,W,C,numFrames);

% Read frames
i = 0;
while hasFrame(vr)
    i = i + 1;
    video(:,:,:,i) = readFrame(vr);
end

% Remove unallocated frames
if size(video,4) > i
    video(:,:,:,i+1:end) = [];
end

end

centerCrop функциональные обрезки самые длинные ребра видео и изменяют размер его, имеют размер inputSize.

function videoResized = centerCrop(video,inputSize)

sz = size(video);

if sz(1) < sz(2)
    % Video is landscape
    idx = floor((sz(2) - sz(1))/2);
    video(:,1:(idx-1),:,:) = [];
    video(:,(sz(1)+1):end,:,:) = [];
    
elseif sz(2) < sz(1)
    % Video is portrait
    idx = floor((sz(1) - sz(2))/2);
    video(1:(idx-1),:,:,:) = [];
    video((sz(2)+1):end,:,:,:) = [];
end

videoResized = imresize(video,inputSize(1:2));

end

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

| | | | | |

Похожие темы