YOLO v2的算法细节——以李沐的Gluon代码为例

这篇博客通过李沐的深度学习公开课中的Gluon代码详细介绍了YOLO v2算法的实现过程。文章涵盖了数据读取、模型加载、训练模型和测试模型四个部分,重点解析了模型加载中的YOLO2Output类和训练过程中的yolo2_forward及yolo2_target函数。内容还包括数据预处理、损失函数、训练过程以及预测结果的展示和处理。
摘要由CSDN通过智能技术生成

YOLO算法在object detection领域算是比较有意思的一个分支,2017年CVPR上的YOLO v2对原来的YOLO算法进行了升级,论文本身包含较多的算法细节,可以先参考博客:YOLO9000算法详解,这里借助李沐的深度学习公开课中的代码(通过MXNet框架下的Gluon接口实现)来详细了解YOLO v2算法的细节。
参考链接:https://zh.gluon.ai/chapter_computer-vision/yolo.html

在参考链接中完整地给出了实现YOLO v2算法的代码,主要包括数据读取、模型加载、训练模型、测试模型四个部分,最重要的是模型加载部分的YOLO2Output类、训练模型部分的yolo2_forward函数和yolo2_target函数。接下来按这四个部分依次介绍。

1、数据读取部分的实现代码主要在get_iteratirs函数中,在该函数中主要采用mxnet.image.ImageDetIter接口来读取,需要提前准备好train.rec和val.rec文件,class_names就是object名称的列表。另外这里定义了输入图像的大小是3*256*256,后面的代码都基于这个定义。两个参数的含义:1、min_object_covered (float, default=0.1) – The cropped area of the image must contain at least this fraction of any bounding box supplied. The value of this parameter should be non-negative. In the case of 0, the cropped area does not need to overlap any of the bounding boxes supplied. 2、max_attempts (int, default=50) – Number of attempts at generating a cropped/padded region of the image of the specified constraints. After max_attempts failures, return the original image.

from mxnet import image
from mxnet import nd

data_shape = 256
batch_size = 32
rgb_mean = nd.array([123, 117, 104])
rgb_std = nd.array([58.395, 57.12, 57.375])

def get_iterators(data_shape, batch_size):
    class_names = ['pikachu', 'dummy']
    num_class = len(class_names)
    train_iter = image.ImageDetIter(
        batch_size=batch_size,
        data_shape=(3, data_shape, data_shape),
        path_imgrec=data_dir+'train.rec',
        path_imgidx=data_dir+'train.idx',
        shuffle=True,
        mean=True,
        std=True,
        rand_crop=1,
        min_object_covered=0.95,
        max_attempts=200)
    val_iter = image.ImageDetIter(
        batch_size=batch_size,
        data_shape=(3, data_shape, data_shape),
        path_imgrec=data_dir+'val.rec',
        shuffle=False,
        mean=True,
        std=True)
    return train_iter, val_iter, class_names, num_class

train_data, test_data, class_names, num_class = get_iterators(
    data_shape, batch_size)

2、模型加载部分,先通过mxnet.gluon.model_zoo.vision.get_model接口导入模型,这个用法和PyTorch很像。在导入的时候注意到最后的.features,这个features是ResNetV1类的初始化函数中的变量,通过mxnet.gluon.nn.HybridSequential接口初始化,mxnet.gluon.nn.HybridSequential是mxnet.gluon.nn.Sequential的特例,mxnet.gluon.nn.Sequential将添加进来的层按先后顺序执行。这里通过HybridSequential类的add方法添加层(更抽象点就是层或者网络都是通过block实现的),最后包含网络除最后的全连接层以外的所有层。也就是说net存放pretrained这个网络中除了最后两层的网络结构,是用来构造主网络的,所以net是包含7*7卷积加3个block(pretrained中包含7*7卷积加4个block加pooling层),net最后一层输出feature map大小就变成256/16=16。scales变量用来存放anchor的尺寸信息,是一个二维列表,每一行表示一个anchor,第一列表示width,第二列表示height。这个scales里面的值根据主网络最后一层的输出feature map大小来定,比如这个net最后一层输出是16*16,那么这里的scale里面的值是3或者9这样大小是比较正常的。YOLO2Output这个类用来构造预测层,最后的net.add(predictor)就完成了主网络和预测层的连接,后面会详细介绍YOLO2Output类。predictor.initialize()是调用了mxnet.gluon.Block类(YOLO2Output类是基于HybridBlock类实现的,HybridBlock的底层是通过Block基类实现的)的initialize方法,是用来初始化网络参数的,这一步是必须的,否则构造的网络结构没有参数就跑前向会报错。initialize方法的两个主要参数是初始化方式(一般默认即可)和ctx(也就是指定的cpu或gpu,比如ctx=[mx.gpu(0),mx.gpu(1)])。另外initialize方法和block.collect_params().initialize()结果一样,block类的collect_params()方法是block中比较常用的,返回的是block及其children的参数,官网中有个关于collect_params()的例子:假如你要用dense0层的参数初始化dense1层,可以这样实现:dense0 = nn.Dense(20);dense1 = nn.Dense(20, params=dense0.collect_params())。至于net为什么没有运行initialize方法,是因为在得到pretrained的时候设置了pretrained=True,也就是用了预训练模型进行参数初始化了。

from mxnet.gluon.model_zoo import vision
pretrained = vision.get_model('resnet18_v1', pretrained=True).features
net = nn.HybridSequential()
for i in range(len(pretrained) - 2):
    net.add(pretrained[i])

# anchor scales, try adjust it yourself
scales = [[3.3004, 3.59034],
          [9.84923, 8.23783]]

# use 2 classes, 1 as dummy class, otherwise softmax won't work
predictor = YOLO2Output(2, scales)
predictor.initialize()
net.add(predictor)

YOLO2Output类用来构造预测层,代码如下。几个assert语句用来确保输入数据的格式符合要求,比较重要的一个是out_channels = len(anchor_scales) * (num_class + 1 + 4),首先len(anchor_scales)表示anchor的数量, (num_class + 1 + 4)中的num_class标object的数量,1表示score,4表示框的中心点坐标和宽高信息。self.output = nn.Conv2D(out_channels, 1, 1)这一行点明了用1*1的卷积来完成预测层。

class YOLO2Output(HybridBlock):
    def __init__(self, num_class, anchor_scales, **kwargs):
        super(YOLO2Output,
  • 5
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值