Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
陈小光丶
yolov4-keras
提交
6d2d287c
Y
yolov4-keras
项目概览
陈小光丶
/
yolov4-keras
与 Fork 源项目一致
从无法访问的项目Fork
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
Y
yolov4-keras
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
未验证
提交
6d2d287c
编写于
5月 17, 2021
作者:
B
Bubbliiiing
提交者:
GitHub
5月 17, 2021
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Add files via upload
上级
2bf7c212
变更
8
隐藏空白更改
内联
并排
Showing
8 changed file
with
109 addition
and
45 deletion
+109
-45
FPS_test.py
FPS_test.py
+0
-1
get_dr_txt.py
get_dr_txt.py
+1
-2
nets/CSPdarknet53.py
nets/CSPdarknet53.py
+3
-4
nets/yolo4.py
nets/yolo4.py
+3
-3
train.py
train.py
+34
-23
utils/utils.py
utils/utils.py
+55
-2
voc_annotation.py
voc_annotation.py
+6
-5
yolo.py
yolo.py
+7
-5
未找到文件。
FPS_test.py
浏览文件 @
6d2d287c
import
os
import
time
import
numpy
as
np
...
...
get_dr_txt.py
浏览文件 @
6d2d287c
...
...
@@ -8,8 +8,8 @@ import os
import
numpy
as
np
from
keras
import
backend
as
K
from
keras.applications.imagenet_utils
import
preprocess_input
from
keras.layers
import
Input
from
keras.models
import
load_model
from
PIL
import
Image
from
tqdm
import
tqdm
...
...
@@ -17,7 +17,6 @@ from nets.yolo4 import yolo_body, yolo_eval
from
utils.utils
import
letterbox_image
from
yolo
import
YOLO
'''
这里设置的门限值较低是因为计算map需要用到不同门限条件下的Recall和Precision值。
所以只有保留的框足够多,计算的map才会更精确,详情可以了解map的原理。
...
...
nets/CSPdarknet53.py
浏览文件 @
6d2d287c
from
functools
import
wraps
from
keras
import
backend
as
K
from
keras.layers
import
(
Add
,
Concatenate
,
Conv2D
,
Layer
,
MaxPooling2D
,
UpSampling2D
,
ZeroPadding2D
)
from
keras.layers.advanced_activations
import
LeakyReLU
from
keras.initializers
import
random_normal
from
keras.layers
import
Add
,
Concatenate
,
Conv2D
,
Layer
,
ZeroPadding2D
from
keras.layers.normalization
import
BatchNormalization
from
keras.regularizers
import
l2
from
utils.utils
import
compose
...
...
@@ -32,7 +31,7 @@ class Mish(Layer):
@
wraps
(
Conv2D
)
def
DarknetConv2D
(
*
args
,
**
kwargs
):
# darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}
darknet_conv_kwargs
=
{}
darknet_conv_kwargs
=
{
'kernel_initializer'
:
random_normal
(
stddev
=
0.02
)
}
darknet_conv_kwargs
[
'padding'
]
=
'valid'
if
kwargs
.
get
(
'strides'
)
==
(
2
,
2
)
else
'same'
darknet_conv_kwargs
.
update
(
kwargs
)
return
Conv2D
(
*
args
,
**
darknet_conv_kwargs
)
...
...
nets/yolo4.py
浏览文件 @
6d2d287c
from
functools
import
wraps
import
keras
import
numpy
as
np
import
tensorflow
as
tf
from
keras
import
backend
as
K
from
keras.layers
import
(
Add
,
Concatenate
,
Conv2D
,
MaxPooling2D
,
UpSampling2D
,
from
keras.initializers
import
random_normal
from
keras.layers
import
(
Concatenate
,
Conv2D
,
MaxPooling2D
,
UpSampling2D
,
ZeroPadding2D
)
from
keras.layers.advanced_activations
import
LeakyReLU
from
keras.layers.normalization
import
BatchNormalization
...
...
@@ -23,7 +23,7 @@ from nets.CSPdarknet53 import darknet_body
@
wraps
(
Conv2D
)
def
DarknetConv2D
(
*
args
,
**
kwargs
):
# darknet_conv_kwargs = {'kernel_regularizer': l2(5e-4)}
darknet_conv_kwargs
=
{}
darknet_conv_kwargs
=
{
'kernel_initializer'
:
random_normal
(
stddev
=
0.02
)
}
darknet_conv_kwargs
[
'padding'
]
=
'valid'
if
kwargs
.
get
(
'strides'
)
==
(
2
,
2
)
else
'same'
darknet_conv_kwargs
.
update
(
kwargs
)
return
Conv2D
(
*
args
,
**
darknet_conv_kwargs
)
...
...
train.py
浏览文件 @
6d2d287c
import
keras.backend
as
K
import
numpy
as
np
import
tensorflow
as
tf
from
keras.backend.tensorflow_backend
import
set_session
from
keras.callbacks
import
(
EarlyStopping
,
ModelCheckpoint
,
ReduceLROnPlateau
,
TensorBoard
)
from
keras.layers
import
Input
,
Lambda
...
...
@@ -10,8 +8,8 @@ from keras.optimizers import Adam
from
nets.loss
import
yolo_loss
from
nets.yolo4
import
yolo_body
from
utils.utils
import
(
WarmUpCosineDecayScheduler
,
get_random_data
,
get_random_data
_with_Mosaic
,
rand
)
from
utils.utils
import
(
LossHistory
,
WarmUpCosineDecayScheduler
,
get_random_data
,
get_random_data_with_Mosaic
)
#---------------------------------------------------#
...
...
@@ -216,7 +214,7 @@ if __name__ == "__main__":
# 获取classes和anchor
#----------------------------------------------------#
class_names
=
get_classes
(
classes_path
)
anchors
=
get_anchors
(
anchors_path
)
anchors
=
get_anchors
(
anchors_path
)
#------------------------------------------------------#
# 一共有多少类和多少先验框
#------------------------------------------------------#
...
...
@@ -272,6 +270,7 @@ if __name__ == "__main__":
checkpoint
=
ModelCheckpoint
(
log_dir
+
'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5'
,
monitor
=
'val_loss'
,
save_weights_only
=
True
,
save_best_only
=
False
,
period
=
1
)
early_stopping
=
EarlyStopping
(
monitor
=
'val_loss'
,
min_delta
=
0
,
patience
=
10
,
verbose
=
1
)
loss_history
=
LossHistory
(
log_dir
)
#----------------------------------------------------------------------#
# 验证集的划分在train.py代码里面进行
...
...
@@ -300,20 +299,20 @@ if __name__ == "__main__":
# 提示OOM或者显存不足请调小Batch_size
#------------------------------------------------------#
if
True
:
Init_epoch
=
0
Freeze_epoch
=
50
batch_size
=
8
learning_rate_base
=
1e-3
Init_epoch
=
0
Freeze_epoch
=
50
batch_size
=
8
learning_rate_base
=
1e-3
if
Cosine_scheduler
:
# 预热期
warmup_epoch
=
int
((
Freeze_epoch
-
Init_epoch
)
*
0.2
)
warmup_epoch
=
int
((
Freeze_epoch
-
Init_epoch
)
*
0.2
)
# 总共的步长
total_steps
=
int
((
Freeze_epoch
-
Init_epoch
)
*
num_train
/
batch_size
)
total_steps
=
int
((
Freeze_epoch
-
Init_epoch
)
*
num_train
/
batch_size
)
# 预热步长
warmup_steps
=
int
(
warmup_epoch
*
num_train
/
batch_size
)
warmup_steps
=
int
(
warmup_epoch
*
num_train
/
batch_size
)
# 学习率
reduce_lr
=
WarmUpCosineDecayScheduler
(
learning_rate_base
=
learning_rate_base
,
reduce_lr
=
WarmUpCosineDecayScheduler
(
learning_rate_base
=
learning_rate_base
,
total_steps
=
total_steps
,
warmup_learning_rate
=
1e-4
,
warmup_steps
=
warmup_steps
,
...
...
@@ -325,23 +324,29 @@ if __name__ == "__main__":
reduce_lr
=
ReduceLROnPlateau
(
monitor
=
'val_loss'
,
factor
=
0.5
,
patience
=
3
,
verbose
=
1
)
model
.
compile
(
optimizer
=
Adam
(
learning_rate_base
),
loss
=
{
'yolo_loss'
:
lambda
y_true
,
y_pred
:
y_pred
})
epoch_size
=
num_train
//
batch_size
epoch_size_val
=
num_val
//
batch_size
if
epoch_size
==
0
or
epoch_size_val
==
0
:
raise
ValueError
(
"数据集过小,无法进行训练,请扩充数据集。"
)
print
(
'Train on {} samples, val on {} samples, with batch size {}.'
.
format
(
num_train
,
num_val
,
batch_size
))
model
.
fit_generator
(
data_generator
(
lines
[:
num_train
],
batch_size
,
input_shape
,
anchors
,
num_classes
,
mosaic
=
mosaic
,
random
=
True
),
steps_per_epoch
=
max
(
1
,
num_train
//
batch_size
)
,
steps_per_epoch
=
epoch_size
,
validation_data
=
data_generator
(
lines
[
num_train
:],
batch_size
,
input_shape
,
anchors
,
num_classes
,
mosaic
=
False
,
random
=
False
),
validation_steps
=
max
(
1
,
num_val
//
batch_size
)
,
validation_steps
=
epoch_size_val
,
epochs
=
Freeze_epoch
,
initial_epoch
=
Init_epoch
,
callbacks
=
[
logging
,
checkpoint
,
reduce_lr
,
early_stopping
])
callbacks
=
[
logging
,
checkpoint
,
reduce_lr
,
early_stopping
,
loss_history
])
model
.
save_weights
(
log_dir
+
'trained_weights_stage_1.h5'
)
for
i
in
range
(
freeze_layers
):
model_body
.
layers
[
i
].
trainable
=
True
if
True
:
Freeze_epoch
=
50
Epoch
=
100
batch_size
=
2
learning_rate_base
=
1e-4
Freeze_epoch
=
50
Epoch
=
100
batch_size
=
2
learning_rate_base
=
1e-4
if
Cosine_scheduler
:
# 预热期
...
...
@@ -363,12 +368,18 @@ if __name__ == "__main__":
reduce_lr
=
ReduceLROnPlateau
(
monitor
=
'val_loss'
,
factor
=
0.5
,
patience
=
3
,
verbose
=
1
)
model
.
compile
(
optimizer
=
Adam
(
learning_rate_base
),
loss
=
{
'yolo_loss'
:
lambda
y_true
,
y_pred
:
y_pred
})
epoch_size
=
num_train
//
batch_size
epoch_size_val
=
num_val
//
batch_size
if
epoch_size
==
0
or
epoch_size_val
==
0
:
raise
ValueError
(
"数据集过小,无法进行训练,请扩充数据集。"
)
print
(
'Train on {} samples, val on {} samples, with batch size {}.'
.
format
(
num_train
,
num_val
,
batch_size
))
model
.
fit_generator
(
data_generator
(
lines
[:
num_train
],
batch_size
,
input_shape
,
anchors
,
num_classes
,
mosaic
=
mosaic
,
random
=
True
),
steps_per_epoch
=
max
(
1
,
num_train
//
batch_size
)
,
steps_per_epoch
=
epoch_size
,
validation_data
=
data_generator
(
lines
[
num_train
:],
batch_size
,
input_shape
,
anchors
,
num_classes
,
mosaic
=
False
,
random
=
False
),
validation_steps
=
max
(
1
,
num_val
//
batch_size
)
,
validation_steps
=
epoch_size_val
,
epochs
=
Epoch
,
initial_epoch
=
Freeze_epoch
,
callbacks
=
[
logging
,
checkpoint
,
reduce_lr
,
early_stopping
])
callbacks
=
[
logging
,
checkpoint
,
reduce_lr
,
early_stopping
,
loss_history
])
model
.
save_weights
(
log_dir
+
'last1.h5'
)
utils/utils.py
浏览文件 @
6d2d287c
import
os
from
functools
import
reduce
import
cv2
import
keras
import
keras.backend
as
K
import
matplotlib.pyplot
as
plt
import
numpy
as
np
from
matplotlib.colors
import
hsv_to_rgb
,
rgb_to_hsv
import
scipy.signal
from
PIL
import
Image
...
...
@@ -298,6 +300,58 @@ def get_random_data(annotation_line, input_shape, max_boxes=100, jitter=.3, hue=
return
image_data
,
box_data
class
LossHistory
(
keras
.
callbacks
.
Callback
):
def
__init__
(
self
,
log_dir
):
import
datetime
curr_time
=
datetime
.
datetime
.
now
()
time_str
=
datetime
.
datetime
.
strftime
(
curr_time
,
'%Y_%m_%d_%H_%M_%S'
)
self
.
log_dir
=
log_dir
self
.
time_str
=
time_str
self
.
save_path
=
os
.
path
.
join
(
self
.
log_dir
,
"loss_"
+
str
(
self
.
time_str
))
self
.
losses
=
[]
self
.
val_loss
=
[]
os
.
makedirs
(
self
.
save_path
)
def
on_epoch_end
(
self
,
batch
,
logs
=
{}):
self
.
losses
.
append
(
logs
.
get
(
'loss'
))
self
.
val_loss
.
append
(
logs
.
get
(
'val_loss'
))
with
open
(
os
.
path
.
join
(
self
.
save_path
,
"epoch_loss_"
+
str
(
self
.
time_str
)
+
".txt"
),
'a'
)
as
f
:
f
.
write
(
str
(
logs
.
get
(
'loss'
)))
f
.
write
(
"
\n
"
)
with
open
(
os
.
path
.
join
(
self
.
save_path
,
"epoch_val_loss_"
+
str
(
self
.
time_str
)
+
".txt"
),
'a'
)
as
f
:
f
.
write
(
str
(
logs
.
get
(
'val_loss'
)))
f
.
write
(
"
\n
"
)
self
.
loss_plot
()
def
loss_plot
(
self
):
iters
=
range
(
len
(
self
.
losses
))
plt
.
figure
()
plt
.
plot
(
iters
,
self
.
losses
,
'red'
,
linewidth
=
2
,
label
=
'train loss'
)
plt
.
plot
(
iters
,
self
.
val_loss
,
'coral'
,
linewidth
=
2
,
label
=
'val loss'
)
try
:
if
len
(
self
.
losses
)
<
25
:
num
=
5
else
:
num
=
15
plt
.
plot
(
iters
,
scipy
.
signal
.
savgol_filter
(
self
.
losses
,
num
,
3
),
'green'
,
linestyle
=
'--'
,
linewidth
=
2
,
label
=
'smooth train loss'
)
plt
.
plot
(
iters
,
scipy
.
signal
.
savgol_filter
(
self
.
val_loss
,
num
,
3
),
'#8B4513'
,
linestyle
=
'--'
,
linewidth
=
2
,
label
=
'smooth val loss'
)
except
:
pass
plt
.
grid
(
True
)
plt
.
xlabel
(
'Epoch'
)
plt
.
ylabel
(
'Loss'
)
plt
.
title
(
'A Loss Curve'
)
plt
.
legend
(
loc
=
"upper right"
)
plt
.
savefig
(
os
.
path
.
join
(
self
.
save_path
,
"epoch_loss_"
+
str
(
self
.
time_str
)
+
".png"
))
plt
.
cla
()
plt
.
close
(
"all"
)
def
cosine_decay_with_warmup
(
global_step
,
learning_rate_base
,
total_steps
,
...
...
@@ -339,7 +393,6 @@ def cosine_decay_with_warmup(global_step,
learning_rate
=
max
(
learning_rate
,
min_learn_rate
)
return
learning_rate
class
WarmUpCosineDecayScheduler
(
keras
.
callbacks
.
Callback
):
"""
继承Callback,实现对学习率的调度
...
...
voc_annotation.py
浏览文件 @
6d2d287c
...
...
@@ -7,7 +7,9 @@ import xml.etree.ElementTree as ET
from
os
import
getcwd
sets
=
[(
'2007'
,
'train'
),
(
'2007'
,
'val'
),
(
'2007'
,
'test'
)]
#-----------------------------------------------------#
# 这里设定的classes顺序要和model_data里的txt一样
#-----------------------------------------------------#
classes
=
[
"aeroplane"
,
"bicycle"
,
"bird"
,
"boat"
,
"bottle"
,
"bus"
,
"car"
,
"cat"
,
"chair"
,
"cow"
,
"diningtable"
,
"dog"
,
"horse"
,
"motorbike"
,
"person"
,
"pottedplant"
,
"sheep"
,
"sofa"
,
"train"
,
"tvmonitor"
]
def
convert_annotation
(
year
,
image_id
,
list_file
):
...
...
@@ -19,20 +21,19 @@ def convert_annotation(year, image_id, list_file):
difficult
=
0
if
obj
.
find
(
'difficult'
)
!=
None
:
difficult
=
obj
.
find
(
'difficult'
).
text
cls
=
obj
.
find
(
'name'
).
text
if
cls
not
in
classes
or
int
(
difficult
)
==
1
:
continue
cls_id
=
classes
.
index
(
cls
)
xmlbox
=
obj
.
find
(
'bndbox'
)
b
=
(
int
(
xmlbox
.
find
(
'xmin'
).
text
),
int
(
xmlbox
.
find
(
'ymin'
).
text
),
int
(
xmlbox
.
find
(
'xmax'
).
text
),
int
(
xmlbox
.
find
(
'ymax'
).
text
))
b
=
(
int
(
float
(
xmlbox
.
find
(
'xmin'
).
text
)),
int
(
float
(
xmlbox
.
find
(
'ymin'
).
text
)),
int
(
float
(
xmlbox
.
find
(
'xmax'
).
text
)),
int
(
float
(
xmlbox
.
find
(
'ymax'
).
text
)
))
list_file
.
write
(
" "
+
","
.
join
([
str
(
a
)
for
a
in
b
])
+
','
+
str
(
cls_id
))
wd
=
getcwd
()
for
year
,
image_set
in
sets
:
image_ids
=
open
(
'VOCdevkit/VOC%s/ImageSets/Main/%s.txt'
%
(
year
,
image_set
)).
read
().
strip
().
split
()
list_file
=
open
(
'%s_%s.txt'
%
(
year
,
image_set
),
'w'
)
image_ids
=
open
(
'VOCdevkit/VOC%s/ImageSets/Main/%s.txt'
%
(
year
,
image_set
)
,
encoding
=
'utf-8'
).
read
().
strip
().
split
()
list_file
=
open
(
'%s_%s.txt'
%
(
year
,
image_set
),
'w'
,
encoding
=
'utf-8'
)
for
image_id
in
image_ids
:
list_file
.
write
(
'%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg'
%
(
wd
,
year
,
image_id
))
convert_annotation
(
year
,
image_id
,
list_file
)
...
...
yolo.py
浏览文件 @
6d2d287c
import
colorsys
import
copy
import
os
from
timeit
import
default_timer
as
timer
import
numpy
as
np
from
keras
import
backend
as
K
...
...
@@ -131,6 +129,11 @@ class YOLO(object):
# 检测图片
#---------------------------------------------------#
def
detect_image
(
self
,
image
):
#---------------------------------------------------------#
# 在这里将图像转换成RGB图像,防止灰度图在预测时报错。
#---------------------------------------------------------#
image
=
image
.
convert
(
'RGB'
)
#---------------------------------------------------------#
# 给图像增加灰条,实现不失真的resize
# 也可以直接resize进行识别
...
...
@@ -138,8 +141,7 @@ class YOLO(object):
if
self
.
letterbox_image
:
boxed_image
=
letterbox_image
(
image
,
(
self
.
model_image_size
[
1
],
self
.
model_image_size
[
0
]))
else
:
boxed_image
=
image
.
convert
(
'RGB'
)
boxed_image
=
boxed_image
.
resize
((
self
.
model_image_size
[
1
],
self
.
model_image_size
[
0
]),
Image
.
BICUBIC
)
boxed_image
=
image
.
resize
((
self
.
model_image_size
[
1
],
self
.
model_image_size
[
0
]),
Image
.
BICUBIC
)
image_data
=
np
.
array
(
boxed_image
,
dtype
=
'float32'
)
image_data
/=
255.
#---------------------------------------------------------#
...
...
@@ -162,7 +164,7 @@ class YOLO(object):
#---------------------------------------------------------#
# 设置字体
#---------------------------------------------------------#
font
=
ImageFont
.
truetype
(
font
=
'
font
/simhei.ttf'
,
font
=
ImageFont
.
truetype
(
font
=
'
model_data
/simhei.ttf'
,
size
=
np
.
floor
(
3e-2
*
image
.
size
[
1
]
+
0.5
).
astype
(
'int32'
))
thickness
=
max
((
image
.
size
[
0
]
+
image
.
size
[
1
])
//
300
,
1
)
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录