PaddleVideo将一个算法分解为以下几个部分,并对各部分进行模块化处理,方便快速组合出新的算法。
示例代码如下:
import numpy as np
import paddle
from paddle.io import Dataset, DataLoader
import paddle.nn as nn
# 1. 数据加载和处理
## 1.2 数据预处理Pipeline
class ExamplePipeline(object):
""" Example Pipeline"""
def __init__(self, mean=0, std=1.0):
self.mean = mean
self.std = std
def __call__(self, results):
data = results['data']
norm_data = (data - self.mean) / self.std
results['data'] = norm_data
return results
## 1.1 数据集类
class ExampleDataset(Dataset):
"""ExampleDataset"""
def __init__(self):
super(ExampleDataset, self).__init__()
self.x = np.random.rand(100, 20, 20)
self.y = np.random.randint(10, size = (100, 1))
def __getitem__(self, idx):
x_item = self.x[idx]
results = {}
results['data'] = x_item
pipeline = ExamplePipeline()
results = pipeline(results)
x_item = results['data'].astype('float32')
y_item = self.y[idx].astype('int64')
return x_item, y_item
def __len__(self):
return self.x.shape[0]
train_dataset = ExampleDataset()
## 1.3 封装为Dataloader对象
train_loader = DataLoader(train_dataset, batch_size=10, shuffle=True)
# 2. 网络
class ExampleModel(nn.Layer):
"""Example Model"""
def __init__(self):
super(ExampleModel, self).__init__()
## 2.1 网络Backbobe
self.layer1 = paddle.nn.Flatten(1, -1)
self.layer2 = paddle.nn.Linear(400, 512)
self.layer3 = paddle.nn.ReLU()
self.layer4 = paddle.nn.Dropout(0.2)
## 2.2 网络Head
self.layer5 = paddle.nn.Linear(512, 10)
def forward(self, x):
""" model forward"""
y = self.layer1(x)
y = self.layer2(y)
y = self.layer3(y)
y = self.layer4(y)
y = self.layer5(y)
return y
model = ExampleModel()
model.train()
# 3. 优化器
optim = paddle.optimizer.Adam(parameters=model.parameters())
epochs = 5
for epoch in range(epochs):
for batch_id, data in enumerate(train_loader()):
x_data = data[0]
y_data = data[1]
predicts = model(x_data)
## 2.3 网络Loss
loss = paddle.nn.functional.cross_entropy(predicts, y_data)
acc = paddle.metric.accuracy(predicts, y_data)
loss.backward()
print("epoch: {}, batch_id: {}, loss is: {}, acc is: {}".format(epoch, batch_id, loss.numpy(), acc.numpy()))
optim.step()
optim.clear_grad()
上述代码的运行输出日志如下:
epoch: 0, batch_id: 0, loss is: [2.5613842], acc is: [0.]
epoch: 0, batch_id: 1, loss is: [2.5776138], acc is: [0.1]
epoch: 0, batch_id: 2, loss is: [2.551022], acc is: [0.1]
epoch: 0, batch_id: 3, loss is: [2.782001], acc is: [0.]
epoch: 0, batch_id: 4, loss is: [2.787499], acc is: [0.1]
将以上代码集成进PaddleVideo的示例pr参考 #257
下面将分别对每个部分进行介绍,并介绍如何在该部分里添加新算法所需模块。
数据加载和处理部分由Dataset类
、预处理Pipeline
和Dataloader对象
组成。Dataset类
是数据集类,其中的__getitem__
方法定义了每一个视频样本数据的处理方式。预处理Pipeline
定义了数据预处理步骤,包括视频的读取,解码以及数据增强等操作。预处理定义的Pipeline
通常在Dataset类
的__getitem__
方法中被调用,以完成对视频预处理操作。这一部分在paddlevideo/loader下。 各个文件及文件夹作用说明如下:
paddlevideo/loader/
├── dataset
│ ├── base.py # Dataset基类
│ ├── frame.py # 处理Frame格式输入的Dataset类
│ └── video.py # 处理Video格式输入的Dataset类
├── pipelines
│ ├── decode.py # 解码Pipeline,对视频进行解码
│ ├── sample.py # 抽帧Pipeline,对视频抽帧的方式
│ ├── augmentations.py # 数据增强Pipeline,包括缩放、裁剪、反转、正则化等
...
PaddleVideo内置了针对不同数据集的Dataset相关模块,对于没有内置的模块可通过如下步骤添加:
- 在 paddlevideo/loader/dataset 文件夹下新建文件,如my_dataset.py。
- 在 my_dataset.py 文件内添加相关代码,示例代码如下:
@DATASETS.register() # 通过装饰器,自动进行注册
class MyDataset:
def __init__(self, *args, **kwargs):
# your init code
pass
def load_file(self):
info = []
# load file list
return info
def prepare_train(self, idx):
results = copy.deepcopy(self.info[idx])
results = self.pipeline(results) # train pipeline
return results['image'], results['labels'] #return your data item
def prepare_test(self, idx):
results = copy.deepcopy(self.info[idx])
results = self.pipeline(results) # test pipeline
return results['image'], results['labels'] #return your data item
- 在 paddlevideo/loader/dataset/__init__.py 文件内导入添加的模块。
最后在config文件中指定Dataset类名即可使用。如:
# Define your Dataset name and args
DATASET:
batch_size: 16 # single-card bacth size
num_workers: 4 # the number of subprocess on each GPU.
train:
format: "FrameDataset" # Dataset class
data_prefix: "data/k400/rawframes" # train data root path
file_path: "data/k400/train_frames.list" # train data list file path
suffix: 'img_{:05}.jpg'
valid:
format: "FrameDataset" # Dataset class
data_prefix: "data/k400/rawframes" # valid data root path
file_path: "data/k400/train_frames.list" # valid data list file path
suffix: 'img_{:05}.jpg'
test:
format: "FrameDataset" # Dataset class
data_prefix: "data/k400/rawframes" # test data root path
file_path: "data/k400/train_frames.list" # test data list file path
suffix: 'img_{:05}.jpg'
- 关于模块注册机制的详细说明,可以参考配置系统设计
PaddleVideo内置了大量视频编解码及图像变换相关模块,对于没有内置的模块可通过如下步骤添加:
- 在 paddlevideo/loader/pipelines 文件夹下新建文件,如my_pipeline.py。
- 在 my_pipeline.py 文件内添加相关代码,示例代码如下:
@PIPELINES.register() # 通过装饰器,自动进行注册
class MyPipeline:
def __init__(self, *args, **kwargs):
# your init code
pass
def __call__(self, results):
img = results['image']
label = results['label']
# your process code
results['image'] = img
results['label'] = label
return results
- 在 paddlevideo/loader/pipelines/__init__.py 文件内导入添加的模块。
数据处理的所有处理步骤由不同的模块顺序执行而成,在config文件中按照列表的形式组合并执行。如:
# Define your pipeline name and args
PIPELINE:
train:
decode:
name: "FrameDecoder" # Pipeline Class name
sample:
name: "Sampler" # Pipeline Class name
num_seg: 8 # init args
seg_len: 1 # init args
valid_mode: False # init args
transform:
- Scale: # Pipeline Class name
short_size: 256 # init args
网络部分完成了网络的组网操作,PaddleVideo将网络划分为四三部分,这一部分在paddlevideo/modeling下。 进入网络的数据将按照顺序(backbones->heads->loss)依次通过这三个部分。backbone用于特征提取,loss通过heads的loss方法被调用。除了损失值,训练过程中如果想观察其它的精度指标(如top1, top5),也可以在head中定义相应的计算方法,参考get_acc方法,loss模块最终返回一个loss字典,存储loss值以及其它需要的精度指标。
├── framework # 组合backbones->heads->loss,定义从输入数据到输出loss的过程
├── backbones # 网络的特征提取模块
├── heads # 网络的输出模块
└── losses # 网络的损失函数模块
PaddleVideo内置了TSN、TSM、SlowFast、ST-GCN、BMN等算法相关的常用模块,对于没有内置的模块可通过如下步骤添加,四个部分添加步骤一致,以backbones为例:
- 在 paddlevideo/modeling/backbones 文件夹下新建文件,如my_backbone.py。
- 在 my_backbone.py 文件内添加相关代码,示例代码如下:
@BACKBONES.register() # 通过装饰器,自动进行注册
class MyBackbone(nn.Layer):
def __init__(self, *args, **kwargs):
super(MyBackbone, self).__init__()
# your init code
self.conv = nn.xxxx
def forward(self, inputs):
# your network forward
y = self.conv(inputs)
return y
- 在 paddlevideo/modeling/backbones/__init__.py文件内导入添加的模块。
在完成网络的四部分模块添加之后,只需要配置文件中进行配置即可使用,如:
MODEL:
framework: "Recognizer2D" # Framework class name
backbone:
name: "ResNetTweaksTSM" # Backbone class name
depth: 50 # init args
head:
name: "ppTSMHead" # Heads class name
num_classes: 400 # init args
loss:
name: "MyLoss" # Losses class name
scale: 0.1 # init args
优化器用于训练网络。优化器内部还包含了网络正则化和学习率衰减模块。 这一部分在paddlevideo/solver/下。 PaddleVideo内置了飞桨框架所有的优化器模块和学习率衰减模块。只需要在配置文件中指定相应模块名称及参数即可方便的调用,示例:
OPTIMIZER:
name: 'Momentum' # Optimizer class name
momentum: 0.9 # init args
learning_rate:
name: 'PiecewiseDecay' # Learning rate scheduler class name
boundaries: [10, 20] # init args
values: [0.001, 0.0001, 0.00001] # init args
对于没有内置的模块可通过如下步骤添加,以learning rate
为例:
- 在 paddlevideo/solver/custom_lr.py 文件内创建自己的学习率调整策略,示例代码如下:
class MyLR(LRScheduler):
def __init__(self, *args, **kwargs):
self.learning_rate = learning_rate
def step(self, epoch):
# learning rate step scheduler
self.last_lr = xxx
在学习率模块添加之后,只需要配置文件中进行配置即可使用,如:
OPTIMIZER:
name: 'Momentum'
momentum: 0.9
learning_rate:
iter_step: True
name: 'CustomWarmupCosineDecay' # LR class name
max_epoch: 80 # init args
warmup_epochs: 10 # init args
PaddleVideo内置了很多模型训练相关trick,包括标签平滑、数据增强Mix-up、PreciseBN等,只需要在配置文件中指定相应模块名称及参数即可方便的调用,示例:
MODEL:
framework: "Recognizer2D"
backbone:
name: "ResNetTweaksTSM"
head:
name: "ppTSMHead"
ls_eps: 0.1 # ls_eps字段添加label smooth,并指定平滑系数
MIX:
name: "Mixup" # 添加数据增强 Mix-up策略
alpha: 0.2 # 指定mix系数
PRECISEBN: # 添加preciseBN策略
preciseBN_interval: 5 # 指定prciseBN间隔
num_iters_preciseBN: 200 # 指定preciseBN运行的batchs数量
训练相关的代码通过paddlevideo/tasks/train.py被组织起来,最终被PaddleVideo/main.py调用启动训练,单卡训练和多卡训练的启动方式略有不同。单卡训练启动方式如下:
export CUDA_VISIBLE_DEVICES=0 #指定使用的GPU显卡id
python3.7 main.py --validate -c configs_path/your_config.yaml
--validate
参数指定训练时运行validation-c
参数指定配置文件路径-o
: 指定重写参数,例如:-o DATASET.batch_size=16
用于重写train时batch size大小
多卡训练通过paddle.distributed.launch启动,方式如下:
export CUDA_VISIBLE_DEVICES=0,1,2,3,4,5,6,7
python3.7 -B -m paddle.distributed.launch --gpus="0,1,2,3,4,5,6,7" --log_dir=log_pptsm main.py --validate -c configs/recognition/pptsm/pptsm_k400_frames_uniform.yaml
--gpus
参数指定使用的GPU显卡id--log_dir
参数指定日志保存目录 多卡训练详细说明可以参考单机多卡训练
训练完成后,需要进行指标评估,paddlevideo将指标评估模块与训练模块解耦,通过在PaddleVideo/main.py运行时指定--test
参数调用test模块进行指标评估,评估方法的实现主体在paddlevideo/metrics/下。 PaddleVideo内置了Uniform、Dense等相关的指标评估模块,对于没有内置的模块可通过如下步骤添加:
- 在 paddlevideo/metrics/ 文件夹下新建文件,如my_metric.py。
- 在 my_metric.py 文件内添加相关代码,示例代码如下:
@METRIC.register # 通过装饰器,自动进行注册
class MyMetric(BaseMetric):
def __init__(self, *args, **kwargs):
self.top1 = []
def update(self, batch_id, data, outputs):
# update metrics during each iter
self.top1.append(xx)
def accumulate(self):
# accumulate metrics when finished all iters.
xxx
print(np.mean(np.array(self.top1)))
- 在 paddlevideo/metrics/__init__.py文件内导入添加的模块。
在指标评估模块添加之后,只需要配置文件中进行配置即可使用,如:
METRIC:
name: 'CenterCropMetric' # Metric class name
模型测试运行方法如下:
python3.7 main.py --test -c config_path/your_config.yaml -w weight_path/your_weight.pdparams
--test
参数指定运行测试模式-c
参数指定配置文件-w
参数指定训练好的权重保存路径