配置文件

配置文件yolov3.cfg定义了网络的结构

....

[convolutional]
batch_normalize=1
filters=64
size=3
stride=2
pad=1
activation=leaky [convolutional]
batch_normalize=1
filters=32
size=1
stride=1
pad=1
activation=leaky [convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky [shortcut]
from=-3
activation=linear .....

配置文件描述了model的结构.

yolov3 layer

yolov3有以下几种结构

  • Convolutional
  • Shortcut
  • Upsample
  • Route
  • YOLO

Convolutional

[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky

Shortcut

[shortcut]
from=-3
activation=linear

类似于resnet,用以加深网络深度.上述配置的含义是shortcut layer的输出是前一层和前三层的输出的叠加.

resnet skip connection解释详细见https://zhuanlan.zhihu.com/p/28124810

Upsample

[upsample]
stride=2

通过双线性插值法将N*N的feature map变为(stride*N) * (stride*N)的feature map.模仿特征金字塔,生成多尺度feature map.加强小目标检测效果.

Route

[route]
layers = -4 [route]
layers = -1, 61

以上述配置为例:

当layers只有一个值,代表route layer输出的是router layer - 4那一层layer的feature map.

当layers有2个值时,代表route layer的输出为route layer -1和第61 layer的feature map在深度方向连接起来.(比如说3*3*100,3*3*200add起来变成3*3*300)

yolo

[yolo]
mask = 0,1,2
anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326
classes=80
num=9
jitter=.3
ignore_thresh = .5
truth_thresh = 1
random=1

yolo层负责预测. anchors是9个anchor,事先聚类得到,表示最有可能的anchor形状.

mask表示哪几组anchor被使用.比如mask=0,1,2代表使用10,13 16,30 30,61这几组anchor. 在原理篇里说过了,每个cell预测3个boudingbox. 三种尺度,总计9种.

Net

[net]
# Testing
batch=1
subdivisions=1
# Training
# batch=64
# subdivisions=16
width= 320
height = 320
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1

定义了model的输入,batch等等.

现在开始写代码:

解析配置文件

这一步里,做配置文件的解析.把每一块的配置内容存储于一个dict.

def parse_cfg(cfgfile):
"""
Takes a configuration file Returns a list of blocks. Each blocks describes a block in the neural
network to be built. Block is represented as a dictionary in the list """
file = open(cfgfile, 'r')
# store the lines in a list
lines = file.read().split('\n')
# get read of the empty lines
lines = [x for x in lines if len(x) > 0]
lines = [x for x in lines if x[0] != '#'] # get rid of comments
# get rid of fringe whitespaces
lines = [x.rstrip().lstrip() for x in lines] block = {}
blocks = [] for line in lines:
if line[0] == "[": # This marks the start of a new block
# If block is not empty, implies it is storing values of previous block.
if len(block) != 0:
blocks.append(block) # add it the blocks list
block = {} # re-init the block
block["type"] = line[1:-1].rstrip()
else:
key, value = line.split("=")
block[key.rstrip()] = value.lstrip()
blocks.append(block) return blocks

用pytorch创建各个layer

逐个layer创建.

def create_modules(blocks):
# Captures the information about the input and pre-processing
net_info = blocks[0]
module_list = nn.ModuleList()
prev_filters = 3 #卷积的时候需要知道卷积核的depth.卷积核的size在配置文件里定义了.depeth就是上一层的output的depth.
output_filters = [] #用以保存每一个layer的输出的feature map #index代表了当前layer位于网络的第几层
for index, x in enumerate(blocks[1:]):
#生成每一个layer module_list.append(module)
prev_filters = filters
output_filters.append(filters) return(net_info,module_list)
  • 卷积层
[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky

除了卷积之外实际上还包括了bn和leaky.batchnormalize基本成了标配了现在,用来解决梯度消失的问题(反向传播梯度越乘越小).leaky是激活函数RLU.

所以用到了nn.Sequential()

module = nn.Sequential()
module.add_module("conv_{0}".format(index), conv)
module.add_module("batch_norm_{0}".format(index), bn)
module.add_module("leaky_{0}".format(index), activn)

卷积层创建完整代码

涉及到一个python语法enumerate. 就是为一个list中的每个元素添加一个index,形成新的list.

>>>seasons = ['Spring', 'Summer', 'Fall', 'Winter']
>>> list(enumerate(seasons))
[(0, 'Spring'), (1, 'Summer'), (2, 'Fall'), (3, 'Winter')]
>>> list(enumerate(seasons, start=1)) # 下标从 1 开始
[(1, 'Spring'), (2, 'Summer'), (3, 'Fall'), (4, 'Winter')]

卷积层创建

    #index代表了当前layer位于网络的第几层
for index, x in enumerate(blocks[1:]):
module = nn.Sequential() #check the type of block
#create a new module for the block
#append to module_list if (x["type"] == "convolutional"):
#Get the info about the layer
activation = x["activation"]
try:
batch_normalize = int(x["batch_normalize"])
bias = False
except:
batch_normalize = 0
bias = True filters= int(x["filters"])
padding = int(x["pad"])
kernel_size = int(x["size"])
stride = int(x["stride"]) if padding:
pad = (kernel_size - 1) // 2
else:
pad = 0 #Add the convolutional layer
#prev_filters是上一层输出的feature map的depth.比如上层有64个卷积核,则输出为m*n*64
conv = nn.Conv2d(prev_filters, filters, kernel_size, stride, pad, bias = bias)
module.add_module("conv_{0}".format(index), conv) #Add the Batch Norm Layer
if batch_normalize:
bn = nn.BatchNorm2d(filters)
module.add_module("batch_norm_{0}".format(index), bn) #Check the activation.
#It is either Linear or a Leaky ReLU for YOLO
if activation == "leaky":
activn = nn.LeakyReLU(0.1, inplace = True)
module.add_module("leaky_{0}".format(index), activn)
  • upsample层
        #If it's an upsampling layer
#We use Bilinear2dUpsampling
elif (x["type"] == "upsample"):
stride = int(x["stride"])
upsample = nn.Upsample(scale_factor = 2, mode = "bilinear")
module.add_module("upsample_{}".format(index), upsample)
  • route层
[route]
layers = -4 [route]
layers = -1, 61

首先是解析配置文件,然后将相应层的feature map 连接起来作为输出

        #If it is a route layer
elif (x["type"] == "route"):
x["layers"] = x["layers"].split(',')
#Start of a route
start = int(x["layers"][0])
#end, if there exists one.
try:
end = int(x["layers"][1])
except:
end = 0
#Positive anotation
if start > 0:
start = start - index #start转换成相对于当前layer的偏移
if end > 0:
end = end - index #end转换成相对于当前layer的偏移
route = EmptyLayer()
module.add_module("route_{0}".format(index), route)
if end < 0: #route层concat当前layer前面的某2个layer,所以index>0是无意义的.
filters = output_filters[index + start] + output_filters[index + end]
else:
filters= output_filters[index + start]

这里我们自定义了一个EmptyLayer

class EmptyLayer(nn.Module):
def __init__(self):
super(EmptyLayer, self).__init__()

这里定义EmptyLayer是为了代码的简便起见.在pytorch里定义一个自定义的layer.要写一个类,继承自nn.Module,然后实现forward方法.

关于如何定义一个自定义layer,参见下面的link.

https://pytorch.org/tutorials/beginner/examples_nn/two_layer_net_module.html

import torch

class TwoLayerNet(torch.nn.Module):
def __init__(self, D_in, H, D_out):
"""
In the constructor we instantiate two nn.Linear modules and assign them as
member variables.
"""
super(TwoLayerNet, self).__init__()
self.linear1 = torch.nn.Linear(D_in, H)
self.linear2 = torch.nn.Linear(H, D_out) def forward(self, x):
"""
In the forward function we accept a Tensor of input data and we must return
a Tensor of output data. We can use Modules defined in the constructor as
well as arbitrary operators on Tensors.
"""
h_relu = self.linear1(x).clamp(min=0)
y_pred = self.linear2(h_relu)
return y_pred # N is batch size; D_in is input dimension;
# H is hidden dimension; D_out is output dimension.
N, D_in, H, D_out = 64, 1000, 100, 10 # Create random Tensors to hold inputs and outputs
x = torch.randn(N, D_in)
y = torch.randn(N, D_out) # Construct our model by instantiating the class defined above
model = TwoLayerNet(D_in, H, D_out) # Construct our loss function and an Optimizer. The call to model.parameters()
# in the SGD constructor will contain the learnable parameters of the two
# nn.Linear modules which are members of the model.
criterion = torch.nn.MSELoss(reduction='sum')
optimizer = torch.optim.SGD(model.parameters(), lr=1e-4)
for t in range(500):
# Forward pass: Compute predicted y by passing x to the model
y_pred = model(x) # Compute and print loss
loss = criterion(y_pred, y)
print(t, loss.item()) # Zero gradients, perform a backward pass, and update the weights.
optimizer.zero_grad()
loss.backward()
optimizer.step()

这里由于我们的route layer要做的事情很简单,就是concat两个layer里的feature map,调用torch.cat一行代码的事情,所以没必要定义一个RouteLayer了,直接在代表darknet的nn.Module的forward方法里做concat操作就可以啦.

  • shorcut层
        #shortcut corresponds to skip connection
elif x["type"] == "shortcut":
shortcut = EmptyLayer()
module.add_module("shortcut_{}".format(index), shortcut)

和route层类似,这边也用个EmptyLayer替代.shortcut所做操作即对两个feature map做addition.

  • yolo层

    yolo层负责根据feature map做预测

    首先是解析出有效的anchors.然后用我们自己定义的layer保存这些anchors.然后生成一个module.

    涉及到一个python语法super

    详细地看:http://www.runoob.com/python/python-func-super.html 简单地说就是为了安全地继承.记住怎么用的就行了.没必要深究
        #Yolo is the detection layer
elif x["type"] == "yolo":
mask = x["mask"].split(",")
mask = [int(x) for x in mask] anchors = x["anchors"].split(",")
anchors = [int(a) for a in anchors]
anchors = [(anchors[i], anchors[i+1]) for i in range(0, len(anchors),2)]
anchors = [anchors[i] for i in mask] detection = DetectionLayer(anchors)
module.add_module("Detection_{}".format(index), detection) #我们自己定义了一个yolo层
class DetectionLayer(nn.Module):
def __init__(self, anchors):
super(DetectionLayer, self).__init__()
self.anchors = anchors

测试代码

blocks = parse_cfg("cfg/yolov3.cfg")
print(create_modules(blocks))

输出如下

完整代码如下:

#coding=utf-8

from __future__ import division

import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.autograd import Variable
import numpy as np def parse_cfg(cfgfile):
"""
Takes a configuration file Returns a list of blocks. Each blocks describes a block in the neural
network to be built. Block is represented as a dictionary in the list """
file = open(cfgfile, 'r')
# store the lines in a list
lines = file.read().split('\n')
# get read of the empty lines
lines = [x for x in lines if len(x) > 0]
lines = [x for x in lines if x[0] != '#'] # get rid of comments
# get rid of fringe whitespaces
lines = [x.rstrip().lstrip() for x in lines] block = {}
blocks = [] for line in lines:
if line[0] == "[": # This marks the start of a new block
# If block is not empty, implies it is storing values of previous block.
if len(block) != 0:
blocks.append(block) # add it the blocks list
block = {} # re-init the block
block["type"] = line[1:-1].rstrip()
else:
key, value = line.split("=")
block[key.rstrip()] = value.lstrip()
blocks.append(block) return blocks class EmptyLayer(nn.Module):
def __init__(self):
super(EmptyLayer, self).__init__() class DetectionLayer(nn.Module):
def __init__(self, anchors):
super(DetectionLayer, self).__init__()
self.anchors = anchors def create_modules(blocks):
# Captures the information about the input and pre-processing
net_info = blocks[0]
module_list = nn.ModuleList()
prev_filters = 3
output_filters = [] #index代表了当前layer位于网络的第几层
for index, x in enumerate(blocks[1:]):
module = nn.Sequential() #check the type of block
#create a new module for the block
#append to module_list if (x["type"] == "convolutional"):
#Get the info about the layer
activation = x["activation"]
try:
batch_normalize = int(x["batch_normalize"])
bias = False
except:
batch_normalize = 0
bias = True filters= int(x["filters"])
padding = int(x["pad"])
kernel_size = int(x["size"])
stride = int(x["stride"]) if padding:
pad = (kernel_size - 1) // 2
else:
pad = 0 #Add the convolutional layer
#prev_filters是上一层输出的feature map的depth.比如上层有64个卷积核,则输出为m*n*64
conv = nn.Conv2d(prev_filters, filters, kernel_size, stride, pad, bias = bias)
module.add_module("conv_{0}".format(index), conv) #Add the Batch Norm Layer
if batch_normalize:
bn = nn.BatchNorm2d(filters)
module.add_module("batch_norm_{0}".format(index), bn) #Check the activation.
#It is either Linear or a Leaky ReLU for YOLO
if activation == "leaky":
activn = nn.LeakyReLU(0.1, inplace = True)
module.add_module("leaky_{0}".format(index), activn) #If it's an upsampling layer
#We use Bilinear2dUpsampling
elif (x["type"] == "upsample"):
stride = int(x["stride"])
upsample = nn.Upsample(scale_factor = 2, mode = "bilinear")
module.add_module("upsample_{}".format(index), upsample) #If it is a route layer
elif (x["type"] == "route"):
x["layers"] = x["layers"].split(',')
#Start of a route
start = int(x["layers"][0])
#end, if there exists one.
try:
end = int(x["layers"][1])
except:
end = 0
#Positive anotation
if start > 0:
start = start - index
if end > 0:
end = end - index
route = EmptyLayer()
module.add_module("route_{0}".format(index), route)
if end < 0:
filters = output_filters[index + start] + output_filters[index + end]
else:
filters= output_filters[index + start] #shortcut corresponds to skip connection
elif x["type"] == "shortcut":
shortcut = EmptyLayer()
module.add_module("shortcut{}".format(index), shortcut) #Yolo is the detection layer
elif x["type"] == "yolo":
mask = x["mask"].split(",")
mask = [int(x) for x in mask] anchors = x["anchors"].split(",")
anchors = [int(a) for a in anchors]
anchors = [(anchors[i], anchors[i+1]) for i in range(0, len(anchors),2)]
anchors = [anchors[i] for i in mask] detection = DetectionLayer(anchors)
module.add_module("Detection_{}".format(index), detection) module_list.append(module)
prev_filter = filters
output_filters.append(filters) return (net_info,module_list) blocks = parse_cfg("/home/suchang/work_codes/keepgoing/yolov3-torch/cfg/yolov3.cfg")
print(create_modules(blocks))

pytorch实现yolov3(2) 配置文件解析及各layer生成的更多相关文章

  1. Pytorch版本yolov3源码阅读

    目录 Pytorch版本yolov3源码阅读 1. 阅读test.py 1.1 参数解读 1.2 data文件解析 1.3 cfg文件解析 1.4 根据cfg文件创建模块 1.5 YOLOLayer ...

  2. MyBatis配置文件解析

    MyBatis配置文件解析(概要) 1.configuration:根元素 1.1 properties:定义配置外在化 1.2 settings:一些全局性的配置 1.3 typeAliases:为 ...

  3. Nginx安装与配置文件解析

    导读 Nginx是一款开放源代码的高性能HTTP服务器和反向代理服务器,同时支持IMAP/POP3代理服务,是一款自由的软件,同时也是运维工程师必会的一种服务器,下面我就简单的说一下Nginx服务器的 ...

  4. Hadoop配置文件解析

    Hadoop源码解析 2 --- Hadoop配置文件解析 1 Hadoop Configuration简介    Hadoop没有使用java.util.Properties管理配置文件, 也没有使 ...

  5. Python3 配置文件 解析

    /************************************************************************ * Python3 配置文件 解析 * 说明: * ...

  6. Hibernate的配置文件解析

    配置mybatis.xml或hibernate.cfg.xml报错: <property name="connection.url">jdbc:mysql://loca ...

  7. WCF中配置文件解析

    WCF中配置文件解析[1] 2014-06-14 WCF中配置文件解析 参考 WCF中配置文件解析 返回 在WCF Service Configuration Editor的使用中,我们通过配置工具自 ...

  8. haproxy之配置文件解析

    功能--> 提供高可用/负载均衡/基于tcp和http应用的代理;支持虚拟主机,特别适用于负载特大的web站点. 配置文件解析--> #配置文件-->开启/proc/net/ipv4 ...

  9. nginx源代码分析--配置文件解析

    ngx-conf-parsing 对 Nginx 配置文件的一些认识: 配置指令具有作用域,分为全局作用域和使用 {} 创建其他作用域. 同一作用域的不同的配置指令没有先后顺序:同一作用域能否使用同样 ...

随机推荐

  1. wxWindows

    用C++编写跨平台程序 中文版说明 本教程由Gxl117翻译并将继续维护,这是本教程的第一稿,假设发现错误请与我(Email:gxl117@yahoo.com.cn)联系让我能及时修正它.之后还会对这 ...

  2. java 压缩以及解压文件,有tar,zip,gz(gizp)和解压

    package com.yabsz.decompCompr; import java.io.File; import java.util.ArrayList; import java.util.Lis ...

  3. 两个QWidget叠加,可部分代替layout的功能

    在QT开发过程中,有时候会遇到这样的问题,当我们自己创建了一个Layout对象以后,使用QWidget的setLayout方法,将这个Layout对象应用到窗口中的时候,发现窗口上没有我们添加的控件, ...

  4. python中对文件、文件夹的操作

    python中对文件.文件夹的操作需要涉及到os模块和shutil模块. 创建文件: 1) os.mknod("test.txt")       创建空文件 2) open(&qu ...

  5. WPF 实现波浪浮动效果

    原文:WPF 实现波浪浮动效果 目标:实现界面图标Load时,整体图标出现上下波浪浮动效果,如下图: 前台代码: <Windowxmlns="http://schemas.micros ...

  6. Mac OS X通过结合80port

    Mac OS X 由于要绑定80port须要ROOT权限, 可是假设用root权限启动eclipse或tomcat又会造成, 启动创建的各类文件是root的,普通用户无法删除. 为此. 我们能够通过p ...

  7. WPF教程002 - 实现Step步骤条控件

    原文:WPF教程002 - 实现Step步骤条控件 在网上看到这么一个效果,刚好在用WPF做控件,就想着用WPF来实现一下 1.实现原理 1.1.该控件分为2个模块,类似ComboBox控件分为Ste ...

  8. swagger ui 值类型形参加文字注释

    例: @ApiImplicitParams({ @ApiImplicitParam(paramType = "query", name = "indexCondition ...

  9. 在WPF里面实现以鼠标位置为中心缩放移动图片

    原文:在WPF里面实现以鼠标位置为中心缩放移动图片 在以前的文章使用WPF Resource以及Transform等技术实现鼠标控制图片缩放和移动的效果里面,介绍了如何在WPF里面移动和放大缩小图片, ...

  10. 关于SetLength报Out of memory的研究及解决办法

    关于SetLength报Out of memory的研究及解决办法 最近在做一个GIS系统, 在读GIS数据时采用了动态数组,突然读一个数据时SetLength报错!Out of memory 仔细研 ...