В этом примере показано, как создать сеть для видео классификации путем объединения предварительно обученной модели классификации изображений и сети классификации последовательностей.
Можно выполнить видео классификацию, не используя пользовательский учебный цикл при помощи trainNetwork
функция. Для примера смотрите, Классифицируют Видео Используя Глубокое обучение. Однако, Если trainingOptions
не предоставляет возможности, в которых вы нуждаетесь (например, пользовательское расписание скорости обучения), затем можно задать собственный учебный цикл как показано в этом примере.
Создать нейронную сеть для глубокого обучения для видео классификации:
Преобразуйте видео в последовательности характеристических векторов с помощью предварительно обученной сверточной нейронной сети, такие как GoogLeNet, чтобы извлечь функции из каждой системы координат.
Обучите сеть классификации последовательностей на последовательностях, чтобы предсказать видео метки.
Соберите сеть, которая классифицирует видео непосредственно путем объединения слоев от обеих сетей.
Следующая схема иллюстрирует сетевую архитектуру:
Чтобы ввести последовательности изображений к сети, используйте входной слой последовательности.
Чтобы извлечь функции из последовательностей изображений, используйте сверточные слои от предварительно обученной сети GoogLeNet.
Чтобы классифицировать последовательности итогового вектора, включайте слои классификации последовательностей.
Когда обучение этот тип сети с trainNetwork
функция (не сделанный в этом примере), необходимо использовать последовательность складные и разворачивающиеся слои, чтобы обработать видеокадры независимо. Когда вы обучаете этот тип сети с dlnetwork
возразите и пользовательский учебный цикл, (как в этом примере), последовательность, складные и разворачивающиеся слои не требуются, потому что сеть использует информацию о размерности, данную dlarray
метки размерности.
Чтобы преобразовать системы координат видео к характеристическим векторам, используйте активации предварительно обученной сети.
Загрузите предварительно обученную модель GoogLeNet с помощью googlenet
функция. Эта функция требует Модели Deep Learning Toolbox™ для пакета Сетевой поддержки GoogLeNet. Если этот пакет поддержки не установлен, то функция обеспечивает ссылку на загрузку.
netCNN = googlenet;
Загрузите набор данных HMBD51 с HMDB: большая человеческая база данных движения и извлечение файл RAR в папку под названием "hmdb51_org"
. Набор данных содержит приблизительно 2 Гбайт видеоданных для 7 000 клипов более чем 51 класс, такие как "drink"
запущенный
, и "shake_hands"
.
После извлечения файла RAR убедитесь что папка hmdb51_org
содержит подпапки, названные в честь движений тела. Если это содержит файлы RAR, необходимо извлечь их также. Используйте функцию поддержки hmdb51Files
получить имена файлов и метки видео. Чтобы ускорить обучение за счет точности, задайте часть в области значений [0 1] к только для чтения случайное подмножество файлов от базы данных. Если fraction
входной параметр не задан, функциональный hmdb51Files
читает полный набор данных, не изменяя порядок файлов.
dataFolder = "hmdb51_org";
fraction = 1;
[files,labels] = hmdb51Files(dataFolder,fraction);
Считайте первое видео с помощью readVideo
функция помощника, заданная в конце этого примера и представления размер видео. Видео является H W C T массивом, где H, W, C, и T являются высотой, шириной, количеством каналов и количеством систем координат видео, соответственно.
idx = 1; filename = files(idx); video = readVideo(filename); size(video)
ans = 1×4
240 352 3 115
Просмотрите соответствующую метку.
labels(idx)
ans = categorical
shoot_ball
Чтобы смотреть видео, цикл по индивидууму структурирует и использовать image
функция. В качестве альтернативы можно использовать implay
функция (требует Image Processing Toolbox).
numFrames = size(video,4); figure for i = 1:numFrames frame = video(:,:,:,i); image(frame); xticklabels([]); yticklabels([]); drawnow end
Используйте сверточную сеть в качестве экстрактора функции: введите видеокадры к сети и извлеките активации. Преобразуйте видео в последовательности характеристических векторов, где характеристическими векторами является выход activations
функция на последнем слое объединения сети GoogLeNet ("pool5-7x7_s1"
).
Эта схема иллюстрирует поток данных через сеть.
Считайте видеоданные с помощью readVideo
функция, заданная в конце этого примера, и, изменяет размер его, чтобы совпадать с входным размером сети GoogLeNet. Обратите внимание на то, что этот шаг может занять много времени, чтобы запуститься. После преобразования видео к последовательностям сохраните последовательности и соответствующие метки в файле MAT в tempdir
папка. Если файл MAT уже существует, то загрузите последовательности и метки из файла MAT непосредственно. В случае, если файл MAT уже существует, но вы хотите перезаписать его, установить переменную overwriteSequences
к true
.
inputSize = netCNN.Layers(1).InputSize(1:2); layerName = "pool5-7x7_s1"; tempFile = fullfile(tempdir,"hmdb51_org.mat"); overwriteSequences = false; if exist(tempFile,'file') && ~overwriteSequences load(tempFile) 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 = imresize(video,inputSize); sequences{i,1} = activations(netCNN,video,layerName,'OutputAs','columns'); end % Save the sequences and the labels associated with them. save(tempFile,"sequences","labels","-v7.3"); end
Просмотрите размеры первых нескольких последовательностей. Каждая последовательность является D-by-T массивом, где D является количеством функций (выходной размер слоя объединения), и T является количеством систем координат видео.
sequences(1:10)
ans=10×1 cell array
{1024×115 single}
{1024×227 single}
{1024×180 single}
{1024×40 single}
{1024×60 single}
{1024×156 single}
{1024×83 single}
{1024×42 single}
{1024×82 single}
{1024×110 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) = [];
Создайте arrayDatastore
объект для последовательностей и меток, и затем комбинирует их в один datastore.
dsXTrain = arrayDatastore(sequencesTrain,'OutputType','same'); dsYTrain = arrayDatastore(labelsTrain,'OutputType','cell'); dsTrain = combine(dsXTrain,dsYTrain);
Определите классы в обучающих данных.
classes = categories(labelsTrain);
Затем создайте сеть классификации последовательностей, которая может классифицировать последовательности характеристических векторов, представляющих видео.
Задайте архитектуру сети классификации последовательностей. Задайте следующие слоя сети:
Последовательность ввела слой с входным размером, соответствующим размерности признаков характеристических векторов.
Слой BiLSTM с 2 000 скрытых модулей со слоем уволенного впоследствии. Чтобы вывести только одну метку для каждой последовательности, установите 'OutputMode'
опция слоя BiLSTM к 'last'.
Слой уволенного с вероятностью 0,5.
Полносвязный слой с выходным размером, соответствующим количеству классов и 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') ];
Преобразуйте слои в layerGraph
объект.
lgraph = layerGraph(layers);
Создайте dlnetwork
объект от графика слоев.
dlnet = dlnetwork(lgraph);
Обучайтесь в течение 15 эпох и задайте мини-пакетный размер 16.
numEpochs = 15; miniBatchSize = 16;
Задайте опции для оптимизации Адама. Задайте начальную скорость обучения 1e-4
с затуханием 0,001, затуханием градиента 0,9 и затуханием градиента в квадрате 0,999.
initialLearnRate = 1e-4; decay = 0.001; gradDecay = 0.9; sqGradDecay = 0.999;
Визуализируйте процесс обучения в графике.
plots = "training-progress";
Создайте minibatchqueue
возразите, что процессы и управляют мини-пакетами последовательностей во время обучения. Для каждого мини-пакета:
Используйте пользовательский мини-пакет, предварительно обрабатывающий функциональный preprocessLabeledSequences
(заданный в конце этого примера), чтобы преобразовать метки в фиктивные переменные.
Формат векторные данные о последовательности с размерностью маркирует 'CTB'
(канал, время, пакет). По умолчанию, minibatchqueue
объект преобразует данные в dlarray
объекты с базовым типом single
. Не добавляйте формат в метки класса.
Обучайтесь на графическом процессоре, если вы доступны. По умолчанию, minibatchqueue
объект преобразует каждый выход в gpuArray
возразите, доступен ли графический процессор. Используя графический процессор требует Parallel Computing Toolbox™ и поддерживаемого устройства графического процессора. Для получения информации о поддерживаемых устройствах смотрите Поддержку графического процессора Релизом (Parallel Computing Toolbox).
mbq = minibatchqueue(dsTrain,... 'MiniBatchSize',miniBatchSize,... 'MiniBatchFcn', @preprocessLabeledSequences,... 'MiniBatchFormat',{'CTB',''});
Инициализируйте график процесса обучения.
if plots == "training-progress" figure lineLossTrain = animatedline('Color',[0.85 0.325 0.098]); ylim([0 inf]) xlabel("Iteration") ylabel("Loss") grid on end
Инициализируйте средний градиент и средние параметры градиента в квадрате для решателя Адама.
averageGrad = []; averageSqGrad = [];
Обучите модель с помощью пользовательского учебного цикла. В течение каждой эпохи переставьте данные и цикл по мини-пакетам данных. Для каждого мини-пакета:
Оцените градиенты модели, состояние и потерю с помощью dlfeval
и modelGradients
функционируйте и обновите сетевое состояние.
Определите скорость обучения для основанного на времени расписания скорости обучения затухания: для каждой итерации решатель использует скорость обучения, данную , где t является номером итерации, начальная скорость обучения, и k является затуханием.
Обновите сетевые параметры с помощью adamupdate
функция.
Отобразите прогресс обучения.
Обратите внимание на то, что обучение может занять много времени, чтобы запуститься.
iteration = 0; start = tic; % Loop over epochs. for epoch = 1:numEpochs % Shuffle data. shuffle(mbq); % Loop over mini-batches. while hasdata(mbq) iteration = iteration + 1; % Read mini-batch of data. [dlX, dlY] = next(mbq); % Evaluate the model gradients, state, and loss using dlfeval and the % modelGradients function. [gradients,state,loss] = dlfeval(@modelGradients,dlnet,dlX,dlY); % Determine learning rate for time-based decay learning rate schedule. learnRate = initialLearnRate/(1 + decay*iteration); % Update the network parameters using the Adam optimizer. [dlnet,averageGrad,averageSqGrad] = adamupdate(dlnet,gradients,averageGrad,averageSqGrad, ... iteration,learnRate,gradDecay,sqGradDecay); % Display the training progress. if plots == "training-progress" D = duration(0,0,toc(start),'Format','hh:mm:ss'); addpoints(lineLossTrain,iteration,double(gather(extractdata(loss)))) title("Epoch: " + epoch + " of " + numEpochs + ", Elapsed: " + string(D)) drawnow end end end
Протестируйте точность классификации модели путем сравнения предсказаний на наборе валидации с истинными метками.
После того, как обучение завершено, создание предсказаний на новых данных не требует меток.
Создать minibatchqueue
объект для тестирования:
Создайте datastore массивов, содержащий только предикторы тестовых данных.
Задайте тот же мини-пакетный размер, используемый для обучения.
Предварительно обработайте предикторы с помощью preprocessUnlabeledSequences
функция помощника, перечисленная в конце примера.
Для одного выхода datastore задайте мини-пакетный формат 'CTB'
(канал, время, пакет).
dsXValidation = arrayDatastore(sequencesValidation,'OutputType','same'); mbqTest = minibatchqueue(dsXValidation, ... 'MiniBatchSize',miniBatchSize, ... 'MiniBatchFcn',@preprocessUnlabeledSequences, ... 'MiniBatchFormat','CTB');
Цикл по мини-пакетам и классифицирует изображения с помощью modelPredictions
функция помощника, перечисленная в конце примера.
predictions = modelPredictions(dlnet,mbqTest,classes);
Оцените точность классификации путем сравнения предсказанных меток с истинными метками валидации.
accuracy = mean(predictions == labelsValidation)
accuracy = 0.6721
Чтобы создать сеть, которая классифицирует видео непосредственно, соберите использование сети слои от обеих из созданных сетей. Используйте слои от сверточной сети, чтобы преобразовать видео в векторные последовательности и слои от сети классификации последовательностей, чтобы классифицировать векторные последовательности.
Следующая схема иллюстрирует сетевую архитектуру:
Чтобы ввести последовательности изображений к сети, используйте входной слой последовательности.
Чтобы использовать сверточные слои, чтобы извлечь функции, то есть, применить сверточные операции к каждой системе координат видео независимо, используют GoogLeNet сверточные слои.
Чтобы классифицировать последовательности итогового вектора, включайте слои классификации последовательностей.
Когда обучение этот тип сети с trainNetwork
функция (не сделанный в этом примере), необходимо использовать последовательность складные и разворачивающиеся слои, чтобы обработать видеокадры независимо. Когда обучение этот тип сети с dlnetwork
возразите и пользовательский учебный цикл, (как в этом примере), последовательность, складные и разворачивающиеся слои не требуются, потому что сеть использует информацию о размерности, данную dlarray
метки размерности.
Добавьте сверточные слои
Во-первых, создайте график слоев сети 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"
).
lgraph = addLayers(cnnLayers,inputLayer); lgraph = connectLayers(lgraph,"input","conv1-7x7_s2");
Добавьте слои классификации последовательностей
Добавьте ранее обученные слоя сети классификации последовательностей в график слоев и соедините их.
Возьмите слои из сети классификации последовательностей и удалите входной слой последовательности.
lstmLayers = dlnet.Layers; lstmLayers(1) = [];
Добавьте слои классификации последовательностей в график слоев. Соедините последний сверточный слой pool5-7x7_s1
к bilstm
слой.
lgraph = addLayers(lgraph,lstmLayers); lgraph = connectLayers(lgraph,"pool5-7x7_s1","bilstm");
Преобразуйте в dlnetwork
Чтобы смочь сделать предсказания, преобразуйте график слоев в dlnetwork
объект.
dlnetAssembled = dlnetwork(lgraph)
dlnetAssembled = dlnetwork with properties: Layers: [144×1 nnet.cnn.layer.Layer] Connections: [170×2 table] Learnables: [119×3 table] State: [2×3 table] InputNames: {'input'} OutputNames: {'softmax'} Initialized: 1
Разархивируйте файл pushup_mathworker.zip.
unzip("pushup_mathworker.zip")
Извлеченный pushup_mathworker
папка содержит видео выжимания в упоре. Создайте datastore файла для этой папки. Используйте пользовательскую функцию чтения, чтобы считать видео.
ds = fileDatastore("pushup_mathworker", ... 'ReadFcn',@readVideo);
Считайте первое видео из datastore. Чтобы смочь считать видео снова, сбросьте datastore.
video = read(ds); reset(ds);
Чтобы смотреть видео, цикл по индивидууму структурирует и использовать image
функция. В качестве альтернативы можно использовать implay
функция (требует Image Processing Toolbox).
numFrames = size(video,4); figure for i = 1:numFrames frame = video(:,:,:,i); image(frame); xticklabels([]); yticklabels([]); drawnow end
Чтобы предварительно обработать видео, чтобы ожидать входной размер сетью, используйте transform
функционируйте и примените imresize
функционируйте к каждому изображению в datastore.
dsXTest = transform(ds,@(x) imresize(x,inputSize));
Чтобы управлять и обработать непомеченные видео, создайте minibatchqueue:
Задайте мини-пакетный размер 1.
Предварительно обработайте видео с помощью preprocessUnlabeledVideos
функция помощника, перечисленная в конце примера.
Для одного выхода datastore задайте мини-пакетный формат 'SSCTB'
(пространственный, пространственный, канал, время, пакет).
mbqTest = minibatchqueue(dsXTest,... 'MiniBatchSize',1,... 'MiniBatchFcn', @preprocessUnlabeledVideos,... 'MiniBatchFormat',{'SSCTB'});
Классифицируйте видео с помощью modelPredictions
функция помощника, заданная в конце этого примера. Функция ожидает три входных параметров: dlnetwork
объект, minibatchqueue
объект и массив ячеек, содержащий сетевые классы.
[predictions] = modelPredictions(dlnetAssembled,mbqTest,classes)
predictions = categorical
pushup
readVideo
функционируйте читает видео в filename
и возвращает H W C-
- T массив, где H, W, C, и T являются высотой, шириной, количеством каналов и количеством систем координат видео, соответственно.
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,'uint8'); % 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
modelGradients
функционируйте берет в качестве входа dlnetwork
объект dlnet
и мини-пакет входных данных dlX
с соответствием маркирует Y
, и возвращает градиенты потери относительно настраиваемых параметров в dlnet
, сетевое состояние и потеря. Чтобы вычислить градиенты автоматически, используйте dlgradient
функция.
function [gradients,state,loss] = modelGradients(dlnet,dlX,Y) [dlYPred,state] = forward(dlnet,dlX); loss = crossentropy(dlYPred,Y); gradients = dlgradient(loss,dlnet.Learnables); end
modelPredictions
функционируйте берет в качестве входа dlnetwork
объект dlnet
, minibatchqueue
объект входных данных mbq
, и сетевые классы, и вычисляют предсказания модели путем итерации по всем данным в мини-пакетной очереди. Функция использует onehotdecode
функционируйте, чтобы найти предсказанный класс с самым высоким счетом. Функция возвращает предсказанные метки.
function [predictions] = modelPredictions(dlnet,mbq,classes) predictions = []; while hasdata(mbq) % Extract a mini-batch from the minibatchqueue and pass it to the % network for predictions [dlXTest] = next(mbq); dlYPred = predict(dlnet,dlXTest); % To obtain categorical labels, one-hot decode the predictions YPred = onehotdecode(dlYPred,classes,1)'; predictions = [predictions; YPred]; end end
preprocessLabeledSequences
функция предварительно обрабатывает данные о последовательности с помощью следующих шагов:
Используйте padsequences
функционируйте, чтобы заполнить последовательности в измерении времени и конкатенировать их в пакетной размерности.
Извлеките данные о метке из массива входящей ячейки и конкатенируйте в категориальный массив.
Одногорячий кодируют категориальные метки в числовые массивы.
Транспонируйте массив одногорячих закодированных меток, чтобы совпадать с формой сетевого выхода.
function [X, Y] = preprocessLabeledSequences(XCell,YCell) % Pad the sequences with zeros in the second dimension (time) and concatenate along the third % dimension (batch) X = padsequences(XCell,2); % Extract label data from cell and concatenate Y = cat(1,YCell{1:end}); % One-hot encode labels Y = onehotencode(Y,2); % Transpose the encoded labels to match the network output Y = Y'; end
preprocessUnlabeledSequences
функция предварительно обрабатывает данные о последовательности с помощью padsequences
функция. Эта функция заполняет последовательности нулями в измерении времени и конкатенирует результат в пакетной размерности.
function [X] = preprocessUnlabeledSequences(XCell) % Pad the sequences with zeros in the second dimension (time) and concatenate along the third % dimension (batch) X = padsequences(XCell,2); end
preprocessUnlabeledVideos
функция предварительно обрабатывает непомеченные видеоданные с помощью padsequences
функция. Эта функция заполняет видео нулем в измерении времени и конкатенирует результат в пакетной размерности.
function [X] = preprocessUnlabeledVideos(XCell) % Pad the sequences with zeros in the fourth dimension (time) and % concatenate along the fifth dimension (batch) X = padsequences(XCell,4); end
dlarray
| dlfeval
| dlgradient
| lstmLayer
| sequenceInputLayer