1、绪论
1.1 CNN-RNN概述
1.1.1 结构组成
CNN-RNN架构结合了卷积神经网络(CNN)和循环神经网络(RNN)两种不同类型的神经网络结构。
-
卷积神经网络(CNN):
- 主要用于处理具有网格状拓扑结构的数据,如图像和视频。
- 结构上包括卷积层、池化层和全连接层。
- 卷积层通过滤波器(或称为核函数)在图像上滑动,计算滤波器与覆盖像素之间的点积,以提取图像中的特定模式或特征。
- 池化层对特征图进行下采样操作,减少数据的空间维度,有助于降低计算复杂度并防止过拟合。
- 全连接层将卷积层和池化层的输出平铺并通过这些层进行最终预测。
-
循环神经网络(RNN):
- 主要用于处理时间序列、语音和自然语言等序列数据。
- 通过递归连接将当前时刻的输出与下一时刻的输入相关联,从而能够捕获序列中的时间依赖性。
- 结构上包括输入层、递归层和输出层。
1.1.2 工作原理
在CNN-RNN架构中,CNN首先处理视频的每一帧,提取图像中的空间特征。然后,RNN接收CNN提取的特征作为输入,处理这些特征在时间序列上的变化,从而进行视频分类或其他相关任务。
1.1.3 应用场景
CNN-RNN架构广泛应用于视频处理任务,如视频分类、动作识别、事件检测等。在这些任务中,CNN-RNN能够充分利用视频中的空间和时间信息,提高模型的性能。
1.2 CNN-RNN的特点
CNN-RNN架构结合了卷积神经网络(CNN)和循环神经网络(RNN)的优点,特别适用于处理视频分类等任务。
-
特征提取能力:
-
CNN:通过卷积层和池化层的组合,CNN能够有效地提取图像的局部特征,对空间特征具有强大的提取能力。这种能力使得CNN在图像识别、图像分类等任务上取得了出色的性能。
-
RNN:RNN特别适合于处理序列数据,如文本、语音和时间序列等。它能够通过循环连接将当前时刻的输出与下一时刻的输入相关联,从而捕获序列中的时间依赖性。
-
处理能力:
-
CNN-RNN组合:CNN和RNN的结合使得该架构能够同时处理视频的空间特征和时间依赖性。CNN负责从视频帧中提取空间特征,而RNN则负责处理这些特征在时间序列上的变化,从而进行视频分类。
-
学习能力:
-
RNN的中间状态可以用来表示标签之间的关系,当与CNN结合时,CNN-RNN结构可以学习到语义标签的依赖性和图片-标签的相互关系。这种能力使得CNN-RNN在处理多标签图像分类等任务时具有优势。
-
适用性:
-
CNN-RNN架构适用于处理视频数据,尤其是那些需要同时考虑空间特征和时间依赖性的任务。例如,在动作识别、事件检测等视频中,这种架构能够捕捉到视频帧之间的时间关系和帧内的空间特征。
-
可扩展性:
-
CNN-RNN架构可以与其他深度学习技术相结合,如注意力机制、Transformer等,以进一步提高其性能。例如,通过引入注意力机制,CNN-RNN可以更好地关注视频中的关键部分,从而提高分类的准确性。
CNN-RNN架构通过结合CNN和RNN的优点,具有强大的特征提取能力、处理能力、学习能力、适用性和可扩展性,特别适用于处理视频分类等任务。
2、视频分类任务详解
本章节展示了一个视频分类示例,它在推荐、安全等方面有官方的应用。
示例将使用UCF101数据集来构建我们的视频分类器。该数据集包含被分类为不同动作的视频,如板球击球、拳击、骑自行车等。这个数据集通常用于构建动作识别器,这是视频分类的一个应用。
一个视频由一系列有序的帧组成。每一帧包含空间信息,这些帧的序列包含时间信息。为了模拟这两个方面,我们使用一个混合架构,它由用于空间处理的卷积和用于时间处理的循环层组成。
具体来说,示例将使用一个卷积神经网络(CNN)和一个由GRU层组成的循环神经网络(RNN)。
这种混合架构通常被称为CNN-RNN。
2.1 视频分类准备
2.1.1 软件安装
示例需要TensorFlow 2.5或更高版本,以及TensorFlow Docs,可以使用以下命令安装:
!pip install -q git+https://github.com/tensorflow/docs
2.1.2 数据收集
为了使这个例子的运行时间相对较短,我们将使用原始UCF101数据集的子采样版本。程序员可以查安装下面的示例方式下载数据集。
!!wget -q https://github.com/sayakpaul/Action-Recognition-in-TensorFlow/releases/download/v1.0.0/ucf101_top5.tar.gz
!tar xf ucf101_top5.tar.gz
2.1.3 设置
设置需要用到的软件库
import os
import keras
from imutils import paths
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import imageio
import cv2
from IPython.display import Image
2.2 视频分类预处理
2.2.1 定义超参数
在CNN-RNN视频分类任务中,定义合适的超参数对于模型训练和性能至关重要。超参数包括输入图像大小、卷积层参数(如卷积核大小、数量、步长和填充)、池化层参数(如池化窗口大小和步长)、全连接层参数(如神经元数量),以及RNN部分的参数,如序列长度、RNN类型(如LSTM或GRU)、隐藏层单元数量和堆叠层数。此外,训练相关的超参数如学习率、批量大小和迭代次数也需仔细设置。学习率决定了模型参数更新的步长,批量大小影响计算资源消耗和训练稳定性,而迭代次数则控制了整个训练数据集被遍历的次数。这些超参数的选择应根据任务需求、数据集特点和硬件资源进行调整和优化,以确保模型能够达到最佳性能。
2.2.1.1 CNN部分超参数
-
输入图像大小:
- 定义:输入到CNN模型的视频帧的像素尺寸。
- 建议:常见的尺寸包括32x32、96x96和224x224。分辨率越高,通常有助于性能提升,但也会增加计算成本。因此,在选择时应考虑硬件资源和任务需求。
-
卷积层参数:
- 卷积核大小(Kernel Size):定义了卷积操作的窗口大小。
- 常见设置:3x3或5x5的小卷积核。小卷积核有助于增加网络容量和模型复杂度,同时减少参数数量。
- 卷积核数量(Number of Filters):每个卷积层中的卷积核数量。
- 常见设置:使用2的次幂,如64、128等。更多的卷积核可以提取更丰富的特征,但也会增加计算量和内存需求。
- 步长(Stride):卷积核在输入图像上滑动的步长。
- 常见设置:通常为1,这有助于保持空间维度的信息。
- 填充(Padding):在输入图像周围添加的像素值。
- 常见操作:使用“zeros-padding”来保持卷积层的输出与输入具有相同的空间尺寸。
- 卷积核大小(Kernel Size):定义了卷积操作的窗口大小。
-
池化层参数:
- 池化窗口大小(Pooling Size):定义了池化操作的窗口大小。
- 常见设置:2x2。
- 池化步长(Pooling Stride):池化窗口在特征图上的滑动步长。
- 常见设置:与池化窗口大小相同,这样输出结果大小仅为输入数据长宽大小的四分之一。
- 池化窗口大小(Pooling Size):定义了池化操作的窗口大小。
2.2.1.2 RNN部分超参数
-
序列长度:
- 定义:输入到RNN模型的视频帧序列的长度。
- 考虑因素:序列长度应根据视频内容和任务需求来确定。较长的序列可以捕获更多的时序信息,但也会增加计算复杂度。
-
RNN类型:
- 定义:选择使用的RNN模型类型,如LSTM或GRU。
- 考虑因素:LSTM和GRU在处理长期依赖关系方面有不同的特点。LSTM通常具有更强的能力,但计算成本也更高。GRU则是一种更轻量级的替代品。
-
隐藏层单元数量:
- 定义:RNN隐藏层中的神经元数量。
- 考虑因素:较多的隐藏层单元数量可以提高模型的表达能力,但也可能导致过拟合。应根据任务需求和验证集的性能来选择合适的数量。
2.1.1.3 训练相关超参数
-
学习率:
- 定义:模型参数更新时的步长大小。
- 考虑因素:学习率过大可能导致模型不稳定,学习率过小则可能导致训练速度过慢。常见的学习率调整策略包括轮数减缓、指数减缓和分数减缓。
-
批量大小(Batch Size):
- 定义:每次训练时使用的样本数量。
- 考虑因素:较大的批量大小可以加速训练,但也会增加内存需求。较小的批量大小可能提高模型的泛化能力,但可能导致训练过程更加不稳定。
-
迭代次数(Epochs):
- 定义:整个训练数据集被模型处理的次数。
- 考虑因素:增加迭代次数可以提高模型性能,但过多的迭代可能导致过拟合。应根据验证集的性能来选择合适的迭代次数。
通过仔细调整这些超参数,可以优化CNN-RNN视频分类模型的性能,并使其适应特定的任务和数据集。
IMG_SIZE = 224
BATCH_SIZE = 64
EPOCHS = 10
MAX_SEQ_LENGTH = 20
NUM_FEATURES = 2048
2.2.2 数据准备
在CNN-RNN视频分类任务中,视频预处理是一个至关重要的步骤,它直接影响模型训练和推理的效果。预处理通常包括以下几个步骤:首先,将视频文件拆分成连续的图像帧,这可以通过视频处理库如OpenCV实现。接着,为了适配CNN模型的输入要求,需要将图像帧调整为统一的尺寸,并保持图像的宽高比以避免失真。此外,根据任务需求,可以选择将彩色图像转换为灰度图像以简化计算。之后,对图像进行归一化操作,将像素值缩放到[0, 1]或[-1, 1]的范围内,以加速模型训练和提高性能。此外,为了增加数据的多样性和提高模型的泛化能力,可以进行数据增强,如随机裁剪、旋转、翻转和亮度调整等。完成这些处理后,将预处理后的图像帧按照原始顺序构建成特征序列,这些特征序列随后将作为RNN模型的输入。最后,为了提高计算效率,可以将多个特征序列组合成一个批次进行批处理。通过这些预处理步骤,原始视频文件将被转化为适合CNN-RNN模型处理的数据格式,为后续的视频分类任务提供有力支持。
train_df = pd.read_csv("train.csv")
test_df = pd.read_csv("test.csv")
print(f"Total videos for training: {
len(train_df)}")
print(f"Total videos for testing: {
len(test_df)}")
train_df.sample(10)
Total videos for training: 594
Total videos for testing: 224
video_name | tag | |
---|---|---|
492 | v_TennisSwing_g10_c03.avi | TennisSwing |
536 | v_TennisSwing_g16_c05.avi | TennisSwing |
413 | v_ShavingBeard_g16_c05.avi | ShavingBeard |
268 | v_Punch_g12_c04.avi | Punch |
288 | v_Punch_g15_c03.avi | Punch |
30 | v_CricketShot_g12_c03.avi | CricketShot |
449 | v_ShavingBeard_g21_c07.avi | ShavingBeard |
524 | v_TennisSwing_g14_c07.avi | TennisSwing |
145 | v_PlayingCello_g12_c01.avi | PlayingCello |
566 | v_TennisSwing_g21_c03.avi | TennisSwing |
训练视频分类器的众多挑战之一是找出一种将视频输入网络的方法。这篇博客文章讨论了五种这样的方法。由于视频是一系列有序的帧,我们可以提取帧并将它们放入一个三维张量中。但是,不同视频的帧数可能会有所不同,这将阻止我们将它们堆叠成批次(除非我们使用填充)。作为一种替代方案,我们可以以固定间隔保存视频帧,直到达到最大帧数。在这个例子中,我们将执行以下操作:
- 捕获视频的帧。
- 提取视频帧,直到达到最大帧数。
- 如果视频的帧数少于最大帧数,我们将用零填充视频。
请注意,这个工作流程与涉及文本序列的问题相同。UCF101数据集的视频在帧之间的对象和动作上没有极端变化。因此,可能可以只考虑几个帧来进行学习任务。但是,这种方法可能不会很好地泛化到其他视频分类问题。我们将使用OpenCV的VideoCapture()
方法来读取视频中的帧。
# 下面的两个函数取自连接提供的教程:
#https://www.tensorflow.org/hub/tutorials/action_recognition_with_tf_hub
def crop_center_square(frame):
y, x = frame.shape[0:2]
min_dim = min(y, x)
start_x = (x // 2) - (min_dim // 2)
start_y = (y // 2) - (min_dim // 2)
return frame[start_y : start_y + min_dim, start_x : start_x + min_dim]
def load_video(path, max_frames=0, resize=(IMG_SIZE, IMG_SIZE)):
cap = cv2.VideoCapture(path)
frames = []
try:
while True:
ret, frame = cap.read()
if not ret:
break
frame = crop_center_square(frame)
frame = cv2.resize(frame, resize)
frame = frame[:, :, [2, 1, 0]]
frames.append(frame)
if len(frames) == max_frames:
break
finally:
cap.release()
return np.array(frames)
2.2.3 视频特征提取
在CNN-RNN视频分类任务中,视频特征提取的CNN部分扮演着至关重要的角色。首先,视频被分割成一系列帧,作为CNN的输入。为了适应模型的输入要求,这些帧通常会被调整到固定的尺寸,如224x224像素,并进行归一化处理,将像素值缩放到[0, 1]的范围内。
接着,这些预处理后的帧被送入卷积神经网络(CNN)模型中进行特征提取。CNN模型通过多个卷积层对帧进行卷积操作,使用不同大小的卷积核来提取帧中的局部特征,这些特征可能包括颜色、纹理、边缘等。在卷积层之后,通常会使用激活函数(如ReLU)来增加模型的非线性,使得模型能够学习更复杂的特征表示。
为了进一步减小特征图的空间尺寸并保留重要特征,CNN模型还包含了池化层。常见的池化操作有最大池化和平均池化,它们有助于减少模型的计算量和参数数量,同时防止过拟合。
经过多个卷积和池化层的处理后,CNN模型会输出一个特征图或特征向量。这个特征向量包含了帧的全局特征信息,这些特征在空间上是不变的,即它们在视频的不同帧中可能是相似的。这些特征向量将作为视频帧的特征表示,用于后续的RNN处理。
在实际应用中,为了提高特征提取的准确性和效率,通常会使用在大型数据集上预训练的CNN模型(如ResNet、VGG等)进行特征提取。这些模型已经学习到了丰富的图像特征表示,可以显著提高视频分类的性能。此外,根据具体任务和数据集的特点,可以对CNN模型的结构和参数进行调整,以优化特征提取的性能。
CNN-RNN视频分类任务中的CNN部分通过一系列卷积、池化和全连接层的处理,从视频帧中提取出丰富的特征信息,为后续的RNN处理提供了有力的支持。这些特征不仅包含了帧中的空间信息,还通过预训练和模型调整等手段得到了优化,为视频分类任务提供了坚实的基础。
本文我们使用预训练的网络从提取的帧中提取有意义的特征。Keras Applications
模块提供了许多在ImageNet-1k数据集上预训练的最新模型。
我们将为此目的使用InceptionV3模型。
def build_feature_extractor():
feature_extractor = keras.applications.InceptionV3(
weights="imagenet",
include_top=False,
pooling="avg",
input_shape=(IMG_SIZE, IMG_SIZE, 3),
)
preprocess_input = keras.applications.inception_v3.preprocess_input
inputs = keras.Input((IMG_SIZE, IMG_SIZE, 3))
preprocessed = preprocess_input(inputs)
outputs = feature_extractor(preprocessed)
return keras.Model(inputs, outputs, name="feature_extractor")
feature_extractor = build_feature_extractor()
视频的标签是字符串。神经网络不理解字符串值,所以在将它们输入模型之前,它们必须被转换为某种数值形式。在这里,我们将使用