手把手带你训练 CVPR2022 视频超分模型

简介: RealBasicVSR 小课堂继续开课啦!上一期文章中我们解读了真实视频超分的文章 RealBasicVSR,今天我们将手把手带大家一起使用 MMEditing 训练 RealBasicVSR。这一次我们会重点关注数据处理,希望大家看完这一期的内容后能更了解 RealBasicVSR 的训练方式和 MMEditing 的数据处理流程。

RealBasicVSR 小课堂继续开课啦!上一期文章中我们解读了真实视频超分的文章 RealBasicVSR,今天我们将手把手带大家一起使用 MMEditing 训练 RealBasicVSR。这一次我们会重点关注数据处理,希望大家看完这一期的内容后能更了解 RealBasicVSR 的训练方式和 MMEditing 的数据处理流程。


RealBasicVSR 训练方式



让我们来先了解一下 RealBasicVSR 是如何训练的。RealBasicVSR 是通过大量生成退化(例如Gaussian blur, Poisson noise, JPEG compression)的不同组合用作监督训练。通过不同退化的组合,RealBasicVSR 在一定程度上可以泛化到真实场景当中。RealBasicVSR 使用的是 Real-ESRGAN 的二阶退化模型,下图是 Real-ESRGAN 原文中的图解:

640.png

上图的二阶退化模型仅使用了 Blur, resize, noise 等等生成退化。因此,我们可以在训练中很容易获得相应的退化,在作用到高清图片中得到低清图片。RealBasicVSR 的退化大致跟上图一样。唯一不同的是为了针对视频退化,我们在 JPEG 压缩后加上视频压缩。


定义 Model 和 Backbone



我们要先在配置文件中定义 Model 和 Backbone,如下:

# model settings
model = dict(
    type='RealBasicVSR',
    generator=dict(
        type='RealBasicVSRNet',
        mid_channels=64,
        num_propagation_blocks=20,
        num_cleaning_blocks=20,
        dynamic_refine_thres=255,  # change to 1.5 for test
        spynet_pretrained='https://download.openmmlab.com/mmediting/restorers/'
        'basicvsr/spynet_20210409-c6c1bd09.pth',
        is_fix_cleaning=False,
        is_sequential_cleaning=False),
    pixel_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean'),
    cleaning_loss=dict(type='L1Loss', loss_weight=1.0, reduction='mean'),
    is_use_sharpened_gt_in_pixel=True,
    is_use_ema=True,
)


定义数据处理流程



早期的超分辨率模型假设退化是固定的(例如 bicubic 下采样),因为不需要考虑泛他性的问题,我们只需要对于高清图片作出对应的退化得到低清图片然后保存,再在训练期间直接读取高清和低清的图片对。


但是使用二阶退化模型时,为了提高泛化性,退化的参数是随机选取的。因此,我们不能先生成图片对再直接读取。所以,我们要对数据处理作出修改:只读取高清图片,再加上随机退化得到低清图片。我们再来看看配置文件。从 52 行到 195 行都是我们的随机退化,我们接下来看看不同退化的设置。


RandomBlur


dict(
    type='RandomBlur',
    params=dict(
        kernel_size=[7, 9, 11, 13, 15, 17, 19, 21],
        kernel_list=[
            'iso', 'aniso', 'generalized_iso', 'generalized_aniso',
            'plateau_iso', 'plateau_aniso', 'sinc'
        ],
        kernel_prob=[0.405, 0.225, 0.108, 0.027, 0.108, 0.027, 0.1],
        sigma_x=[0.2, 3],
        sigma_y=[0.2, 3],
        rotate_angle=[-3.1416, 3.1416],
        beta_gaussian=[0.5, 4],
        beta_plateau=[1, 2],
        sigma_x_step=0.02,
        sigma_y_step=0.02,
        rotate_angle_step=0.31416,
        beta_gaussian_step=0.05,
        beta_plateau_step=0.1,
        omega_step=0.0628),
    keys=['lq'],
),

首先我们会对图片加上模糊。MMEditing 支持不同的模糊,例如 isotropic gaussian, anisotropic gaussian 等等。在使用的时候我们可以在 kernel_list 标明想使用的 kernel。另外,我们需要设置不同模糊的参数,例如 kernel_size 和各个模糊被选取的概率等等。


值得注意的是, RealBasicVSR 提出了 stochastic degradation scheme。大概意思就是每一帧之间的退化参数都会有区别。所以我们在上述代码中可以看到 xxx_step 的设置,而 xxx_step 就是定义参数区别的大小。以 sigma_x_step 作为例子,如果现在的 sigma_x 是 0.5, 在 sigma_x_step 是 0.02 的情况下, sigma_x 在下一帧就会在 0.48 和 0.52 之间随机选取。


RandomResize


dict(
    type='RandomResize',
    params=dict(
        resize_mode_prob=[0.2, 0.7, 0.1],  # up, down, keep
        resize_scale=[0.15, 1.5],
        resize_opt=['bilinear', 'area', 'bicubic'],
        resize_prob=[1 / 3.0, 1 / 3.0, 1 / 3.0],
        resize_step=0.015,
        is_size_even=True),
    keys=['lq'],
),

接着就是随机更改图片大小。上述代码中看到的 resize_mode_prob 就是图片被上采样、下采样,和保持大小的概率。以 resize_scale=[0.15, 1.5] 为例子,上述代码中的设定就是一个图片会有 0.2 的概率上采样到 1x-1.5x 的大小,0.7 的概率下采样的 0.15x-1x 的大小,和有 0.1 的概率维持现有的大小。值得注意的是,这里的 scale 是随机选取的。接着 resize_opt 就是使用什么 resize 的操作。现在 MMEditing 支持 cv2 里的 “bilinear”, “area” 和 “bicubic”。而 resize_step 的意思和 Blur 的 xxx_step 意思一样,在一定范围内改变下一帧的 resize_scale 。我们还提供了 is_size_even 的参数,因为在作用视频压缩时,图片的大小要求是偶数。如果需要使用视频压缩时,我们需要把 is_size_even 置为 True。


RandomNoise

dict(
    type='RandomNoise',
    params=dict(
        noise_type=['gaussian', 'poisson'],
        noise_prob=[0.5, 0.5],
        gaussian_sigma=[1, 30],
        gaussian_gray_noise_prob=0.4,
        poisson_scale=[0.05, 3],
        poisson_gray_noise_prob=0.4,
        gaussian_sigma_step=0.1,
        poisson_scale_step=0.005),
    keys=['lq'],
),

然后就是加上噪声。MMEditing 现在支持 Gaussian 和 Poisson 两种噪声。参数的设置和之前大同小异。


RandomJPEGCompression

dict(
    type='RandomJPEGCompression',
    params=dict(quality=[30, 95], quality_step=3),
    keys=['lq'],
),


RandomVideoCompression

dict(
    type='RandomVideoCompression',
    params=dict(
        codec=['libx264', 'h264', 'mpeg4'],
        codec_prob=[1 / 3., 1 / 3., 1 / 3.],
        bitrate=[1e4, 1e5]),
    keys=['lq'],
),

除了上面的图像退化外,我们在训练 RealBasicVSR 时也加上了视频压缩。MMEditing 提供了 libx264,h264 和 mpeg4 的压缩,用户只需要注明 bit rate 就可以。


DegradationsWithShuffle

dict(
    type='DegradationsWithShuffle',
    degradations=[
        dict(
            type='RandomVideoCompression',
            params=dict(
                codec=['libx264', 'h264', 'mpeg4'],
                codec_prob=[1 / 3., 1 / 3., 1 / 3.],
                bitrate=[1e4, 1e5]),
            keys=['lq'],
        ),
        [
            dict(
                type='RandomResize',
                params=dict(
                    target_size=(64, 64),
                    resize_opt=['bilinear', 'area', 'bicubic'],
                    resize_prob=[1 / 3., 1 / 3., 1 / 3.]),
            ),
            dict(
                type='RandomBlur',
                params=dict(
                    prob=0.8,
                    kernel_size=[7, 9, 11, 13, 15, 17, 19, 21],
                    kernel_list=['sinc'],
                    kernel_prob=[1],
                    omega=[3.1416 / 3, 3.1416],
                    omega_step=0.0628),
            ),
        ]
    ],
    keys=['lq'],
),

最后要介绍的是随机改变退化顺序的操作。在BSRGAN 这个工作中,退化的顺序就是随机组合,从而提高退化的多样性。MMEditing 当然也支持这个操作。我们只需要使用 DegradationsWithShuffle ,里面的 degradations 参数就是你希望使用的退化,与个别的退化定义是一样的。在有一些情况中,你可能想保留个别退化的顺序,这时候你只需要把对应的退化放在一个 list 里就可以。例如上述代码中的 RandomResize 和 RandomBlur,因为它们在一个 list 里面,他们的顺序是保持不变的,即是永远都是先 resize 然后 blur。

定义训练和测试配置



data = dict(
    workers_per_gpu=10,
    train_dataloader=dict(
        samples_per_gpu=2, drop_last=True, persistent_workers=False),
    val_dataloader=dict(samples_per_gpu=1, persistent_workers=False),
    test_dataloader=dict(samples_per_gpu=1, workers_per_gpu=1),
    # train
    train=dict(
        type='RepeatDataset',
        times=150,
        dataset=dict(
            type=train_dataset_type,
            lq_folder='data/REDS/train_sharp_sub',
            gt_folder='data/REDS/train_sharp_sub',
            num_input_frames=15,
            pipeline=train_pipeline,
            scale=4,
            test_mode=False)),
    # val
    val=dict(
        type=val_dataset_type,
        lq_folder='data/UDM10/BIx4',
        gt_folder='data/UDM10/GT',
        pipeline=val_pipeline,
        scale=4,
        test_mode=True),
    # test
    test=dict(
        type=val_dataset_type,
        lq_folder='data/VideoLQ',
        gt_folder='data/VideoLQ',
        pipeline=test_pipeline,
        scale=4,
        test_mode=True),
)
# optimizer
optimizers = dict(generator=dict(type='Adam', lr=1e-4, betas=(0.9, 0.99)))
# learning policy
total_iters = 300000
lr_config = dict(policy='Step', by_epoch=False, step=[400000], gamma=1)
checkpoint_config = dict(interval=5000, save_optimizer=True, by_epoch=False)
# remove gpu_collect=True in non distributed training
evaluation = dict(interval=5000, save_image=False, gpu_collect=True)
log_config = dict(
    interval=100,
    hooks=[
        dict(type='TextLoggerHook', by_epoch=False),
        dict(type='TensorboardLoggerHook'),
    ])
visual_config = None
# custom hook
custom_hooks = [
    dict(
        type='ExponentialMovingAverageHook',
        module_keys=('generator_ema', ),
        interval=1,
        interp_cfg=dict(momentum=0.999),
    )
]

最后就是定义训练和测试时的 batch size,数据路径,优化器等等的配置。在 custom_hooks 里我们看到 ExponentialMovingAverageHook,这是对于 RealBasicVSR 的 weights 做一个 moving average,这个设计可以让表现稳定一点,大家可以留意一下。


训练过程



当把数据准备好后,我们就可以开始训练和测试。训练的时候只需要输入:

./tools/dist_train.sh configs/restorers/real_basicvsr/realbasicvsr_wogan_c64b20_2x30x8_lr1e-4_300k_reds.py 8


训练完毕后可以用相同指令加上 adversarial loss 和 perceptual loss 训练:

./tools/dist_train.sh configs/restorers/real_basicvsr/realbasicvsr_c64b20_1x30x8_lr5e-5_150k_reds.py 8

要留意的是需要在第二阶段的配置文件中的 load_from 更改成第一阶段训练的模型路径。


结语



MMEditing 提供了最前沿的真实图像和视频超分训练模式。我们的模块化设计也可以让大家方便的增加或减少各种退化。当你需要新的退化时,只需要写出对应的代码和修改配置文入件,不用重新写一个新的 dataloader。欢迎大家来试试,享受一下高清的快感


文章来源:【OpenMMLab

 2022-04-18 18:05

相关实践学习
在云上部署ChatGLM2-6B大模型(GPU版)
ChatGLM2-6B是由智谱AI及清华KEG实验室于2023年6月发布的中英双语对话开源大模型。通过本实验,可以学习如何配置AIGC开发环境,如何部署ChatGLM2-6B大模型。
目录
相关文章
|
机器学习/深度学习 监控 算法
图像去雾综述
图像去雾综述
uni-app项目配置手机端底部的tab栏(一)
uni-app项目配置手机端底部的tab栏(一)
1197 0
|
Python Windows
Win10安装Python3.9
Win10安装Python3.9
2208 0
|
存储 索引 Python
python字典:怎么取出key对应的值
python字典:怎么取出key对应的值
957 0
|
数据采集 测试技术 Python
自动化淘宝秒杀:使用Selenium WebDriver的实战指南
本文详细介绍了如何利用Selenium WebDriver自动化淘宝秒杀操作,包括环境配置、代码实现及注意事项,旨在帮助读者提升秒杀成功率,同时提醒合理使用以遵守平台规则。
688 8
|
10月前
|
安全 Java 程序员
《从头开始学java,一天一个知识点》之:控制流程:if-else条件语句实战
**你是否也经历过这些崩溃瞬间?** - 看了三天教程,连`i++`和`++i`的区别都说不清 - 面试时被追问"`a==b`和`equals()`的区别",大脑突然空白 - 写出的代码总是莫名报NPE,却不知道问题出在哪个运算符 这个系列为你打造Java「速效救心丸」!每天1分钟,地铁通勤、午休间隙即可完成学习。直击高频考点和实际开发中的「坑位」,拒绝冗长概念,每篇都有可运行的代码示例。明日预告:《for与while循环的使用场景》。 ---
227 19
|
9月前
|
人工智能 算法 自动驾驶
人工智能适合什么人学
本文探讨了适合学习人工智能(AI)的人群,涵盖技术爱好者、数学与逻辑能力出众者、跨学科背景人才、持续学习者及实践驱动者五大类。文章指出,AI领域需要热情与探索精神,同时依赖扎实的数学基础和逻辑思维。跨学科融合与实践经验也是掌握AI技术的关键。全球领先教育机构培生推出的生成式AI认证项目,为职场人士和学生提供了紧跟技术前沿的机会。最终强调,无论背景如何,保持开放心态与学习热情是进入AI领域的核心要素,共同迎接智能化未来。
|
11月前
|
存储 前端开发 开发工具
利用阿里云OSS(对象存储服务)快速搭建私人网盘
本文介绍了如何使用阿里云OSS搭建个人网盘的详细步骤。首先,注册阿里云账号并开通OSS服务,创建Bucket;接着,配置AccessKey和跨域访问(CORS)规则。然后,选择开源项目(如FileBrowser)或自定义前端,结合OSS SDK实现文件上传下载功能。最后,部署到服务器并绑定域名,确保安全与性能优化,如权限控制、数据备份及CDN加速。
2759 7
|
数据采集 安全 文件存储
NAS极速远程访问!贝锐花生壳推出飞牛fnOS专属内网穿透服务
贝锐花生壳与飞牛私有云fnOS合作,推出专属客户端及映射服务,实现3倍传输速率提升。用户只需在fnOS应用中心搜索“花生壳”,一键安装即可享受高速、稳定、安全的远程NAS访问体验。
1657 9