Skip to content

Commit a745d7d

Browse files
authored
Update 3.2_yolov4tiny_from_colab.py
1 parent 084d0f4 commit a745d7d

File tree

1 file changed

+42
-145
lines changed

1 file changed

+42
-145
lines changed

3.YoloV4/3.2_yolov4tiny_from_colab.py

+42-145
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,6 @@
1515

1616

1717

18-
#---------------------------------------------------#
19-
# 获得类和先验框
20-
#---------------------------------------------------#
2118
def get_classes(classes_path):
2219
'''loads the classes'''
2320
with open(classes_path) as f:
@@ -32,9 +29,7 @@ def get_anchors(anchors_path):
3229
anchors = [float(x) for x in anchors.split(',')]
3330
return np.array(anchors).reshape(-1, 2)
3431

35-
#---------------------------------------------------#
36-
# 训练数据生成器
37-
#---------------------------------------------------#
32+
3833
def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes, mosaic=False, random=True):
3934
n = len(annotation_lines)
4035
i = 0
@@ -63,75 +58,49 @@ def data_generator(annotation_lines, batch_size, input_shape, anchors, num_class
6358
y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes)
6459
yield [image_data, *y_true], np.zeros(batch_size)
6560

66-
#---------------------------------------------------#
67-
# 读入xml文件,并输出y_true
68-
#---------------------------------------------------#
61+
6962
def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
7063
assert (true_boxes[..., 4]<num_classes).all(), 'class id must be less than num_classes'
71-
# 一共有两个特征层数
64+
7265
num_layers = len(anchors)//3
73-
#-----------------------------------------------------------#
74-
# 13x13的特征层对应的anchor是[81,82], [135,169], [344,319]
75-
# 26x26的特征层对应的anchor是[23,27], [37,58], [81,82]
76-
#-----------------------------------------------------------#
66+
7767
anchor_mask = [[6,7,8], [3,4,5], [0,1,2]] if num_layers==3 else [[3,4,5], [1,2,3]]
7868

79-
#-----------------------------------------------------------#
80-
# 获得框的坐标和图片的大小
81-
#-----------------------------------------------------------#
69+
8270
true_boxes = np.array(true_boxes, dtype='float32')
8371
input_shape = np.array(input_shape, dtype='int32')
84-
#-----------------------------------------------------------#
85-
# 通过计算获得真实框的中心和宽高
86-
# 中心点(m,n,2) 宽高(m,n,2)
87-
#-----------------------------------------------------------#
72+
8873
boxes_xy = (true_boxes[..., 0:2] + true_boxes[..., 2:4]) // 2
8974
boxes_wh = true_boxes[..., 2:4] - true_boxes[..., 0:2]
90-
#-----------------------------------------------------------#
91-
# 将真实框归一化到小数形式
92-
#-----------------------------------------------------------#
75+
9376
true_boxes[..., 0:2] = boxes_xy/input_shape[::-1]
9477
true_boxes[..., 2:4] = boxes_wh/input_shape[::-1]
9578

96-
# m为图片数量,grid_shapes为网格的shape
79+
9780
m = true_boxes.shape[0]
9881
grid_shapes = [input_shape//{0:32, 1:16, 2:8}[l] for l in range(num_layers)]
99-
#-----------------------------------------------------------#
100-
# y_true的格式为(m,13,13,3,85)(m,26,26,3,85)(m,52,52,3,85)
101-
#-----------------------------------------------------------#
82+
10283
y_true = [np.zeros((m,grid_shapes[l][0],grid_shapes[l][1],len(anchor_mask[l]),5+num_classes),
10384
dtype='float32') for l in range(num_layers)]
10485

105-
#-----------------------------------------------------------#
106-
# [6,2] -> [1,6,2]
107-
#-----------------------------------------------------------#
86+
10887
anchors = np.expand_dims(anchors, 0)
10988
anchor_maxes = anchors / 2.
11089
anchor_mins = -anchor_maxes
11190

112-
#-----------------------------------------------------------#
113-
# 长宽要大于0才有效
114-
#-----------------------------------------------------------#
91+
11592
valid_mask = boxes_wh[..., 0]>0
11693

11794
for b in range(m):
118-
# 对每一张图进行处理
95+
11996
wh = boxes_wh[b, valid_mask[b]]
12097
if len(wh)==0: continue
121-
#-----------------------------------------------------------#
122-
# [n,2] -> [n,1,2]
123-
#-----------------------------------------------------------#
98+
12499
wh = np.expand_dims(wh, -2)
125100
box_maxes = wh / 2.
126101
box_mins = -box_maxes
127102

128-
#-----------------------------------------------------------#
129-
# 计算所有真实框和先验框的交并比
130-
# intersect_area [n,6]
131-
# box_area [n,1]
132-
# anchor_area [1,6]
133-
# iou [n,6]
134-
#-----------------------------------------------------------#
103+
135104
intersect_mins = np.maximum(box_mins, anchor_mins)
136105
intersect_maxes = np.minimum(box_maxes, anchor_maxes)
137106
intersect_wh = np.maximum(intersect_maxes - intersect_mins, 0.)
@@ -141,141 +110,76 @@ def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
141110
anchor_area = anchors[..., 0] * anchors[..., 1]
142111

143112
iou = intersect_area / (box_area + anchor_area - intersect_area)
144-
#-----------------------------------------------------------#
145-
# 维度是[n,] 感谢 消尽不死鸟 的提醒
146-
#-----------------------------------------------------------#
113+
147114
best_anchor = np.argmax(iou, axis=-1)
148115

149116
for t, n in enumerate(best_anchor):
150-
#-----------------------------------------------------------#
151-
# 找到每个真实框所属的特征层
152-
#-----------------------------------------------------------#
117+
153118
for l in range(num_layers):
154119
if n in anchor_mask[l]:
155-
#-----------------------------------------------------------#
156-
# floor用于向下取整,找到真实框所属的特征层对应的x、y轴坐标
157-
#-----------------------------------------------------------#
120+
158121
i = np.floor(true_boxes[b,t,0] * grid_shapes[l][1]).astype('int32')
159122
j = np.floor(true_boxes[b,t,1] * grid_shapes[l][0]).astype('int32')
160-
#-----------------------------------------------------------#
161-
# k指的的当前这个特征点的第k个先验框
162-
#-----------------------------------------------------------#
123+
163124
k = anchor_mask[l].index(n)
164-
#-----------------------------------------------------------#
165-
# c指的是当前这个真实框的种类
166-
#-----------------------------------------------------------#
125+
167126
c = true_boxes[b, t, 4].astype('int32')
168-
#-----------------------------------------------------------#
169-
# y_true的shape为(m,13,13,3,85)(m,26,26,3,85)(m,52,52,3,85)
170-
# 最后的85可以拆分成4+1+80,4代表的是框的中心与宽高、
171-
# 1代表的是置信度、80代表的是种类
172-
#-----------------------------------------------------------#
127+
173128
y_true[l][b, j, i, k, 0:4] = true_boxes[b, t, 0:4]
174129
y_true[l][b, j, i, k, 4] = 1
175130
y_true[l][b, j, i, k, 5+c] = 1
176131

177132
return y_true
178133

179-
#----------------------------------------------------#
180-
# 检测精度mAP和pr曲线计算参考视频
181-
# https://www.bilibili.com/video/BV1zE411u7Vw
182-
#----------------------------------------------------#
134+
183135
if __name__ == "__main__":
184-
#----------------------------------------------------#
185-
# 获得图片路径和标签
186-
#----------------------------------------------------#
136+
187137
annotation_path = '/content/drive/MyDrive/yolov4_tiny/yolov4-tiny-keras-master/new_train.txt'
188-
#------------------------------------------------------#
189-
# 训练后的模型保存的位置,保存在logs文件夹里面
190-
#------------------------------------------------------#
138+
191139
log_dir = 'logs/'
192-
#----------------------------------------------------#
193-
# classes和anchor的路径,非常重要
194-
# 训练前一定要修改classes_path,使其对应自己的数据集
195-
#----------------------------------------------------#
140+
196141
classes_path = '/content/drive/MyDrive/yolov4_tiny/yolov4-tiny-keras-master/voc_classes.txt'
197142
anchors_path = '/content/drive/MyDrive/yolov4_tiny/yolov4-tiny-keras-master/yolo_anchors.txt'
198-
#------------------------------------------------------#
199-
# 权值文件请看README,百度网盘下载
200-
# 训练自己的数据集时提示维度不匹配正常
201-
# 预测的东西都不一样了自然维度不匹配
202-
#------------------------------------------------------#
143+
203144
weights_path = '/content/drive/MyDrive/yolov4_tiny/yolov4-tiny-keras-master/yolov4_tiny_weights_voc.h5'
204-
#------------------------------------------------------#
205-
# 训练用图片大小
206-
# 一般在416x416和608x608选择
207-
#------------------------------------------------------#
145+
208146
input_shape = (416,416)
209-
#------------------------------------------------------#
210-
# 是否对损失进行归一化,用于改变loss的大小
211-
# 用于决定计算最终loss是除上batch_size还是除上正样本数量
212-
#------------------------------------------------------#
147+
213148
normalize = False
214149

215-
#----------------------------------------------------#
216-
# 获取classes和anchor
217-
#----------------------------------------------------#
218150
class_names = get_classes(classes_path)
219151
anchors = get_anchors(anchors_path)
220-
#------------------------------------------------------#
221-
# 一共有多少类和多少先验框
222-
#------------------------------------------------------#
223152
num_classes = len(class_names)
224153
num_anchors = len(anchors)
225-
#------------------------------------------------------#
226-
# Yolov4的tricks应用
227-
# mosaic 马赛克数据增强 True or False
228-
# 实际测试时mosaic数据增强并不稳定,所以默认为False
229-
# Cosine_scheduler 余弦退火学习率 True or False
230-
# label_smoothing 标签平滑 0.01以下一般 如0.01、0.005
231-
#------------------------------------------------------#
232154
mosaic = False
233155
Cosine_scheduler = False
234156
label_smoothing = 0
235157

236158
K.clear_session()
237-
#------------------------------------------------------#
238-
# 创建yolo模型
239-
#------------------------------------------------------#
159+
240160
image_input = Input(shape=(None, None, 3))
241161
h, w = input_shape
242162
print('Create YOLOv4-Tiny model with {} anchors and {} classes.'.format(num_anchors, num_classes))
243163
model_body = yolo_body(image_input, num_anchors//2, num_classes)
244164

245-
#------------------------------------------------------#
246-
# 载入预训练权重
247-
#------------------------------------------------------#
165+
248166
print('Load weights {}.'.format(weights_path))
249167
model_body.load_weights(weights_path, by_name=True, skip_mismatch=True)
250168

251-
#------------------------------------------------------#
252-
# 在这个地方设置损失,将网络的输出结果传入loss函数
253-
# 把整个模型的输出作为loss
254-
#------------------------------------------------------#
255169
y_true = [Input(shape=(h//{0:32, 1:16}[l], w//{0:32, 1:16}[l], num_anchors//2, num_classes+5)) for l in range(2)]
256170
loss_input = [*model_body.output, *y_true]
257171
model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss',
258172
arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5, 'label_smoothing': label_smoothing, 'normalize':normalize})(loss_input)
259173

260174
model = Model([model_body.input, *y_true], model_loss)
261175

262-
#-------------------------------------------------------------------------------#
263-
# 训练参数的设置
264-
# logging表示tensorboard的保存地址
265-
# checkpoint用于设置权值保存的细节,period用于修改多少epoch保存一次
266-
# reduce_lr用于设置学习率下降的方式
267-
# early_stopping用于设定早停,val_loss多次不下降自动结束训练,表示模型基本收敛
268-
#-------------------------------------------------------------------------------#
176+
269177
logging = TensorBoard(log_dir=log_dir)
270178
checkpoint = ModelCheckpoint(log_dir + 'ep{epoch:03d}-loss{loss:.3f}-val_loss{val_loss:.3f}.h5',
271179
monitor='val_loss', save_weights_only=True, save_best_only=False, period=1)
272180
early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1)
273181

274-
#----------------------------------------------------------------------#
275-
# 验证集的划分在train.py代码里面进行
276-
# 2007_test.txt和2007_val.txt里面没有内容是正常的。训练不会使用到。
277-
# 当前划分方式下,验证集和训练集的比例为1:9
278-
#----------------------------------------------------------------------#
182+
279183
val_split = 0.1
280184
with open(annotation_path) as f:
281185
lines = f.readlines()
@@ -285,33 +189,26 @@ def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
285189
num_val = int(len(lines)*val_split)
286190
num_train = len(lines) - num_val
287191

288-
#------------------------------------------------------#
289-
# 主干特征提取网络特征通用,冻结训练可以加快训练速度
290-
# 也可以在训练初期防止权值被破坏。
291-
# Init_Epoch为起始世代
292-
# Freeze_Epoch为冻结训练的世代
293-
# Epoch总训练世代
294-
# 提示OOM或者显存不足请调小Batch_size
295-
#------------------------------------------------------#
192+
296193
freeze_layers = 60
297194
for i in range(freeze_layers): model_body.layers[i].trainable = False
298195
print('Freeze the first {} layers of total {} layers.'.format(freeze_layers, len(model_body.layers)))
299196

300-
# 调整非主干模型first
197+
301198
if True:
302199
Init_epoch = 0
303200
Freeze_epoch = 50
304201
batch_size = 32
305202
learning_rate_base = 1e-3
306203

307204
if Cosine_scheduler:
308-
# 预热期
205+
309206
warmup_epoch = int((Freeze_epoch-Init_epoch)*0.2)
310-
# 总共的步长
207+
311208
total_steps = int((Freeze_epoch-Init_epoch) * num_train / batch_size)
312-
# 预热步长
209+
313210
warmup_steps = int(warmup_epoch * num_train / batch_size)
314-
# 学习率
211+
315212
reduce_lr = WarmUpCosineDecayScheduler(learning_rate_base=learning_rate_base,
316213
total_steps=total_steps,
317214
warmup_learning_rate=1e-4,
@@ -336,21 +233,21 @@ def preprocess_true_boxes(true_boxes, input_shape, anchors, num_classes):
336233

337234
for i in range(freeze_layers): model_body.layers[i].trainable = True
338235

339-
# 解冻后训练
236+
340237
if True:
341238
Freeze_epoch = 50
342239
Epoch = 100
343240
batch_size = 16
344241
learning_rate_base = 1e-4
345242

346243
if Cosine_scheduler:
347-
# 预热期
244+
348245
warmup_epoch = int((Epoch-Freeze_epoch)*0.2)
349-
# 总共的步长
246+
350247
total_steps = int((Epoch-Freeze_epoch) * num_train / batch_size)
351-
# 预热步长
248+
352249
warmup_steps = int(warmup_epoch * num_train / batch_size)
353-
# 学习率
250+
354251
reduce_lr = WarmUpCosineDecayScheduler(learning_rate_base=learning_rate_base,
355252
total_steps=total_steps,
356253
warmup_learning_rate=1e-5,

0 commit comments

Comments
 (0)