赞
踩
The test environment is
- Python 2.7
- Keras 2.2.4
- tensorflow 1.10.0
annotation_path = 'train.txt'
需要注意:文件描述格式 val_split = 0.1
with open(annotation_path) as f:
lines = f.readlines()
np.random.seed(10101)
np.random.shuffle(lines)
np.random.seed(None)
num_val = int(len(lines)*val_split)
num_train = len(lines) - num_val
59等,训练:model.fit_generator(data_generator_wrapper(...))
184 data_generator_wrapper -> 165
175image, box = get_random_data(annotation_lines[i], input_shape, random=True)
这里的 annotation_lines为上面的lines[:,part]
现在研究yolo3.utils 的get_random_data,然后再回过头去看train.py的相关代码.
def get_random_data(annotation_line, input_shape, random=True, max_boxes=20, jitter=.3, hue=.1, sat=1.5, val=1.5, proc_img=True): '''random preprocessing for real-time data augmentation''' # 以空格为分隔符,包含 \n line = annotation_line.split() #取第一个图片 image = Image.open(line[0]) iw, ih = image.size h, w = input_shape #进行字符分割,以','分割,从第二位取分割后的str转为int. 然后转为矩阵 # path/to/img2.jpg 120,300,250,600,2 Box format: `x_min,y_min,x_max,y_max,class_id` (no space). box = np.array([np.array(list(map(int,box.split(',')))) for box in line[1:]]) if not random: # resize image scale = min(w/iw, h/ih) nw = int(iw*scale) nh = int(ih*scale) dx = (w-nw)//2 dy = (h-nh)//2 image_data=0 if proc_img: image = image.resize((nw,nh), Image.BICUBIC) new_image = Image.new('RGB', (w,h), (128,128,128)) new_image.paste(image, (dx, dy)) image_data = np.array(new_image)/255. # correct boxes box_data = np.zeros((max_boxes,5)) if len(box)>0: np.random.shuffle(box) if len(box)>max_boxes: box = box[:max_boxes] box[:, [0,2]] = box[:, [0,2]]*scale + dx box[:, [1,3]] = box[:, [1,3]]*scale + dy box_data[:len(box)] = box return image_data, box_data #所以输入图像组大小可以不是416*416;下面有图像缩放\标签纠正的过程 # resize image new_ar = w/h * rand(1-jitter,1+jitter)/rand(1-jitter,1+jitter) scale = rand(.25, 2) 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) # place image dx = int(rand(0, w-nw)) dy = int(rand(0, h-nh)) new_image = Image.new('RGB', (w,h), (128,128,128)) new_image.paste(image, (dx, dy)) image = new_image # flip image or not flip = rand()<.5 if flip: image = image.transpose(Image.FLIP_LEFT_RIGHT) # distort image 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_data = hsv_to_rgb(x) # numpy array, 0 to 1 # correct boxes box_data = np.zeros((max_boxes,5)) 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 if flip: box[:, [0,2]] = w - box[:, [2,0]] 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)] # discard invalid box if len(box)>max_boxes: box = box[:max_boxes] box_data[:len(box)] = box return image_data, box_data
# -*- coding:utf-8 import cv2 import os import sys import numpy as np import random traintxt = 'train.txt' imgs_path = '/home/jiteng/private_app/dataset/ccpd/ccpd_dataset/ccpd_base/' #199,998 项 img_names = os.listdir(imgs_path) start_get = random.randint(0,180) start_get*1000 img_names = img_names[start_get:start_get+10000] print len(img_names) f = open(traintxt,'a+') for imgname in img_names: #imgname -- 025-95_113-154&383_386&473-386&473_177&454_154&383_363&402-0_0_22_27_27_33_16-37-15.jpg name = imgs_path + imgname image = cv2.imread(name) label = imgname.split('-')[2] #154&383_386&473 label = label.replace('&',',') label = label.replace('_',',') save_str = name + ' ' + label + ',0\n' print save_str f.write(save_str) f.close()
Make sure you have run
python convert.py -w yolov3.cfg yolov3.weights model_data/yolo_weights.h5
The file model_data/yolo_weights.h5 is used to load pretrained weights.
Modify train.py and start training.
python train.py
Use your trained weights or checkpoint weights with command line option--model model_file
when using yolo_video.py
Remember to modify class path or anchor path, with--classes class_file
and--anchors anchor_file
.
If you want to use original pretrained weights for YOLOv3:
1.wget https://pjreddie.com/media/files/darknet53.conv.74
2. rename it as darknet53.weights
3.python convert.py -w darknet53.cfg darknet53.weights model_data/darknet53_weights.h5
4. use model_data/darknet53_weights.h5 in train.py
#!/usr/bin/env python # -- coding: utf-8 -- """ Copyright (c) 2018. All rights reserved. Created by C. L. Wang on 2018/7/4 """ import os import numpy as np import tensorflow as tf import keras.backend as K from keras.backend import mean from keras.layers import Input, Lambda from keras.models import Model from keras.optimizers import Adam from keras.callbacks import TensorBoard, ModelCheckpoint, ReduceLROnPlateau, EarlyStopping from keras.utils import plot_model from keras.utils.training_utils import multi_gpu_model from yolo3.model import preprocess_true_boxes, yolo_body, tiny_yolo_body, yolo_loss from yolo3.utils import get_random_data def _main(): import os os.environ["CUDA_VISIBLE_DEVICES"] = "0,1" from keras import backend as K config = tf.ConfigProto() config.gpu_options.allow_growth = True sess = tf.Session(config=config) K.set_session(sess) annotation_path = 'train.txt' # 数据 classes_path = 'model_data/voc_classes.txt' # 类别 log_dir = 'logs/000/' # 日志文件夹 # pretrained_path = 'model_data/yolo_weights.h5' # 预训练模型 pretrained_path = 'model_data/yolo_weights.h5' # 预训练模型 anchors_path = 'model_data/yolo_anchors.txt' # anchors class_names = get_classes(classes_path) # 类别列表 num_classes = len(class_names) # 类别数 anchors = get_anchors(anchors_path) # anchors列表 input_shape = (416, 416) # 32的倍数,输入图像 model = create_model(input_shape, anchors, num_classes, freeze_body=2, weights_path=pretrained_path) # make sure you know what you freeze logging = TensorBoard(log_dir=log_dir) 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=True, period=3) # 只存储weights, reduce_lr = ReduceLROnPlateau(monitor='val_loss', factor=0.1, patience=3, verbose=1) # 当评价指标不在提升时,减少学习率 early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=1) # 测试集准确率,下降前终止 val_split = 0.1 # 训练和验证的比例 with open(annotation_path) as f: lines = f.readlines() np.random.seed(47) np.random.shuffle(lines) np.random.seed(None) num_val = int(len(lines) * val_split) # 验证集数量 num_train = len(lines) - num_val # 训练集数量 # # model = multi_gpu_model(model, gpus=2) # 设置使用2个gpu,该句放在模型compile之前 """ 把目标当成一个输入,构成多输入模型,把loss写成一个层,作为最后的输出,搭建模型的时候, 就只需要将模型的output定义为loss,而compile的时候, 直接将loss设置为y_pred(因为模型的输出就是loss,所以y_pred就是loss), 无视y_true,训练的时候,y_true随便扔一个符合形状的数组进去就行了。 """ if False: model.compile(optimizer=Adam(lr=1e-3), loss={ # 使用定制的 yolo_loss Lambda层 'yolo_loss': lambda y_true, y_pred: y_pred}) # 损失函数 batch_size = 32 # batch尺寸 print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes), steps_per_epoch=max(1, num_train // batch_size), validation_data=data_generator_wrapper( lines[num_train:], batch_size, input_shape, anchors, num_classes), validation_steps=max(1, num_val // batch_size), epochs=50, initial_epoch=0, callbacks=[logging, checkpoint]) model.save_weights(log_dir + 'trained_weights_stage_1.h5') # 存储最终的参数,再训练过程中,通过回调存储 if True: # 全部训练 for i in range(len(model.layers)): model.layers[i].trainable = True model.compile(optimizer=Adam(lr=1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred}) # recompile to apply the change print('Unfreeze all of the layers.') batch_size = 8 # note that more GPU memory is required after unfreezing the body print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size)) model.fit_generator(data_generator_wrapper(lines[:num_train], batch_size, input_shape, anchors, num_classes), steps_per_epoch=max(1, num_train // batch_size), validation_data=data_generator_wrapper(lines[num_train:], batch_size, input_shape, anchors, num_classes), validation_steps=max(1, num_val // batch_size), epochs=100, initial_epoch=50, callbacks=[logging, checkpoint, reduce_lr, early_stopping]) model.save_weights(log_dir + 'trained_weights_final.h5') def get_classes(classes_path): '''loads the classes''' with open(classes_path) as f: class_names = f.readlines() class_names = [c.strip() for c in class_names] return class_names def get_anchors(anchors_path): '''loads the anchors from a file''' with open(anchors_path) as f: anchors = f.readline() anchors = [float(x) for x in anchors.split(',')] return np.array(anchors).reshape(-1, 2) def create_model(input_shape, anchors, num_classes, load_pretrained=True, freeze_body=2, weights_path='model_data/yolo_weights.h5'): K.clear_session() # 清除session h, w = input_shape # 尺寸 image_input = Input(shape=(w, h, 3)) # 图片输入格式 num_anchors = len(anchors) # anchor数量 # YOLO的三种尺度,每个尺度的anchor数,类别数+边框4个+置信度1 y_true = [Input(shape=(h // {0: 32, 1: 16, 2: 8}[l], w // {0: 32, 1: 16, 2: 8}[l], num_anchors // 3, num_classes + 5)) for l in range(3)] model_body = yolo_body(image_input, num_anchors // 3, num_classes) # model print('Create YOLOv3 model with {} anchors and {} classes.'.format(num_anchors, num_classes)) if load_pretrained: # 加载预训练模型 model_body.load_weights(weights_path, by_name=True, skip_mismatch=True) # 加载参数,跳过错误 print('Load weights {}.'.format(weights_path)) if freeze_body in [1, 2]: # Freeze darknet53 body or freeze all but 3 output layers. num = (185, len(model_body.layers) - 3)[freeze_body - 1] for i in range(num): model_body.layers[i].trainable = False # 将其他层的训练关闭 print('Freeze the first {} layers of total {} layers.'.format(num, len(model_body.layers))) model_loss = Lambda(yolo_loss, output_shape=(1,), name='yolo_loss', arguments={'anchors': anchors, 'num_classes': num_classes, 'ignore_thresh': 0.5} )(model_body.output + y_true) model = Model(inputs=[model_body.input] + y_true, outputs=model_loss) # 模型,inputs和outputs #plot_model(model, to_file=os.path.join('model_data', 'model.png'), show_shapes=True, show_layer_names=True) model.summary() return model def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes): '''data generator for fit_generator''' n = len(annotation_lines) i = 0 while True: image_data = [] box_data = [] for b in range(batch_size): if i == 0: np.random.shuffle(annotation_lines) image, box = get_random_data(annotation_lines[i], input_shape, random=True) # 获取图片和盒子 image_data.append(image) # 添加图片 box_data.append(box) # 添加盒子 i = (i + 1) % n image_data = np.array(image_data) box_data = np.array(box_data) y_true = preprocess_true_boxes(box_data, input_shape, anchors, num_classes) # 真值 yield [image_data] + y_true, np.zeros(batch_size) def data_generator_wrapper(annotation_lines, batch_size, input_shape, anchors, num_classes): """ 用于条件检查 """ n = len(annotation_lines) # 标注图片的行数 if n == 0 or batch_size <= 0: return None return data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes) if __name__ == '__main__': _main()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。