政安晨:【Keras机器学习示例演绎】(三十)—— 使用变换器进行视频分类

本文主要是介绍政安晨:【Keras机器学习示例演绎】(三十)—— 使用变换器进行视频分类,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

数据收集

设置

定义超参数

数据准备

构建基于变换器的模型

培训的效用函数

模型训练和推理


政安晨的个人主页政安晨

欢迎 👍点赞✍评论⭐收藏

收录专栏: TensorFlow与Keras机器学习实战

希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正!

本文目标:用混合变压器训练视频分类器。

本示例是使用 CNN-RNN 架构(卷积神经网络-循环神经网络)进行视频分类示例的后续。这一次,我们将使用基于变换器的模型(Vaswani 等人)对视频进行分类。阅读本示例后,您将了解如何开发基于变换器的混合模型,用于在 CNN 特征图上运行的视频分类。

数据收集


与本例的前身一样,我们将使用 UCF101 数据集的子样本,这是一个著名的基准数据集。如果您想对更大的子样本甚至整个数据集进行操作,请参考下面这个Notebook。

!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

设置

import os
import keras
from keras import layers
from keras.applications.densenet import DenseNet121from tensorflow_docs.vis import embedimport matplotlib.pyplot as plt
import pandas as pd
import numpy as np
import imageio
import cv2

定义超参数

MAX_SEQ_LENGTH = 20
NUM_FEATURES = 1024
IMG_SIZE = 128EPOCHS = 5

数据准备


在本例中,除了以下改动外,我们将主要沿用相同的数据准备步骤:

我们将图像大小从 224x224 缩小到 128x128,以加快计算速度。
我们不再使用预先训练好的 InceptionV3 网络,而是使用预先训练好的 DenseNet121 进行特征提取。
我们直接将较短的视频填充到 MAX_SEQ_LENGTH 长度。


首先,让我们加载 DataFrames。

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)}")center_crop_layer = layers.CenterCrop(IMG_SIZE, IMG_SIZE)def crop_center(frame):cropped = center_crop_layer(frame[None, ...])cropped = keras.ops.convert_to_numpy(cropped)cropped = keras.ops.squeeze(cropped)return cropped# Following method is modified from this tutorial:
# https://www.tensorflow.org/hub/tutorials/action_recognition_with_tf_hub
def load_video(path, max_frames=0, offload_to_cpu=False):cap = cv2.VideoCapture(path)frames = []try:while True:ret, frame = cap.read()if not ret:breakframe = frame[:, :, [2, 1, 0]]frame = crop_center(frame)if offload_to_cpu and keras.backend.backend() == "torch":frame = frame.to("cpu")frames.append(frame)if len(frames) == max_frames:breakfinally:cap.release()if offload_to_cpu and keras.backend.backend() == "torch":return np.array([frame.to("cpu").numpy() for frame in frames])return np.array(frames)def build_feature_extractor():feature_extractor = DenseNet121(weights="imagenet",include_top=False,pooling="avg",input_shape=(IMG_SIZE, IMG_SIZE, 3),)preprocess_input = keras.applications.densenet.preprocess_inputinputs = 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()# Label preprocessing with StringLookup.
label_processor = keras.layers.StringLookup(num_oov_indices=0, vocabulary=np.unique(train_df["tag"]), mask_token=None
)
print(label_processor.get_vocabulary())def prepare_all_videos(df, root_dir):num_samples = len(df)video_paths = df["video_name"].values.tolist()labels = df["tag"].valueslabels = label_processor(labels[..., None]).numpy()# `frame_features` are what we will feed to our sequence model.frame_features = np.zeros(shape=(num_samples, MAX_SEQ_LENGTH, NUM_FEATURES), dtype="float32")# For each video.for idx, path in enumerate(video_paths):# Gather all its frames and add a batch dimension.frames = load_video(os.path.join(root_dir, path))# Pad shorter videos.if len(frames) < MAX_SEQ_LENGTH:diff = MAX_SEQ_LENGTH - len(frames)padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3))frames = np.concatenate(frames, padding)frames = frames[None, ...]# Initialize placeholder to store the features of the current video.temp_frame_features = np.zeros(shape=(1, MAX_SEQ_LENGTH, NUM_FEATURES), dtype="float32")# Extract features from the frames of the current video.for i, batch in enumerate(frames):video_length = batch.shape[0]length = min(MAX_SEQ_LENGTH, video_length)for j in range(length):if np.mean(batch[j, :]) > 0.0:temp_frame_features[i, j, :] = feature_extractor.predict(batch[None, j, :])else:temp_frame_features[i, j, :] = 0.0frame_features[idx,] = temp_frame_features.squeeze()return frame_features, labels
Total videos for training: 594
Total videos for testing: 224
['CricketShot', 'PlayingCello', 'Punch', 'ShavingBeard', 'TennisSwing']

在 train_df 和 test_df 上调用 prepare_all_videos() 需要约 20 分钟才能完成。因此,为了节省时间,我们在这里下载已经预处理过的 NumPy 数组:

!!wget -q https://git.io/JZmf4 -O top5_data_prepared.tar.gz
!!tar -xf top5_data_prepared.tar.gz
train_data, train_labels = np.load("train_data.npy"), np.load("train_labels.npy")
test_data, test_labels = np.load("test_data.npy"), np.load("test_labels.npy")print(f"Frame features in train set: {train_data.shape}")
[]Frame features in train set: (594, 20, 1024)

构建基于变换器的模型


我们将在弗朗索瓦-乔莱(François Chollet)所著《深度学习与 Python》(第二版)一书这一章中分享的代码基础上构建模型。

首先,构成 Transformer 基本模块的自我注意层是不分顺序的。

由于视频是有序的帧序列,我们需要我们的变换器模型考虑到顺序信息。我们通过位置编码来实现这一点。我们只需用嵌入层嵌入视频中帧的位置。然后,我们将这些位置嵌入添加到预先计算的 CNN 特征图中。

class PositionalEmbedding(layers.Layer):def __init__(self, sequence_length, output_dim, **kwargs):super().__init__(**kwargs)self.position_embeddings = layers.Embedding(input_dim=sequence_length, output_dim=output_dim)self.sequence_length = sequence_lengthself.output_dim = output_dimdef build(self, input_shape):self.position_embeddings.build(input_shape)def call(self, inputs):# The inputs are of shape: `(batch_size, frames, num_features)`inputs = keras.ops.cast(inputs, self.compute_dtype)length = keras.ops.shape(inputs)[1]positions = keras.ops.arange(start=0, stop=length, step=1)embedded_positions = self.position_embeddings(positions)return inputs + embedded_positions

现在,我们可以为变换器创建一个子类层。

class TransformerEncoder(layers.Layer):def __init__(self, embed_dim, dense_dim, num_heads, **kwargs):super().__init__(**kwargs)self.embed_dim = embed_dimself.dense_dim = dense_dimself.num_heads = num_headsself.attention = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim, dropout=0.3)self.dense_proj = keras.Sequential([layers.Dense(dense_dim, activation=keras.activations.gelu),layers.Dense(embed_dim),])self.layernorm_1 = layers.LayerNormalization()self.layernorm_2 = layers.LayerNormalization()def call(self, inputs, mask=None):attention_output = self.attention(inputs, inputs, attention_mask=mask)proj_input = self.layernorm_1(inputs + attention_output)proj_output = self.dense_proj(proj_input)return self.layernorm_2(proj_input + proj_output)

培训的效用函数

def get_compiled_model(shape):sequence_length = MAX_SEQ_LENGTHembed_dim = NUM_FEATURESdense_dim = 4num_heads = 1classes = len(label_processor.get_vocabulary())inputs = keras.Input(shape=shape)x = PositionalEmbedding(sequence_length, embed_dim, name="frame_position_embedding")(inputs)x = TransformerEncoder(embed_dim, dense_dim, num_heads, name="transformer_layer")(x)x = layers.GlobalMaxPooling1D()(x)x = layers.Dropout(0.5)(x)outputs = layers.Dense(classes, activation="softmax")(x)model = keras.Model(inputs, outputs)model.compile(optimizer="adam",loss="sparse_categorical_crossentropy",metrics=["accuracy"],)return modeldef run_experiment():filepath = "/tmp/video_classifier.weights.h5"checkpoint = keras.callbacks.ModelCheckpoint(filepath, save_weights_only=True, save_best_only=True, verbose=1)model = get_compiled_model(train_data.shape[1:])history = model.fit(train_data,train_labels,validation_split=0.15,epochs=EPOCHS,callbacks=[checkpoint],)model.load_weights(filepath)_, accuracy = model.evaluate(test_data, test_labels)print(f"Test accuracy: {round(accuracy * 100, 2)}%")return model

模型训练和推理

trained_model = run_experiment()
Epoch 1/516/16 ━━━━━━━━━━━━━━━━━━━━ 0s 160ms/step - accuracy: 0.5286 - loss: 2.6762
Epoch 1: val_loss improved from inf to 7.75026, saving model to /tmp/video_classifier.weights.h516/16 ━━━━━━━━━━━━━━━━━━━━ 7s 272ms/step - accuracy: 0.5387 - loss: 2.6139 - val_accuracy: 0.0000e+00 - val_loss: 7.7503
Epoch 2/515/16 ━━━━━━━━━━━━━━━━━━[37m━━  0s 4ms/step - accuracy: 0.9396 - loss: 0.2264 
Epoch 2: val_loss improved from 7.75026 to 1.96635, saving model to /tmp/video_classifier.weights.h516/16 ━━━━━━━━━━━━━━━━━━━━ 0s 20ms/step - accuracy: 0.9406 - loss: 0.2186 - val_accuracy: 0.4000 - val_loss: 1.9664
Epoch 3/514/16 ━━━━━━━━━━━━━━━━━[37m━━━  0s 4ms/step - accuracy: 0.9823 - loss: 0.0384 
Epoch 3: val_loss did not improve from 1.9663516/16 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9822 - loss: 0.0391 - val_accuracy: 0.3667 - val_loss: 3.7076
Epoch 4/515/16 ━━━━━━━━━━━━━━━━━━[37m━━  0s 4ms/step - accuracy: 0.9825 - loss: 0.0681 
Epoch 4: val_loss did not improve from 1.9663516/16 ━━━━━━━━━━━━━━━━━━━━ 0s 5ms/step - accuracy: 0.9831 - loss: 0.0674 - val_accuracy: 0.4222 - val_loss: 3.7957
Epoch 5/515/16 ━━━━━━━━━━━━━━━━━━[37m━━  0s 4ms/step - accuracy: 1.0000 - loss: 0.0035 
Epoch 5: val_loss improved from 1.96635 to 1.56071, saving model to /tmp/video_classifier.weights.h516/16 ━━━━━━━━━━━━━━━━━━━━ 0s 15ms/step - accuracy: 1.0000 - loss: 0.0033 - val_accuracy: 0.6333 - val_loss: 1.56077/7 ━━━━━━━━━━━━━━━━━━━━ 0s 3ms/step - accuracy: 0.9286 - loss: 0.4434
Test accuracy: 89.29%

注:该模型有 ~423 万个参数,远远多于我们在本例前传中所使用的序列模型(99918 个参数)。这种 Transformer 模型最好使用更大的数据集和更长的预训练时间。

def prepare_single_video(frames):frame_features = np.zeros(shape=(1, MAX_SEQ_LENGTH, NUM_FEATURES), dtype="float32")# Pad shorter videos.if len(frames) < MAX_SEQ_LENGTH:diff = MAX_SEQ_LENGTH - len(frames)padding = np.zeros((diff, IMG_SIZE, IMG_SIZE, 3))frames = np.concatenate(frames, padding)frames = frames[None, ...]# Extract features from the frames of the current video.for i, batch in enumerate(frames):video_length = batch.shape[0]length = min(MAX_SEQ_LENGTH, video_length)for j in range(length):if np.mean(batch[j, :]) > 0.0:frame_features[i, j, :] = feature_extractor.predict(batch[None, j, :])else:frame_features[i, j, :] = 0.0return frame_featuresdef predict_action(path):class_vocab = label_processor.get_vocabulary()frames = load_video(os.path.join("test", path), offload_to_cpu=True)frame_features = prepare_single_video(frames)probabilities = trained_model.predict(frame_features)[0]plot_x_axis, plot_y_axis = [], []for i in np.argsort(probabilities)[::-1]:plot_x_axis.append(class_vocab[i])plot_y_axis.append(probabilities[i])print(f"  {class_vocab[i]}: {probabilities[i] * 100:5.2f}%")plt.bar(plot_x_axis, plot_y_axis, label=plot_x_axis)plt.xlabel("class_label")plt.xlabel("Probability")plt.show()return frames# This utility is for visualization.
# Referenced from:
# https://www.tensorflow.org/hub/tutorials/action_recognition_with_tf_hub
def to_gif(images):converted_images = images.astype(np.uint8)imageio.mimsave("animation.gif", converted_images, fps=10)return embed.embed_file("animation.gif")test_video = np.random.choice(test_df["video_name"].values.tolist())
print(f"Test video path: {test_video}")
test_frames = predict_action(test_video)
to_gif(test_frames[:MAX_SEQ_LENGTH])
Test video path: v_ShavingBeard_g03_c02.avi1/1 ━━━━━━━━━━━━━━━━━━━━ 20s 20s/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 8ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 10ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 11ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 12ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 0s 9ms/step1/1 ━━━━━━━━━━━━━━━━━━━━ 1s 557ms/stepShavingBeard: 100.00%Punch:  0.00%CricketShot:  0.00%TennisSwing:  0.00%PlayingCello:  0.00%

我们的模型性能远未达到最佳,因为它是在一个小数据集上训练出来的。


这篇关于政安晨:【Keras机器学习示例演绎】(三十)—— 使用变换器进行视频分类的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/957002

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

【Prometheus】PromQL向量匹配实现不同标签的向量数据进行运算

✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全栈,前后端开发,小程序开发,人工智能,js逆向,App逆向,网络系统安全,数据分析,Django,fastapi