2024年最全深度学习进阶,Keras视频分类

│ ├── soccer.mp4

│ └── tennis.mp4

├── model

│ ├── activity.model

│ └── lb.pickle

├── output

├── plot.png

├── predict_video.py

└── train.py

我们的训练图像数据位于 Sports-Type-Classifier/data/ 目录中,按类别组织。

我从 YouTube 中为我们提取了三个 example_clips/ 来测试我们的模型。三个剪辑的积分位于“Keras 视频分类结果”部分的底部。

我们的分类器文件位于 model/ 目录中。包括 activity.model(经过训练的 Keras 模型)和 lb.pickle(我们的标签二值化器)。

一个空的 output/ 文件夹是我们将存储视频分类结果的位置。

我们将在今天的教程中介绍两个 Python 脚本:

  • ​ train.py :一个 Keras 训练脚本,它抓取我们关心的数据集类图像,加载 ResNet50 CNN,并应用 ImageNet 权重的转移学习/微调来训练我们的模型。训练脚本生成/输出三个文件:

  • model/activity.model :基于 ResNet50 的微调分类器,用于识别运动。

  • model/lb.pickle :包含我们独特的类标签的序列化标签二值化器。

  • plot.png :准确率/损失训练历史图。

  • predict_video.py :从 example_clips/ 加载输入视频,然后使用今天的滚动平均方法对视频进行理想的分类。

实施我们的 Keras 培训脚本

让我们继续实施我们的训练脚本,用于训练Keras CNN来识别每一项体育活动。

打开train.py文件并插入以下代码:

set the matplotlib backend so figures can be saved in the background

import matplotlib

matplotlib.use(“Agg”)

import the necessary packages

from tensorflow.keras.preprocessing.image import ImageDataGenerator

from tensorflow.keras.layers import AveragePooling2D

from tensorflow.keras.applications import ResNet50

from tensorflow.keras.layers import Dropout

from tensorflow.keras.layers import Flatten

from tensorflow.keras.layers import Dense

from tensorflow.keras.layers import Input

from tensorflow.keras.models import Model

from tensorflow.keras.optimizers import SGD

from sklearn.preprocessing import LabelBinarizer

from sklearn.model_selection import train_test_split

from sklearn.metrics import classification_report

from imutils import paths

import matplotlib.pyplot as plt

import numpy as np

import argparse

import pickle

import cv2

import os

导入必要的包来训练我们的分类器:

matplotlib :用于绘图。第 3 行设置后端,以便我们可以将训练图输出到 .png 图像文件。

tensorflow.keras:用于深度学习。也就是说,我们将使用 ResNet50 CNN。我们还将使用您可以在上周的教程中阅读的 ImageDataGenerator。

sklearn :从 scikit-learn,我们将使用他们的 LabelBinarizer 实现来对我们的类标签进行单热编码。

train_test_split 函数将我们的数据集分割成训练和测试分割。我们还将以传统格式打印分类报告。

path :包含用于列出给定路径中的所有图像文件的便利函数。从那里我们将能够将我们的图像加载到内存中。

numpy :Python 的事实上的数值处理库。

argparse :用于解析命令行参数。

pickle :用于将我们的标签二值化器序列化到磁盘。

cv2:OpenCV。

os :操作系统模块将用于确保我们获取与操作系统相关的正确文件/路径分隔符。

现在让我们继续解析我们的命令行参数:

construct the argument parser and parse the arguments

ap = argparse.ArgumentParser()

ap.add_argument(“-d”, “–dataset”, required=True,

help=“path to input dataset”)

ap.add_argument(“-m”, “–model”, required=True,

help=“path to output serialized model”)

ap.add_argument(“-l”, “–label-bin”, required=True,

help=“path to output label binarizer”)

ap.add_argument(“-e”, “–epochs”, type=int, default=25,

help=“# of epochs to train our network for”)

ap.add_argument(“-p”, “–plot”, type=str, default=“plot.png”,

help=“path to output loss/accuracy plot”)

args = vars(ap.parse_args())

我们的脚本接受五个命令行参数,其中前三个是必需的:

–dataset :输入数据集的路径。

–model :我们输出 Keras 模型文件的路径。

–label-bin :我们的输出标签二值化器pickle文件的路径。

–epochs :我们的网络要训练多少个时期——默认情况下,我们将训练 25 个时期,但正如我将在本教程后面展示的,50 个时期可以带来更好的结果。

–plot :我们的输出绘图图像文件的路径——默认情况下,它将被命名为 plot.png 并放置在与此训练脚本相同的目录中。

解析并掌握我们的命令行参数后,让我们继续初始化我们的 LABELS 并加载我们的数据:

initialize the set of labels from the spots activity dataset we are

going to train our network on

LABELS = set([“weight_lifting”, “tennis”, “football”])

grab the list of images in our dataset directory, then initialize

the list of data (i.e., images) and class images

print(“[INFO] loading images…”)

imagePaths = list(paths.list_images(args[“dataset”]))

data = []

labels = []

loop over the image paths

for imagePath in imagePaths:

extract the class label from the filename

label = imagePath.split(os.path.sep)[-2]

if the label of the current image is not part of of the labels

are interested in, then ignore the image

if label not in LABELS:

continue

load the image, convert it to RGB channel ordering, and resize

it to be a fixed 224x224 pixels, ignoring aspect ratio

image = cv2.imread(imagePath)

image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)

image = cv2.resize(image, (224, 224))

update the data and labels lists, respectively

data.append(image)

labels.append(label)

定义类 LABELS 的集合。该集合中不存在的所有标签都将被排除在我们的数据集之外。为了节省训练时间,我们的数据集将只包含举重、网球和足球/足球。通过对 LABELS 集进行更改,您可以随意使用其他类。

初始化我们的数据和标签列表。

遍历所有 imagePath。

在循环中,首先我们从 imagePath 中提取类标签

忽略不在 LABELS 集合中的任何标签。

加载并预处理图像。预处理包括将 OpenCV 的颜色通道交换到 Keras 兼容性并将大小调整为 224×224px。在此处阅读有关调整 CNN 图像大小的更多信息。要了解有关预处理重要性的更多信息,请务必参阅使用 Python 进行计算机视觉深度学习。

然后分别将图像和标签添加到数据和标签列表中。

继续,我们将对我们的标签进行单热编码并分区我们的数据:

convert the data and labels to NumPy arrays

data = np.array(data)

labels = np.array(labels)

perform one-hot encoding on the labels

lb = LabelBinarizer()

labels = lb.fit_transform(labels)

partition the data into training and testing splits using 75% of

the data for training and the remaining 25% for testing

(trainX, testX, trainY, testY) = train_test_split(data, labels,

test_size=0.25, stratify=labels, random_state=42)

将我们的数据和标签列表转换为 NumPy 数组。

执行one-hot编码。one-hot编码是一种通过二进制数组元素标记活动类标签的方法。 例如,“足球”可能是 array([1, 0, 0]) 而“举重”可能是 array([0, 0, 1]) 。 请注意在任何给定时间只有一个类是“热的”。

按照4:1的比例,将我们的数据分成训练和测试部分。

初始化我们的数据增强对象:

initialize the training data augmentation object

trainAug = ImageDataGenerator(

rotation_range=30,

zoom_range=0.15,

width_shift_range=0.2,

height_shift_range=0.2,

shear_range=0.15,

horizontal_flip=True,

fill_mode=“nearest”)

initialize the validation/testing data augmentation object (which

we’ll be adding mean subtraction to)

valAug = ImageDataGenerator()

define the ImageNet mean subtraction (in RGB order) and set the

the mean subtraction value for each of the data augmentation

objects

mean = np.array([123.68, 116.779, 103.939], dtype=“float32”)

trainAug.mean = mean

valAug.mean = mean

初始化了两个数据增强对象——一个用于训练,一个用于验证。 在计算机视觉的深度学习中,几乎总是建议使用数据增强来提高模型泛化能力。

trainAug 对象对我们的数据执行随机旋转、缩放、移位、剪切和翻转。 您可以在此处阅读有关 ImageDataGenerator 和 fit 的更多信息。 正如我们上周强调的那样,请记住,使用 Keras,图像将即时生成(这不是附加操作)。

不会对验证数据 (valAug) 进行扩充,但我们将执行均值减法。

平均像素值,设置 trainAug 和 valAug 的均值属性,以便在训练/评估期间生成图像时进行均值减法。 现在,我们将执行我喜欢称之为“网络手术”的操作,作为微调的一部分:

load the ResNet-50 network, ensuring the head FC layer sets are left

off

baseModel = ResNet50(weights=“imagenet”, include_top=False,

input_tensor=Input(shape=(224, 224, 3)))

construct the head of the model that will be placed on top of the

the base model

headModel = baseModel.output

headModel = AveragePooling2D(pool_size=(7, 7))(headModel)

headModel = Flatten(name=“flatten”)(headModel)

headModel = Dense(512, activation=“relu”)(headModel)

headModel = Dropout(0.5)(headModel)

headModel = Dense(len(lb.classes_), activation=“softmax”)(headModel)

place the head FC model on top of the base model (this will become

the actual model we will train)

model = Model(inputs=baseModel.input, outputs=headModel)

loop over all layers in the base model and freeze them so they will

not be updated during the training process

for layer in baseModel.layers:

layer.trainable = False

加载用 ImageNet 权重预训练的 ResNet50,同时切掉网络的头部。

组装了一个新的 headModel 并将其缝合到 baseModel 上。

我们现在将冻结 baseModel,以便它不会通过反向传播进行训练。

让我们继续编译+训练我们的模型:

compile our model (this needs to be done after our setting our

layers to being non-trainable)

print(“[INFO] compiling model…”)

opt = SGD(lr=1e-4, momentum=0.9, decay=1e-4 / args[“epochs”])

model.compile(loss=“categorical_crossentropy”, optimizer=opt,

metrics=[“accuracy”])

train the head of the network for a few epochs (all other layers

are frozen) – this will allow the new FC layers to start to become

initialized with actual “learned” values versus pure random

print(“[INFO] training head…”)

H = model.fit(

x=trainAug.flow(trainX, trainY, batch_size=32),

steps_per_epoch=len(trainX) // 32,

validation_data=valAug.flow(testX, testY),

validation_steps=len(testX) // 32,

epochs=args[“epochs”])

以前,TensorFlow/Keras 需要使用一种名为 .fit_generator 的方法来完成数据增强。现在,.fit 方法也可以处理数据增强,从而使代码更加一致。这也适用于从 .predict_generator 到 .predict 的迁移。请务必查看我关于 fit 和 fit_generator 以及数据增强的文章。

使用随机梯度下降 (SGD) 优化器编译我们的模型,初始学习率为 1e-4,学习率衰减。我们使用“categorical_crossentropy”损失来训练多类。如果您只使用两个类,请务必使用“binary_crossentropy”损失。

在我们的模型上调用 fit_generator 函数用数据增强和均值减法训练我们的网络。 请记住,我们的 baseModel 已冻结,我们只训练头部。这被称为“微调”。要快速了解微调,请务必阅读我之前的文章。要更深入地了解微调,请获取使用 Python 进行计算机视觉深度学习的 Practitioner Bundle 的副本。

我们将通过评估我们的网络并绘制训练历史来开始总结:

evaluate the network

print(“[INFO] evaluating network…”)

predictions = model.predict(x=testX.astype(“float32”), batch_size=32)

print(classification_report(testY.argmax(axis=1),

predictions.argmax(axis=1), target_names=lb.classes_))

plot the training loss and accuracy

N = args[“epochs”]

plt.style.use(“ggplot”)

plt.figure()

plt.plot(np.arange(0, N), H.history[“loss”], label=“train_loss”)

plt.plot(np.arange(0, N), H.history[“val_loss”], label=“val_loss”)

plt.plot(np.arange(0, N), H.history[“accuracy”], label=“train_acc”)

plt.plot(np.arange(0, N), H.history[“val_accuracy”], label=“val_acc”)

plt.title(“Training Loss and Accuracy on Dataset”)

plt.xlabel(“Epoch #”)

plt.ylabel(“Loss/Accuracy”)

plt.legend(loc=“lower left”)

plt.savefig(args[“plot”])

为了使此绘图片段与 TensorFlow 2+ 兼容,更新了 H.history 字典键以完全拼出“accuracy”无“acc”(H.history[“val_accuracy”] 和 H.历史[“准确性”])。 “val”没有拼写为“validation”,这有点令人困惑; 我们必须学会热爱 API 并与之共存,并始终记住这是一项正在进行的工作,世界各地的许多开发人员都在为之做出贡献。

在我们在测试集上评估我们的网络并打印分类报告

之后,我们继续使用 matplotlib

绘制准确率/损失曲线。 该图通过第 164 行保存到磁盘。

最后将我们的模型和标签二值化器 (lb) 序列化到磁盘:

serialize the model to disk

print(“[INFO] serializing network…”)

model.save(args[“model”], save_format=“h5”)

serialize the label binarizer to disk

f = open(args[“label_bin”], “wb”)

f.write(pickle.dumps(lb))

f.close()

训练结果

===============================================================

在我们 使用我们的 CNN 对视频中的帧进行分类,然后) 利用我们的 CNN 进行视频分类之前,我们首先需要训练模型。

确保您已使用本教程的“下载”部分将源代码下载到此图像(以及下载运动类型数据集)。

从那里,打开一个终端并执行以下命令:

$ python train.py --dataset Sports-Type-Classifier/data --model model/activity.model \

–label-bin output/lb.pickle --epochs 50

[INFO] loading images…

[INFO] compiling model…

[INFO] training head…

Epoch 1/50

48/48 [==============================] - 10s 209ms/step - loss: 1.4184 - accuracy: 0.4421 - val_loss: 0.7866 - val_accuracy: 0.6719

Epoch 2/50

48/48 [==============================] - 10s 198ms/step - loss: 0.9002 - accuracy: 0.6086 - val_loss: 0.5476 - val_accuracy: 0.7832

Epoch 3/50

48/48 [==============================] - 9s 198ms/step - loss: 0.7188 - accuracy: 0.7020 - val_loss: 0.4690 - val_accuracy: 0.8105

Epoch 4/50

48/48 [==============================] - 10s 203ms/step - loss: 0.6421 - accuracy: 0.7375 - val_loss: 0.3986 - val_accuracy: 0.8516

Epoch 5/50

48/48 [==============================] - 10s 200ms/step - loss: 0.5496 - accuracy: 0.7770 - val_loss: 0.3599 - val_accuracy: 0.8652

Epoch 46/50

48/48 [==============================] - 9s 192ms/step - loss: 0.2066 - accuracy: 0.9217 - val_loss: 0.1618 - val_accuracy: 0.9336

Epoch 47/50

48/48 [==============================] - 9s 193ms/step - loss: 0.2064 - accuracy: 0.9204 - val_loss: 0.1622 - val_accuracy: 0.9355

Epoch 48/50

48/48 [==============================] - 9s 192ms/step - loss: 0.2092 - accuracy: 0.9217 - val_loss: 0.1604 - val_accuracy: 0.9375

Epoch 49/50

48/48 [==============================] - 9s 195ms/step - loss: 0.1935 - accuracy: 0.9290 - val_loss: 0.1620 - val_accuracy: 0.9375

Epoch 50/50

48/48 [==============================] - 9s 192ms/step - loss: 0.2109 - accuracy: 0.9164 - val_loss: 0.1561 - val_accuracy: 0.9395

[INFO] evaluating network…

precision recall f1-score support

football 0.93 0.96 0.95 196

tennis 0.92 0.92 0.92 179

weight_lifting 0.97 0.92 0.95 143

accuracy 0.94 518

macro avg 0.94 0.94 0.94 518

weighted avg 0.94 0.94 0.94 518

[INFO] serializing network…

image-20211029223546026

如您所见,在体育数据集上对 ResNet50 进行微调后,我们获得了约 94% 的准确率。 检查我们的模型目录,我们可以看到微调模型和标签二值化器已经序列化到磁盘:

使用 Keras 进行视频分类和滚动预测平均

=================================================================================

我们现在准备通过滚动预测精度使用 Keras 实现视频分类! 为了创建这个脚本,我们将利用视频的时间特性,特别是假设视频中的后续帧将具有相似的语义内容。 通过执行滚动预测准确性,我们将能够“平滑”预测并避免“预测闪烁”。 让我们开始吧——打开 predict_video.py 文件并插入以下代码:

import the necessary packages

from tensorflow.keras.models import load_model

from collections import deque

import numpy as np

import argparse

import pickle

import cv2

construct the argument parser and parse the arguments

ap = argparse.ArgumentParser()

ap.add_argument(“-m”, “–model”, required=True,

help=“path to trained serialized model”)

ap.add_argument(“-l”, “–label-bin”, required=True,

help=“path to label binarizer”)

ap.add_argument(“-i”, “–input”, required=True,

help=“path to our input video”)

ap.add_argument(“-o”, “–output”, required=True,

help=“path to our output video”)

ap.add_argument(“-s”, “–size”, type=int, default=128,

help=“size of queue for averaging”)

args = vars(ap.parse_args())

加载必要的包和模块。 特别是,我们将使用 Python 集合模块中的 deque 来协助我们的滚动平均算法。

然后,解析五个命令行参数,其中四个是必需的:

–model :从我们之前的训练步骤生成的输入模型的路径。

–label-bin :前一个脚本生成的序列化 pickle 格式标签二值化器的路径。

–input :用于视频分类的输入视频的路径。

–output :我们将保存到磁盘的输出视频的路径。

–size :滚动平均队列的最大大小(默认为 128)。 对于稍后的一些示例结果,我们将大小设置为 1,以便不执行平均。

有了我们的导入和命令行 args ,我们现在准备执行初始化:

load the trained model and label binarizer from disk

print(“[INFO] loading model and label binarizer…”)

model = load_model(args[“model”])

lb = pickle.loads(open(args[“label_bin”], “rb”).read())

initialize the image mean for mean subtraction along with the

predictions queue

mean = np.array([123.68, 116.779, 103.939][::1], dtype=“float32”)

Q = deque(maxlen=args[“size”])

加载我们的模型和标签二值化器。

然后设置我们的平均减法值。 我们将使用双端队列来实现我们的滚动预测平均。

我们的双端队列 Q 使用等于 args[“size”] 值的 maxlen 初始化。

让我们初始化我们的 cv2.VideoCapture 对象并开始循环视频帧:

initialize the video stream, pointer to output video file, and

frame dimensions

vs = cv2.VideoCapture(args[“input”])

writer = None

(W, H) = (None, None)

loop over frames from the video file stream

while True:

read the next frame from the file

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里无偿获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 20
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值