3训练模型代码详解_代码详解:手把手教你建立自己的视频分类模型

3训练模型代码详解_代码详解:手把手教你建立自己的视频分类模型_第1张图片
全文共 12783字,预计学习时长 26分钟甚至更长

3训练模型代码详解_代码详解:手把手教你建立自己的视频分类模型_第2张图片

视频正以前所未有的速度融入我们的生活,对于数据科学家而言,这个领域有很大的开发潜力。那么在计算机视觉中,建立图像分类模型的方法是否在视频分类中也具有普适性呢?

让机器直接处理视频还是略显吃力。视频的动态特性——和图片的静态特性相对——会使数据科学家在建立相关模型时遇到麻烦。

但别担心,处理视频数据和处理图像数据的差别并不大。在这篇文章中,我们会用Python建立自己的视频分类模型,学习如何运用计算机视觉和深度学习技巧来处理视频数据。这是一个实操性非常强的讲解,所以确保你的Jupyter notebook能高效的陪你走过这段有趣的旅程。

7917abe0f24d9e19ce0b934b05911eae.png

目录

1. 视频分类概述

2. 建立视频分类模型的步骤

3. 探索视频分类数据库

4. 训练视频分类模型

5. 评估视频分类模型

7917abe0f24d9e19ce0b934b05911eae.png

视频分类概述

你如何定义视频?

我们可以说视频是多组有序排列图像的合集。这些图像组也可称之为帧(frames)。

这也就是为什么视频分类问题和图像分类问题的区别不大。在执行图像分类任务的时候,我们会用特征提取器(比如卷积神经网络或者说CNNs)来提取图像的特征,并且基于这些特征对图像进行分类。而视频分类就比这多一步而已。

我们先从给定的视频中提取帧。接着,我们可以采取和图像分类相同的方式来处理视频。这是处理视频数据最为简单的方法。

实际上,处理视频的方法有很多,甚至专门有一个十分有吸引力的领域就是视频分析。我们会用到CNNs来从一帧帧的视频中提取特征。

7917abe0f24d9e19ce0b934b05911eae.png

建立视频分类模型的步骤

是不是很兴奋,学习建立可以将视频归入自己特定类别的模型?我们将会以UCF101 – 动作识别数据组(https://www.crcv.ucf.edu/data/UCF101.php)为对象,这个数据组包括13,320个不同的视频片段,它们分属于101个不同的类别。

先列出建立视频分类模型所需的步骤:

1. 探索这个数据库,并且创建训练集和验证集。我们将会用训练集来训练这个模型,用验证集来评估已经训练过的模型。

2. 从训练集和验证集中,提取出所有视频的帧。

3. 预处理这些帧,之后,用训练集里的帧来训练我们的模型。用验证集里的帧来评估模型。

4. 我们对模型在验证集上的表现满意后,就可以用它对新视频进行分类了。

让我们现在开始探索这些数据吧!

7917abe0f24d9e19ce0b934b05911eae.png

探索视频分类数据库

你可以从UCF101官方网站(https://www.crcv.ucf.edu/data/UCF101.php)上下载所需的数据库。这些数据库都是.rar格式,所以我们首先需要把视频提取出来。建立一个新的文件夹,把它命名为‘Videos’(或者任意你想起的名字),接着通过下面的指令来提取所有下载过的视频:

unrar e UCF101.rar Videos/

UCF101的官方文件中写道:

“把属于同一组的视频分别保存到训练组和测试组中是非常重要的,因为同组的视频都来自于单个长视频,所以训练组和测试组共享视频数据会有更好的运行结果。”

因此,我们会像官方文件里建议的那样,将数据库分为训练组和测试组。

你可以在这里下载分好的两组数据:https://www.crcv.ucf.edu/data/UCF101/UCF101TrainTestSplits-RecognitionTask.zip。

值得注意的是,我们需要处理的数据库很大,所以必须保证你的电脑有很好的计算能力。

现在,我们有一个包含所有视频的文件夹,以及训练/测试视频分开保存的另一个文件夹。接下来,我们要建立自己的数据库。打开你的Jupyter notebook,并输以下代码。我们会先输入必需的库(libraries):

import cv2 # for capturing videos 用于抓取视频import math # for mathematical operations 用于数学运算import matplotlib.pyplot as plt # for plotting the images 用于标记图像%matplotlib inlineimport pandas as pdfrom keras.preprocessing import image # for preprocessing the images 用于预处理这些图像import numpy as np # for mathematical operations 用于数学运算from keras.utils import np_utilsfrom skimage.transform import resize # for resizing images 用于调整图像大小from sklearn.model_selection import train_test_splitfrom glob import globfrom tqdm import tqdm

现在,把视频的名称保存在一个数据帧(dataframe)里:

# open the .txt file which have names of training videos 打开有训练视频名称的.txt文件

f = open("trainlist01.txt", "r")

temp = f.read()

videos = temp.split('n')

# creating a dataframe having video names 创建一个有视频名称的数据帧

train = pd.DataFrame()train['video_name'] = videostrain = train[:-1]train.head()

3训练模型代码详解_代码详解:手把手教你建立自己的视频分类模型_第3张图片

这是.txt文件里原有的视频名称。它们没有完全排列整齐,所以我们需要预处理一下。在此之前,让我们为测试视频创建一个相似的数据帧:

# open the .txt file which have names of test videos 打开有测试视频名称的.txt文件f = open("testlist01.txt", "r")temp = f.read()videos = temp.split('n')# creating a dataframe having video name创建一个有视频名称的数据帧test = pd.DataFrame()test['video_name'] = videostest = test[:-1]test.head()

3训练模型代码详解_代码详解:手把手教你建立自己的视频分类模型_第4张图片

然后,我们给每一个视频加标签(训练和测试组的视频都要)。你注意到了吗,在“/”之前的整个部分就代表着那个视频的标签。因此,我们把整个字符串从“/”分开,然后为所有视频选取它们的标签:

# creating tags for training videos 为训练视频创建标签train_video_tag = []for i in range(train.shape[0]): train_video_tag.append(train['video_name'][i].split('/')[0])train['tag'] = train_video_tag# creating tags for test videos 为测试视频创建标签test_video_tag = []for i in range(test.shape[0]): test_video_tag.append(test['video_name'][i].split('/')[0])test['tag'] = test_video_tag

那么接下来要做什么呢?我们要从训练组视频里提取帧,用以训练我们的模型。我会把所有的帧保存在一个命名为train_1的文件夹里。

所以,我们先建一个名为“train_1”的新文件夹,然后按照以下的代码来提取帧:

# storing the frames from training videos 保存从训练视频中提取的帧for i in tqdm(range(train.shape[0])): count = 0 videoFile = train['video_name'][i]cap = cv2.VideoCapture('UCF/'+videoFile.split(' ')[0].split('/')[1]) # capturing the video from the given path 从已知路径里抓取视频 frameRate = cap.get(5) #frame rate 帧率 x=1 while(cap.isOpened()): frameId = cap.get(1) #current frame number 当下帧数 ret, frame = cap.read() if (ret != True): break if (frameId % math.floor(frameRate) == 0): # storing the frames in a new folder named train_1 将帧保存到名为train_1的新文件夹 filename ='train_1/' + videoFile.split('/')[1].split(' ')[0] +"_frame%d.jpg" % count;count+=1 cv2.imwrite(filename, frame) cap.release()

鉴于训练组里有超过9,500个视频,这一步骤会花掉相当一部分时间。提取好这些帧之后,我们就把它们的名称,即相对应的标签保存在.csv文件里。创建这个文件可以帮助我们读取在下一阶段要用到的帧:

# getting the names of all the images 获得所有图像的名称images = glob("train_1/*.jpg")train_image = []train_class = []for i in tqdm(range(len(images))):# creating the image name 创建图像的名称train_image.append(images[i].split('/')[1])# creating the class of image 创建图像的类train_class.append(images[i].split('/')[1].split('_')[1])# storing the images and their class in a dataframe 将图像和它们的类保存到数据帧里train_data = pd.DataFrame()train_data['image'] = train_imagetrain_data['class'] = train_class# converting the dataframe into csv file 将数据帧转为csv.文件train_data.to_csv('UCF/train_new.csv',header=True, index=False)

目前为止,我们已经从所有的训练视频里提取出帧,以及它们相对应的标签,并将它们保存至.csv文件。现在,可以训练模型了,我们将要用它来预测测试组中视频的标签。

7917abe0f24d9e19ce0b934b05911eae.png

训练视频分类模型

我们终于可以训练这个视频分类模型了!在整个导修中,这是最令人期待的部分。便于理解,我们把这一步分为几个子步骤:

1. 读取我们之前为训练图像而提取的所有帧。

2. 创建一个验证集,它会帮助我们检验模型在未知数据上的表现。

3. 确定模型的结构。

4. 最后,训练这个模型并且保存它的权重。

读取所有帧

第一步,提取帧。我们首先要输入库:

import kerasfrom keras.models import Sequentialfrom keras.applications.vgg16 import VGG16from keras.layers import Dense, InputLayer, Dropout, Flattenfrom keras.layers import Conv2D, MaxPooling2D, GlobalMaxPooling2Dfrom keras.preprocessing import imageimport numpy as npimport pandas as pdimport matplotlib.pyplot as pltfrom tqdm import tqdmfrom sklearn.model_selection import train_test_split

还记得吗?我们创建了一个.csv文件,里面包括每一帧的名字和它们相对应的标签。这一部分也需要读取:

train = pd.read_csv('UCF/train_new.csv')train.head()

3训练模型代码详解_代码详解:手把手教你建立自己的视频分类模型_第5张图片

上图显示了前五个名称处理后的样子。每一帧,我们都有相对应的类或者标签。现在,利用这个.csv文件,我们将要读取之前提取出的帧,然后把它们储存为一个Numpy 数组:

# creating an empty list 创建一个空的列表train_image = []# for loop to read and store frames 让loop循环可以读取和储存帧for i in tqdm(range(train.shape[0])): # loading the image and keeping the target size as (224,224,3) 加载图像,并将目标大小设置为(224,224,3) img = image.load_img('train_1/'+train['image'][i], target_size=(224,224,3)) # converting it to array 将其转换为数组 img = image.img_to_array(img) # normalizing the pixel value 把像素值正常化 img = img/255 # appending the image to the train_image list 把图像加入train_image列表 train_image.append(img)# converting the list to numpy array 将列表转为numpy数组X = np.array(train_image)# shape of the array 数组的大小X.shape

输出:(73844, 224, 224, 3)

我们得到了73,844张图像,每张大小为 (224, 224, 3)。接下来,让我们创建验证集。

创建一个验证集

创建验证集,我们首先要保证每一类的分布在训练集和验证集里是相似的。我们可以用分层参数(stratify parameter)达到这一目的:

# separating the target 区分目标y = train['class']# creating the training and validation set 创建训练集和验证集X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42, test_size=0.2, stratify = y)

上图,stratify = y (也就是每一帧的类或者标签)保持着相似的分布,无论是在训练集还是验证集里。

但请牢记,我们要分类所有视频总共归属于101个不同的类别。所以我们要创建101不同的栏,每一栏都对应一个类别。get_dummies()函数可以做到这一点:

# creating dummies of target variable for train and validation set 为训练集和验证集创建目标变量的数据列y_train = pd.get_dummies(y_train)y_test = pd.get_dummies(y_test)

下一步,确定我们视频分类模型的结构。

确定视频分类模型的结构

鉴于我们的数据库并不大,从零开始创建一个模型可能并不是明智之举。因此,我们将用到预训练模型(pre-trained model),然后利用它的学习结果来解决我们的问题。

对于现有的数据库,我们用VGG-16预训练模型来处理。我们先创建这个预训练模型的基本模型:

# creating the base model of pre-trained VGG16 model 创建VGG16预训练模型的基本模型base_model = VGG16(weights='imagenet', include_top=False)

这个模型在有1,000个类的数据库里训练过了。我们将微调这个模型,来迎合我们的需求。根据我们的需求,include_top = False 可以移除这个模型的最后一层。

接下来,我们将为训练图像和验证图像,提取预训练模型中的帧:

# extracting features for training frames 为训练帧提取特征X_train = base_model.predict(X_train)X_train.shape

输出:(59075, 7, 7, 512)

在训练集中,有59,075张图像,它们的大小已经改为(7, 7, 512),因为我们已将这些图像输入VGG16结构。同样地,我们要为验证帧提取特征:

# extracting features for validation frames 为验证帧提取特征X_test = base_model.predict(X_test)X_test.shape

输出:(14769, 7, 7, 512)

验证集里有14,769张图像,它们的大小也已经改为(7, 7, 512)。现在,我们用全连接网络(fully connected network)来微调这个模型。这个全连接网络是单维度输入的。因此,我们要将这些图像也改为单维度的。

# reshaping the training as well as validation frames in single dimension 将训练集和验证集帧的大小都改为单维度的X_train = X_train.reshape(59075, 7*7*512)X_test = X_test.reshape(14769, 7*7*512)

处理图片时,最好将像素值正常化,例如将像素值存为0到1之间。这也帮助模型更快地整合数据。

# normalizing the pixel values 正常化像素值max = X_train.max()X_train = X_train/maxX_test = X_test/max

然后,我们要建立模型的结构。首先需要确定输入图像的大小,所以,让我们来检查这些图像的大小:

# shape of images 图像大小 X_train.shape

输出: (59075, 25088)

输入图像的大小是25,008。建立结构代码:

#defining the model architecture 确定模型结构model = Sequential()model.add(Dense(1024, activation='relu', input_shape=(25088,)))model.add(Dropout(0.5))model.add(Dense(512, activation='relu'))model.add(Dropout(0.5))model.add(Dense(256, activation='relu'))model.add(Dropout(0.5))model.add(Dense(128, activation='relu'))model.add(Dropout(0.5))model.add(Dense(101, activation='softmax'))

我们得到了多个全连接稠密层。但为了防止CNNs过拟合,我增加了几个dropout层。最后一层里神经元的数量,应该等同于类的个数,也就是101个神经元。

训练这个视频分类模型

我们现在要用训练帧来训练模型,用验证来检验这个模型。我们需要保存模型的权重,这样以后就不用一遍一遍地重新训练它了。

现在,确定一个函数来保存模型的权重:

# defining a function to save the weights of best model 确定一个函数来保存模型的权重from keras.callbacks import ModelCheckpointmcp_save = ModelCheckpoint('weight.hdf5', save_best_only=True, monitor='val_loss', mode='min')

我们要根据验证集损失来确定最优化模型。不要忘记把权重存为weights.hdf5.。当然,你也可以起别的名字。训练模型之前,我们要先编译它:

# compiling the model编译模型model.compile(loss='categorical_crossentropy',optimizer='Adam',metrics=['accuracy'])

我们用categorical_crossentropy损失函数,和Adam优化器,然后训练模型:

# training the model 训练模型model.fit(X_train, y_train, epochs=200, validation_data=(X_test, y_test), callbacks=[mcp_save], batch_size=128)

我们将这个模型训练了200个时期(epoch)。想要下载训练模型后得到的权重,你可以打开这个链接:https://drive.google.com/file/d/1vo2TBWFlA-yish_h8CgxIrSHnqCHxUUM/view?usp=sharing

我们现在就有了用来预测新视频的权重。下一部分,我们就要检验这个模型在视频分类任务上的表现。

7917abe0f24d9e19ce0b934b05911eae.png

评估视频分类模型

让我们打开一个新的Jupyter notebook来评估这个模型。评估部分也可以分为几个步骤来更清楚地理解:

1. 确定模型结构和加载权重。

2. 创建测试数据。

3. 为测试视频做预测。

4. 最后,评估模型。

确定评估模型和加载权重

这部分的第一步你应该还有印象——输入要求的库:

from keras.models import Sequentialfrom keras.layers import Dense, Dropout, Flattenfrom keras.layers import Conv2D, MaxPooling2Dfrom keras.preprocessing import imageimport numpy as npimport pandas as pdfrom tqdm import tqdmfrom keras.applications.vgg16 import VGG16import cv2import mathimport osfrom glob import globfrom scipy import stats as s

下一步,我们要确定模型的结构,这和我们在训练模型时的操作相似:

base_model = VGG16(weights='imagenet', include_top=False)

上面是预训练模型,我们要微调它:

#defining the model architecture 确定模型结构model = Sequential()model.add(Dense(1024, activation='relu', input_shape=(25088,)))model.add(Dropout(0.5))model.add(Dense(512, activation='relu'))model.add(Dropout(0.5))model.add(Dense(256, activation='relu'))model.add(Dropout(0.5))model.add(Dense(128, activation='relu'))model.add(Dropout(0.5))model.add(Dense(101, activation='softmax'))

鉴于我们已经确定了结构,现在我们要加载训练过的权重,我们把它储存为了weights.hdf5:

# loading the trained weights 加载训练过的权重model.load_weights("weights.hdf5")

编译这个模型:

# compiling the model 编译模型model.compile(loss='categorical_crossentropy',optimizer='Adam',metrics=['accuracy'])

确定使用的损失函数,优化器,以及评测指标和训练模型使用的是同一套。

创建测试数据

按照UCF101官方文件的要求,你应该已经分别下载了训练/测试文件。在这个文件夹里,有一个“testlist01.txt”文件,它包含了所有的测试视频。我们将用这些视频来创建测试数据:

# getting the test list 获取测试列表f = open("testlist01.txt", "r")temp = f.read()videos = temp.split('n')# creating the dataframe 创建数据帧test = pd.DataFrame()test['video_name'] = videostest = test[:-1]test_videos = test['video_name']test.head()

3训练模型代码详解_代码详解:手把手教你建立自己的视频分类模型_第6张图片

我们现在有全部视频的列表储存在一个数据帧里面。想要把预期类别和实际类别编排在一起,我们还需要用到train_new.csv文件:

# creating the tags 创建标签train = pd.read_csv('UCF/train_new.csv')y = train['class']y = pd.get_dummies(y)

现在,我们要对测试组里的视频进行预测。

为测试视频进行预测

在写代码之前,先概括一下接下来的步骤。这几步能帮助你理解预测部分:

1. 首先,我们要创建两个空列表,一个用来储存预测结果,另一个来保存实际标签。

2. 然后,我们要从测试组里提取每个视频的帧,并将它们保存至一个文件夹中(在现有目录中创建一个名为temp的文件夹来保存)。在任意迭代上,我们将移除这个文件夹里其他的文件。

3. 之后,我们要读取temp文件夹i所有的帧,用预训练模型和预测标签提取它们的特征,然后用这个模型为特定的视频加标签,并将它们保存在列表里。

4. 我们会把视频的实际标签放在第二个列表里。

让我们来完成这些步骤,以及做出预测:

# creating two lists to store predicted and actual tags 创建两个列表来保存预测和实际标签predict = []actual = []# for loop to extract frames from each test video 为了loop循环从每个视频里提取帧for i in tqdm(range(test_videos.shape[0])): count = 0 videoFile = test_videos[i]cap = cv2.VideoCapture('UCF/'+videoFile.split(' ')[0].split('/')[1]) # capturing the video from the given path 从已知路径里获取视频 frameRate = cap.get(5) #frame rate 帧率 x=1 # removing all other files from the temp folder 将其他文件从temp文件夹移除 files = glob('temp/*') for f in files: os.remove(f) while(cap.isOpened()): frameId = cap.get(1) #current frame number 当下帧数 ret, frame = cap.read() if (ret != True): break if (frameId % math.floor(frameRate) == 0): # storing the frames of this particular video in temp folder 将该视频的帧保存至temp文件夹 filename ='temp/' + "_frame%d.jpg" % count;count+=1 cv2.imwrite(filename, frame) cap.release() # reading all the frames from temp folder 读取temp文件夹里的所有帧 images = glob("temp/*.jpg") prediction_images = [] for i in range(len(images)): img = image.load_img(images[i], target_size=(224,224,3)) img = image.img_to_array(img) img = img/255 prediction_images.append(img) # converting all the frames for a test video into numpy array 将测试组里所有帧转换为numpy数组 prediction_images = np.array(prediction_images) # extracting features using pre-trained model 用预训练模型提取特征 prediction_images = base_model.predict(prediction_images) # converting features in one dimensional array 将特征转为单维度数组 prediction_images = prediction_images.reshape(prediction_images.shape[0], 7*7*512) # predicting tags for each array 预测每一数组的标签 prediction = model.predict_classes(prediction_images) # appending the mode of predictions in predict list to assign the tag to the video 添加预测模式到预测列表来分配视频的标签 predict.append(y.columns.values[s.mode(prediction)[0][0]]) # appending the actual tag of the video 添加视频的实际标签 actual.append(videoFile.split('/')[1].split('_')[1])

这一步会花一些时间,鉴于测试组中有大约3,800个视频。得到预测结果后,我们就要推测模型的性能。

评估视频分类模型

终于要评估我们的模型了,让我们看看都有可能出现哪些问题。

我们有实际的视频标签,以及模型推测出的标签。我们将会用到这些数据来计算这个软件的准确度。在UCF101的官方文件页上,当前准确度为43.90%。我们的模型能够超过这个表现吗?让我们来看看吧!

# checking the accuracy of the predicted tags 核验预测标签的准确度from sklearn.metrics import accuracy_scoreaccuracy_score(predict, actual)*100

输出: 44.80570975416337

太好了!我们模型44.8%的准确度可以和官方文件中的43.9%一教高下。

你也许会疑惑我们为什么要对一个低于50%的准确度感到满意。事实上,这主要是因为我们的数据库太小了。我们只有大概13,000个视频,即使这个大小都是一个很小的区间。

c494130d9258de0862111c6680e8c5eb.png

留言 点赞 关注

我们一起分享AI学习与发展的干货

相关链接:

https://www.analyticsvidhya.com/blog/2019/09/step-by-step-deep-learning-tutorial-video-classification-python/

如需转载,请后台留言,遵守转载规范

你可能感兴趣的:(3训练模型代码详解)