-
基于PyTorch的深度学习入门教程(一)——PyTorch安装和配置
2017-11-30 12:01:42前言 深度神经网络是一种目前被广泛使用的工具,可以用于图像识别、分类,物体检测,机器翻译等等。深度学习(DeepLearning)是一种学习神经网络...PyTorch是一种Python接口的深度学习框架,使用灵活,学习方便。...前言
深度神经网络是一种目前被广泛使用的工具,可以用于图像识别、分类,物体检测,机器翻译等等。深度学习(DeepLearning)是一种学习神经网络各种参数的方法。因此,我们将要介绍的深度学习,指的是构建神经网络结构,并且运用各种深度学习算法训练网络参数,进而解决各种任务。本文从PyTorch环境配置开始。PyTorch是一种Python接口的深度学习框架,使用灵活,学习方便。还有其他主流的深度学习框架,例如Caffe,TensorFlow,CNTK等等,各有千秋。笔者认为,初期学习还是选择一种入门,不要期望全都学会。须知,发力集中才能深入挖掘。乱花渐欲迷人眼,选择适合自己的,从一而终,相信会对科研大有裨益!
***************************************************
说明:本文乃至本系列全部文章都是在PyTorch0.2版本下做的。现在版本更新了很多,会有一些API的更改,请大家注意Follow最新的变化,以免由于版本问题受到困扰。
***************************************************
1. 环境说明
PyTorch目前支持OSX和Linux两种系统,并且支持多种安装方式。在官网上介绍了基于conda,pip和源代码编译几种不同的安装方式。支持的Python版本有2.7,3.5和3.6。鉴于深度学习需要的计算量一般比较大,强烈建议找到一个有独立显卡的电脑来展开学习,当然没有显卡也能用,就是计算慢很多了。
如果你的电脑是Windows,那么可以安装一个虚拟机来运行Linux,但是性能可能会折扣。我的电脑操作系统是Ubuntu16.04,所以我会以此为例子来介绍后面的内容。
2. Anaconda 和Python
PackageManager我们选择conda,于是我们需要安装Anaconda这个功能强大的包,下载地质:https://www.anaconda.com/download/#linux 。里面包含了conda工具,也有Python,以及很多Python需要的扩展工具包。选择2.7版本,下载并安装即可。接下来,如果有显卡,进入第3步;没有显卡直接到第4步。
3. 显卡驱动和CUDA
要使用显卡进行运算,你需要使用支持CUDA的NVIDIA显卡,目前比较好的显卡有NVIDIATITANX、GTX1080Ti 等。好的显卡将会是深度学习研究的有力武器。当然,普通的显卡例如GTX970、GTX1060等也是可以用的。实在没有显卡,那只能做简单的小数据量的实验,效果不会太好。
在Ubuntu16.04上可以采用以下方法安装显卡驱动,这种方式比较稳定。打开“SystemSettings”——“Software&Updates” —— “AdditionalDrivers”,联网状态会自动搜索可用的显卡驱动,选择可用的版本,点击“ApplyChanges”即可。可能重启动后显卡驱动才能生效。
接下来安装CUDAToolkit。因为最新的CUDA是9.0版本,但是PyTorch只能支持到8.0。所以转到该网址“https://developer.nvidia.com/cuda-80-ga2-download-archive”,选择对应的参数,下方会有对应的CUDA文件。例如,下图是我选择的版本。选择deb文件,发现有1.9G大小,下载下来,按照baseinstaller的指示来完成安装。附加的cuBLAS也可以下载下来安装上。
CUDA安装完成后,在主目录下打开“.bachrc”文件,在末尾添加如下代码:
export PATH=/usr/local/cuda-8.0/bin${PATH:+:${PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}4. 运行PyTorch的安装命令
如果你和我一样,采用8.0CUDA,2.7版本Python,可以运行以下命令:
conda install pytorch torchvision cuda80 -c soumith
如果没有显卡,2.7版本Python,可以这样:
conda install pytorch torchvision -c soumith
5. 验证安装是否成功
要显示显卡信息,在终端输入:
sudo lshw -c video
要显示CUDA信息,在终端输入:
nvcc -V
查看Phthon版本,在终端输入:
python --version
验证pytorch是否安装成功,在终端输入:
python
此时进入python环境。然后,继续输入
import torch
import torchvision
不报错就表明安装成功。
-
pytorch
2018-09-23 21:40:39安装pytorch(豆瓣源) pip3 install http://download.pytorch.org/whl/cu90/torch-0.4.1-cp36-cp36m-win_amd64.whl --user -i http://pypi.douban.com/安装PyTorch(豆瓣源)
pip3 install http://download.pytorch.org/whl/cu90/torch-0.4.1-cp36-cp36m-win_amd64.whl --user -i http://pypi.douban.com/
-
Pytorch(一) Pytorch 的安装
2020-01-13 23:57:192.下载pytorch安装文件,进行pytorch的安装 检查电脑是否有合适的GPU 在桌面上右击如果能找到NVIDA控制面板,则说明该电脑有GPU。控制面板如下,并通过查看系统信息获取支持的Cuda版本 点击 帮助->点击 系统...安装步骤
1.检查是否有合适的GPU, 若有安装Cuda与CuDNN
2.下载pytorch安装文件,进行pytorch的安装
检查电脑是否有合适的GPU
在桌面上右击如果能找到NVIDA控制面板,则说明该电脑有GPU。控制面板如下,并通过查看系统信息获取支持的Cuda版本
点击 帮助->点击 系统信息 弹出下面的对话框,在驱动程序版本那一栏就能看到该计算机使用的驱动版本。
下载Cuda
官网:https://developer.nvidia.com/cuda-10.1-download-archive-update2
在https://docs.nvidia.com/cuda/cuda-toolkit-release-notes/index.html 这里可以查询到我们应该下载哪个版本
然后,根据实际情况选择合适的版本
下载CuDNN
官网 https://developer.nvidia.com/rdp/cudnn-download
安装Cuda
1.与安装其他的软件类似
2.安装结束后将 ~/nvcc/bin(因为版本的不同可能在不同的地方) 目录添加到环境变量
3.在命令行下输入 nvcc -V, 出现下列信息说明Cuda安装成功
4.将CuDNN压缩包解压后,下面的三个文件夹复制到Cuda的安装目录下
5.然后执行Demo, 如果Demo中显示PASS则说明安装成功
安装Pytorch
官网 https://download.pytorch.org/whl/torch_stable.html 选择合适的版本
torch/torchvision 都需要安装
验证Pytorch
安装成功
通过上面输出的信息,说明安装是成功的
-
睿智的目标检测30——Pytorch搭建YoloV4目标检测平台
2020-05-24 14:14:13睿智的目标检测30——Pytorch搭建YoloV4目标检测平台学习前言什么是YOLOV4代码下载YOLOV4改进的部分(不完全)YOLOV4结构解析1、主干特征提取网络Backbone2、特征金字塔3、YoloHead利用获得到的特征进行预测4、预测...睿智的目标检测30——Pytorch搭建YoloV4目标检测平台
学习前言
也做了一下pytorch版本的。
什么是YOLOV4
YOLOV4是YOLOV3的改进版,在YOLOV3的基础上结合了非常多的小Tricks。
尽管没有目标检测上革命性的改变,但是YOLOV4依然很好的结合了速度与精度。
根据上图也可以看出来,YOLOV4在YOLOV3的基础上,在FPS不下降的情况下,mAP达到了44,提高非常明显。YOLOV4整体上的检测思路和YOLOV3相比相差并不大,都是使用三个特征层进行分类与回归预测。
请注意!
强烈建议在学习YOLOV4之前学习YOLOV3,因为YOLOV4确实可以看作是YOLOV3结合一系列改进的版本!
强烈建议在学习YOLOV4之前学习YOLOV3,因为YOLOV4确实可以看作是YOLOV3结合一系列改进的版本!
强烈建议在学习YOLOV4之前学习YOLOV3,因为YOLOV4确实可以看作是YOLOV3结合一系列改进的版本!
(重要的事情说三遍!)
YOLOV3可参考该博客:
https://blog.csdn.net/weixin_44791964/article/details/105310627代码下载
https://github.com/bubbliiiing/yolov4-pytorch
喜欢的可以给个star噢!YOLOV4改进的部分(不完全)
1、主干特征提取网络:DarkNet53 => CSPDarkNet53
2、特征金字塔:SPP,PAN
3、分类回归层:YOLOv3(未改变)
4、训练用到的小技巧:Mosaic数据增强、Label Smoothing平滑、CIOU、学习率余弦退火衰减
5、激活函数:使用Mish激活函数
以上并非全部的改进部分,还存在一些其它的改进,由于YOLOV4使用的改进实在太多了,很难完全实现与列出来,这里只列出来了一些我比较感兴趣,而且非常有效的改进。
还有一个重要的事情:
论文中提到的SAM,作者自己的源码也没有使用。还有其它很多的tricks,不是所有的tricks都有提升,我也没法实现全部的tricks。
整篇BLOG会结合YOLOV3与YOLOV4的差别进行解析
YOLOV4结构解析
为方便理解,本文将所有通道数都放到了最后一维度。
为方便理解,本文将所有通道数都放到了最后一维度。
为方便理解,本文将所有通道数都放到了最后一维度。1、主干特征提取网络Backbone
当输入是416x416时,特征结构如下:
当输入是608x608时,特征结构如下:
主干特征提取网络Backbone的改进点有两个:
a).主干特征提取网络:DarkNet53 => CSPDarkNet53
b).激活函数:使用Mish激活函数如果大家对YOLOV3比较熟悉的话,应该知道Darknet53的结构,其由一系列残差网络结构构成。在Darknet53中,其存在resblock_body模块,其由一次下采样和多次残差结构的堆叠构成,Darknet53便是由resblock_body模块组合而成。
而在YOLOV4中,其对该部分进行了一定的修改。
1、其一是将DarknetConv2D的激活函数由LeakyReLU修改成了Mish,卷积块由DarknetConv2D_BN_Leaky变成了DarknetConv2D_BN_Mish。
Mish函数的公式与图像如下:
2、其二是将resblock_body的结构进行修改,使用了CSPnet结构。此时YOLOV4当中的Darknet53被修改成了CSPDarknet53。
CSPnet结构并不算复杂,就是将原来的残差块的堆叠进行了一个拆分,拆成左右两部分:
主干部分继续进行原来的残差块的堆叠;
另一部分则像一个残差边一样,经过少量处理直接连接到最后。
因此可以认为CSP中存在一个大的残差边。#---------------------------------------------------# # CSPdarknet的结构块 # 存在一个大残差边 # 这个大残差边绕过了很多的残差结构 #---------------------------------------------------# class Resblock_body(nn.Module): def __init__(self, in_channels, out_channels, num_blocks, first): super(Resblock_body, self).__init__() self.downsample_conv = BasicConv(in_channels, out_channels, 3, stride=2) if first: self.split_conv0 = BasicConv(out_channels, out_channels, 1) self.split_conv1 = BasicConv(out_channels, out_channels, 1) self.blocks_conv = nn.Sequential( Resblock(channels=out_channels, hidden_channels=out_channels//2), BasicConv(out_channels, out_channels, 1) ) self.concat_conv = BasicConv(out_channels*2, out_channels, 1) else: self.split_conv0 = BasicConv(out_channels, out_channels//2, 1) self.split_conv1 = BasicConv(out_channels, out_channels//2, 1) self.blocks_conv = nn.Sequential( *[Resblock(out_channels//2) for _ in range(num_blocks)], BasicConv(out_channels//2, out_channels//2, 1) ) self.concat_conv = BasicConv(out_channels, out_channels, 1) def forward(self, x): x = self.downsample_conv(x) x0 = self.split_conv0(x) x1 = self.split_conv1(x) x1 = self.blocks_conv(x1) x = torch.cat([x1, x0], dim=1) x = self.concat_conv(x) return x
全部实现代码为:
import torch import torch.nn.functional as F import torch.nn as nn import math from collections import OrderedDict #-------------------------------------------------# # MISH激活函数 #-------------------------------------------------# class Mish(nn.Module): def __init__(self): super(Mish, self).__init__() def forward(self, x): return x * torch.tanh(F.softplus(x)) #-------------------------------------------------# # 卷积块 # CONV+BATCHNORM+MISH #-------------------------------------------------# class BasicConv(nn.Module): def __init__(self, in_channels, out_channels, kernel_size, stride=1): super(BasicConv, self).__init__() self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, kernel_size//2, bias=False) self.bn = nn.BatchNorm2d(out_channels) self.activation = Mish() def forward(self, x): x = self.conv(x) x = self.bn(x) x = self.activation(x) return x #---------------------------------------------------# # CSPdarknet的结构块的组成部分 # 内部堆叠的残差块 #---------------------------------------------------# class Resblock(nn.Module): def __init__(self, channels, hidden_channels=None, residual_activation=nn.Identity()): super(Resblock, self).__init__() if hidden_channels is None: hidden_channels = channels self.block = nn.Sequential( BasicConv(channels, hidden_channels, 1), BasicConv(hidden_channels, channels, 3) ) def forward(self, x): return x+self.block(x) #---------------------------------------------------# # CSPdarknet的结构块 # 存在一个大残差边 # 这个大残差边绕过了很多的残差结构 #---------------------------------------------------# class Resblock_body(nn.Module): def __init__(self, in_channels, out_channels, num_blocks, first): super(Resblock_body, self).__init__() self.downsample_conv = BasicConv(in_channels, out_channels, 3, stride=2) if first: self.split_conv0 = BasicConv(out_channels, out_channels, 1) self.split_conv1 = BasicConv(out_channels, out_channels, 1) self.blocks_conv = nn.Sequential( Resblock(channels=out_channels, hidden_channels=out_channels//2), BasicConv(out_channels, out_channels, 1) ) self.concat_conv = BasicConv(out_channels*2, out_channels, 1) else: self.split_conv0 = BasicConv(out_channels, out_channels//2, 1) self.split_conv1 = BasicConv(out_channels, out_channels//2, 1) self.blocks_conv = nn.Sequential( *[Resblock(out_channels//2) for _ in range(num_blocks)], BasicConv(out_channels//2, out_channels//2, 1) ) self.concat_conv = BasicConv(out_channels, out_channels, 1) def forward(self, x): x = self.downsample_conv(x) x0 = self.split_conv0(x) x1 = self.split_conv1(x) x1 = self.blocks_conv(x1) x = torch.cat([x1, x0], dim=1) x = self.concat_conv(x) return x class CSPDarkNet(nn.Module): def __init__(self, layers): super(CSPDarkNet, self).__init__() self.inplanes = 32 self.conv1 = BasicConv(3, self.inplanes, kernel_size=3, stride=1) self.feature_channels = [64, 128, 256, 512, 1024] self.stages = nn.ModuleList([ Resblock_body(self.inplanes, self.feature_channels[0], layers[0], first=True), Resblock_body(self.feature_channels[0], self.feature_channels[1], layers[1], first=False), Resblock_body(self.feature_channels[1], self.feature_channels[2], layers[2], first=False), Resblock_body(self.feature_channels[2], self.feature_channels[3], layers[3], first=False), Resblock_body(self.feature_channels[3], self.feature_channels[4], layers[4], first=False) ]) self.num_features = 1 # 进行权值初始化 for m in self.modules(): if isinstance(m, nn.Conv2d): n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels m.weight.data.normal_(0, math.sqrt(2. / n)) elif isinstance(m, nn.BatchNorm2d): m.weight.data.fill_(1) m.bias.data.zero_() def forward(self, x): x = self.conv1(x) x = self.stages[0](x) x = self.stages[1](x) out3 = self.stages[2](x) out4 = self.stages[3](out3) out5 = self.stages[4](out4) return out3, out4, out5 def darknet53(pretrained, **kwargs): model = CSPDarkNet([1, 2, 8, 8, 4]) if pretrained: if isinstance(pretrained, str): model.load_state_dict(torch.load(pretrained)) else: raise Exception("darknet request a pretrained path. got [{}]".format(pretrained)) return model
2、特征金字塔
当输入是416x416时,特征结构如下:
当输入是608x608时,特征结构如下:
在特征金字塔部分,YOLOV4结合了两种改进:
a).使用了SPP结构。
b).使用了PANet结构。
如上图所示,除去CSPDarknet53和Yolo Head的结构外,都是特征金字塔的结构。
1、SPP结构参杂在对CSPdarknet53的最后一个特征层的卷积里,在对CSPdarknet53的最后一个特征层进行三次DarknetConv2D_BN_Leaky卷积后,分别利用四个不同尺度的最大池化进行处理,最大池化的池化核大小分别为13x13、9x9、5x5、1x1(1x1即无处理)#---------------------------------------------------# # SPP结构,利用不同大小的池化核进行池化 # 池化后堆叠 #---------------------------------------------------# class SpatialPyramidPooling(nn.Module): def __init__(self, pool_sizes=[5, 9, 13]): super(SpatialPyramidPooling, self).__init__() self.maxpools = nn.ModuleList([nn.MaxPool2d(pool_size, 1, pool_size//2) for pool_size in pool_sizes]) def forward(self, x): features = [maxpool(x) for maxpool in self.maxpools[::-1]] features = torch.cat(features + [x], dim=1) return features
其可以它能够极大地增加感受野,分离出最显著的上下文特征。
2、PANet是2018的一种实例分割算法,其具体结构由反复提升特征的意思。
上图为原始的PANet的结构,可以看出来其具有一个非常重要的特点就是特征的反复提取。
在(a)里面是传统的特征金字塔结构,在完成特征金字塔从下到上的特征提取后,还需要实现(b)中从上到下的特征提取。而在YOLOV4当中,其主要是在三个有效特征层上使用了PANet结构。
实现代码如下:#---------------------------------------------------# # yolo_body #---------------------------------------------------# class YoloBody(nn.Module): def __init__(self, config): super(YoloBody, self).__init__() self.config = config # backbone self.backbone = darknet53(None) self.conv1 = make_three_conv([512,1024],1024) self.SPP = SpatialPyramidPooling() self.conv2 = make_three_conv([512,1024],2048) self.upsample1 = Upsample(512,256) self.conv_for_P4 = conv2d(512,256,1) self.make_five_conv1 = make_five_conv([256, 512],512) self.upsample2 = Upsample(256,128) self.conv_for_P3 = conv2d(256,128,1) self.make_five_conv2 = make_five_conv([128, 256],256) # 3*(5+num_classes)=3*(5+20)=3*(4+1+20)=75 final_out_filter2 = len(config["yolo"]["anchors"][2]) * (5 + config["yolo"]["classes"]) self.yolo_head3 = yolo_head([256, final_out_filter2],128) self.down_sample1 = conv2d(128,256,3,stride=2) self.make_five_conv3 = make_five_conv([256, 512],512) # 3*(5+num_classes)=3*(5+20)=3*(4+1+20)=75 final_out_filter1 = len(config["yolo"]["anchors"][1]) * (5 + config["yolo"]["classes"]) self.yolo_head2 = yolo_head([512, final_out_filter1],256) self.down_sample2 = conv2d(256,512,3,stride=2) self.make_five_conv4 = make_five_conv([512, 1024],1024) # 3*(5+num_classes)=3*(5+20)=3*(4+1+20)=75 final_out_filter0 = len(config["yolo"]["anchors"][0]) * (5 + config["yolo"]["classes"]) self.yolo_head1 = yolo_head([1024, final_out_filter0],512) def forward(self, x): # backbone x2, x1, x0 = self.backbone(x) P5 = self.conv1(x0) P5 = self.SPP(P5) P5 = self.conv2(P5) P5_upsample = self.upsample1(P5) P4 = self.conv_for_P4(x1) P4 = torch.cat([P4,P5_upsample],axis=1) P4 = self.make_five_conv1(P4) P4_upsample = self.upsample2(P4) P3 = self.conv_for_P3(x2) P3 = torch.cat([P3,P4_upsample],axis=1) P3 = self.make_five_conv2(P3) P3_downsample = self.down_sample1(P3) P4 = torch.cat([P3_downsample,P4],axis=1) P4 = self.make_five_conv3(P4) P4_downsample = self.down_sample2(P4) P5 = torch.cat([P4_downsample,P5],axis=1) P5 = self.make_five_conv4(P5) out2 = self.yolo_head3(P3) out1 = self.yolo_head2(P4) out0 = self.yolo_head1(P5) return out0, out1, out2
3、YoloHead利用获得到的特征进行预测
当输入是416x416时,特征结构如下:
当输入是608x608时,特征结构如下:
1、在特征利用部分,YoloV4提取多特征层进行目标检测,一共提取三个特征层,分别位于中间层,中下层,底层,三个特征层的shape分别为(76,76,256)、(38,38,512)、(19,19,1024)。2、输出层的shape分别为(19,19,75),(38,38,75),(76,76,75),最后一个维度为75是因为该图是基于voc数据集的,它的类为20种,YoloV4只有针对每一个特征层存在3个先验框,所以最后维度为3x25;
如果使用的是coco训练集,类则为80种,最后的维度应该为255 = 3x85,三个特征层的shape为(19,19,255),(38,38,255),(76,76,255)实现代码如下:
#---------------------------------------------------# # 最后获得yolov4的输出 #---------------------------------------------------# def yolo_head(filters_list, in_filters): m = nn.Sequential( conv2d(in_filters, filters_list[0], 3), nn.Conv2d(filters_list[0], filters_list[1], 1), ) return m #---------------------------------------------------# # yolo_body #---------------------------------------------------# class YoloBody(nn.Module): def __init__(self, config): super(YoloBody, self).__init__() self.config = config # backbone self.backbone = darknet53(None) self.conv1 = make_three_conv([512,1024],1024) self.SPP = SpatialPyramidPooling() self.conv2 = make_three_conv([512,1024],2048) self.upsample1 = Upsample(512,256) self.conv_for_P4 = conv2d(512,256,1) self.make_five_conv1 = make_five_conv([256, 512],512) self.upsample2 = Upsample(256,128) self.conv_for_P3 = conv2d(256,128,1) self.make_five_conv2 = make_five_conv([128, 256],256) # 3*(5+num_classes)=3*(5+20)=3*(4+1+20)=75 final_out_filter2 = len(config["yolo"]["anchors"][2]) * (5 + config["yolo"]["classes"]) self.yolo_head3 = yolo_head([256, final_out_filter2],128) self.down_sample1 = conv2d(128,256,3,stride=2) self.make_five_conv3 = make_five_conv([256, 512],512) # 3*(5+num_classes)=3*(5+20)=3*(4+1+20)=75 final_out_filter1 = len(config["yolo"]["anchors"][1]) * (5 + config["yolo"]["classes"]) self.yolo_head2 = yolo_head([512, final_out_filter1],256) self.down_sample2 = conv2d(256,512,3,stride=2) self.make_five_conv4 = make_five_conv([512, 1024],1024) # 3*(5+num_classes)=3*(5+20)=3*(4+1+20)=75 final_out_filter0 = len(config["yolo"]["anchors"][0]) * (5 + config["yolo"]["classes"]) self.yolo_head1 = yolo_head([1024, final_out_filter0],512) def forward(self, x): # backbone x2, x1, x0 = self.backbone(x) P5 = self.conv1(x0) P5 = self.SPP(P5) P5 = self.conv2(P5) P5_upsample = self.upsample1(P5) P4 = self.conv_for_P4(x1) P4 = torch.cat([P4,P5_upsample],axis=1) P4 = self.make_five_conv1(P4) P4_upsample = self.upsample2(P4) P3 = self.conv_for_P3(x2) P3 = torch.cat([P3,P4_upsample],axis=1) P3 = self.make_five_conv2(P3) P3_downsample = self.down_sample1(P3) P4 = torch.cat([P3_downsample,P4],axis=1) P4 = self.make_five_conv3(P4) P4_downsample = self.down_sample2(P4) P5 = torch.cat([P4_downsample,P5],axis=1) P5 = self.make_five_conv4(P5) out2 = self.yolo_head3(P3) out1 = self.yolo_head2(P4) out0 = self.yolo_head1(P5) return out0, out1, out2
4、预测结果的解码
由第二步我们可以获得三个特征层的预测结果,shape分别为(N,19,19,255),(N,38,38,255),(N,76,76,255)的数据,对应每个图分为19x19、38x38、76x76的网格上3个预测框的位置。
但是这个预测结果并不对应着最终的预测框在图片上的位置,还需要解码才可以完成。
此处要讲一下yolo3的预测原理,yolo3的3个特征层分别将整幅图分为19x19、38x38、76x76的网格,每个网络点负责一个区域的检测。
我们知道特征层的预测结果对应着三个预测框的位置,我们先将其reshape一下,其结果为(N,19,19,3,85),(N,38,38,3,85),(N,76,76,3,85)。
最后一个维度中的85包含了4+1+80,分别代表x_offset、y_offset、h和w、置信度、分类结果。
yolo3的解码过程就是将每个网格点加上它对应的x_offset和y_offset,加完后的结果就是预测框的中心,然后再利用 先验框和h、w结合 计算出预测框的长和宽。这样就能得到整个预测框的位置了。
当然得到最终的预测结构后还要进行得分排序与非极大抑制筛选
这一部分基本上是所有目标检测通用的部分。不过该项目的处理方式与其它项目不同。其对于每一个类进行判别。
1、取出每一类得分大于self.obj_threshold的框和得分。
2、利用框的位置和得分进行非极大抑制。实现代码如下,当调用yolo_eval时,就会对每个特征层进行解码:
class DecodeBox(nn.Module): def __init__(self, anchors, num_classes, img_size): super(DecodeBox, self).__init__() self.anchors = anchors self.num_anchors = len(anchors) self.num_classes = num_classes self.bbox_attrs = 5 + num_classes self.img_size = img_size def forward(self, input): # input为bs,3*(1+4+num_classes),13,13 # 一共多少张图片 batch_size = input.size(0) # 13,13 input_height = input.size(2) input_width = input.size(3) # 计算步长 # 每一个特征点对应原来的图片上多少个像素点 # 如果特征层为13x13的话,一个特征点就对应原来的图片上的32个像素点 # 416/13 = 32 stride_h = self.img_size[1] / input_height stride_w = self.img_size[0] / input_width # 把先验框的尺寸调整成特征层大小的形式 # 计算出先验框在特征层上对应的宽高 scaled_anchors = [(anchor_width / stride_w, anchor_height / stride_h) for anchor_width, anchor_height in self.anchors] # bs,3*(5+num_classes),13,13 -> bs,3,13,13,(5+num_classes) prediction = input.view(batch_size, self.num_anchors, self.bbox_attrs, input_height, input_width).permute(0, 1, 3, 4, 2).contiguous() # 先验框的中心位置的调整参数 x = torch.sigmoid(prediction[..., 0]) y = torch.sigmoid(prediction[..., 1]) # 先验框的宽高调整参数 w = prediction[..., 2] # Width h = prediction[..., 3] # Height # 获得置信度,是否有物体 conf = torch.sigmoid(prediction[..., 4]) # 种类置信度 pred_cls = torch.sigmoid(prediction[..., 5:]) # Cls pred. FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor LongTensor = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor # 生成网格,先验框中心,网格左上角 batch_size,3,13,13 grid_x = torch.linspace(0, input_width - 1, input_width).repeat(input_width, 1).repeat( batch_size * self.num_anchors, 1, 1).view(x.shape).type(FloatTensor) grid_y = torch.linspace(0, input_height - 1, input_height).repeat(input_height, 1).t().repeat( batch_size * self.num_anchors, 1, 1).view(y.shape).type(FloatTensor) # 生成先验框的宽高 anchor_w = FloatTensor(scaled_anchors).index_select(1, LongTensor([0])) anchor_h = FloatTensor(scaled_anchors).index_select(1, LongTensor([1])) anchor_w = anchor_w.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(w.shape) anchor_h = anchor_h.repeat(batch_size, 1).repeat(1, 1, input_height * input_width).view(h.shape) # 计算调整后的先验框中心与宽高 pred_boxes = FloatTensor(prediction[..., :4].shape) pred_boxes[..., 0] = x.data + grid_x pred_boxes[..., 1] = y.data + grid_y pred_boxes[..., 2] = torch.exp(w.data) * anchor_w pred_boxes[..., 3] = torch.exp(h.data) * anchor_h # 用于将输出调整为相对于416x416的大小 _scale = torch.Tensor([stride_w, stride_h] * 2).type(FloatTensor) output = torch.cat((pred_boxes.view(batch_size, -1, 4) * _scale, conf.view(batch_size, -1, 1), pred_cls.view(batch_size, -1, self.num_classes)), -1) return output.data
5、在原图上进行绘制
通过第四步,我们可以获得预测框在原图上的位置,而且这些预测框都是经过筛选的。这些筛选后的框可以直接绘制在图片上,就可以获得结果了。
YOLOV4的训练
1、YOLOV4的改进训练技巧
a)、Mosaic数据增强
Yolov4的mosaic数据增强参考了CutMix数据增强方式,理论上具有一定的相似性!
CutMix数据增强方式利用两张图片进行拼接。
但是mosaic利用了四张图片,根据论文所说其拥有一个巨大的优点是丰富检测物体的背景!且在BN计算的时候一下子会计算四张图片的数据!
就像下图这样:
实现思路如下:
1、每次读取四张图片。
2、分别对四张图片进行翻转、缩放、色域变化等,并且按照四个方向位置摆好。
3、进行图片的组合和框的组合
def rand(a=0, b=1): return np.random.rand()*(b-a) + a def merge_bboxes(bboxes, cutx, cuty): merge_bbox = [] for i in range(len(bboxes)): for box in bboxes[i]: tmp_box = [] x1,y1,x2,y2 = box[0], box[1], box[2], box[3] if i == 0: if y1 > cuty or x1 > cutx: continue if y2 >= cuty and y1 <= cuty: y2 = cuty if y2-y1 < 5: continue if x2 >= cutx and x1 <= cutx: x2 = cutx if x2-x1 < 5: continue if i == 1: if y2 < cuty or x1 > cutx: continue if y2 >= cuty and y1 <= cuty: y1 = cuty if y2-y1 < 5: continue if x2 >= cutx and x1 <= cutx: x2 = cutx if x2-x1 < 5: continue if i == 2: if y2 < cuty or x2 < cutx: continue if y2 >= cuty and y1 <= cuty: y1 = cuty if y2-y1 < 5: continue if x2 >= cutx and x1 <= cutx: x1 = cutx if x2-x1 < 5: continue if i == 3: if y1 > cuty or x2 < cutx: continue if y2 >= cuty and y1 <= cuty: y2 = cuty if y2-y1 < 5: continue if x2 >= cutx and x1 <= cutx: x1 = cutx if x2-x1 < 5: continue tmp_box.append(x1) tmp_box.append(y1) tmp_box.append(x2) tmp_box.append(y2) tmp_box.append(box[-1]) merge_bbox.append(tmp_box) return merge_bbox def get_random_data(annotation_line, input_shape, random=True, hue=.1, sat=1.5, val=1.5, proc_img=True): '''random preprocessing for real-time data augmentation''' h, w = input_shape min_offset_x = 0.4 min_offset_y = 0.4 scale_low = 1-min(min_offset_x,min_offset_y) scale_high = scale_low+0.2 image_datas = [] box_datas = [] index = 0 place_x = [0,0,int(w*min_offset_x),int(w*min_offset_x)] place_y = [0,int(h*min_offset_y),int(w*min_offset_y),0] for line in annotation_line: # 每一行进行分割 line_content = line.split() # 打开图片 image = Image.open(line_content[0]) image = image.convert("RGB") # 图片的大小 iw, ih = image.size # 保存框的位置 box = np.array([np.array(list(map(int,box.split(',')))) for box in line_content[1:]]) # image.save(str(index)+".jpg") # 是否翻转图片 flip = rand()<.5 if flip and len(box)>0: image = image.transpose(Image.FLIP_LEFT_RIGHT) box[:, [0,2]] = iw - box[:, [2,0]] # 对输入进来的图片进行缩放 new_ar = w/h scale = rand(scale_low, scale_high) if new_ar < 1: nh = int(scale*h) nw = int(nh*new_ar) else: nw = int(scale*w) nh = int(nw/new_ar) image = image.resize((nw,nh), Image.BICUBIC) # 进行色域变换 hue = rand(-hue, hue) sat = rand(1, sat) if rand()<.5 else 1/rand(1, sat) val = rand(1, val) if rand()<.5 else 1/rand(1, val) x = rgb_to_hsv(np.array(image)/255.) x[..., 0] += hue x[..., 0][x[..., 0]>1] -= 1 x[..., 0][x[..., 0]<0] += 1 x[..., 1] *= sat x[..., 2] *= val x[x>1] = 1 x[x<0] = 0 image = hsv_to_rgb(x) image = Image.fromarray((image*255).astype(np.uint8)) # 将图片进行放置,分别对应四张分割图片的位置 dx = place_x[index] dy = place_y[index] new_image = Image.new('RGB', (w,h), (128,128,128)) new_image.paste(image, (dx, dy)) image_data = np.array(new_image)/255 # Image.fromarray((image_data*255).astype(np.uint8)).save(str(index)+"distort.jpg") index = index + 1 box_data = [] # 对box进行重新处理 if len(box)>0: np.random.shuffle(box) box[:, [0,2]] = box[:, [0,2]]*nw/iw + dx box[:, [1,3]] = box[:, [1,3]]*nh/ih + dy box[:, 0:2][box[:, 0:2]<0] = 0 box[:, 2][box[:, 2]>w] = w box[:, 3][box[:, 3]>h] = h box_w = box[:, 2] - box[:, 0] box_h = box[:, 3] - box[:, 1] box = box[np.logical_and(box_w>1, box_h>1)] box_data = np.zeros((len(box),5)) box_data[:len(box)] = box image_datas.append(image_data) box_datas.append(box_data) img = Image.fromarray((image_data*255).astype(np.uint8)) for j in range(len(box_data)): thickness = 3 left, top, right, bottom = box_data[j][0:4] draw = ImageDraw.Draw(img) for i in range(thickness): draw.rectangle([left + i, top + i, right - i, bottom - i],outline=(255,255,255)) img.show() # 将图片分割,放在一起 cutx = np.random.randint(int(w*min_offset_x), int(w*(1 - min_offset_x))) cuty = np.random.randint(int(h*min_offset_y), int(h*(1 - min_offset_y))) new_image = np.zeros([h,w,3]) new_image[:cuty, :cutx, :] = image_datas[0][:cuty, :cutx, :] new_image[cuty:, :cutx, :] = image_datas[1][cuty:, :cutx, :] new_image[cuty:, cutx:, :] = image_datas[2][cuty:, cutx:, :] new_image[:cuty, cutx:, :] = image_datas[3][:cuty, cutx:, :] # 对框进行进一步的处理 new_boxes = merge_bboxes(box_datas, cutx, cuty) return new_image, new_boxes
b)、Label Smoothing平滑
标签平滑的思想很简单,具体公式如下:
new_onehot_labels = onehot_labels * (1 - label_smoothing) + label_smoothing / num_classes
当label_smoothing的值为0.01得时候,公式变成如下所示:
new_onehot_labels = y * (1 - 0.01) + 0.01 / num_classes
其实Label Smoothing平滑就是将标签进行一个平滑,原始的标签是0、1,在平滑后变成0.005(如果是二分类)、0.995,也就是说对分类准确做了一点惩罚,让模型不可以分类的太准确,太准确容易过拟合。
实现代码如下:
#---------------------------------------------------# # 平滑标签 #---------------------------------------------------# def smooth_labels(y_true, label_smoothing,num_classes): return y_true * (1.0 - label_smoothing) + label_smoothing / num_classes
c)、CIOU
IoU是比值的概念,对目标物体的scale是不敏感的。然而常用的BBox的回归损失优化和IoU优化不是完全等价的,寻常的IoU无法直接优化没有重叠的部分。
于是有人提出直接使用IOU作为回归优化loss,CIOU是其中非常优秀的一种想法。
CIOU将目标与anchor之间的距离,重叠率、尺度以及惩罚项都考虑进去,使得目标框回归变得更加稳定,不会像IoU和GIoU一样出现训练过程中发散等问题。而惩罚因子把预测框长宽比拟合目标框的长宽比考虑进去。
CIOU公式如下
其中,分别代表了预测框和真实框的中心点的欧式距离。 c代表的是能够同时包含预测框和真实框的最小闭包区域的对角线距离。而和的公式如下
把1-CIOU就可以得到相应的LOSS了。
def box_ciou(b1, b2): """ 输入为: ---------- b1: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh b2: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh 返回为: ------- ciou: tensor, shape=(batch, feat_w, feat_h, anchor_num, 1) """ # 求出预测框左上角右下角 b1_xy = b1[..., :2] b1_wh = b1[..., 2:4] b1_wh_half = b1_wh/2. b1_mins = b1_xy - b1_wh_half b1_maxes = b1_xy + b1_wh_half # 求出真实框左上角右下角 b2_xy = b2[..., :2] b2_wh = b2[..., 2:4] b2_wh_half = b2_wh/2. b2_mins = b2_xy - b2_wh_half b2_maxes = b2_xy + b2_wh_half # 求真实框和预测框所有的iou intersect_mins = torch.max(b1_mins, b2_mins) intersect_maxes = torch.min(b1_maxes, b2_maxes) intersect_wh = torch.max(intersect_maxes - intersect_mins, torch.zeros_like(intersect_maxes)) intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1] b1_area = b1_wh[..., 0] * b1_wh[..., 1] b2_area = b2_wh[..., 0] * b2_wh[..., 1] union_area = b1_area + b2_area - intersect_area iou = intersect_area / (union_area + 1e-6) # 计算中心的差距 center_distance = torch.sum(torch.pow((b1_xy - b2_xy), 2), axis=-1) # 找到包裹两个框的最小框的左上角和右下角 enclose_mins = torch.min(b1_mins, b2_mins) enclose_maxes = torch.max(b1_maxes, b2_maxes) enclose_wh = torch.max(enclose_maxes - enclose_mins, torch.zeros_like(intersect_maxes)) # 计算对角线距离 enclose_diagonal = torch.sum(torch.pow(enclose_wh,2), axis=-1) ciou = iou - 1.0 * (center_distance) / (enclose_diagonal + 1e-7) v = (4 / (math.pi ** 2)) * torch.pow((torch.atan(b1_wh[..., 0]/b1_wh[..., 1]) - torch.atan(b2_wh[..., 0]/b2_wh[..., 1])), 2) alpha = v / (1.0 - iou + v) ciou = ciou - alpha * v return ciou
d)、学习率余弦退火衰减
余弦退火衰减法,学习率会先上升再下降,这是退火优化法的思想。(关于什么是退火算法可以百度。)
上升的时候使用线性上升,下降的时候模拟cos函数下降。执行多次。
效果如图所示:
pytorch有直接实现的函数,可直接调用。lr_scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=5, eta_min=1e-5)
2、loss组成
a)、计算loss所需参数
在计算loss的时候,实际上是y_pre和y_true之间的对比:
y_pre就是一幅图像经过网络之后的输出,内部含有三个特征层的内容;其需要解码才能够在图上作画
y_true就是一个真实图像中,它的每个真实框对应的(19,19)、(38,38)、(76,76)网格上的偏移位置、长宽与种类。其仍需要编码才能与y_pred的结构一致
实际上y_pre和y_true内容的shape都是
(batch_size,19,19,3,85)
(batch_size,38,38,3,85)
(batch_size,76,76,3,85)b)、y_pre是什么
网络最后输出的内容就是三个特征层每个网格点对应的预测框及其种类,即三个特征层分别对应着图片被分为不同size的网格后,每个网格点上三个先验框对应的位置、置信度及其种类。
对于输出的y1、y2、y3而言,[…, : 2]指的是相对于每个网格点的偏移量,[…, 2: 4]指的是宽和高,[…, 4: 5]指的是该框的置信度,[…, 5: ]指的是每个种类的预测概率。
现在的y_pre还是没有解码的,解码了之后才是真实图像上的情况。c)、y_true是什么。
y_true就是一个真实图像中,它的每个真实框对应的(19,19)、(38,38)、(76,76)网格上的偏移位置、长宽与种类。其仍需要编码才能与y_pred的结构一致
d)、loss的计算过程
在得到了y_pre和y_true后怎么对比呢?不是简单的减一下!
loss值需要对三个特征层进行处理,这里以最小的特征层为例。
1、利用y_true取出该特征层中真实存在目标的点的位置(m,19,19,3,1)及其对应的种类(m,19,19,3,80)。
2、将prediction的预测值输出进行处理,得到reshape后的预测值y_pre,shape为(m,19,19,3,85)。还有解码后的xy,wh。
3、对于每一幅图,计算其中所有真实框与预测框的IOU,如果某些预测框和真实框的重合程度大于0.5,则忽略。
4、计算ciou作为回归的loss,这里只计算正样本的回归loss。
5、计算置信度的loss,其有两部分构成,第一部分是实际上存在目标的,预测结果中置信度的值与1对比;第二部分是实际上不存在目标的,在第四步中得到其最大IOU的值与0对比。
6、计算预测种类的loss,其计算的是实际上存在目标的,预测类与真实类的差距。其实际上计算的总的loss是三个loss的和,这三个loss分别是:
- 实际存在的框,CIOU LOSS。
- 实际存在的框,预测结果中置信度的值与1对比;实际不存在的框,预测结果中置信度的值与0对比,该部分要去除被忽略的不包含目标的框。
- 实际存在的框,种类预测结果与实际结果的对比。
其实际代码如下:
#---------------------------------------------------# # 平滑标签 #---------------------------------------------------# def smooth_labels(y_true, label_smoothing,num_classes): return y_true * (1.0 - label_smoothing) + label_smoothing / num_classes def box_ciou(b1, b2): """ 输入为: ---------- b1: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh b2: tensor, shape=(batch, feat_w, feat_h, anchor_num, 4), xywh 返回为: ------- ciou: tensor, shape=(batch, feat_w, feat_h, anchor_num, 1) """ # 求出预测框左上角右下角 b1_xy = b1[..., :2] b1_wh = b1[..., 2:4] b1_wh_half = b1_wh/2. b1_mins = b1_xy - b1_wh_half b1_maxes = b1_xy + b1_wh_half # 求出真实框左上角右下角 b2_xy = b2[..., :2] b2_wh = b2[..., 2:4] b2_wh_half = b2_wh/2. b2_mins = b2_xy - b2_wh_half b2_maxes = b2_xy + b2_wh_half # 求真实框和预测框所有的iou intersect_mins = torch.max(b1_mins, b2_mins) intersect_maxes = torch.min(b1_maxes, b2_maxes) intersect_wh = torch.max(intersect_maxes - intersect_mins, torch.zeros_like(intersect_maxes)) intersect_area = intersect_wh[..., 0] * intersect_wh[..., 1] b1_area = b1_wh[..., 0] * b1_wh[..., 1] b2_area = b2_wh[..., 0] * b2_wh[..., 1] union_area = b1_area + b2_area - intersect_area iou = intersect_area / (union_area + 1e-6) # 计算中心的差距 center_distance = torch.sum(torch.pow((b1_xy - b2_xy), 2), axis=-1) # 找到包裹两个框的最小框的左上角和右下角 enclose_mins = torch.min(b1_mins, b2_mins) enclose_maxes = torch.max(b1_maxes, b2_maxes) enclose_wh = torch.max(enclose_maxes - enclose_mins, torch.zeros_like(intersect_maxes)) # 计算对角线距离 enclose_diagonal = torch.sum(torch.pow(enclose_wh,2), axis=-1) ciou = iou - 1.0 * (center_distance) / (enclose_diagonal + 1e-7) v = (4 / (math.pi ** 2)) * torch.pow((torch.atan(b1_wh[..., 0]/b1_wh[..., 1]) - torch.atan(b2_wh[..., 0]/b2_wh[..., 1])), 2) alpha = v / (1.0 - iou + v) ciou = ciou - alpha * v return ciou def clip_by_tensor(t,t_min,t_max): t=t.float() result = (t >= t_min).float() * t + (t < t_min).float() * t_min result = (result <= t_max).float() * result + (result > t_max).float() * t_max return result def MSELoss(pred,target): return (pred-target)**2 def BCELoss(pred,target): epsilon = 1e-7 pred = clip_by_tensor(pred, epsilon, 1.0 - epsilon) output = -target * torch.log(pred) - (1.0 - target) * torch.log(1.0 - pred) return output class YOLOLoss(nn.Module): def __init__(self, anchors, num_classes, img_size, label_smooth=0, cuda=True): super(YOLOLoss, self).__init__() self.anchors = anchors self.num_anchors = len(anchors) self.num_classes = num_classes self.bbox_attrs = 5 + num_classes self.img_size = img_size self.label_smooth = label_smooth self.ignore_threshold = 0.5 self.lambda_conf = 1.0 self.lambda_cls = 1.0 self.lambda_loc = 1.0 self.cuda = cuda def forward(self, input, targets=None): # input为bs,3*(5+num_classes),13,13 # 一共多少张图片 bs = input.size(0) # 特征层的高 in_h = input.size(2) # 特征层的宽 in_w = input.size(3) # 计算步长 # 每一个特征点对应原来的图片上多少个像素点 # 如果特征层为13x13的话,一个特征点就对应原来的图片上的32个像素点 stride_h = self.img_size[1] / in_h stride_w = self.img_size[0] / in_w # 把先验框的尺寸调整成特征层大小的形式 # 计算出先验框在特征层上对应的宽高 scaled_anchors = [(a_w / stride_w, a_h / stride_h) for a_w, a_h in self.anchors] # bs,3*(5+num_classes),13,13 -> bs,3,13,13,(5+num_classes) prediction = input.view(bs, int(self.num_anchors/3), self.bbox_attrs, in_h, in_w).permute(0, 1, 3, 4, 2).contiguous() # 对prediction预测进行调整 conf = torch.sigmoid(prediction[..., 4]) # Conf pred_cls = torch.sigmoid(prediction[..., 5:]) # Cls pred. # 找到哪些先验框内部包含物体 mask, noobj_mask, t_box, tconf, tcls, box_loss_scale_x, box_loss_scale_y = self.get_target(targets, scaled_anchors,in_w, in_h,self.ignore_threshold) noobj_mask, pred_boxes_for_ciou = self.get_ignore(prediction, targets, scaled_anchors, in_w, in_h, noobj_mask) if self.cuda: mask, noobj_mask = mask.cuda(), noobj_mask.cuda() box_loss_scale_x, box_loss_scale_y= box_loss_scale_x.cuda(), box_loss_scale_y.cuda() tconf, tcls = tconf.cuda(), tcls.cuda() pred_boxes_for_ciou = pred_boxes_for_ciou.cuda() t_box = t_box.cuda() box_loss_scale = 2-box_loss_scale_x*box_loss_scale_y # losses. ciou = (1 - box_ciou( pred_boxes_for_ciou[mask.bool()], t_box[mask.bool()]))* box_loss_scale[mask.bool()] loss_loc = torch.sum(ciou / bs) loss_conf = torch.sum(BCELoss(conf, mask) * mask / bs) + \ torch.sum(BCELoss(conf, mask) * noobj_mask / bs) # print(smooth_labels(tcls[mask == 1],self.label_smooth,self.num_classes)) loss_cls = torch.sum(BCELoss(pred_cls[mask == 1], smooth_labels(tcls[mask == 1],self.label_smooth,self.num_classes))/bs) # print(loss_loc,loss_conf,loss_cls) loss = loss_conf * self.lambda_conf + loss_cls * self.lambda_cls + loss_loc * self.lambda_loc return loss, loss_conf.item(), loss_cls.item(), loss_loc.item() def get_target(self, target, anchors, in_w, in_h, ignore_threshold): # 计算一共有多少张图片 bs = len(target) # 获得先验框 anchor_index = [[0,1,2],[3,4,5],[6,7,8]][[13,26,52].index(in_w)] subtract_index = [0,3,6][[13,26,52].index(in_w)] # 创建全是0或者全是1的阵列 mask = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) noobj_mask = torch.ones(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) tx = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) ty = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) tw = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) th = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) t_box = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, 4, requires_grad=False) tconf = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) tcls = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, self.num_classes, requires_grad=False) box_loss_scale_x = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) box_loss_scale_y = torch.zeros(bs, int(self.num_anchors/3), in_h, in_w, requires_grad=False) for b in range(bs): for t in range(target[b].shape[0]): # 计算出在特征层上的点位 gx = target[b][t, 0] * in_w gy = target[b][t, 1] * in_h gw = target[b][t, 2] * in_w gh = target[b][t, 3] * in_h # 计算出属于哪个网格 gi = int(gx) gj = int(gy) # 计算真实框的位置 gt_box = torch.FloatTensor(np.array([0, 0, gw, gh])).unsqueeze(0) # 计算出所有先验框的位置 anchor_shapes = torch.FloatTensor(np.concatenate((np.zeros((self.num_anchors, 2)), np.array(anchors)), 1)) # 计算重合程度 anch_ious = bbox_iou(gt_box, anchor_shapes) # Find the best matching anchor box best_n = np.argmax(anch_ious) if best_n not in anchor_index: continue # Masks if (gj < in_h) and (gi < in_w): best_n = best_n - subtract_index # 判定哪些先验框内部真实的存在物体 noobj_mask[b, best_n, gj, gi] = 0 mask[b, best_n, gj, gi] = 1 # 计算先验框中心调整参数 tx[b, best_n, gj, gi] = gx ty[b, best_n, gj, gi] = gy # 计算先验框宽高调整参数 tw[b, best_n, gj, gi] = gw th[b, best_n, gj, gi] = gh # 用于获得xywh的比例 box_loss_scale_x[b, best_n, gj, gi] = target[b][t, 2] box_loss_scale_y[b, best_n, gj, gi] = target[b][t, 3] # 物体置信度 tconf[b, best_n, gj, gi] = 1 # 种类 tcls[b, best_n, gj, gi, int(target[b][t, 4])] = 1 else: print('Step {0} out of bound'.format(b)) print('gj: {0}, height: {1} | gi: {2}, width: {3}'.format(gj, in_h, gi, in_w)) continue t_box[...,0] = tx t_box[...,1] = ty t_box[...,2] = tw t_box[...,3] = th return mask, noobj_mask, t_box, tconf, tcls, box_loss_scale_x, box_loss_scale_y def get_ignore(self,prediction,target,scaled_anchors,in_w, in_h,noobj_mask): bs = len(target) anchor_index = [[0,1,2],[3,4,5],[6,7,8]][[13,26,52].index(in_w)] scaled_anchors = np.array(scaled_anchors)[anchor_index] # 先验框的中心位置的调整参数 x = torch.sigmoid(prediction[..., 0]) y = torch.sigmoid(prediction[..., 1]) # 先验框的宽高调整参数 w = prediction[..., 2] # Width h = prediction[..., 3] # Height FloatTensor = torch.cuda.FloatTensor if x.is_cuda else torch.FloatTensor LongTensor = torch.cuda.LongTensor if x.is_cuda else torch.LongTensor # 生成网格,先验框中心,网格左上角 grid_x = torch.linspace(0, in_w - 1, in_w).repeat(in_w, 1).repeat( int(bs*self.num_anchors/3), 1, 1).view(x.shape).type(FloatTensor) grid_y = torch.linspace(0, in_h - 1, in_h).repeat(in_h, 1).t().repeat( int(bs*self.num_anchors/3), 1, 1).view(y.shape).type(FloatTensor) # 生成先验框的宽高 anchor_w = FloatTensor(scaled_anchors).index_select(1, LongTensor([0])) anchor_h = FloatTensor(scaled_anchors).index_select(1, LongTensor([1])) anchor_w = anchor_w.repeat(bs, 1).repeat(1, 1, in_h * in_w).view(w.shape) anchor_h = anchor_h.repeat(bs, 1).repeat(1, 1, in_h * in_w).view(h.shape) # 计算调整后的先验框中心与宽高 pred_boxes = FloatTensor(prediction[..., :4].shape) pred_boxes[..., 0] = x + grid_x pred_boxes[..., 1] = y + grid_y pred_boxes[..., 2] = torch.exp(w) * anchor_w pred_boxes[..., 3] = torch.exp(h) * anchor_h for i in range(bs): pred_boxes_for_ignore = pred_boxes[i] pred_boxes_for_ignore = pred_boxes_for_ignore.view(-1, 4) for t in range(target[i].shape[0]): gx = target[i][t, 0] * in_w gy = target[i][t, 1] * in_h gw = target[i][t, 2] * in_w gh = target[i][t, 3] * in_h gt_box = torch.FloatTensor(np.array([gx, gy, gw, gh])).unsqueeze(0).type(FloatTensor) anch_ious = bbox_iou(gt_box, pred_boxes_for_ignore, x1y1x2y2=False) anch_ious = anch_ious.view(pred_boxes[i].size()[:3]) noobj_mask[i][anch_ious>self.ignore_threshold] = 0 return noobj_mask, pred_boxes
训练自己的YOLOV4模型
yolo4整体的文件夹构架如下:
本文使用VOC格式进行训练。
训练前将标签文件放在VOCdevkit文件夹下的VOC2007文件夹下的Annotation中。
训练前将图片文件放在VOCdevkit文件夹下的VOC2007文件夹下的JPEGImages中。
在训练前利用voc2yolo3.py文件生成对应的txt。
再运行根目录下的voc_annotation.py,运行前需要将classes改成你自己的classes。classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"]
就会生成对应的2007_train.txt,每一行对应其图片位置及其真实框的位置。
在训练前需要修改model_data里面的voc_classes.txt文件,需要将classes改成你自己的classes。
运行train.py即可开始训练。
-
conda安装Pytorch下载过慢解决办法(11月26日更新ubuntu下pytorch1.3安装方法)
2019-03-04 16:31:313月5日更新ubuntu下pytorch1.0.1安装方法(Ubuntu16.04+CUDA9.0+PyTorch1.0.1) 7月23日更新ubuntu下pytorch1.1安装方法(通过pip) 11月26日更新ubuntu下pytorch1.3安装(通过conda) pytorch最近已经更新到了稳定... -
Win10 Python3.6下安装PyTorch
2017-06-20 16:27:15更新提醒:本文已过期,PyTorch0.4.0已经有官方的Windows支持,Windows下安装最新的PyTorch0.4.0请移步本人另一篇博客:Windows下安装PyTorch0.4.0。 2017年1月18日,周董生日这一天,facebook下的torch7团队宣布... -
Pytorch详解NLLLoss和CrossEntropyLoss
2018-12-24 10:46:12pytorch的官方文档写的也太简陋了吧…害我看了这么久… NLLLoss 在图片单标签分类时,输入m张图片,输出一个m*N的Tensor,其中N是分类个数。比如输入3张图片,分三类,最后的输出是一个3*3的Tensor,举个例子: ... -
使用anaconda安装pytorch
2019-07-09 15:14:33使用anaconda安装pytorch使用anaconda安装pytorch过程中出现的问题使用anaconda直接从网上下载使用anaconda从本地下载torch使用anaconda从本地下载torchvision 使用anaconda安装pytorch过程中出现的问题 在使用... -
pytorch 加载(.pth)格式的模型
2018-12-28 22:42:02有一些非常流行的网络如 resnet、squeezenet、densenet等在pytorch里面都有,包括网络结构和训练好的模型。 pytorch自带模型网址:https://pytorch-cn.readthedocs.io/zh/latest/torchvision/torchvision-models/ ... -
安装pytorch
2017-03-15 12:27:58已经过时,可以参考官网自行安装https://pytorch.org/ 需要注明,官网稳定版本安装在https://pytorch.org/get-started/locally/ 之前的版本在https://pytorch.org/get-started/previous-versions/ (更新于2019年... -
PyTorch 学习笔记(一):让PyTorch读取你的数据集
2018-12-19 20:15:08本文截取自《PyTorch 模型训练实用教程》,获取全文pdf请点击:https://github.com/tensor-yu/PyTorch_Tutorial 转载请注明出处: -
win10下pytorch-gpu安装以及CUDA详细安装过程
2019-08-17 09:23:55win10下pytorch-gpu安装以及CUDA详细安装过程 1.Cuda的下载安装及配置 首先我们要确定本机是否有独立显卡。在计算机-管理-设备管理器-显示适配器中,查看是否有独立显卡。 可以看到本机有一个集成显卡和独立... -
pytorch快速安装【清华源】
2019-02-28 19:14:14step1:安装anacoda 下载地址 ...conda install pytorch torchvision cudatoolkit=10.0 -c pytorch 在此期间你会发现特别的慢,慢也就算了主要它还老安装不成功,出现的错误见我的另一篇博客 补充说明:... -
pytorch高级库pytorch-ignite
2020-03-27 11:17:25Pytorch 高级库Pytorch-Ignite的使用 8行代码训练模型,2行代码搞定pytorch训练的进度条。 这个库训练模型时,比较简洁,不需要写一大堆前向传播,后向传播等代码,代码更干净,逻辑更清晰。甚至进度条的调用也... -
pytorch安装对应版本torchvision
2019-08-17 15:37:211.代码要求版本1.0,pytorch1.1会报错 ImportError: /usr/local/lib/python3.5/dist-packages/torchvision/_C.cpython-35m-x86_64-linux-gnu.so: undefined symbol: _ZN2at7getTypeERKNS_6TensorE 2. 安装torch... -
Pytorch中的torch.gather函数的含义
2018-05-30 20:05:56pytorch中的gather函数 pytorch比tensorflow更加编程友好,所以准备用pytorch试着做最近要做的一些实验。 立个flag开始学习pytorch,新开一个分类整理学习pytorch中的一些踩到的泥坑。 今天刚开始接触,读了... -
pytorch各个版本下载地址大全
2019-05-09 15:06:09pytorch官方网站有一个问题就是默认的是只推荐安装最新版本的pytorch,但是有时候我想安装一些老一点的版本,不太好找,而通过pip在线安装当然可以,可是在国内常常下载速度又很慢,或者是下载到一半就被终止了,... -
Pytorch快速入门
2020-09-14 10:29:41掌握深度学习框架PyTorch的基本语法 熟练使用PyTorch进行数据处理 掌握神经网络构建方法,构建各种网络架构 图像分类项目实战 -
Windows环境下Anaconda3安装配置pytorch详细步骤(踩坑汇总)
2019-08-09 17:34:30【机器学习】Windows环境下Anaconda安装配置pytorch详细步骤(踩坑汇总)Anaconda安装Anaconda检验安装并配置基础环境Pytorch 这次安装过程可以说是一波三折了,感觉几乎所有奇奇怪怪的问题都遇见了。感觉很少有遇见... -
Windows下安装PyTorch0.4.0
2018-04-27 09:21:52PyTorch简介 ...在2017年1月18日,facebook下的Torch7团队宣布PyTorch开源后就引来了剧烈的反响。PyTorch 是 Torch 在 Python 上的衍生版本。Torch 是一个使用 Lua 语言的神经网络库, Torch 很... -
pytorch简介
2019-04-21 14:54:53一.Pytorch是什么? Pytorch是torch的python版本,是由Facebook开源的神经网络框架,专门针对 GPU 加速的深度神经网络(DNN)编程。Torch 是一个经典的对多维矩阵数据进行操作的张量(tensor )库,在机器学习... -
pytorch: PyTorch中 使用 Tensorboard
2018-03-29 12:53:14因为工作需要,改用pytorch。但如何将训练过程可视化成了大问题。听说pytorch代码中可以插入tensorboard代码,第一反应是居然可以这么玩。。 网络上PyTorch中使用tensorboard的方法有很多。但毕竟tensorboard不是... -
什么是PyTorch,为何要使用PyTorch
2017-12-01 13:23:49什么是PyTorch PyTorch 是Torch7 团队开发的,从它的名字就可以看出,其与Torch 的不同之处在于PyTorch 使用了Python 作为开发语言。所谓“Python first”,同样说明它是一个以Python 优先的深度学习框架,不仅能够... -
ubuntu 16.04 PyTorch - 卸载 PyTorch
2018-11-07 10:15:47ubuntu 16.04 PyTorch - 卸载 PyTorch https://github.com/pytorch/pytorch/blob/master/CONTRIBUTING.md Uninstall all existing PyTorch installs: conda uninstall pytorch pip uninstall torch pip uninstall ... -
[Pytorch]pytorch中的LSTM模型
2019-03-03 11:15:06Pytorch中LSTM的公式表示为: 定义 Pytorch中LSTM的定义如下: class torch.nn.LSTM(*args, **kwargs) 参数列表 输入数据格式: input(seq_len, batch, input_size) h0(num_layers * num_directions, ... -
卸载pytorch
2020-01-05 16:59:071、使用conda卸载Pytorch conda uninstall pytorch conda uninstall libtorch 2、使用pip卸载Pytorch pip uninstall torch 如果当初是用conda install 命令安装的pytorch,则用第一种方法,如果是用pip ... -
玩PyTorch?你不得不看的PyTorch资源大列表
2020-02-29 18:16:32PyTorch我就不多说了吧,我搞CV平时也就用PyTorch和TensorFlow,不过最近更喜欢用PyTorch 了,因为TensorFlow的API真的搞的我头疼,不过因为我有时候用C++写算法的话,还是会用TensorFlow的。PyTorch 能在短时间内被... -
深度学习与PyTorch实战
2019-12-30 10:00:44通俗讲解深度学习中两大经典网络架构CNN与RNN模型,结合当下最主流PyTorch框架进行实战演练,选择当下NLP与CV中经典项目与解决方案,全部基于真实数据集,结合项目源码展开网络架构分析与实例应用。课程风格通俗易懂... -
Pytorch打怪路(一)pytorch进行CIFAR-10分类(1)CIFAR-10数据加载和处理
2018-01-30 21:26:58pytorch进行CIFAR-10分类(1)CIFAR-10数据加载和处理 1、写在前面的话 这一篇博文的内容主要来自于pytorch的官方tutorial,然后根据自己的理解把cifar10这个示例讲一遍,权当自己做笔记。因为这个cifar10是官方... -
Pytorch(五)入门:DataLoader 和 Dataset
2018-09-22 09:57:55然后加载数据,我们以前手动加载数据的方式,在数据量小的时候,并没有太大问题,但是到了大数据量,我们需要使用 shuffle, 分割成mini-batch 等操作的时候,我们可以使用PyTorch的API快速地完成这些操作。...
-
2021适用三级网络技术.rar
-
Linux如何察看一个进程内存的信息.zip
-
基于VLC封装的简单播放器
-
三级网络未来教育2020版最新.zip
-
flash actionscript3游戏编程之AS3 PureMVC设计模式在AS中的应用例子_详解MVC的作用.zip
-
【数据分析-随到随学】Tableau数据分 析+PowerBI
-
Cocos Creator游戏开发-连连看 (接入腾讯优量汇广告)
-
(新)备战2021软考网络工程师培训学习套餐
-
数据挖掘学习笔记-第一讲【你需要知道的数据挖掘潜台词】
-
阿里巴巴-大数据工程师必读手册.pdf
-
Linux 探索系统时间对数据库的若干影响
-
技术人的宅家指南
-
为什么PHP在很多公司遭到弃用?
-
广州大学概率论与数理统计学习资料.zip
-
鸿蒙开发笔记十:剪切板
-
TLP-Task06学习笔记
-
知识图谱task4
-
转行做IT-第2章 HTML入门及高级应用
-
【数据分析-随到随学】数据可视化
-
visual c++ vc修改文件属性中的创建时间,修改时间,访问时间.zip