准备 Kinetics400 数据集

Kinetics400 是一个从 YouTube 收集的现实动作视频的行为识别数据集。它包含来自 400 个动作类别的 306,245 个短截视频,是研究界用于衡量最先进视频行为识别模型性能的最大、最广泛使用的数据集之一。本教程将详细介绍为 GluonCV 准备此数据集的步骤。

下载

请参阅官方网站了解如何下载视频。请注意,下载的视频将占用约 450G 磁盘空间,请在下载前确保有足够的空间。抓取过程可能需要几天时间。

下载完成后,请重命名文件夹名称(因为类名包含空格和括号)以便于处理。假设视频下载到 ~/.mxnet/datasets/kinetics400,其中将有三个文件夹:annotationstrainval。您可以使用以下命令重命名文件夹名称

# sudo apt-get install detox
detox -r train/
detox -r val/

解码为帧

如果您决定直接使用视频数据训练模型,可以跳过此部分。

将视频准备为帧格式的最简单方法是下载辅助脚本 kinetics400.py 并运行以下命令

python kinetics400.py --src_dir ~/.mxnet/datasets/kinetics400/train --out_dir ~/.mxnet/datasets/kinetics400/rawframes_train --decode_video --new_width 450 --new_height 340
python kinetics400.py --src_dir ~/.mxnet/datasets/kinetics400/val --out_dir ~/.mxnet/datasets/kinetics400/rawframes_val --decode_video --new_width 450 --new_height 340

此脚本将帮助您将视频解码为原始帧。我们指定了帧的宽度和高度进行缩放,因为这可以节省大量磁盘空间,同时不会损失太多精度。所有缩放后的帧将占用 2.9T 磁盘空间。如果我们不指定尺寸,原始解码的帧将占用 6.8T 磁盘空间。数据准备过程可能需要一些时间。准备数据集的总时间取决于您的机器。例如,在具有 EBS 并使用 56 个 worker 的 AWS EC2 实例上,大约需要 8 小时。

生成训练文件

最后一步是为标准数据加载生成训练文件。您可以按如下方式运行辅助脚本

python kinetics400.py --build_file_list --frame_path ~/.mxnet/datasets/kinetics400/rawframes_train --subset train --shuffle
python kinetics400.py --build_file_list --frame_path  ~/.mxnet/datasets/kinetics400/rawframes_val --subset val --shuffle

现在您可以开始在 Kinetics400 数据集上训练您的行为识别模型了。

注意

您至少需要 4T 磁盘空间来下载和提取数据集。由于速度更快,推荐使用 SSD(固态硬盘)而非 HDD(机械硬盘)。

您可能需要通过 pip install Cython mmcv 安装 Cythonmmcv

使用 GluonCV 读取

准备好的数据集可以直接使用工具类 gluoncv.data.Kinetics400 加载。在本教程中,我们提供了从数据集中读取数据的三种示例:(1)每视频加载一帧;(2)每视频加载一个片段,该片段包含五帧连续帧;(3)每视频均匀加载三个片段,每个片段包含 12 帧。

我们首先展示一个示例,该示例每次随机读取 5 个视频,每视频随机选择一帧并执行中心裁剪。

import os
from gluoncv.data import Kinetics400
from mxnet.gluon.data import DataLoader
from mxnet.gluon.data.vision import transforms
from gluoncv.data.transforms import video

transform_train = transforms.Compose([
    video.VideoCenterCrop(size=224),
    video.VideoToTensor()
])

# Default location of the data is stored on ~/.mxnet/datasets/kinetics400.
# You need to specify ``setting`` and ``root`` for Kinetics400 if you decoded the video frames into a different folder.
train_dataset = Kinetics400(train=True, transform=transform_train)
train_data = DataLoader(train_dataset, batch_size=5, shuffle=True)

我们可以看到加载数据的形状如下。extra 表示我们是否从视频中选择了多个裁剪或多个片段。在这里,我们每视频只选择一帧,所以 extra 维度是 1。

for x, y in train_data:
    print('Video frame size (batch, extra, channel, height, width):', x.shape)
    print('Video label:', y.shape)
    break

输出

Video frame size (batch, extra, channel, height, width): (5, 1, 3, 224, 224)
Video label: (5,)

这里是第二个示例,该示例每次随机读取 25 个视频,每视频随机选择一个片段并执行中心裁剪。一个片段可以包含 N 帧连续帧,例如 N=5。

train_dataset = Kinetics400(train=True, new_length=5, transform=transform_train)
train_data = DataLoader(train_dataset, batch_size=5, shuffle=True)

我们可以看到加载数据的形状如下。现在我们有了另一个 depth 维度,它表示每个片段中有多少帧(也称为时间维度)。

for x, y in train_data:
    print('Video frame size (batch, extra, channel, depth, height, width):', x.shape)
    print('Video label:', y.shape)
    break

输出

Video frame size (batch, extra, channel, depth, height, width): (5, 1, 3, 5, 224, 224)
Video label: (5,)

让我们绘制一个包含 5 帧连续视频帧的训练样本。索引 0 是图像,1 是标签

from matplotlib import pyplot as plt
# subplot 1 for video frame 1
fig = plt.figure()
fig.add_subplot(1,5,1)
frame1 = train_dataset[150][0][0,:,0,:,:].transpose((1,2,0)).asnumpy()*255.0
plt.imshow(frame1.astype('uint8'))
# subplot 2 for video frame 2
fig.add_subplot(1,5,2)
frame2 = train_dataset[150][0][0,:,1,:,:].transpose((1,2,0)).asnumpy()*255.0
plt.imshow(frame2.astype('uint8'))
# subplot 3 for video frame 3
fig.add_subplot(1,5,3)
frame3 = train_dataset[150][0][0,:,2,:,:].transpose((1,2,0)).asnumpy()*255.0
plt.imshow(frame3.astype('uint8'))
# subplot 4 for video frame 4
fig.add_subplot(1,5,4)
frame4 = train_dataset[150][0][0,:,3,:,:].transpose((1,2,0)).asnumpy()*255.0
plt.imshow(frame4.astype('uint8'))
# subplot 5 for video frame 5
fig.add_subplot(1,5,5)
frame5 = train_dataset[150][0][0,:,4,:,:].transpose((1,2,0)).asnumpy()*255.0
plt.imshow(frame5.astype('uint8'))
# display
plt.show()
kinetics400

最后一个示例是,我们每次随机读取 5 个视频,每视频均匀选择 3 个片段并执行中心裁剪。一个片段包含 12 帧连续帧。

train_dataset = Kinetics400(train=True, new_length=12, num_segments=3, transform=transform_train)
train_data = DataLoader(train_dataset, batch_size=5, shuffle=True)

我们可以看到加载数据的形状如下。现在 extra 维度是 3,表示每个视频有三个片段。

for x, y in train_data:
    print('Video frame size (batch, extra, channel, depth, height, width):', x.shape)
    print('Video label:', y.shape)
    break

输出

Video frame size (batch, extra, channel, depth, height, width): (5, 3, 3, 12, 224, 224)
Video label: (5,)

使用 VideoLoader 读取

如果您不想将视频解码为帧,我们提供了一个快速视频加载器 Decord 来读取数据集。我们仍然使用工具类 gluoncv.data.Kinetics400。用法类似于使用图像加载器。

例如,如果我们想随机读取 5 个视频,每视频随机选择一帧并执行中心裁剪。

from gluoncv.utils.filesystem import import_try_install
import_try_install('decord')

# Since we are loading videos directly, we need to change the ``root`` location.
# ``tiny_train_videos`` contains a small subset of Kinetics400 dataset, which is used for demonstration only.
train_dataset = Kinetics400(root=os.path.expanduser('~/.mxnet/datasets/kinetics400/tiny_train_videos'), train=True,
                            transform=transform_train, video_loader=True, use_decord=True)
train_data = DataLoader(train_dataset, batch_size=5, shuffle=True)

输出

WARNING: pip is being invoked by an old script wrapper. This will fail in a future version of pip.
Please see https://github.com/pypa/pip/issues/5599 for advice on fixing the underlying issue.
To avoid this problem you can invoke Python with '-m pip' instead of running pip directly.
Collecting decord
  Downloading decord-0.6.0-py3-none-manylinux2010_x86_64.whl (13.6 MB)
Requirement already satisfied: numpy>=1.14.0 in /usr/local/lib/python3.6/dist-packages (from decord) (1.19.5)
Installing collected packages: decord
Successfully installed decord-0.6.0
WARNING: You are using pip version 21.0.1; however, version 21.3.1 is available.
You should consider upgrading via the '/usr/bin/python3 -m pip install --upgrade pip' command.

我们可以看到加载数据的形状如下。

for x, y in train_data:
    print('Video frame size (batch, extra, channel, height, width):', x.shape)
    print('Video label:', y.shape)
    break

输出

Video frame size (batch, extra, channel, height, width): (5, 1, 3, 224, 224)
Video label: (5,)

这里是第二个示例,该示例每次随机读取 25 个视频,每视频随机选择一个片段并执行中心裁剪。一个片段可以包含 N 帧连续帧,例如 N=5。

train_dataset = Kinetics400(root=os.path.expanduser('~/.mxnet/datasets/kinetics400/tiny_train_videos'),
                            train=True, new_length=5, transform=transform_train,
                            video_loader=True, use_decord=True)
train_data = DataLoader(train_dataset, batch_size=5, shuffle=True)

我们可以看到加载数据的形状如下。

for x, y in train_data:
    print('Video frame size (batch, extra, channel, depth, height, width):', x.shape)
    print('Video label:', y.shape)
    break

输出

Video frame size (batch, extra, channel, depth, height, width): (5, 1, 3, 5, 224, 224)
Video label: (5,)

最后一个示例是,我们每次随机读取 5 个视频,每视频均匀选择 3 个片段并执行中心裁剪。一个片段包含 12 帧连续帧。

train_dataset = Kinetics400(root=os.path.expanduser('~/.mxnet/datasets/kinetics400/tiny_train_videos'), train=True,
                            new_length=12, num_segments=3, transform=transform_train,
                            video_loader=True, use_decord=True)
train_data = DataLoader(train_dataset, batch_size=5, shuffle=True)

我们可以看到加载数据的形状如下。

for x, y in train_data:
    print('Video frame size (batch, extra, channel, depth, height, width):', x.shape)
    print('Video label:', y.shape)
    break

输出

Video frame size (batch, extra, channel, depth, height, width): (5, 3, 3, 12, 224, 224)
Video label: (5,)

我们也支持其他视频加载器,例如 OpenCV VideoReader,但 Decord 要快得多。建议用户查阅文档了解更多信息。

脚本总运行时间: ( 0 minutes 7.742 seconds)

由 Sphinx-Gallery 生成的画廊