什么?不会人工智能也想训练模型?

前言:

在一个风和日丽,雷电交加的…好吧不编了~

因为这学期有人工智能的课,然后呢也有个像自己弄个小模型出来。

在跟某研二师兄的交流中,他叫我去团队跑下模型做个对比实验,顺便带着发个paper。

我心想:我在机器学习领域,属于纯小白~ 试试吧!然后再开始了漫长的学习之路~

阅读这篇你能学到什么?

  • 搭建环境的心路历程的复盘~
  • 逐渐了解各个参数的含义,文件夹的意义~
  • 各大基础模型的base训练,找到成就感~
  • 必要的经验总结✈️~

搭建Pytorch环境

在开始之前,我简直属于是打昏的小鸡摸不着头脑。啥是啥~

我只知道,当我第一次硬着头皮,找哪些N年前的博客啥的,进行复盘时的痛苦

我当时想弄个SSD啥的,找了一篇像样子的开始做(当时环境搭好了)。

走一步卡半个小时,改源码,根据报错一个个找,才痛苦

回归正题!

搭建环境:https://tangshusen.me/Dive-into-DL-PyTorch/#/

上面这个网站是一个翻译的开源了的机器学习教程。我先翻看了仓库…

搭建环境直接在左侧的2.1进行配置,这个文章里面介绍了2篇知乎的文章,点进去手把手教你~

Mac的话搭建的时候会变一下(2021-11),也就是最新的更那个有一点不同,但是谷歌下就能出来。

Windows的话存在显卡,要去下载Cuda然后配置环境啥的,网速嘛这个东西dddd~

最好换成镜像,下载好镜像轮子安装这样是最快的~

当你能打印出️torch的版本的时候就是成功了。

制作自己的数据集

Mac标注文件的下载就很简单,直接谷歌搜索labellmg,这里我贴个网址

结合brew就很快,Windows我就没试过了

如何使用的话,网上大片教程,不多赘述~

入门的YoloV4

正当我一筹莫展的时候,师兄推荐了一个博主

B站搜索:Bubbliiiing 在动态里面搜索pytorch

这是你就会发现:手把手教你训练自己的数据集

这位博主的教学思路是:

简单介绍网络的训练思路到每一层网络如何运作

再到如何训练自己的数据集。

我从头到尾的看了YoloV4的教学视频后就开始动手了。

添加自己的class

根据自己的数据集所列出来的标签️

添加到/model_data/voc_classes.txt

格式相同,一行一个~

制作自己的数据集

这里的数据集你可以下载博主分享的数据集 提取码: uack

也可以自己制作,但是自己制作得保证自己的数量跟得上

目录结构:VOC数据集解析 VOC2007解析

我的话是师兄已经清洗过的(实际上没清洗完全的)

我拿过来直接删除了ImageSet下main中的文件

因为这个博主存在voc_annotation.py的文件会随机自动的分成9:1

然后在主目录下出现2007_val.txt2007_train.txt

所以如果你是在本机上弄好的话,记住放上去重新生成下,因为路径变了~

拿到预训练权重,准备训练。

注意⚠️: 以下都是在有显卡的环境下进行的

到对应的仓库下,选择对应的预训练权重。下载⏬上传⏫到/model/

开始编辑train.py,学问逐渐体现:

首先是配置好class,model路径

其次是开始动手配置参数

batch_size就是:一次训练所选取的样本数。

lr 就是:学习率

num_workers就是: 线程


  • batch_size搭配

​ 如果你的配置好一点:

​ Freeze_batch_size:16;Unfreeze_batch_size:8

​ 差一点就:

​ Freeze_batch_size:8;Unfreeze_batch_size:4

​ Freeze_batch_size:4;Unfreeze_batch_size:2

  • 学习率的搭配:

​ Freeze_lr :1e-3(0.001);Unfreeze_lr: 1e-4

​ Freeze_lr :1e-4(0.0001);Unfreeze_lr: 1e-5

这里面有炼丹的学问了。

  • 首先呢:UnFreeze_Epoch=100;Freeze_Epoch=50;

    它会跑100Epoch,所以保证你的空间不会炸。

  • 其次:loss会逐渐缩小,逐渐稳定到一个范围

    这时候跑出来的mAP如果不满意。

    有几个方法:

    1. 加迭代次数:UnFreeze_Epoch=150次 loss可能会下去点
    2. 调试lr,batch_size
  • 最后:

    当你loss为Nan的时候,就可以停了,然后调试参数。

    具体怎么炼丹,经验与运气,推荐:Github,知乎看看文章

如果你碰到了问题:

解决方法:

  1. 查看Up的常见问题清单
  2. 根据报错⚠️谷歌搜索
  3. Github开Issue提问

最后附一个多个cuda错误:就是没规定哪张显卡的错❌:

# 在train.py加入这几句话
# 编号就是你要用的卡
import os
os.environ['KMP_DUPLICATE_LIB_OK']='True'
os.environ["CUDA_VISIBLE_DEVICES"] = "1,4,7"

到这里你的train.py应该就是跑起来了~坐等吧。

测试拿到mAP

当你跑完100迭代个时候,就要开始测试了。

你的训练权重都在logs下面~

编辑yolo.pyget_map.py改几处path就能跑起来了。

然后会输出map_out文件,拿到map_out/reslut/mAP.png就好了。

但是一个个跑是不是不现实?

我最开始的解决方式是跑10,20,30…共10个。

但是我发现到后面loss逐渐稳定,mAP才会高起来~

所以你可以先删除前N十个,留下20个左右来测试。

反正看谁顺眼!测试他!

image验证~

这个我没怎么用~看看效果呗
当你看到有框框出现的时候,还是挺开心的

一通多吃faster,ssd

同样的道理faster,SSD一模一样的道理

  • fater-rcnn-pytorch https://github.com/bubbliiiing/faster-rcnn-pytorch
  • SSD - Pytorch https://github.com/bubbliiiing/ssd-pytorch

进阶的faster

当我看到faster的用上面那个博主训练出来的时候不理想的时候。

我就准备开始换个库了

我就试试github星星✨最多的库

仓库地址:https://github.com/jwyang/faster-rcnn.pytorch

我直接贴步骤吧:

部署基本环境

# 克隆
$ git clone https://github.com/jwyang/faster-rcnn.pytorch.git

# 切换版本
$ git checkout pytorch-1.0

# 你应该有Anaconda包了,基本都有了
$ cd faster-rcnn.pytorch && mkdir data
$ cd data && mkdir pretrained_model
$ cd ../
$ cd lib
$ python setup.py build develop

下载对应的权重

  • VGG 16:VT Server

  • ResNet 101 VT Server

放到/data/pretrained_model下面

数据集拿过来就好了

将数据集除了主目录下出现2007_val.txt2007_train.txt不要

整个拿过来放到data下面重新命名为:VOCdevkit2007

调整class并注意大小写

这里特别注意⚠️大小写

请以你的标注class为准!!!不然测试mAP全是0

编辑lib/datasets/pascal_voc.py换上自己的

self._classes = ('__background__',  # always index 0
                         'Dark shoes','Light shoes')

如果开始跑的时候报错❌不存在keyvalue。

你看他的报错栈,会发现一个lower方法~ 直接到pascal_voc.py里面删除了就好了

训练数据集

$ CUDA_VISIBLE_DEVICES=0 python trainval_net.py --dataset pascal_voc --net vgg16 --epochs 30 --bs 8 --nw 4 --lr 1e-3 --lr_decay_step 6 --use_tfb --mGPUs --cuda
  • CUDA_VISIBLE_DEVICES:使用哪一个GPU
  • –dataset pascal_voc:用pascal数据集
  • –net res101:用res101作为特征提取网络,可选vgg16
  • –epochs 30:每个epoch都过一遍全部的训练图。
  • –bs 8:见上面
  • –nw 4:num_works=4,4线程进行读取
  • –lr : 初始学习率
  • –lr_decay_step: 每几个epoch学习率衰减一次
  • –use_tfb: 使用tensorboardX实现记录和可视化,不用就不写;可选参数
  • –mGPUs: 多GPU训练,不用就不写该命令;可选参数
  • –cuda:使用cuda;

结果是在models,如果你要重新训练的话

记得删除缓存与logs,缓存在data下面

测试数据集的mAP

 test_net.py --dataset pascal_voc --net res101 --checksession 1 --checkepoch 30 --checkpoint 610 --cuda

–checksession 1 --checkepoch 30 --checkpoint 610 怎么选?

model/res101/pascal_voc下面看例如faster_rcnn_1_30_610.pth懂了吧

他不会生成对应的文件png,所以留意输出的

Flops

我当时私信B站博主的时候~他说自己去搜下

然后我根据自己的理解写个对应的版本

SSD - Pytorch

from nets.ssd import SSD300
import torchvision.models as models
from ptflops import get_model_complexity_info


def get_classes(classes_path): # 去取name和数量
    with open(classes_path, encoding='utf-8') as f:
        class_names = f.readlines()
    class_names = [c.strip() for c in class_names]
    return class_names, len(class_names)


if __name__ == "__main__":
    classes_path = 'model_data/voc_classes.txt'
    class_names, num_classes  = get_classes(classes_path)
    # 对应的模型创建
    myNet = SSD300(num_classes + 1, 'vgg')  
    # 根据层数 以及跑的图片大小进行设置
    flops, params = get_model_complexity_info(myNet, (3,416,416), as_strings=True, print_per_layer_stat=True)
    print("Flops: {}".format(flops))
    print("Params: " + params)

FLOPs-yolov4

from nets.yolo import YoloBody
from ptflops import get_model_complexity_info


def get_classes(classes_path): # 去取name和数量
    with open(classes_path, encoding='utf-8') as f:
        class_names = f.readlines()
    class_names = [c.strip() for c in class_names]
    return class_names, len(class_names)


if __name__ == "__main__":
    classes_path = 'model_data/voc_clothes_classes.txt'
    class_names, num_classes  = get_classes(classes_path)
    # 对应的模型创建
    anchors_mask = [[6, 7, 8], [3, 4, 5], [0, 1, 2]]
    myNet = YoloBody(anchors_mask, num_classes)  
    # 根据层数 以及跑的图片大小进行设置
    flops, params = get_model_complexity_info(myNet, (3,416,416), as_strings=True, print_per_layer_stat=True)
    print("Flops: {}".format(flops))
    print("Params: " + params)

Faster-rcnn

from nets.frcnn import FasterRCNN
import torchvision.models as models
from ptflops import get_model_complexity_info


def get_classes(classes_path):
    with open(classes_path, encoding='utf-8') as f:
        class_names = f.readlines()
    class_names = [c.strip() for c in class_names]
    return class_names, len(class_names)


if __name__ == "__main__":
    # print("Load model.")
    # ssd = SSD(confidence = 0.01, nms_iou = 0.5)
    # print("Load model done.")

    # net = models.vgg16() #可以为自己搭建的模型
    classes_path = 'model_data/voc_classes.txt'
    class_names, num_classes  = get_classes(classes_path)
    myNet = FasterRCNN(num_classes, "predict", anchor_scales = [8, 16, 32], backbone = 'vgg')
    flops, params = get_model_complexity_info(myNet, (3,416,416), as_strings=True, print_per_layer_stat=True)
    print("Flops: {}".format(flops))
    print("Params: " + params)

经验总结

炼丹很枯燥!

经验多去搜搜,多去尝试!

会基础的Linux命令

会Screen命令以及 watch -n 0.5 nvidia-smi 命名抢显卡

get_map很枯燥 自己魔改脚本

首先你先把觉得肯定不行的删除,避免浪费时间⌚️

  • 首先你把get_map包装成main函数传(model_path)
def main(model_path):
  ####
  • 在get_map下面补上(多线程自己写下)
if __name__ == "__main__":
    Path = os.path.abspath(os.path.abspath(os.path.dirname(__file__)))
    logs_path = Path + '/logs'
    files= os.listdir(logs_path)
    want = []
    for each in files:
        if '.pth' in each:
            want.append(each)
    
    # want = ['ep144-loss2.422-val_loss3.563.pth'] # 单独测试
    for each in want:
        path = 'logs/' + each
        with open(Path + '/log.txt', "a+", encoding='utf-8') as f:
            f.write(each + '*****')
        main(path)
  • 更改yolo.py将默认的self.model_path改为接受的
#---------------------------------------------------#
    #   初始化YOLO
    #---------------------------------------------------#
    def __init__(self, model_path, **kwargs): # 手动添加接受 model_path
        self.__dict__.update(self._defaults)
        for name, value in kwargs.items():
            setattr(self, name, value)
            
        #---------------------------------------------------#
        #   获得种类和先验框的数量
        #---------------------------------------------------#
        self.class_names, self.num_classes  = get_classes(self.classes_path)
        self.anchors, self.num_anchors      = get_anchors(self.anchors_path)
        self.bbox_util                      = DecodeBox(self.anchors, self.num_classes, (self.input_shape[0], self.input_shape[1]), self.anchors_mask)

        #---------------------------------------------------#
        #   画框设置不同的颜色
        #---------------------------------------------------#
        hsv_tuples = [(x / self.num_classes, 1., 1.) for x in range(self.num_classes)]
        self.colors = list(map(lambda x: colorsys.hsv_to_rgb(*x), hsv_tuples))
        self.colors = list(map(lambda x: (int(x[0] * 255), int(x[1] * 255), int(x[2] * 255)), self.colors))
        self.generate(model_path) # 传入model_path

    #---------------------------------------------------#
    #   生成模型
    #---------------------------------------------------#
    def generate(self, model_path):
        #---------------------------------------------------#
        #   建立yolo模型,载入yolo模型的权重
        #---------------------------------------------------#
        self.net    = YoloBody(self.anchors_mask, self.num_classes)
        device      = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
        self.net.load_state_dict(torch.load(model_path, map_location=device)) # 不要self
        self.net    = self.net.eval()
        print('{} model, anchors, and classes loaded.'.format(model_path)) # 不要self

        if self.cuda:
            self.net = nn.DataParallel(self.net)
            self.net = self.net.cuda()
  • 更改utils/utils_map.py从638行开始
    if show_animation:
        cv2.destroyAllWindows()

    results_file.write("\n# mAP of all classes\n")
    mAP     = sum_AP / n_classes
    text    = "mAP = {0:.2f}%".format(mAP*100)

    # 写入
    PATH = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    with open(PATH + '/log.txt', "a+", encoding='utf-8') as f:
        f.write(text + '\n')
    
    results_file.write(text + "\n")
    print(text)

mAP大幅度上升

就是因为数据集量太少了!加上随机分配的话

你训练的变成了你测试的,当然高了

修改voc_annotation.py 从63行开始

        for xml in temp_xml:
            if xml.endswith(".xml"):
                total_xml.append(xml)
        
        total_xml.sort()
        num     = len(total_xml)  
        list    = range(num)  
        tv      = int(num*trainval_percent)  
        tr      = int(tv*train_percent)  
        # trainval= random.sample(list,tv) 
        trainval= list[:tv]  
        # train   = random.sample(trainval,tr)  
        train   = trainval[:tr]

最后

我开始去补基础了。累~

实践再来学习,我觉得就没那么抽象了吧~

暂时总结到这里了!

你可能感兴趣的:(小白的成长,人工智能,pytorch,python)