Windows+Keras+Tensorflow Keras-Yolo-v3大事记

跑通预训练模型

这是一篇记录自己花费三(半)天三(半)夜时间跑通keras-yolov3的开山之作,自此作者踏上了深度学习的不归路。江湖上关于yolov3的传说由来已久,各位大虾们也早已各显神通,一时间风云四起硝烟弥漫(讲这个的博客是真的多…我在部署的过程中看到了不下数百个),却让人有一种迷茫之感。废话不多说,开始操作🤦‍♂️

材料准备

把大象放进冰箱需要三步,其实跑通yolov3也只需要3步

  1. 下载模型
  2. 安装环境
  3. 直接运行

话虽这么说,但是实际操作起来还是会遇到很多问题,这里我们来一步步给出

下载模型

keras-yolov3已经由q神开源,项目下载地址keras-yolov3,这一步只需要会打开github即可操作

安装环境

**写在前面:**目前作者本人尝试的时候,只在应用tensorflow1.x的时候容易成功,tensorflow2.x在应用时需要对程序做不少修改,建议新手直接安装1.x版本的tensorflow方便进行后续操作,作者所用环境如下:

  • tensorflow-gpu1.6.0
  • keras 2.1.5

剩余的一些附加组件都是些无关紧要的内容,这里建议一下安装顺序和方法,默认大家都已经成功安装了conda并且为了运行这个模型建立了名为keras-gpu(这个命名无所谓,自己认得就行)的conda 环境,进入(conda activate keras-gpu)所创建的环境开始操作:

conda install tensorflow-gpu=1.6.0

漫长的等待…
到最后
done
done
done
结束
随后:

pip install keras==2.1.5

为了方便大家应用,我这里导出了一份自己kera-gpu环境中所安装的所有package的清单,conda 导出后是保存的.yaml格式文件conda env package list,大家如果嫌自己创建一个全新环净比较麻烦的话可以尝试用conda命令直接创建一个满足条件的环境(当然是和我这边一模一样的):

conda create -f keras-gpu.yaml

这样子只需要一步就可以部署好全部的keras和tensorflow环境啦~
到了这儿是不是可以直接去运行模型文件了呢?答案当然是否定的(气不气气不气,就是不让你清闲),因为这里我们安装的是GPU版本的TF,因此必须完成对GPU的相关配置,这里就要引出我们的下一个敌人:CUDA(单击链接发现新世界~),CUDA ToolKit是Nvidia给出的官方的开发工具,本文不需要操作人员对CUDA有什么太深的理解,只需要会傻瓜式下一步即可,但是在下载CUDA之前,我们需要了解一下其与TF的对应关系tensorflow与CUDA版本对应关系,可以看到不同版本的tensorflow对CUDA的要求是不一样的,如果你有幸装错了,恭喜你,可以回炉重造了。有了上面的对应关系,我们就可以选择合适版本的CUDA进行安装啦
在这里插入图片描述
在这里插入图片描述

如上图,选择CUDA9.0作为安装对象,安装过程中不要轻易改动安装目录(否则后面环境变量不好配置),双击之后等待文件解压
在这里插入图片描述
解压完成后疯狂下一步即可,安装时选择自定义,在CUDA项中取消勾选Visual Studio Integration
在这里插入图片描述
等待安装完毕即可。随后配置CUDA环境变量(这里默认大家都知道在哪里修改哈,Windows还是很简单的),配置完之后应该长这个样子:
在这里插入图片描述
在这里插入图片描述
图1中的环境变量是安装过程中自动添加,图2中是在Path后附加以上三个位置,具体内容还需参照CUDA安装位置,默认都在

C:\Program Files\NVIDIA GPU Computing Toolkit

随后就是安装cudnn了,说是安装其实不需要去执行安装步骤,下载对应版本的cudnn并解压即可。cudnn版本与CUDA相对应,如图:
在这里插入图片描述
所以我们需要找到7.0版本的cudnn进行解压,下载地址cudnn for CUDA,找到里面对应的项目
在这里插入图片描述
下载,解压,获得如下文件夹:
在这里插入图片描述
将三个文件夹复制,进入文件夹C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0进行粘贴即可,至此CUDA和cudnn的安装和环境配置基本结束

运行模型

到这一步,我们已经完成了所有准备工作,下面准备开始运行模型吧~
事情的发展总是不太令人满意,这不我们还有些准备工作要做,那就是获取预训练的权重文件,这里给出权重文件的下载地址yolov3.weights,下载之后直接放进我们工程的根目录即可
在这里插入图片描述
由于该.weights文件是由darknet训练而来,在keras中并不能直接使用,作者想的十分周到,给了我们convert.py文件食用,在工程目录下打开命令行,进入之前创建的keras-gpu环境,运行如下命令:

python convert.py -w yolov3.cfg yolov3.weights model_data/yolo.h5

等待奇迹发生吧~,如果上述步骤都成功完成的话应该可以直接在model_data文件夹下看到yolo_weights.h5已经生成,这就意味着我们可以运行模型咯!
预训练的模型中总共有80个类,我们想测试的话只需要找到包含其中某类的图片,测试命令如下:

python yolo_video.py --image

执行上述命令后会进行模型加载等工作,随后出现input image提示,输入要进行测试的图片的路径:path/to/imgTest.jpg,程序就会返回检测到的目标的位置和置信度:
在这里插入图片描述
这里我用boat做了测试,返回结果如上图,可以看到效果还是不错的。至此,预训练模型已经可以成功跑通,工作也算告一段落。

训练自己的模型

数据集制作

经过前面一番折腾,我们终于成功运行了预训练模型。但是,但是,但是,yolo老哥里面没有的类型我们怎么去检测呢?等人家帮我们训练,可能等的花都谢了。因此,我们有了训练自己模型的想法。说干就干,由于yolo本身是基于VOC数据集或者COCO数据集进行训练的,为了减少修改程序的成本,我们选择自己动手满足人家的需求吧(程序员的卑微.jpg)。在工程文件夹下新建如下一系列文件夹:
在这里插入图片描述
文件夹的命名一定要跟着人家走,如果任性想改程序的话也是可以自己特立独行的。将自己的数据存放在JPEGImages文件夹下,对应的标注文件存在Annotations文件夹下
在这里插入图片描述
这是数据集的图片文件
在这里插入图片描述
对应的标注文件
**注意:**图片与对应的xml应有相同名称,具体的数据标注方式详见labelImg tools.有了上述数据集之后,在VOC2007目录下新建allocation.py文件用于分配训练集和验证集,具体代码可以直接copy下方内容:

import os
import random

trainval_percent = 0.2
train_percent = 0.8
xmlfilepath = 'Annotations'
txtsavepath = 'ImageSets\Main'
total_xml = os.listdir(xmlfilepath)

num = len(total_xml)
list = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list, tv)
train = random.sample(trainval, tr)

ftrainval = open('ImageSets/Main/trainval.txt', 'w')
ftest = open('ImageSets/Main/test.txt', 'w')
ftrain = open('ImageSets/Main/train.txt', 'w')
fval = open('ImageSets/Main/val.txt', 'w')

for i in list:
    name = total_xml[i][:-4] + '\n'
    if i in trainval:
        ftrainval.write(name)
        if i in train:
            ftest.write(name)
        else:
            fval.write(name)
    else:
        ftrain.write(name)

ftrainval.close()
ftrain.close()
fval.close()
ftest.close()

运行上述allocation.py文件,会在Main文件夹下生成四个txt文件,大概长这样:
在这里插入图片描述
每个txt文件中存放的都是图片的名称,检查一下是否正常。到了这一步VOC数据集的格式已经有了,但是事情并没有结束,因为yolo它不用VOC的格式…(江湖骗子!)但是好心的作者又给我们提供了现成的工具,在工程目录下有一个voc_annotation.py文件,修改里面的classes为自己的内容,比如我要训练的是targetBoat,修改之后的文件大致如图:
在这里插入图片描述
按照上面的步骤来,该文件中其余部分不需要修改。运行voc_annotation.py会在工程目录下生成三个2007开头的txt文件,这些文件就是我们的网络需要用到的输入了(建议打开文件看一眼,yolo标准的训练用txt文件应该是每行一张图片信息,大致长下图的样子):
在这里插入图片描述

自定义训练类别

随后,还有一步重要的工作,打开model_data文件夹,修改里面的voc_classes.txt和coco_classes.txt,去除原来的类别,将自己要训练的类别添加进去,每行一个,由于我只有一类要训练,修改之后文件中就只有一行。
在这里插入图片描述
在这里插入图片描述

开始训练

到此,我们已经可以去训练啦。但是问题还是有滴。作者最终在保存模型时发现程序指出不存在目录‘logs/000/’,因此在运行train.py之前还需要新建一个目录,原作者在程序里面写得是’logs/000/’,这里我们也直接建立一个同名文件夹
在这里插入图片描述
最终文件夹内会保存训练完之后的权重文件。
有了前面一堆准备,终于可以进入我们的train环节了,这里我们还需要修改原始的train.py文件,具体的文件内容可以复制一下代码:

"""
Retrain the YOLO model for your own dataset.
"""
import numpy as np
import keras.backend as K
from keras.layers import Input, Lambda
from keras.models import Model
from keras.callbacks import TensorBoard, ModelCheckpoint, EarlyStopping

from yolo3.model import preprocess_true_boxes, yolo_body, tiny_yolo_body, yolo_loss
from yolo3.utils import get_random_data


def _main():
    annotation_path = '2007_train.txt'
    log_dir = 'logs/000/'
    classes_path = 'model_data/voc_classes.txt'
    anchors_path = 'model_data/yolo_anchors.txt'
    class_names = get_classes(classes_path)
    anchors = get_anchors(anchors_path)
    input_shape = (416,416) # multiple of 32, hw
    model = create_model(input_shape, anchors, len(class_names) )
    train(model, annotation_path, input_shape, anchors, len(class_names), log_dir=log_dir)

def train(model, annotation_path, input_shape, anchors, num_classes, log_dir='logs/'):
    model.compile(optimizer='adam', loss={
        'yolo_loss': lambda y_true, y_pred: y_pred})
    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=1)
    batch_size = 4
    val_split = 0.1
    with open(annotation_path) as f:
        lines = f.readlines()
    np.random.shuffle(lines)
    num_val = int(len(lines)*val_split)
    num_train = len(lines) - num_val
    print('Train on {} samples, val on {} samples, with batch size {}.'.format(num_train, num_val, batch_size))

    model.fit_generator(data_generator_wrap(lines[:num_train], batch_size, input_shape, anchors, num_classes),
            steps_per_epoch=max(1, num_train//batch_size),
            validation_data=data_generator_wrap(lines[num_train:], batch_size, input_shape, anchors, num_classes),
            validation_steps=max(1, num_val//batch_size),
            epochs=50,
            initial_epoch=0)
    model.save_weights(log_dir + 'trained_weights.h5')

def get_classes(classes_path):
    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):
    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=False, freeze_body=False,
            weights_path='model_data/yolo_weights.h5'):
    K.clear_session() # get a new session
    image_input = Input(shape=(None, None, 3))
    h, w = input_shape
    num_anchors = len(anchors)
    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)
    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:
            # Do not freeze 3 output layers.
            num = len(model_body.layers)-7
            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([model_body.input, *y_true], model_loss)
    return model
def data_generator(annotation_lines, batch_size, input_shape, anchors, num_classes):
    n = len(annotation_lines)
    np.random.shuffle(annotation_lines)
    i = 0
    while True:
        image_data = []
        box_data = []
        for b in range(batch_size):
            i %= n
            image, box = get_random_data(annotation_lines[i], input_shape, random=True)
            image_data.append(image)
            box_data.append(box)
            i += 1
        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_wrap(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()

将上述内容直接覆盖原始train.py文件即可,运行train.py,开始静候佳音我的数据集大小是600张图片,运行环境是Nvidia RTX2060 6G显存,设定batch=8的时候显存会溢出,后来修改为4正常运行。Epoch程序中设定是50,根据自己需求修改,跑到最后loss基本降到了10左右,检测效果还不错。
训练完之后需要加载自己的模型进行测试,打开yolo.py文件,修改defaults中的对应项即可:
在这里插入图片描述
再次执行:

python yolo_video.py --image

输入待检测图片的位置,输出结果如下:

在这里插入图片描述

结语

整个过程其实并不是很难,但是,自己在跑的过程中还是遇到了不少问题,即便是我已经说的很清楚了,相信大家在参考的时候还是会遇到很多问题,欢迎在评论区交流讨论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值