使用 TensorFlow 进行深度学习,轻松处理视频数据

发布人: Shilpa Kancharla

视频数据包含丰富的信息,其结构比图像数据的结构更庞大、更复杂。使用深度学习以节省内存的方式对视频进行分类,可以帮助我们更好地理解数据内容。我们在 tensorflow.google.cn 上发布了一系列教程,介绍了如何加载、预处理和分类视频数据。您可以查阅相关教程进行详细了解:

在本文中,我们将介绍一些有趣的内容,深入学习部分教程的特定内容,并讨论如何综合运用这些知识构建自有模型,使这些模型可以使用 TensorFlow 以节省内存的方式处理视频或 MRI 扫描等三维数据,如运用 Python 生成器以及调整数据大小或对其进行下采样。

△ 视频数据形状示例,具有以下维度: 帧数 (时间) x 长度 x 宽度 x 通道

使用 FrameGenerator 加载视频数据

在 “加载视频数据” 教程中,我们将借此机会探讨大多数该系列教程的核心部分: FrameGenerator 类。我们可以借助此类得到视频的张量表征,以及视频的标签或类。

class FrameGenerator:
  def __init__(self, path, n_frames, training = False):
    """返回一组带有其关联标签的帧。

      Args:
        path:视频文件路径。
        n_frames:帧数。
        training:用于确定是否正在创建训练数据集的布尔值。
    """
    self.path = path
    self.n_frames = n_frames
    self.training = training
    self.class_names = sorted(set(p.name for p in self.path.iterdir() if p.is_dir()))
    self.class_ids_for_name = dict((name, idx) for idx, name in enumerate(self.class_names))

  def get_files_and_class_names(self):
    video_paths = list(self.path.glob('*/*.avi'))
    classes = [p.parent.name for p in video_paths]
    return video_paths, classes

  def __call__(self):
    video_paths, classes = self.get_files_and_class_names()

    pairs = list(zip(video_paths, classes))

    if self.training:
      random.shuffle(pairs)

    for path, name in pairs:
      video_frames = frames_from_video_file(path, self.n_frames)
      label = self.class_ids_for_name[name] # Encode labels
      yield video_frames, label

在创建生成器类时,我们会使用函数 from_generator() 将数据提供给我们的深度学习模型。具体而言,from_generator() API 将会创建一个数据集,其内容由生成器生成。使用 Python 生成器比在内存中存储整个数据序列更节省内存。您不妨考虑创建与 FrameGenerator 类似的生成器类,并使用 from_generator() API 将数据加载到 TensorFlow 和 Keras 模型中。

ut_signature = (tf.TensorSpec(shape = (None, None, None, 3), 
                                  dtype = tf.float32),
                    tf.TensorSpec(shape = (), 
                                  dtype = tf.int16))
train_ds = tf.data.Dataset.from_generator(FrameGenerator(subset_paths['train'], 
                                          10, 
                                          training=True),
                                          output_signature = output_signature)

使用 einops 库调整视频数据大小

在第二个视频教程 “使用 3D 卷积神经网络 (Convolutional Neural Network, CNN) 对视频进行分类” 中,我们将讨论如何使用 einops 库,以及如何将其整合到由 TensorFlow 提供支持的 Keras 模型中。该库对于执行灵活的张量操作十分有帮助,并且与 TensorFlow 和 JAX 均可搭配使用。在本教程中该库的作用尤为明显,当数据通过我们创建的 (2+1)D 卷积神经网络时,我们会使用此库调整数据大小。在第二个教程中,我们希望对视频数据进行下采样。下采样之所以十分有用,是因为这项技术可以使我们的模型检查帧的特定部分,以检测可能特定于视频中某个特定特征的模式。借助下采样技术,我们可以舍弃非必要的信息,从而实现降维,进而加快处理速度。

我们使用 einops 库的函数 parse_shape()rearrange()。此处使用的 parse_shape() 函数可将轴的名称映射到其相应的长度。然后该函数会返回包含此信息的字典,名为 old_shape。接下来,我们会使用 rearrange() 函数,以便重新排列多维张量的轴。将张量和要重新排列的轴的名称传递给该函数。

此处的 b t h w c -> (b t) h w c 符号表示我们希望将批次大小 (以 b 表示) 和时间 (以 t 表示) 维度压缩到一起,以将这些数据传递到 Keras Resizing 层对象中。当我们实例化 ResizeVideo 类时,会传入希望在调整帧大小时达到的目标长度和宽度值。完成这项调整操作后,我们会再次使用 rearrange() 函数来将批次大小和时间维度展开 (使用 (b t) h w c -> b t h w c 符号)。

class ResizeVideo(keras.layers.Layer):
  def __init__(self, height, width):
    super().__init__()
    self.height = height
    self.width = width
    self.resizing_layer = layers.Resizing(self.height, self.width)

  def call(self, video):
    """
     使用 einops 库调整张量的大小。

      Args:
        video:视频的张量表征,以一组帧的形式呈现。

      Return:
       视频的下采样尺寸,根据调整大小时应达到的目标高度和宽度而定。
    """
    # b 表示批次大小、t 表示时间、h 表示高度、
    # w 表示宽度、c 表示通道数。
    old_shape = einops.parse_shape(video, 'b t h w c')
    images = einops.rearrange(video, 'b t h w c -> (b t) h w c')
    images = self.resizing_layer(images)
    videos = einops.rearrange(
        images, '(b t) h w c -> b t h w c',
        t = old_shape['t'])
    return videos

未来计划

以上只是利用 TensorFlow 以节省内存的方式处理视频数据的几种方法,但这些技术并非只能用于视频数据。MRI 扫描等医疗数据或 3D 图像数据也需要高效的数据加载,且可能需要调整数据形状的大小。在计算资源有限的情况下,这些技术对于处理数据而言十分有用。希望这些教程对您有所帮助,感谢阅读!欢迎您持续关注我们,及时获悉更多资讯。

原文:Using TensorFlow for Deep Learning on Video Data
中文:TensorFlow 公众号