zoukankan      html  css  js  c++  java
  • 在Colab上训练YOLO

    由于暂时没有可用的GPU来进行训练,想到了高效利用资本主义过剩的资源 Google Colab

    过程如下

    1.在笔记电脑上,准备相关数据和代码

    2.将数据和代码上传到Google Colaboratory,利用K80进行训练(现在有TPU,暂时还不知道怎么玩 XD )

    3.训练结果下载到笔记电脑,利用笔记本电脑 CPU进行图像推理(目标检测)

    教程参考自日本友人

    https://wakuphas.hatenablog.com/entry/2018/09/19/025941

    环境配置

    笔记电脑

    • Ubuntu18.04
    • Anaconda 3-5.0
    • Python 3.6.2
    • Tensorflow 1.5.0

    云服务器

    • Google Colaboratory

    一.数据准备(在Ubuntu笔记本上)

    1)下载darknet

    这里有两个版本可以选(第一个为YOLO原作者,第二个为A神版)任选都可 (操作都一样)

    我这里使用的第二个

    git clone https://github.com/pjreddie/darknet.git
    git clone https://github.com/AlexeyAB/darknet.git

    GPU=0   #确保为0 因为笔记本没有GPU
    CUDNN=0
    CUDNN_HALF=0
    OPENCV=0
    AVX=1   #CPU相关
    OPENMP=1  #CPU相关
    LIBSO=0

    确保Makefile 与上述一样(其实确保GPU=0就行,其他不用改)

    2)编译make

    $ make

    3)  下载已经训练过的官方权重文件(我们这里需要用到YOLOV2的权重,但建议YOLOV3也下)

    wget https://pjreddie.com/media/files/yolov2.weights
    wget https://pjreddie.com/media/files/yolov3.weights

    4) 下载后,将yolov2.weights移动到bin文件夹(其实也可以放在darknet目录下)

    $ mkdir bin
    $ mv yolov.weights bin/

    检查 图像目标测试./darknet的命令是否有效

    $ ./darknet detect cfg/yolov2.cfg bin/yolov2.weights data/dog.jpg

    目标检测以prediction.jpg的形式存放在darknet目录下

    注意 如果 yolov2.weights 没有移动到bin文件夹 而是在darknet目录下,使用一下命令进行目标检测

    $ ./darknet detect cfg/yolov2.cfg yolov2.weights data/dog.jpg

    运行结果,花费4596毫秒

    2.准备图像训练集

    这里使用13张飞机航拍视角的照片作为初始训练集 (你也可以自行准备,不管是爬虫还是既有的数据集)

    images_airport.zip - Google云端硬盘

    下载ZIP

    3.训练集膨胀

    通过填充,翻转,旋转图像获得更多的图像数据

    通过一个Python脚本实现(脚本保存为increase_img.py 顺利运行需要另外Python依赖库,根据提示,自行pip install xxx安装)

    import glob
    import traceback
    import numpy as np
    from keras.preprocessing.image import ImageDataGenerator, load_img, img_to_array
    import os
    
    base_path = './'
    def generate_images(class_name, generator):
        dir = base_path + class_name + '/'
        if not os.path.exists(dir + "output"):
            os.mkdir(dir + "output")
        savedir = base_path + class_name + '/output/'
        images = glob.glob(dir + '/*.jpg')
        print("input files = ", len(images))
     
        for i, image in enumerate(images):
            image = load_img(image)
            x = img_to_array(image)
            x = np.expand_dims(x, axis=0)
            g = generator.flow(x, save_to_dir=savedir, save_prefix=class_name, save_format='jpg')
           
            for j in range(10):
                g.next()
        print("output files = ", len(glob.glob(savedir + '/*.jpg')))
    
    if __name__ == '__main__':
        try:
            train_datagen = ImageDataGenerator(
                rotation_range=0.,
                width_shift_range=0.,
                height_shift_range=0.,
                shear_range=0.2,
                zoom_range=0.2,
                horizontal_flip=True,
                vertical_flip=True,
                rescale=1.0 / 255
                )
     
            generate_images('airplane', train_datagen)
            #generate_images('gorilla', train_datagen)
            #generate_images('chimpanzee', train_datagen)
     
        except Exception as e:
            traceback.print_exc()

    将图像训练集(13张图片)移动到 darknet / data / increase / airplane /

    increase_img.py 放置到 darknet / data / increase /

    终端运行

    python increase_img.py

    # 下面为终端输出

    # Using TensorFlow backend.
    # ('input files = ', 13)
    # ('output files = ', 130)

    等待一会 发现airplane会多一个output目录

    里面就是膨胀的图片 130张

    4 对数据集图像进行标注

    输入:原始图像

    输出:目标对象(飞机)和位置标签(BOX的xy坐标)

    这里使用 BBox-Label-Tool 完成此项工作

    $ cd data
    $ git clone https://github.com/puzzledqs/BBox-Label-Tool.git

    注意修改脚本

    BBox-Label-Tool默认处理”JPEG“,而我们这里是需要对jpg进行处理,所以需要将main.py中所有的JPEG全部替换成 jpg

    安装Tkinter依赖库

    sudo apt-get install python-tk

    使用BBox-Label-Tool运行main.py,一定要用Python2 运行

    $ python2 main.py 

     这里的001为image目录下的存放图片的文件夹,点击load

    苦力标注做完后 需要将txt标签转换成YOLO所需要的格式 如下

    标签号  x_center  y_center  x_width  y_width

    下面代码存为convert.py

    # -*- coding: utf-8 -*-
    
    """
    Created on Wed Dec  9 14:55:43 2015
    This script is to convert the txt annotation files to appropriate format needed by YOLO 
    @author: Guanghan Ning
    Email: gnxr9@mail.missouri.edu
    """
    
    import os
    from os import walk, getcwd
    from PIL import Image
    
    classes = ["stopsign"]
    
    def convert(size, box):
        dw = 1./size[0]
        dh = 1./size[1]
        x = (box[0] + box[1])/2.0
        y = (box[2] + box[3])/2.0
        w = box[1] - box[0]
        h = box[3] - box[2]
        x = x*dw
        w = w*dw
        y = y*dh
        h = h*dh
        return (x,y,w,h)
        
        
    """-------------------------------------------------------------------""" 
    
    """ Configure Paths"""   
    mypath = "labels/stopsign_original/"
    outpath = "labels/stopsign/"
    
    cls = "stopsign"
    if cls not in classes:
        exit(0)
    cls_id = classes.index(cls)
    
    wd = getcwd()
    list_file = open('%s/%s_list.txt'%(wd, cls), 'w')
    
    """ Get input text file list """
    txt_name_list = []
    for (dirpath, dirnames, filenames) in walk(mypath):
        txt_name_list.extend(filenames)
        break
    print(txt_name_list)
    
    """ Process """
    for txt_name in txt_name_list:
        # txt_file =  open("Labels/stop_sign/001.txt", "r")
        
        """ Open input text files """
        txt_path = mypath + txt_name
        print("
    Input:" + txt_path)
        txt_file = open(txt_path, "r")
        lines = txt_file.read().split('
    ')   #for ubuntu, use "
    " instead of "
    
    "
        
        """ Open output text files """
        txt_outpath = outpath + txt_name
        print("Output:" + txt_outpath)
        txt_outfile = open(txt_outpath, "w")
        
        # 2桁以上のtxtは無視 modified 2018.9.16
        num_lines = sum(1 for line in open(mypath + txt_name))
        print("##### num lines", num_lines, "#####")
        if num_lines >= 10:
            continue
        
        
        """ Convert the data to YOLO format """
        ct = 0
        for line in lines:
            #print('length of line is: ')
            #print(lines, "aa")
            #print(len(line), "bb")
            #print('
    ')
            if(len(line) >= 2):
                #if((len(line) >= 2) and (li < 10)):
                ct = ct + 1
                #print(line + "cc")
                elems = line.split(' ')
                #print(elems)
                xmin = elems[0]
                xmax = elems[2]
                ymin = elems[1]
                ymax = elems[3]
                #
                img_path = str('%s/images/%s/%s.jpg'%(wd, cls, os.path.splitext(txt_name)[0]))
                #t = magic.from_file(img_path)
                #wh= re.search('(d+) x (d+)', t).groups()
                im=Image.open(img_path)
                w= int(im.size[0])
                h= int(im.size[1])
                #w = int(xmax) - int(xmin)
                #h = int(ymax) - int(ymin)
                # print(xmin)
                #print(w, h)
                b = (float(xmin), float(xmax), float(ymin), float(ymax))
                bb = convert((w,h), b)
                print(bb)
                txt_outfile.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '
    ')
    
        """ Save those images with bb into list"""
        if(ct != 0):
            list_file.write('%s/images/%s/%s.jpg
    '%(wd, cls, os.path.splitext(txt_name)[0]))
                    
    list_file.close()       

    将convert.py保存在 darknet / data / convert / 下

    将convert.py ,图像训练集与标签 按下面目录关系进行放置

    ************
    
    darknet/data/convert/
    
      |- images
    
        |- stopsign
    
          |- airport00.jpg
    
          |- airport01.jpg
    
          …
    
      |- labels
    
        |- stopsign
    
        |- stopsign_original
    
          |- airport00.txt
    
          |- airport01.txt
    
          …
    
      |- convert.py
    
    ************

    然后运行 convert.py

    $ python convert.py
    # 成功运行完毕的如下图

    可以进入stopsign目录进行查看txt,格式下同即为成功,因为只是用了一个标签号,此时0对应飞机

    0 0.347736625514 0.552884615385 0.0617283950617 0.0865384615385
    0 0.438271604938 0.747596153846 0.0864197530864 0.0817307692308
    0 0.69341563786 0.545673076923 0.0864197530864 0.110576923077
    0 0.83950617284 0.447115384615 0.082304526749 0.0865384615385

     接着将data/convert/labels/stopsign/airplane****.txt等 复制到 darknet/data/labels/

    将data/convert/images/stopsign/airplane_****.jpg等 复制到 darknet/data/images/ (没有就新建)

    注意:原本darknet/data/labels/文件夹内有很多像32_0.png的字母类图像,建议保持原样不要移动,否则会出现错误。

    本次文件放置目录关系如下

    ************
    
    darknet/data/
    
      |- images
    
        |- airport00.jpg
    
        |- airport01.jpg
    
        …
    
      |- labels
    
        |- airport00.txt
    
        |- airport01.txt
    
        …
    
        |- 32_0.png
    
        ...
    
    ************

    5 划分训练数据和测试数据

     在darknet/data中保存名字为process.py的脚本(复制下面)

    # modified 2018.9.16
    
    import glob, os
    
    # Current directory
    current_dir = os.path.dirname(os.path.abspath(__file__))
    
    # Directory where the data will reside, relative to 'darknet.exe'
    path_data = 'images/'
    
    # Percentage of images to be used for the test set
    percentage_test = 20;
    
    # Create and/or truncate train.txt and test.txt
    file_train = open(path_data + '/train.txt', 'w')
    file_test = open(path_data + '/test.txt', 'w')
    
    # Populate train.txt and test.txt
    counter = 1
    index_test = round(100 / percentage_test)
    for pathAndFilename in glob.iglob(os.path.join(path_data, "*.jpg")):
        title, ext = os.path.splitext(os.path.basename(pathAndFilename))
        if counter == index_test:
            counter = 1
            file_test.write("data/" + path_data + title + '.jpg' + "
    ")
        else:
            file_train.write("data/" + path_data + title + '.jpg' + "
    ")
            counter = counter + 1
    
    print("ok")

    然后运行脚本对图像数据集进行训练/验证集划分,生成test.txt , train.txt在data/images

    $ python process.py
    # 成功会打印 'ok'

    6 设置学习参数

    准备以下两点

    • 在darknet/data/images/上创建类列表obj.names 内容只写一行 airplane
    • 在darknet/data/中创建名为names.list的文件,与保存飞机的obj.names相同,只有一行airplane

    参考下面网站写法

    https://github.com/ecthros/labelReader/blob/master/data/obj.names

    参数设定

    指定目标种类数和数据的位置(用vim写入并创建可读文件 obj.data 放在darknet/cfg/下)

    $ sudo vi obj.data

    保存内容

    classes=1 
    train = data/images/train.txt 
    valid = data/images/test.txt 
    labels = data/images/obj.names 
    backup = backup/

    classes#这次目标种类只有一个 就是airplane

    train#进行训练权重

    valid#进行测试

    labels # class list

    backup#存储权重的位置

    接着编辑模型。根据要使用的模型为基础进行编辑

    这次是yolov2-voc.cfg.

    因为稍后使用的darkflow当前不支持yolov3 , 因此采用yolov2。

    $ cd darknet/cfg/
    $ cp yolov2-voc.cfg yolo-obj.cfg
    # 把yolov2-voc.cfg复制并命名为yolo-obj.cfg

    手动或者vim编辑 yolo-obj.cfg

    第3行:batch=64。每个学习阶段使用的图像的张数。也可以注释掉并打开第6行。
    第4行:subdivisions=8。批次除以8,作细分。也可以注释掉并打开第7行。
    第244行:改classes=1。本次训练只有airplane一个类型。
    第237行:filters=30。filters=(classes+coords+1)*5 必须符合这个设定 coords=4

    顺利完成以上步骤的话,现在可以随时训练。

    在第一次训练时,因为在读取适当的权重作为初始值读入,则较容易收敛

    将以下内容保存/下载到darknet /

    yolov2的初始值文件

    https://pjreddie.com/media/files/darknet19_448.conv.23

    yolov3的初始值文件

    https://pjreddie.com/media/files/darknet53.conv.74

    浏览器下载较慢,建议复制到迅雷下载

     

    测试初始权重的模型推理效果

    可以执行以下操作

    $ ./darknet detector train cfg/obj.data cfg/yolo-obj.cfg darknet19_448.conv.23

    报错Can't open label file. (This can be normal only if you use MSCOCO)

    很奇怪,原因是少了对应的label文件

    于是把data/labels/airplane_*****.txt等文件复制到 data/images/下

    再次执行

    $ ./darknet detector train cfg/obj.data cfg/yolo-obj.cfg darknet19_448.conv.23

    成功运行,killed是因为设定的配置文件yolo-obj.cfg batch等设定 cpu无法承受负荷

    假设训练能够顺利进行,漫长的等待后 当训练完成 ,权重weights文件保存darknet / backup中

    设定重量输出间隔:

    注意:如果你用的是原版darknet 就可以在examples里面找到,我这里是用的AB版detector.c在darknet/ src / detector.c 内容不一样 还没弄清楚怎么修改

    权重文件默认为

    1000epochs之前每隔100epochs输出

    1000epochs以后每隔10000epochs输出

    1000-10000之间比较有宽度,所以设定1000epochs以后每100epochs可以详细输出

     darknet / examples / detector.c改写第138行,如下所示。

    #默认每10000个时期输出一次
    if(i%10000 == 0 ||(i <1000 && i%100 == 0)){

    改为

    #每100个刻度重写一次
    if(i%100 == 0 ||(i <1000 && i%100 == 0)){

    然后在darknet/下 运行编译

    $ make

    7.将darknet文件夹压缩为zip

    由于GPUColab 使用,请打开darknet / Makefile并将其更改为GPU = 1

    此时,不要在笔记本电脑上运行make(因为没有GPU,会发生错误)

    为了在Colab中运行,需要上传到google drive,因此,对这个darknet文件夹进行压缩。

    $ cd ../
    $ zip -r darknet.zip darknet 

    其实这里可以直接右键darknet文件夹 compress操作 更快(但不知道有没有影响)

    二.在Colab上进行训练

    1)将zip文件上传到google drive

    用自己的Google帐户登陆Drive。将zip文件上传到适当的目录

    2)打开google colaboratory

    https://github.com/clemente620/wakuphas/blob/master/AI/Scripts/darknet_airport%20.ipynb

    将其另存为.ipynb 并将其上传到google drive

    右键单击Drive 并选择“Open with application” - >“ Google Colaboratory ”。

     从Runtime选项卡中选择Change Runtime Type,然后选择Python 3GPU

    注意Makefile文件中GPU一定要打开 也就是 写成GPU=1

    可以通过colab命令操作进行修改

    先用cat 查看文本 并复制

    !cat darknet/Makefile

    修改为GPU=1,复制全文 粘贴到下面<>中

    %%writefile darknet/Makefile
    <修改后的内容进行原文覆盖>

    基本上,通过按照darknet_airport.ipynb中的指示执行单元格,完成以下过程

    I)Colab的环境搭建

    Ⅱ)上传zip到“Colab”

    Iii)执行make编译

    Ⅳ)用./daknet命令进行训练

    V)输出权重文件

    2.5. Colab的注意事项


    因为是免费的,所以有各种各样的规则,不过,12小时规则是最应该注意的点。

    GPU连接后最大可以连续运行12小时,不过超过12小时后,上传的数据和输出的文件,甚至连搭建的环境都会重置

    所以每次都需要对ipynb文件进行环境搭建。

    几个小时后GPU本身也可以使用,但由于删除的数据无法恢复,请右键单击以经常下载重量文件等。

    3. 输出权重文件下载到笔记本电脑

    使用./darknet命令学习时,首先读取准备的初始值,但即使它被中断一次,它也可以从权重文件重新开始.weights作为初始值输出。

    #使用准备好的初始值的情况

    $ ./darknet detector train cfg/obj.data cfg/yolo-obj.cfg darknet19_448.conv.23 > train_log.txt

    #读取所输出的权重yolo-obj_xxx .weights时

    $ ./darknet detector train cfg/obj.data cfg/yolo-obj.cfg backup/yolo-obj_XXX.weights > train_log.txt

    输出日志到train_log.txt。 下载并在笔记本电脑上打开它。

    注意每几行出现的以下内容。

    --
    
    937: 34.240753, 37.657242 avg, 0.000771 rate, 5.082382 seconds, 59968 images
    
    --

    第一个数字是迭代次数。 通常需要2000次。 随着avg前的数量减少,准确度会增加。 

    如果avg的数字不减少,就停止学习。

    将Colab上的权重文件darknet/backup/***.weights下载到本地的笔记本电脑上。

    我这边的情况是,train_log.txt的avg值和epoch数之间的关系如下。

    (extractor.sh和plot.py将下载到darkflow /。将根据您的train_log.txt存在的路径将extractor.sh 重写修改)

    $ sh extractor.sh   # epochavg.txt输出
    $ python plot.py 

    因为自2000次epochs以后几乎没有增加或减少,所以我们可以在那里停止,不过,但为了保险期间,总共迭代了3600次。

    虽然最终的avg在2左右

    使用训练的权重进行检测 语句

    sudo ./darknet detector test cfg/obj.data cfg/yolo-obj.cfg yolo-obj_2000.weights data/airplane_0_3611.jpg

    三. 在笔记本电脑上进行推理(测试)

    1. 安装darkflow

    $ git clone https://github.com/thtrieu/darkflow.git
    $ cd darkflow
    $ python3 setup.py build_ext --inplace

    2. 创建一个Python程序

    https://github.com/wakuphas/wakuphas/blob/master/AI/Scripts/detect.py

    把这个detect.py放入darkflow /

    根据需要可以进行的改动部分:


    第6行:写入文件的路径。


    第11行:描述想要识别的图像路径。


    第21行:指定检测的种类名称(标注名称)。


    第35行:显示大于0.1的置信度(confidence)

    3.加载weights文件并进行对象检测
    准备下面的四条

    • Colab训练和输出的weights被放置于darkflow/backup中。
    • 在学习中使用的darknet/cfg/ yoloobj.cfg拷贝到darkflow/cfg中,改为batch=1, subdivisions=1并保存
    • 在darkflow /中创建labels_airplane.txt并写入1行 "airplane" 保存
    • 将test_airplane.jpg作为确认用图像保存在darkflow/中

    运行detect.py

    $ python detect.py

    以上显示推理(目标检测)成功

    可以使用esc按钮退出

    最终结论 colab 不适合中国玩家 经常runtime disconnected 需要按键精灵脚本重连 很烦  用来试玩TPU还行

    (如果你在美国,网络较稳定还是可以玩玩的)

  • 相关阅读:
    使用echo $? 查看命令成功执行的原理
    Ubuntu 12.04下NFS安装配置图解
    使用nfsstat命令查看NFS服务器状态
    ORACLE 中rownum和row_number()的使用区别(可指定取sql结果集的第几个数据)
    toad调用存储过程,存储过程调用sql 类
    指纹协查统计sql
    oracle 解锁表
    【转】经典排序算法
    wget 命令用法详解
    Linux 带宽、CPU、内存占用情况
  • 原文地址:https://www.cnblogs.com/clemente/p/10338545.html
Copyright © 2011-2022 走看看