当前位置: 首页>数据库>正文

MindSpore实现X3D

X3D

X3D是一篇发表在CVPR2020上的关于视频动作分类的文章

原文链接

Code

Gitee

Github

NoteBook

算法原理

X3D的工作受机器学习中特征选择方法的启发,采用一个简单的逐步拓展网络的方法,以X2D图像分类模型为基础,分别在宽度、深度、帧率、帧数以及分辨率等维度逐步进行拓展,从2D空间拓展为3D时空域。每一次只在一个维度上进行拓展,并在计算量和精度上进行权衡,选取最佳的拓展方式。

MindSpore实现X3D,第1张

作者对比了图像分类网络的发展史,这些图像分类模型经历了对深度、分辨率、通道宽度等维度的探索,但视频分类模型只是简单的对时间维度进行扩张。因此作者提出了对不同维度改进的思考。

3D网络最佳的时间采样策略是什么?长的时间序列和较为稀疏的采样是否优于短时间内的稠密采样?(采样帧率)

是否需要一个更好的空间分辨率?目前的工作都为提高效率而使用低分辨率。是否存在一个最大空间分辨率导致性能饱和?(空间分辨率)

更快的帧率+更“瘦”的模型好亦或更慢的帧率+更“宽”的模型好?也即slow分支和fast分支哪种的结构更好?又或者存在一个二者的中间结构更好?(帧率与宽度)

当增加网络宽度时,是增加全局的宽度好还是增加bottleneck的宽度好?(宽度,inverted bottlenetck结构的借鉴)

网络变深的同时,是否应该增加输入的时空分辨率以保证感受野大小足够大?又或者应该增大不同的维度?(深度与时空分辨率)

MindSpore实现X3D,第2张

X3D整体网络结构如上,卷积核的维度表示为{T×$S^2$ , C }。X3D通过6个轴MindSpore实现X3D,\{\gamma_t,\gamma_{\tau},\gamma_{s},\gamma_{w},\gamma_{b},\gamma_{d}\},第3张来对X2D进行拓展,X2D在这6个轴上都为1。

拓张维度:

1. X-Fast:采样帧间隔MindSpore实现X3D, \gamma_T,第4张

2. X-Temporal:采样帧数 MindSpore实现X3D,\gamma_t,第5张

3. X-Spatial:空间分辨率 MindSpore实现X3D,\gamma_{s},第6张

4. X-Depth:网络深度 MindSpore实现X3D,\gamma_{d},第7张

5. X-Width:网络宽度 MindSpore实现X3D,\gamma_{w},第8张

6. X-Bottelneck:bottleneck宽度 MindSpore实现X3D,\gamma_{b},第9张

Forward expansion

前向拓张是给定复杂度,逐步逐维度进行拓张。

首先给定两个指标,一个是衡量当前扩张因子X好坏的J(X),该指标得分越高,拓展因子越好,得分越低,拓展因子越差,这对应的是模型的准确率。第二个是复杂度评判因子C(X),对应的是网络所需的浮点操作计算量,那么目标即为在给定复杂度C(X)=c的条件下,使得J(X)最大的扩张因子。

在网络尝试寻找最佳的拓展因子时,每一步只扩张一个维度,其他维度保持不变,而每一步最好的扩张因子被保留,接着进行下一步扩张。即在初始阶段,模型为X2D,对应着一个计算复杂度,然后给定一个目标复杂度,模型要通过每次改变一个因子,然后一步步变换到目标复杂度。且每一次改变所对应的改变量也是定义好的,即让当前的模型的复杂度变成两倍。再者,每一步的扩张是渐进式的,也即复杂度约2倍增长。这种方法可以看成是坐标下降法的特殊形式,扩张2倍的各维度操作具体如下:

1. X-Fast:MindSpore实现X3D, \gamma_t = 2\gamma_t,第10张

2. X-Temporal: MindSpore实现X3D,\gamma_t = 2\gamma_t,第11张

3. X-Spatial: MindSpore实现X3D,\gamma_{s} = \sqrt{2}\gamma_{s} ,第12张

4. X-Depth: MindSpore实现X3D,\gamma_{d} = 2.2\gamma_{d},第13张

5. X-Width:MindSpore实现X3D, \gamma_{w} = 2\gamma_{w},第14张

6. X-Bottelneck: MindSpore实现X3D,\gamma_{b} = 2.25\gamma_{b},第15张

Backward contraction

后向收缩是在超过复杂度时,进行回溯收缩。

由于前向扩展只在离散步骤中产生模型,如果目标复杂度被前向扩展步骤超过,他们执行后向收缩步骤以满足所需的目标复杂度。此收缩被实现为上一次展开的简单缩减,以便与目标相匹配。例如,如果最后一步将帧率提高了两倍,那么他们就会向后收缩将帧率降低到一个小于2的倍数,以大致匹配所需的目标复杂度。

渐进式拓张

MindSpore实现X3D,第16张

扩张任意一个维度都增加了准确率,验证了最初的想法。

第一步扩张的不是时间维度,而是bottleneck宽度,这验证了MobileNetV2中的倒置残差结构,作者认为原因可能是这些层使用了channel-wise卷积十分轻量,因此首先扩张这个维度比较economical。且不同维度准确率变化很大,扩张bottleneck宽度达到了55.0%,而扩张深度只有51.3%。

第二步扩张的为帧数(因为最初只有单帧,因此扩展采样帧间隔和帧数是等同的),这也是我们认为“最应该在第一步扩张的维度”,因为者提供更多的时间信息。

第三步扩张的为空间分辨率,紧接着第四步为深度,接着是时间分辨率(帧率)和输入长度(帧间隔和帧数),然后是两次空间分辨率扩张,第十步再次扩张深度,这符合直观的想法,扩张深度会扩张滤波器感受野的大小。

值得注意的是,尽管模型一开始宽度比较小,但直到第十一步,模型才开始扩张全局的宽度,这使得X3D很像SlowFast的fast分支设计(时空分辨率很大但宽度很小),最后图里没显示扩张的两步为帧间隔和深度。

结果

MindSpore实现X3D,第17张

?环境准备

????????git clone https://gitee.com/yanlq46462828/zjut_mindvideo.git

????????cd zjut_mindvideo

????????# Please first install mindspore according to instructions on the official website: https://www.mindspore.cn/install

????????pip install -r requirements.txt

????????pip install -e .

训练流程

```python

from mindspore import nn

from mindspore.train import Model

from mindspore.train.callback import ModelCheckpoint, CheckpointConfig, LossMonitor

from mindspore.nn.loss import SoftmaxCrossEntropyWithLogits

from mindspore.nn.metrics import Accuracy

from msvideo.utils.check_param import Validator,Rel

```

数据集加载

通过基于VideoDataset编写的Kinetic400类来加载kinetic400数据集。

```python

from msvideo.data.kinetics400 import Kinetic400

dataset = Kinetic400(path='/home/publicfile/kinetics-400',

? ? ? ? ? ? ? ? ? ? split="train",

? ? ? ? ? ? ? ? ? ? seq=16,

? ? ? ? ? ? ? ? ? ? seq_mode='interval',

? ? ? ? ? ? ? ? ? ? num_parallel_workers=4,

? ? ? ? ? ? ? ? ? ? shuffle=True,

? ? ? ? ? ? ? ? ? ? batch_size=16,

? ? ? ? ? ? ? ? ? ? repeat_num=1,

? ? ? ? ? ? ? ? ? ? frame_interval=5)

ckpt_save_dir = './x3d'

```

数据处理

用VideoShortEdgeResize根据短边来进行Resize,再用VideoRandomCrop对Resize后的视频进行随机裁剪,再用VideoRandomHorizontalFlip根据概率对视频进行水平翻转,通过VideoRescale对视频进行缩放,利用VideoReOrder对维度进行变换,再用VideoNormalize进行归一化处理。

```python

from msvideo.data.transforms import VideoRandomCrop, VideoRandomHorizontalFlip, VideoRescale

from msvideo.data.transforms import VideoNormalize, VideoShortEdgeResize, VideoReOrder

transforms = [VideoShortEdgeResize((256)),

? ? ? ? ? ? ? VideoRandomCrop([224, 224]),

? ? ? ? ? ? ? VideoRandomHorizontalFlip(0.5),

? ? ? ? ? ? ? VideoRescale(shift=0),

? ? ? ? ? ? ? VideoReOrder((3, 0, 1, 2)),

? ? ? ? ? ? ? VideoNormalize([0.45, 0.45, 0.45], [0.225, 0.225, 0.225])]

dataset.transform = transforms

dataset_train = dataset.run()

Validator.check_int(dataset_train.get_dataset_size(), 0, Rel.GT)

step_size = dataset_train.get_dataset_size()

```

网络构建

X3D包含有多个子模型,通过调用X3D_M、X3D_S、X3D_XS、X3D_L来构建不同的模型。X3D模型主要由ResNetX3D和X3DHead两大部分构成。

ResNetX3D继承了ResNet3D,并在这基础上进行了修改。ResNetX3D的第一个模块是由两个3D卷积层以及batchnorm和relu构成的,第一个3D卷积层是空间维度的卷积,输入的通道数为3,输出的通道数是24,kernel大小为(1, 3, 3),stride为(1, 2, 2),第二个3D卷积层是时间维度的卷积,输入和输出通道均为24,kernel大小为(5, 1, 1)。ResNetX3D的后续模块是4个ResStage,每个ResStage中又含有不同数量的ResBlock。在ResBlock中,主要由下采样模块和Transform模块构成,下采样模块主要用于缩小输入的H和W的大小,Transform模块中含有多个conv模块来进行通道数量的变换,并引入了SE通道注意力机制和Swish非线性激活函数。而ResBlock的数量是由模型深度所决定的,每种模型所含有的ResBlock数量各不相同,以X3D-M为例,4个ResStage中所含有的ResBlock数量分别为3、5、11、7,在第一个ResStage中输入通道和输出通道都是24,中间通道是54,重复3次,在第二个ResStage中输入通道是24,输出通道是48,中间通道为108,重复5次,在第三个ResStage中输入通道是48,输出通道是96,中间通道为216,重复11次,在最后一个ResStage中,输入通道为96,输出通道192,中间通道432,重复7次。

X3Dhead是一个用于动作分类任务的Head,主要由3D平均池化层、3D卷积层、ReLU和线性层构成。X3DHead对于输入的特征,先将其变换为2048维的特征向量,再由线性层将其变换到类别数量。

```python

from msvideo.models.x3d import x3d_m,x3d_s,x3d_xs,x3d_l

network = x3d_m(num_classes=400,

? ? ? ? ? ? ? ? dropout_rate=0.5,

? ? ? ? ? ? ? ? depth_factor=2.2,

? ? ? ? ? ? ? ? num_frames=16,

? ? ? ? ? ? ? ? train_crop_size=224)

network_x3d_s = x3d_s(num_classes = 400,

? ? ? ? ? ? ? ? ? ? ? dropout_rate = 0.5,

? ? ? ? ? ? ? ? ? ? ? depth_factor = 2.2,

? ? ? ? ? ? ? ? ? ? ? num_frames = 13,

? ? ? ? ? ? ? ? ? ? ? train_crop_size = 160,

? ? ? ? ? ? ? ? ? ? ? eval_with_clips = False)

network_x3d_xs = x3d_xs(num_classes = 400,

? ? ? ? ? ? ? ? ? ? ? ? dropout_rate = 0.5,

? ? ? ? ? ? ? ? ? ? ? ? depth_factor = 2.2,

? ? ? ? ? ? ? ? ? ? ? ? num_frames = 4,

? ? ? ? ? ? ? ? ? ? ? ? train_crop_size = 160,

? ? ? ? ? ? ? ? ? ? ? ? eval_with_clips = False)

network_x3d_l = x3d_l(num_classes = 400,

? ? ? ? ? ? ? ? ? ? ? dropout_rate = 0.5,

? ? ? ? ? ? ? ? ? ? ? depth_factor = 5.0,

? ? ? ? ? ? ? ? ? ? ? num_frames = 16,

? ? ? ? ? ? ? ? ? ? ? train_crop_size = 312,

? ? ? ? ? ? ? ? ? ? ? eval_with_clips = False)

```

设置学习率、优化器和损失函数

```python

from msvideo.schedule.lr_schedule import warmup_cosine_annealing_lr_v1

learning_rate = warmup_cosine_annealing_lr_v1(lr=0.0125,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? steps_per_epoch=step_size,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? warmup_epochs=35,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? max_epoch=100,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? t_max=100,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? eta_min=0)

network_opt = nn.SGD(network.trainable_params(),

? ? ? ? ? ? ? ? ? ? learning_rate,

? ? ? ? ? ? ? ? ? ? momentum=0.9,

? ? ? ? ? ? ? ? ? ? weight_decay=0.00005)

network_loss = SoftmaxCrossEntropyWithLogits(sparse=True, reduction="mean")

ckpt_config = CheckpointConfig(

? ? ? ? save_checkpoint_steps=step_size,

? ? ? ? keep_checkpoint_max=10)

ckpt_callback = ModelCheckpoint(prefix='x3d_kinetics400',

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? directory=ckpt_save_dir,

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? config=ckpt_config)

```

初始化模型

```python

# Init the model.

model = Model(network,

? ? ? ? ? ? ? loss_fn=network_loss,

? ? ? ? ? ? ? optimizer=network_opt,

? ? ? ? ? ? ? metrics={"Accuracy": Accuracy()})

# Begin to train.

print('[Start training `{}`]'.format('x3d_kinetics400'))

print("=" * 80)

model.train(100,

? ? ? ? ? ? dataset_train,

? ? ? ? ? ? callbacks=[ckpt_callback, LossMonitor()],

? ? ? ? ? ? dataset_sink_mode=False)

print('[End of training `{}`]'.format('x3d_kinetics400'))

```

部分结果

```text

[WARNING] ME(721261:140224885696320,MainProcess):2023-02-28-03:15:02.447.555 [mindspore/dataset/engine/datasets_user_defined.py:766] GeneratorDataset's num_parallel_workers: 4 is too large which may cause a lot of memory occupation (>85%) or out of memory(OOM) during multiprocessing. Therefore, it is recommended to reduce num_parallel_workers to 1 or smaller.

[Start training `x3d_kinetics400`]

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

[WARNING] ME(721261:140224885696320,MainProcess):2023-02-28-03:15:02.899.032 [mindspore/dataset/core/validator_helpers.py:804] 'Compose' from mindspore.dataset.transforms.py_transforms is deprecated from version 1.8 and will be removed in a future version. Use 'Compose' from mindspore.dataset.transforms instead.

[WARNING] ME(721261:140224885696320,MainProcess):2023-02-28-03:15:02.903.305 [mindspore/dataset/engine/datasets_user_defined.py:766] GeneratorDataset's num_parallel_workers: 4 is too large which may cause a lot of memory occupation (>85%) or out of memory(OOM) during multiprocessing. Therefore, it is recommended to reduce num_parallel_workers to 1 or smaller.

[WARNING] ME(721261:140224885696320,MainProcess):2023-02-28-03:15:03.334.464 [mindspore/dataset/core/validator_helpers.py:804] 'Compose' from mindspore.dataset.transforms.py_transforms is deprecated from version 1.8 and will be removed in a future version. Use 'Compose' from mindspore.dataset.transforms instead.

epoch: 1 step: 1, loss is 5.99898624420166

epoch: 1 step: 2, loss is 5.985357761383057

epoch: 1 step: 3, loss is 5.989644527435303

epoch: 1 step: 4, loss is 5.99155330657959

epoch: 1 step: 5, loss is 5.987839698791504

epoch: 1 step: 6, loss is 5.990924835205078

epoch: 1 step: 7, loss is 5.9942498207092285

epoch: 1 step: 8, loss is 6.004180908203125

epoch: 1 step: 9, loss is 5.980659008026123

epoch: 1 step: 10, loss is 5.995561122894287

......

```

评估流程

以X3D-M为例

```python

from mindspore import context

from mindspore.train.callback import Callback

class PrintEvalStep(Callback):

? ? """ print eval step """

? ? def step_end(self, run_context):

? ? ? ? """ eval step """

? ? ? ? cb_params = run_context.original_args()

? ? ? ? print("eval: {}/{}".format(cb_params.cur_step_num, cb_params.batch_num))

context.set_context(mode=context.GRAPH_MODE, device_target="GPU", device_id=1)

```

构建测试用数据集,并作相应的数据增强

```python

from msvideo.data.kinetics400 import Kinetic400

dataset_eval = Kinetic400(path="/home/publicfile/kinetics-400",

? ? ? ? ? ? ? ? ? ? ? ? ? split="val",

? ? ? ? ? ? ? ? ? ? ? ? ? seq=16,

? ? ? ? ? ? ? ? ? ? ? ? ? seq_mode='interval',

? ? ? ? ? ? ? ? ? ? ? ? ? num_parallel_workers=8,

? ? ? ? ? ? ? ? ? ? ? ? ? shuffle=False,

? ? ? ? ? ? ? ? ? ? ? ? ? batch_size=16,

? ? ? ? ? ? ? ? ? ? ? ? ? repeat_num=1,

? ? ? ? ? ? ? ? ? ? ? ? ? frame_interval=5,

? ? ? ? ? ? ? ? ? ? ? ? ? num_clips=10)

from msvideo.data.transforms import VideoReOrder, VideoRescale, VideoNormalize

from msvideo.data.transforms import VideoCenterCrop, VideoShortEdgeResize

transforms = [VideoShortEdgeResize(size=256),

? ? ? ? ? ? ? VideoCenterCrop([256, 256]),

? ? ? ? ? ? ? VideoRescale(shift=0),

? ? ? ? ? ? ? VideoReOrder((3, 0, 1, 2)),

? ? ? ? ? ? ? VideoNormalize([0.45, 0.45, 0.45], [0.225, 0.225, 0.225])]

dataset_eval.transform = transforms

dataset_eval = dataset_eval.run()

```

构建网络

```python

from mindspore import nn

from mindspore.train import Model

from mindspore.nn.loss import SoftmaxCrossEntropyWithLogits

from mindspore import load_checkpoint, load_param_into_net

from msvideo.models.x3d import x3d_m

network = x3d_m(num_classes=400,

? ? ? ? ? ? ? ? eval_with_clips=True)

```

定义损失函数并加载预训练网络

```python

# Define loss function.

network_loss = SoftmaxCrossEntropyWithLogits(sparse=True, reduction="mean")

# Load pretrained model.

param_dict = load_checkpoint(ckpt_file_name='/home/shr/resources/pretrianed_models/x3d_m_kinetics400.ckpt')

load_param_into_net(network, param_dict)

```

设置评估参数并初始化网络

```python

# Define eval_metrics.

eval_metrics = {'Loss': nn.Loss(),

? ? ? ? ? ? ? ? 'Top_1_Accuracy': nn.Top1CategoricalAccuracy(),

? ? ? ? ? ? ? ? 'Top_5_Accuracy': nn.Top5CategoricalAccuracy()}

print_cb = PrintEvalStep()

# Init the model.

model = Model(network, loss_fn=network_loss, metrics=eval_metrics)

```

开始测试

```python

# Begin to eval.

print('[Start eval `{}`]'.format('x3d_kinetics400'))

result = model.eval(dataset_eval,

? ? ? ? ? ? ? ? ? ? callbacks=[print_cb],

? ? ? ? ? ? ? ? ? ? dataset_sink_mode=False)

print(result)

```

测试结果

```text

[WARNING] ME(139331:140018904409920,MainProcess):2023-03-13-08:00:16.289.382 [mindspore/train/model.py:1077] For PrintEvalStep callback, {'step_end'} methods may not be supported in later version, Use methods prefixed with 'on_train' or 'on_eval' instead when using customized callbacks.

[WARNING] ME(139331:140018904409920,MainProcess):2023-03-13-08:00:18.764.562 [mindspore/dataset/engine/datasets_user_defined.py:766] GeneratorDataset's num_parallel_workers: 8 is too large which may cause a lot of memory occupation (>85%) or out of memory(OOM) during multiprocessing. Therefore, it is recommended to reduce num_parallel_workers to 1 or smaller.

[WARNING] ME(139331:140018904409920,MainProcess):2023-03-13-08:00:19.651.789 [mindspore/dataset/core/validator_helpers.py:804] 'Compose' from mindspore.dataset.transforms.py_transforms is deprecated from version 1.8 and will be removed in a future version. Use 'Compose' from mindspore.dataset.transforms instead.

[Start eval `x3d_kinetics400`]

eval: 1/2484

eval: 2/2484

eval: 3/2484

eval: 4/2484

...

eval: 2482/2484

eval: 2483/2484

eval: 2484/2484

{'Loss':5.988906774751, 'Top_1_Accuracy': 0.7455716586151369, 'Top_5_Accuracy': 0.919987922705314}

```


https://www.xamrdz.com/database/6w51994445.html

相关文章: