学生休息室

分享学生如何在日常项目中使用MATLAB和Simulink的技术和现实例子#studentsuccess万博1manbetx

深度冲击:野生动物保护的深度估计- MATLAB基准代码

如我们在上一篇博文,这里我们用MATLAB的基准代码深度影响:野生动物保护挑战的深度估计

来自MathWorks工程开发组的James Drummond尝试了这个挑战,并准备了这个启动代码。在这里,他将讲述他用来解决这个问题的光流+ CNN方法。他还将提供一些关于如何改进这段代码并提高分数的提示和技巧。

签入下面的链接来注册挑战,获得免费的MATLAB许可证,并访问论坛获得支持。万博1manbetx

的数据

这个挑战的目标是估计距离动物在单目跟踪相机镜头。这些视频有夜视彩色和灰度,包含一系列不同的动物作为主题。

该数据集包含超过3500个视频(近200GB的数据),分为训练集和测试集。它们托管在一个公共S3桶中。使用说明可以在竞赛中找到Data选项卡

每个视频都有一个独特的4个字母的名称,格式为。mp4或。avi。标签被提供("train_labels.csv“&”test_labels.csv)作为动物在每个视频中特定时间点到重心的距离。为了促进更快的数据处理,还提供了每个文件的下采样版本。

此外,你还可以“train_metadata.csv”而且“test_metadata.csv”文件。这些文件包括文件名、关于摄像机位置的详细信息以及模型生成的动物在每个时间戳时的边界框估计。

有关数据集的更多详细信息,请参阅问题描述在竞赛网页上。

MATLAB入门

我在MATLAB中提供了一个基本的示例代码,作为开发的起点。在这段代码中,我使用光流对视频帧进行预处理,并使用预先训练的CNN将这些图像输入基本回归模型。然后,我将使用这个模型对测试数据进行深度估计,并以挑战所需的格式保存一个CSV文件。您也可以从以下网站下载此MATLAB启动程序代码这个GitHub回购.这可以作为基本代码,您可以开始分析数据,并致力于开发更高效、优化和准确的模型。此外,我还为下一步的研究提供了一些提示和建议。

所以,让我们开始这个挑战吧!

加载标签和元数据

第一步是加载关于将要使用的数据集的信息。来从文件中访问变量值train_metadata.csv而且train_labels.csv,我将它们导入到MATLAB中使用再保险adtable功能:

标签=可读('train_labels.csv');(标签)

元数据=可读('train_metadata.csv');(元数据)

访问和处理视频文件

MATLAB中的数据存储是处理和表示数据集合的一种方便方法,这些数据集合太大,一次无法装入内存。它是一个读取单个文件或文件或数据集合的对象。数据存储作为具有相同结构和格式的数据的存储库。要了解更多关于不同数据存储的信息,请查看下面的文档:

  1. 从数据存储开始
  2. “应用程序的文件格式”选择“数据存储”
  3. 用于深度学习的数据存储

在本博客中,我将使用imageDatastore从S3桶中加载视频。方法处理每个视频readVideo辅助函数在下一节中概述。在继续下一节之前,我将数据存储保存在tempdir或当前文件夹中的一个mat文件中。如果MAT文件已经存在,则从MAT文件加载数据存储,而无需重新评估它们。

在这里,我使用向下采样的视频,以节省带宽和处理时间。每个视频减少到一帧每秒,这已经足够满足我的需求。要访问完整的视频,您需要替换S3桶上文件的URL。

Tempimds = fullfile(tempdir,"imds.mat");if exist(tempimds,'file') load(tempimds,'imds') else imds = imageDatastore('s3://drivendata-competition-depth- estimate -public/ train_videos_downsamples /',…“ReadFcn”,@(文件名)readVideo(文件名、元数据“TrainingData”),…FileExtensions,{‘mp4’,‘.avi});保存(tempimds, imd);end files = imds.Files;

提示:(可选)为了减少处理时间,你可以选择使用子集imageDatastore的初始调查:

rng (0);%种子随机数生成器的可重复性idx = randperm(numel(files));Imds = Imds .子集(idx(1:100));%用于测试的随机子集

提取视频帧和光流

有了imageDatastore之后,我通过定义一个自定义读函数来提取视频帧readVideo(此文件末尾的代码),它首先使用VideoReader对象。由于下采样的视频每秒只有一帧,所以可以简单地对视频进行索引,以提取必要的帧。

光流

光流是图像中物体的视速度分布。通过估计视频帧之间的光流,可以测量视频中物体的速度。

对于每个标记帧,我计算光流与前一帧/秒相比。如果我们假设动物在一个基本静止的背景下运动,光流会突出它们所处的位置,并为它们的运动提供一些背景。为了提高信噪比,使用所提供的边界盒估计为感兴趣的区域生成二进制掩码。这是用来代替简单地裁剪图像以保留任何空间上下文。

有关使用的技术的更多信息,可在以下链接找到:

为了便于以后的处理,每张图像都用它所来自的视频和时间戳来命名。下面是视频aany.mp4的0帧输出示例:

这清楚地显示了猴子的位置和大小相对于图像的其他部分,但面具避免了背景噪声。

为了生成完整的数据集,我使用下面的命令读取所有的视频。在这里,我使用并行计算工具箱来加快处理速度,因为每个视频都可以独立读取。

洛桑国际管理发展学院。再保险adall("UseParallel", true);

神经网络的设计

在本例中,我将使用预训练的网络ResNet-18执行迁移学习。然而,在您进入实际学习之前,需要对网络进行调整以满足我们的需求。特别是,需要替换输入和输出层。ResNet-18接受224x224x3的输入图像,并输出具有1000个类别的图像分类。然而,我将输入480×640图像并输出单个回归距离估计。

在MATLAB中,这些变化可以通过两种方式实现,要么编程,要么图形化地使用深度网络设计器应用

这两种方法都从安装ResNet-18网络的深度学习工具箱模型扩展浏览器

方法1:使用深度网络设计器

该应用程序可以在Apps ribbon中找到,也可以运行以下命令:

deepNetworkDesigner

在启动页面导入预训练的ResNet-18网络:

在输入端,我替换了224x224x3imageInputLayer用我自己的480×640 imageInputLayer。为了连接到网络的其余部分,图像将需要使用resize3dLayer图层的OutputSize为480x640x3。

接下来在输出端,我删除最后几个图层,用我自己的图层替换它们。特别地,我用final替换了fulllyconnectedlayer, softmaxLayer和classificationLayerfullyConnectedLayer只给出一个输出值—距离估计值—将输入到输出中regressionLayer

在进行这些更改之后,您可以将此网络导出到您的工作空间,以便在培训中使用。网络被导出为一个名为“lgraph_1”的LayerGraph。

方法2:以编程方式创建网络

或者,您可以以编程的方式应用这些更改。安装ResNet-18插件后,可以导入训练过的网络。

lgraph_1 = layerGraph(resnet18);

在输入端,我替换了224x224x3imageInputLayer-命名为“数据”-与我自己的480×640 imageInputLayer。为了连接到网络的其他部分,我使用resize3dLayer图层的OutputSize为480x640x3。

inputLayers = [imageInputLayer([480 640],'Name','imageinput'),…resize3dLayer('OutputSize',[480 640 3],'Name','resize3D-output-size')]

lgraph_1 = replaceLayer(lgraph_1,'data',inputLayers);

接下来在输出端,我删除最后几个图层,用我自己的图层替换它们。特别是,fullyConnectedLayer ' fc1000 ', softmaxLayer ' prob '和classificationLayer ' classificationlayer_prediction '与最终fullyConnectedLayer只给出一个输出值—距离估计值—将输入到输出中regressionLayer

lgraph_1 = removeLayers(lgraph_1,{'fc1000','prob',' classificationlayer_prediction '}) outputLayers = [fullyConnectedLayer(1,'Name','fc'),regressionLayer('Name','regressionoutput')]

现在我将输出图层添加到图层图的末尾。然后我将“pool5”和“fc”与图层图连接起来。

lgraph_1 = addLayers(lgraph_1,outputLayers);lgraph_1 = connectLayers(lgraph_1,'pool5','fc');

更多关于图层图和上面使用的对象函数的信息可以在这里找到:layerGraph

分析网络

然后可以运行网络分析仪来确认我们的新图层是否正确。结果应该显示480×640输入被缩放到480x640x3,通过预先训练的网络运行,然后输出为单个回归值。

analyzeNetwork (lgraph_1)

准备培训数据

现在我已经完成了我们的预处理,设计好了网络,接下来就是准备训练的数据。

首先,我创建我们的数据集。这包含一个CombinedDatastore,由一个imageDatastore对于输入图像和anarrayDatastore为了他们的标签。的generateLabelDS帮助函数必须确保图像和标签正确匹配。从combinedDatastore读取,'fullDataset,返回图像和相应的标签。

imageDS = imageDatastore('TrainingData');标签= generateLabelDS(图像,标签);fullDataset = combine(imageDS, labels);

在这里,我将数据划分为训练和验证分区,为训练准备数据。我将80%的数据分配给训练分区,20%分配给验证分区。

n_images = numel(imageDS.Files);N_training = round(0.8*n_images);Idx = randperm(n_images);trainingIdx = idx(1:n_training);validationIdx = idx(n_training+1:end);trainingDS =子集(fullDataset,trainingIdx);validationDS =子集(fullDataset,validationIdx);

指定培训选项

作为下一步,我将使用trainingOptions功能:

  • 设置小批量大小为8。
  • 设置初始学习率为0.01。
  • 每隔一个时代洗牌数据。
  • 每个纪元验证一次网络。
  • 在图形中显示训练进度,抑制冗长输出。
miniBatchSize = 8;validationFrequency = floor(n_training/miniBatchSize);options = trainingOptions('sgdm',…MiniBatchSize, MiniBatchSize,……“MaxEpochs”,50岁,…“InitialLearnRate”,0.01,…“洗牌”、“every-epoch’,……ValidationData, validationDS,……ValidationFrequency, ValidationFrequency,……“阴谋”、“训练进步”,… 'Verbose',true, ... 'ExecutionEnvironment',"auto");

训练网络

我现在用trainNetwork函数并将输出保存到文件中。注意,根据使用的计算类型(GPU、CPU),这个函数可能需要很长时间才能运行。

[net,info] = trainNetwork(trainingDS,lgraph_1,options);保存(trainedNetwork.mat,净)

使用单个Titan Xp GPU,在完整数据集上进行50次训练需要将近5个小时。但是,考虑到后半段的成绩停滞不前,这一训练本可以提前结束。同样,最初的调查是在数据的子集上进行的,因此训练要快得多。

准备测试数据

就像我处理训练数据一样,我现在将读取imageDatastore中的测试文件,并提取必要帧的光流。

Test_metadata = readtable(' Test_metadata .csv');imds = imageDatastore('s3://drivendata-competition-depth- estimate -public/ test_videos_downsampling /',…“ReadFcn”,@(文件名)readVideo(文件名、test_metadata TestData),…FileExtensions,{‘mp4’,‘.avi});imds.readall(“UseParallel”,真正的);

利用测试数据进行深度估计

使用上面训练过的网络,我现在在测试集上执行预测。为此,我使用预测训练网络的方法。predict方法接受一个480×640图像并返回深度估计。通过遍历测试集中的所有图像,我生成了一个结果表。在这里,我创建了一个11933(测试帧的数量)行表列:video_id,时间和距离。

%初始化输出表结果= table('Size',[height(test_metadata) 3],'VariableTypes',{'string','string','single'},…VariableNames,{‘video_id’,‘时间’,‘距离’});对于I = 1:height(test_metadata) id = test_metadata.video_id{I};T = test_metadata.time(i);文件名= [id(1:4) num2str(t)]“中将”);%查找对应的图像名称文件= fullfile('TestData',filename);if isfile(file) %没有边框的帧将没有输入图像I = imread(file);predict = predict(net,I,'ExecutionEnvironment','auto');Else预测= 0; end results.video_id(i) = id; results.time(i) = t; results.distance(i) = prediction; end head(results)

将提交文件保存到文件

上面生成的结果表与所需的提交格式相匹配,因此您现在需要做的就是将其保存到CSV文件。这是你要提交的挑战文件。

writetable(结果,“Submission.csv”);

辅助函数

视频预处理-光流

这个辅助函数提取并预处理下采样视频中的标记帧。然后它将这些文件保存到提供的文件夹- '/ TrainingData在这个例子中。

这就是我们计算必要帧的光流,然后使用边界框应用二进制掩码的地方。

函数输出=readVideo(文件名,元数据,文件夹)%加载视频vr = VideoReader(文件名);H = vr.高度;W = vr.宽度;[~, name] = fileparts(文件名);Idx = contains(metadata.video_id,name);videoMetadata = rmmissing(元数据(idx,:));忽略没有边框的帧n_Frames = height(videoMetadata);%预分配输出图像数组输出= 0 (480,640,n_Frames);for i = 1:n_Frames opticFlow = opticalFlowLK('NoiseThreshold',0.009);%定义光流t = videometdata .time(i); %Extract timestamp try if t == 0 %If first frame compare with second f1 = vr.read(1); f2 = vr.read(2); else %Otherwise take current frame (t+1) and previous (t) f1 = vr.read(t); f2 = vr.read(t+1); end catch continue %Ignore videos where timings do not match with frames end %Convert to grayscale f1Gray = im2gray(f1); f2Gray = im2gray(f2); %Calculate optical flow estimateFlow(opticFlow,f1Gray); flow = estimateFlow(opticFlow,f2Gray); %Extract corners of bounding box x1 = videoMetadata.x1(i); x2 = videoMetadata.x2(i); y1 = videoMetadata.y1(i); y2 = videoMetadata.y2(i); %Apply mask for bounding box mask = poly2mask([x1 x2 x2 x1]*W,[y1 y1 y2 y2]*H,H,W); maskedFlow = bsxfun(@times, flow.Magnitude, cast(mask, 'like', flow.Magnitude)); maskedFlow = imresize(maskedFlow,'OutputSize',[480 640]); file = fullfile(folder, [name num2str(t) '.png']); %Generate file name %Save image to file if isfolder(folder) imwrite(maskedFlow,file) else mkdir(folder) imwrite(maskedFlow,file) end output(:,:,i) = maskedFlow; end end

为响应创建arrayDatastore

这个函数接受一个图像数据存储和一个标签表。然后建立一个arrayDatastore确保与图像文件对应的标签。

函数labelDS= generateLabelDS(imds,labels) files = imds. files;N_files = length(files);dataLabels = 0 (n_files,1);对于I = 1:n_files [~,id] = fileparts(files{I});视频= id(1:4);Time = str2double(id(5:end));Idx = (contains(labels.video_id,video)) &(标签。时间==时间);dataLabels(i) = labels.distance(idx);结束标签= arrayDatastore(dataLabels);结束

改进的下一步措施

在这个例子中,我研究了许多设计选择,可以用来提高分数:

光流

  • 算法- MATLAB提供了4种计算方法光流.基于视觉外观,我选择了Lucas-Kanade方法。
  • 参数-噪声阈值是信号和背景噪声之间的权衡。
  • 感兴趣地区-我选择使用二元掩码来突出感兴趣的区域,但可能有更好的方法。此外,在本例中,我忽略了没有边界框的帧。相反,你可以尝试包括整个光流图像。

训练数据分区

  • 比例-改变你的训练和验证数据的比例可以帮助控制性能和过拟合。
  • 方法—本例中,图像是随机分割的。不过,你也可以根据视频或相机网站拆分。请注意,测试数据来自不同的站点,与训练集中的站点不同。

转移学习

  • 转移学习-我先从一个训练有素的神经网络并根据比赛数据进行额外的训练,这个过程称为转移学习在这里,我们允许在再训练过程中改变预训练模型的所有层(所有权重都是解冻的)。然而,这意味着我们可能会丢失一些模型现有的知识。相反,迁移学习的第一步通常是用新数据训练预训练模型的最后一层,保持所有早期层不变。一旦训练了新层,早期的层就可以更好地进行微调,而不会失去模型预训练的洞察力。这也可以通过冻结初始早期层的权值来实现。
  • 冻结的重量—将初始层的学习率设置为0,可以防止后续学习更新权值。可以找到更多信息在这里
  • Pre-trained网络- MATLAB提供了大量的选项pretrained网络让你去调查。
  • 培训方案-调整训练参数对最终结果有重要影响。例如:学习率,小批量大小,动量,梯度阈值,损失函数

此代码可以从这个GitHub回购.我们提供此代码作为起点,并很高兴看到您可以提出哪些创新。好运!

|
  • 打印
  • 发送电子邮件

评论

如欲留言,请点击在这里登录到您的MathWorks帐户或创建一个新帐户。