地表最強一階段目標檢測框架:yolov4之tf2+版本

        從第一版的yolov3(http://github.com/qqwweee/keras-yolo3)在這位q神翻譯出來後,在下一直跟進yolo的發展,兩年前第一次遷移了q神的keras版。最近keras版的yolov4(http://github.com/Ma-Dan/keras-yolo4)也問世了。由於tf發展到了tf2+,很多模型建立過程、命名規則、文件讀取方法以及keras的支持等,都做了非常大的調整,再加上該版本的代碼是延續yolov3的代碼,沒有使用論文的很多tricks,加上歷史遺留代碼存在很多的不可讀因素和局部地方的小bug。因此,基於以上兩點考慮,在下聯合一位cv從業同學完成了基於tf2版本的、用keras編寫的yolov4.

        請收下傳送門:https://github.com/robbebluecp/tf2-yolov4

        對於這版的yolov4,我們做了如下優化:

     (1)數據增強。我們在之前的resize、色彩調整、旋轉的基礎上,增加了mixup、mosaic、任意角度旋轉(不建議用任意角            

               度)、背景填充、pixel等數據增強策略;

     (2)模型整合。對yolo整體網絡結構和局部結構做詳細拆分和更詳細的整合。如darknet、spp、pan等;

     (3)loss優化。ciou優化、loss代碼優化;

     (4)convert調整。tf1+和tf2+對darknet權重文件的讀取,從二進制流和命名方法上都有很大不的不同,tf2+轉換非常快,且

               跟tf1不能兼容。tf1和tf2對darknet訓練出來的權重參數轉化的h5文件,是完全不一樣的。所以在使用該版本的convert.py之前。務

               必確保你的tensorflow>=2.0版本!!!

     (5)config配置文件取代動態傳參;

     (6)儘可能使np和tf分離,讓訓練和預測在一定程度上提速;

     (7)生成器兼容、數據增強模塊可擴展等其他優化。

        我用voc2007數據集在V100顯卡上訓練到160+個epochs時,loss和val_loss差不多收斂到13+,預測準確率大約在40%-85%波動。隨着預訓練模型的加入或者更多epoch的訓練,這個值會越來越小。

隨着epoch到200+時,loss又進一步收斂到接近10(忘記留圖了)。

以下展示部分數據增強的效果圖:

 

a、rotate + mixup + resize

b、rotate + pixel + 顏色微調

c、ratate + mosaic + resize + 顏色填充

 

c + resize + moscai  +rotate

我的小夥伴說,這些增強有些過分了,尤其是mixup,甚至連人都不太分辨的處理,這些對模型訓練也是有很大影響的,所以我們用了隨機數控制增強頻率。在Augment類中,你可以自己控制頻率,可以自定義各種增強策略。數據增強還有非常的多的方法,比如隨機裁剪、圖像扭曲等等。增強一時爽,一直增強一直爽。增強雖好,不不要貪強奧~~

這個框架還有很多可以完善的地方,例如loss、augment、draw等等,希望各位來添磚補瓦,集思廣益,哈哈哈~~歡迎各位cv、nlp愛好者戳戳戳!

 

voc2007.zip數據集爲例:

如果你要訓練,使用如下代碼:


import loss
import config
import models
from tensorflow import keras
from generator import data_generator


class_mapping = dict(enumerate(config.classes_names))
class_mapping = {class_mapping[key]: key for key in class_mapping}


model_yolo = models.YOLO(pre_train=None)()

f = open(config.label_path)
label_lines = f.readlines()

train_lines = label_lines[:-int(len(label_lines) * config.validation_split)]
valid_lines = label_lines[-int(len(label_lines) * config.validation_split):]

h, w = config.image_input_shape
y_true = [keras.layers.Input(shape=(h // config.scale_size[l], w // config.scale_size[l], config.num_anchors, config.num_classes + 5)) for l
          in range(3)]

model_loss = keras.layers.Lambda(function=loss.yolo4_loss, output_shape=(1,), name='yolo_loss')([*model_yolo.output, *y_true])

tensorboard = keras.callbacks.TensorBoard()
checkpoint = keras.callbacks.ModelCheckpoint(filepath='model_train/ep{epoch:03d}-loss{loss:.3f}-valloss{val_loss:.3f}.h5',
                                             monitor='val_loss',
                                             save_weights_only=True,
                                             save_best_only=True,
                                             period=1)
reduce_lr = keras.callbacks.ReduceLROnPlateau(monitor='val_loss', factor=0.5, patience=10, verbose=1)
early_stopping = keras.callbacks.EarlyStopping(monitor='val_loss', min_delta=0, patience=20, verbose=1)

model = keras.models.Model([model_yolo.input, *y_true], model_loss)
model.compile(optimizer=keras.optimizers.Adam(1e-4), loss={'yolo_loss': lambda y_true, y_pred: y_pred})


g_train = data_generator(label_lines=train_lines,
                         batch_size=config.batch_size,
                         input_shape=config.image_input_shape,
                         anchors=config.anchors,
                         num_classes=config.num_classes)

g_valid = data_generator(label_lines=valid_lines,
                         batch_size=config.batch_size,
                         input_shape=config.image_input_shape,
                         anchors=config.anchors,
                         num_classes=config.num_classes)
print('fire!')
model.fit(g_train,
          validation_data=g_valid,
          steps_per_epoch=len(label_lines) // config.batch_size,
          validation_steps=int(len(label_lines) * config.validation_split * 0.2),
          epochs=config.epochs,
          callbacks=[tensorboard, checkpoint, reduce_lr]
          )

model_yolo.save_weights('model_train/model_train_final.weights')

預測,則使用:

from tools import utils_image
import config
import cv2 as cv
import numpy as np
import eval
import models
import argparse
import tensorflow as tf

devices = tf.config.experimental.list_physical_devices('GPU')
if devices:
    tf.config.experimental.set_memory_growth(devices[0], True)


parser = argparse.ArgumentParser()

parser.add_argument('-m', '--model', type=str, help='input h5 model path', default='model_train/yolov4.h5')
parser.add_argument('-i', '--image', type=str, help='input image file path', default='data/000030.jpg')


args = parser.parse_args()
model_file_path = args.model
image_file_path = args.image
class_file_path = config.classes_path


anchors = config.anchors
class_names = config.classes_names

num_anchors = len(anchors)
num_classes = len(class_names)
class_mapping = dict(enumerate(class_names))
colors = utils_image.get_random_colors(len(class_names))
class_mapping = {class_mapping[key]: key for key in class_mapping}
model = models.YOLO()()
model.load_weights('model_train/model_train_final.weights')



image = cv.imread(image_file_path)
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)


new_image = utils_image.resize_image(image, config.image_input_shape)

new_image = np.array(new_image, dtype='float32')
new_image /= 255.
new_image = np.expand_dims(new_image, 0)
feats = model.predict(new_image)


boxes, scores, classes = eval.yolo_eval(feats, anchors, len(class_names), (image.shape[0], image.shape[1]))
out_boxes, out_scores, out_classes = boxes[:5], scores[:5], classes[:5]


image = utils_image.draw_rectangle(image, boxes, scores, classes, class_names, colors, mode='pillow')
image = cv.cvtColor(image, cv.COLOR_BGR2RGB)
cv.namedWindow("img", cv.WINDOW_NORMAL)
cv.imshow('img', image)
cv.waitKey()

對於想用darknet版本來做訓練的的各位,可以移步我的另一個repo:https://github.com/robbebluecp/darknet,這個repo引自:https://github.com/AlexeyAB/darknet,因爲有些地方要做修改,並且向對其中的一些代碼做調整,所以獨自fork了一個版本。darknet做訓練有一個最大好處就是:沒有python那麼喫gpu!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章