Skip to content

Commit 4bfebe0

Browse files
committed
main
1 parent cad091c commit 4bfebe0

25 files changed

+2062
-1
lines changed

.assets/00008.jpg

491 KB
Loading

.assets/00034.jpg

383 KB
Loading

.assets/00037.jpg

314 KB
Loading
Loading
Loading
Loading
Loading

.assets/image-20201214193009500.png

51.2 MB
Loading

.assets/image-20201214202949957.png

9.94 KB
Loading

.assets/image-20201214203019767.png

18.5 KB
Loading
Binary file not shown.

README.md

+128-1
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,129 @@
11
# YoloV4_Insulators
2-
YoloV4算法检测绝缘子-人工智能课程设计
2+
3+
### 一、项目简介
4+
5+
Yolo_Insulators是一个基于YoloV4的绝缘子目标检测程序,人工智能课程设计作业。
6+
7+
**依赖**
8+
9+
- Python3.6
10+
- Pytorch1.2.0
11+
- CUDA10.0
12+
13+
### 二、Yolo算法简介 [](https://baijiahao.baidu.com/s?id=1664853943386329436&wfr=spider&for=pc)
14+
15+
“You Only Look Once”或“YOLO”是一个对象检测算法的名字,这是Redmon等人在2016年的一篇研究论文中命名的。YOLO实现了自动驾驶汽车等前沿技术中使用的实时对象检测。让我们看看是什么使该算法如此受欢迎,并概述其工作原理。
16+
17+
![对象检测示例](.assets/4034970a304e251f40223503c86146117d3e5366.jpeg)
18+
19+
#### 1、背景
20+
21+
**实时的重要性**
22+
23+
人们看到图像以后,可以立即识别其中的对象、它们的位置和相对位置。这使得我们能够在几乎无意识的情况下完成复杂的任务,比如开车。因此,对汽车进行自动驾驶训练需要类似水平的反应能力和准确性。在其最基本的形式中,这样的系统必须能够分析实时视频中的道路,并能够在继续确定路径之前检测各种类型的对象及其在现实世界中的位置,所有这些都必须是实时的。
24+
25+
**在YOLO之前**
26+
27+
先前的检测系统使用分类器对测试图像的不同切片进行评估。例如,Deformable Parts Model (DPM)涉及到在图像中均匀间隔的位置上滑动窗口并在这些部件上运行分类器。R-CNN(Region-based Convolutional Neural Networks)是另一种模型,它运行一种分割算法将一幅图像分割成一个个小块,然后在这些小块上运行一个分类器。但是,速度慢、优化困难一直困扰着这种YOLO之前的系统。
28+
29+
#### 2、YOLO算法
30+
31+
YOLO将对象检测重新定义为一个回归问题。它将单个卷积神经网络(CNN)应用于整个图像,将图像分成网格,并预测每个网格的类概率和边界框。例如,以一个100x100的图像为例。我们把它分成网格,比如7x7。
32+
33+
![img](.assets/8694a4c27d1ed21bc6ed15f6c38952c250da3fee.jpeg)
34+
35+
然后,对于每个网格,网络都会预测一个边界框和与每个类别(汽车,行人,交通信号灯等)相对应的概率。
36+
37+
![img](.assets/d52a2834349b033bae87aafa7b29b9d5d439bdf5.jpeg)
38+
39+
每个边界框可以使用四个描述符进行描述:
40+
41+
边界框的中心高度宽度值映射到对象所属的类
42+
43+
此外,该算法还可以预测边界框中存在对象的概率。如果一个对象的中心落在一个网格单元中,则该网格单元负责检测该对象。每个网格中将有多个边界框。在训练时,我们希望每个对象只有一个边界框。因此,我们根据哪个Box与ground truth box的重叠度最高,从而分配一个Box来负责预测对象。
44+
45+
最后,我们对每个类的对象应用一个称为“非最大抑制(Non Max Suppression)”的方法来过滤出“置信度”小于阈值的边界框。这为我们提供了图像预测。
46+
47+
![img](.assets/a8014c086e061d95a3897a4e0b1385d760d9cae3.jpeg)
48+
49+
#### 3、重要性
50+
51+
YOLO非常快。由于检测问题是一个回归问题,所以不需要复杂的管道。它比“R-CNN”快1000倍,比“Fast R-CNN”快100倍。它能够处理实时视频流,延迟小于25毫秒。它的精度是以前实时系统的两倍多。同样重要的是,YOLO遵循的是“端到端深度学习”的实践。
52+
53+
### 三、文件结构
54+
55+
```
56+
.
57+
├─img # 存放预测后的图像
58+
├─logs # 存放训练的模型文件
59+
├─model_data # 存放预训练模型
60+
├─nets # 网络结构
61+
├─utils # 数据加载、NMS等
62+
├─VOCdevkit # VOC数据集
63+
└─VOC2007
64+
├─Annotations # 标注XML文件
65+
├─ImageSets
66+
│ └─Main
67+
└─JPEGImages # 数据集图片
68+
```
69+
70+
71+
72+
### 三、数据集
73+
74+
数据集采用网上开源的绝缘子数据集,共600张图片。数据集格式使用VOC2007,标注文件为xml。
75+
76+
你可以通过百度网盘来下载[绝缘子数据集-提取码:djuf ](https://pan.baidu.com/s/1z_J9tx-151FFAVW9ASJz9A ),以下是部分数据集图片。
77+
78+
![image-20201214193009500](.assets/image-20201214193009500.png)
79+
80+
若需要扩增自己的数据,可以使用[labelimg](https://github.com/tzutalin/labelImg)来标注新的数据,注意标签为insulator。
81+
82+
#### 如何制作数据集
83+
84+
将数据集图片存放至`VOCdevkit/VOC2007/JPEGImages`目录,再将标注文件放至`VOCdevkit/VOC2007/Anootations`目录。
85+
86+
执行
87+
88+
```
89+
python VOCdevkit/VOC2007/voc2yolo4.py
90+
python voc_annotation.py
91+
```
92+
93+
运行成功后会在`VOCdevkit/VOC2007/ImageSets/Main`目录生成训练需要的文件。
94+
95+
### 四、训练模型
96+
97+
由于数据集数量较小,直接训练模型收敛效果可能不佳,达不到高识别率。绝缘子识别是目标检测的一个子应用,其模型的很多参数与其他目标检测的参数相似,因此可以通过一个在完备的数据集上训练好的模型通过迁移学习应用到绝缘子识别上,可以在数据集较小的情况下使模型快速收敛,实现更高的准确率。
98+
99+
迁移学习策略:先冻结CSPDarknet53网络, 只训练FPN部分,后期再将CSPDarknet53解冻,在全网络上训练模型。
100+
101+
`train.py`中可以通过设置`Cosine_lr``mosaic``smoooth_label`来设置是否采用余弦退火策略、mosaic数据增强和标签平滑等。训练集和验证集默认比例为9:1,可在`train.py`文件中修改`val_split`参数来调整比例。同时也可以调整参数`lr``Batch_size``Epoch`来修改学习率、批大小及迭代次数。
102+
103+
训练模型只需运行
104+
105+
```
106+
python train.py
107+
```
108+
109+
训练好的模型会存在`log`文件下。
110+
111+
你可以通过百度网盘来下载我已经训练好的[模型-提取码:t9ct](https://pan.baidu.com/s/1bGBd9821KCpcgOTRuHQseg),以下是训练模型loss的变化,
112+
113+
![image-20201214203019767](.assets/image-20201214203019767.png)
114+
115+
### 五、测试模型
116+
117+
若使用自己训练的模型,需要在根目录的`yolo.py`中修改`model_path`的路径。不过也可以使用这里训练好的[模型-提取码:t9ct](https://pan.baidu.com/s/1bGBd9821KCpcgOTRuHQseg),下载模型后将模型文件放入`logs`文件夹即可。在`predict.py`中修改`imgPath`为需要预测的图片路径,运行
118+
119+
```
120+
python predict.py
121+
```
122+
123+
即可弹出预测成功的窗口,并将预测的结果存放至`img`文件夹中。
124+
125+
以下为我训练的模型的部分测试结果。
126+
127+
<img src=".assets/00034.jpg" alt="00034" style="zoom:50%;" />
128+
129+
<img src=".assets/00037.jpg" alt="00037" style="zoom:50%;" />

VOCdevkit/VOC2007/voc2yolo4.py

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
'''
2+
生成图片的文件名的列表,为了后面annotation做准备,后续还需要运行voc_annotation
3+
'''
4+
import os
5+
import random
6+
7+
xmlfilepath=r'./VOCdevkit/VOC2007/Annotations'
8+
saveBasePath=r"./VOCdevkit/VOC2007/ImageSets/Main/"
9+
10+
trainval_percent=1
11+
train_percent=1
12+
13+
temp_xml = os.listdir(xmlfilepath)
14+
total_xml = []
15+
for xml in temp_xml:
16+
if xml.endswith(".xml"):
17+
total_xml.append(xml)
18+
19+
num=len(total_xml)
20+
list=range(num)
21+
tv=int(num*trainval_percent)
22+
tr=int(tv*train_percent)
23+
trainval= random.sample(list,tv)
24+
train=random.sample(trainval,tr)
25+
26+
print("train and val size",tv)
27+
print("traub suze",tr)
28+
ftrainval = open(os.path.join(saveBasePath,'trainval.txt'), 'w')
29+
ftest = open(os.path.join(saveBasePath,'test.txt'), 'w')
30+
ftrain = open(os.path.join(saveBasePath,'train.txt'), 'w')
31+
fval = open(os.path.join(saveBasePath,'val.txt'), 'w')
32+
33+
for i in list:
34+
name=total_xml[i][:-4]+'\n'
35+
if i in trainval:
36+
ftrainval.write(name)
37+
if i in train:
38+
ftrain.write(name)
39+
else:
40+
fval.write(name)
41+
else:
42+
ftest.write(name)
43+
44+
ftrainval.close()
45+
ftrain.close()
46+
fval.close()
47+
ftest .close()

model_data/new_classes.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
insulator

model_data/simhei.ttf

9.3 MB
Binary file not shown.

model_data/yolo_anchors.txt

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
12, 16, 19, 36, 40, 28, 36, 75, 76, 55, 72, 146, 142, 110, 192, 243, 459, 401

nets/CSPdarknet.py

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import torch
2+
import torch.nn.functional as F
3+
import torch.nn as nn
4+
import math
5+
from collections import OrderedDict
6+
7+
#-------------------------------------------------#
8+
# MISH激活函数
9+
#-------------------------------------------------#
10+
class Mish(nn.Module):
11+
def __init__(self):
12+
super(Mish, self).__init__()
13+
14+
def forward(self, x):
15+
return x * torch.tanh(F.softplus(x))
16+
17+
#-------------------------------------------------#
18+
# 卷积块
19+
# CONV+BATCHNORM+MISH
20+
#-------------------------------------------------#
21+
class BasicConv(nn.Module):
22+
def __init__(self, in_channels, out_channels, kernel_size, stride=1):
23+
super(BasicConv, self).__init__()
24+
25+
self.conv = nn.Conv2d(in_channels, out_channels, kernel_size, stride, kernel_size//2, bias=False)
26+
self.bn = nn.BatchNorm2d(out_channels)
27+
self.activation = Mish()
28+
29+
def forward(self, x):
30+
x = self.conv(x)
31+
x = self.bn(x)
32+
x = self.activation(x)
33+
return x
34+
35+
#---------------------------------------------------#
36+
# CSPdarknet的结构块的组成部分
37+
# 内部堆叠的残差块
38+
#---------------------------------------------------#
39+
class Resblock(nn.Module):
40+
def __init__(self, channels, hidden_channels=None, residual_activation=nn.Identity()):
41+
super(Resblock, self).__init__()
42+
43+
if hidden_channels is None:
44+
hidden_channels = channels
45+
46+
self.block = nn.Sequential(
47+
BasicConv(channels, hidden_channels, 1),
48+
BasicConv(hidden_channels, channels, 3)
49+
)
50+
51+
def forward(self, x):
52+
return x + self.block(x)
53+
54+
#---------------------------------------------------#
55+
# CSPdarknet的结构块
56+
# 存在一个大残差边
57+
# 这个大残差边绕过了很多的残差结构
58+
#---------------------------------------------------#
59+
class Resblock_body(nn.Module):
60+
def __init__(self, in_channels, out_channels, num_blocks, first):
61+
super(Resblock_body, self).__init__()
62+
63+
self.downsample_conv = BasicConv(in_channels, out_channels, 3, stride=2)
64+
65+
if first:
66+
self.split_conv0 = BasicConv(out_channels, out_channels, 1)
67+
self.split_conv1 = BasicConv(out_channels, out_channels, 1)
68+
self.blocks_conv = nn.Sequential(
69+
Resblock(channels=out_channels, hidden_channels=out_channels//2),
70+
BasicConv(out_channels, out_channels, 1)
71+
)
72+
self.concat_conv = BasicConv(out_channels*2, out_channels, 1)
73+
else:
74+
self.split_conv0 = BasicConv(out_channels, out_channels//2, 1)
75+
self.split_conv1 = BasicConv(out_channels, out_channels//2, 1)
76+
77+
self.blocks_conv = nn.Sequential(
78+
*[Resblock(out_channels//2) for _ in range(num_blocks)],
79+
BasicConv(out_channels//2, out_channels//2, 1)
80+
)
81+
self.concat_conv = BasicConv(out_channels, out_channels, 1)
82+
83+
def forward(self, x):
84+
x = self.downsample_conv(x)
85+
86+
x0 = self.split_conv0(x)
87+
88+
x1 = self.split_conv1(x)
89+
x1 = self.blocks_conv(x1)
90+
91+
x = torch.cat([x1, x0], dim=1)
92+
x = self.concat_conv(x)
93+
94+
return x
95+
96+
class CSPDarkNet(nn.Module):
97+
def __init__(self, layers):
98+
super(CSPDarkNet, self).__init__()
99+
self.inplanes = 32
100+
self.conv1 = BasicConv(3, self.inplanes, kernel_size=3, stride=1)
101+
self.feature_channels = [64, 128, 256, 512, 1024]
102+
103+
self.stages = nn.ModuleList([
104+
Resblock_body(self.inplanes, self.feature_channels[0], layers[0], first=True),
105+
Resblock_body(self.feature_channels[0], self.feature_channels[1], layers[1], first=False),
106+
Resblock_body(self.feature_channels[1], self.feature_channels[2], layers[2], first=False),
107+
Resblock_body(self.feature_channels[2], self.feature_channels[3], layers[3], first=False),
108+
Resblock_body(self.feature_channels[3], self.feature_channels[4], layers[4], first=False)
109+
])
110+
111+
self.num_features = 1
112+
# 进行权值初始化
113+
for m in self.modules():
114+
if isinstance(m, nn.Conv2d):
115+
n = m.kernel_size[0] * m.kernel_size[1] * m.out_channels
116+
m.weight.data.normal_(0, math.sqrt(2. / n))
117+
elif isinstance(m, nn.BatchNorm2d):
118+
m.weight.data.fill_(1)
119+
m.bias.data.zero_()
120+
121+
122+
def forward(self, x):
123+
x = self.conv1(x)
124+
125+
x = self.stages[0](x)
126+
x = self.stages[1](x)
127+
out3 = self.stages[2](x)
128+
out4 = self.stages[3](out3)
129+
out5 = self.stages[4](out4)
130+
131+
return out3, out4, out5
132+
133+
def darknet53(pretrained, **kwargs):
134+
model = CSPDarkNet([1, 2, 8, 8, 4])
135+
if pretrained:
136+
if isinstance(pretrained, str):
137+
model.load_state_dict(torch.load(pretrained))
138+
else:
139+
raise Exception("darknet request a pretrained path. got [{}]".format(pretrained))
140+
return model

0 commit comments

Comments
 (0)