详解Centernet训练自己的数据集(win10 + cuda10 + pytorch1.0.1)

网上很多都是基于linux和pytorch0.4.1的训练教程,为了不重装cuda和pytorch,直接在自己电脑上搭建centernet环境并训练自己的数据集;【win10+cuda10.0+cudnn7.6.2+pytorch1.0.1+1660ti】

Centernet

避免本文太长,先分几部分分别介绍
1、centernet论文和理论
2、搭建centernet环境(win10+pytorch1.0.1)
3、训练自己的数据集(记录报错问题)

一、Centernet论文和原理

论文地址:https://arxiv.org/pdf/1904.07850.pdf
论文代码:https://github.com/xingyizhou/CenterNet
2019 年 4 月,得克萨斯奥斯汀大学和伯克利提出了同名的 CenterNet :Objects as Points,取得了最佳的速度-准确性权衡;尝试利用物体中心点的信息进行物体检测。网上对centernet介绍的文章非常多,这里就不单独写篇文章介绍了,直接贴出几篇浏览量排在前面的几篇供参考。
论文精读——CenterNet :Objects as Points
论文也撞衫,你更喜欢哪个无锚点CenterNet?

二、搭建centernet环境

主要是在win10上对centernet环境的搭建,作者源代码在GitHub上是基于linux系统上的,但网上也有很多基于win上环境搭建,改几行代码编译一下就可以,贴上写的较详细的教程:win10 + cuda10.0 + pytorch1.2 + CenterNet 环境搭建
在最后跑模型的时候出了一个错误:

urllib.error.HTTPError: HTTP Error 404: Not Found

主要原因是在运行完

python demo.py ctdet --demo ../images/17790319373_bd19b24cfc_k.jpg --load_model ../models/ctdet_coco_dla_2x.pth

这条命令行后,它会自动下载dla34-ba72cf86.pth模型导致出错,所以可以离线下载并放在C:\Users\ ***\ .torch\models下(每个人路径不一样)
这里增加两点:
如果要调用网络摄像头,命令如下

python demo.py ctdet --demo webcam --load_model ../models/ctdet_coco_dla_2x.pth

如果要输出关键点热力图,命令如下

python demo.py ctdet --demo ../images/17790319373_bd19b24cfc_k.jpg --load_model ../models/ctdet_coco_dla_2x.pth --debug 2
python demo.py multi_pose --demo ../images/17790319373_bd19b24cfc_k.jpg --load_model ../models/multi_pose_dla_3x.pth --debug 2

如果还出现别的问题,来这里:https://github.com/xingyizhou/CenterNet/issues/7 基本上覆盖了所有的问题。

三、训练自己的数据集

1、数据集

1、这里的数据集一般是images和自己标注好的xml文件,需要将voc格式转化为coco格式,也就是将xml转为json文件,val、test、train三部分分别转为成对应的json文件,贴上别人的代码:能找到的尽量不自己写
这里报了一个错误:

SyntaxError: (unicode error) 'unicodeescape' codec can't decode bytes in position 2-3:

这个错误很低级,就是路径转义的问题,在路径前面加上r或者将反斜杠换为正斜杠
2、生成三个json文件后,在centernet/data下新建一个新的文件夹(名字取自己要训练的名称,注意不能中文,不然后面会报错)
详解Centernet训练自己的数据集(win10 + cuda10 + pytorch1.0.1)_第1张图片
三个json文件放annotations里面
详解Centernet训练自己的数据集(win10 + cuda10 + pytorch1.0.1)_第2张图片
images里面把所有的图片放进来(val+test+train)
3、计算均值和标准差,后续改配置文件的时候需要用到图片总的均值和标准差,这里先计算一下,直接计算的代码

import cv2, os, argparse
import numpy as np
from tqdm import tqdm


def main():
    dirs = r'F:\CenterNet\data\headout\images'  # 修改你自己的图片路径
    img_file_names = os.listdir(dirs)
    m_list, s_list = [], []
    for img_filename in tqdm(img_file_names):
        img = cv2.imread(dirs + '/' + img_filename)
        img = img / 255.0
        m, s = cv2.meanStdDev(img)
        m_list.append(m.reshape((3,)))
        s_list.append(s.reshape((3,)))
    m_array = np.array(m_list)
    s_array = np.array(s_list)
    m = m_array.mean(axis=0, keepdims=True)
    s = s_array.mean(axis=0, keepdims=True)
    print("mean = ", m[0][::-1])
    print("std = ", s[0][::-1])


if __name__ == '__main__':
    main()

这里我用的另外一种方法:毕竟还是用的别人的
因为报错了,所以提一下;
报错一:

ImportError: cannot import name 'PILLOW_VERSION' from 'PIL'

原因:torchvision 模块内import pillow的时候发现找不到PILLOW_VERSION, 因为默认装的最新版本为7.0.0,在 7.0.0 后的版本就没有 PILLOW_VERSION了。把它改为6.0.0即可
报错二:

cannot import name 'imread' from 'scipy.misc'

原因:版本过高,把1.3.1改为1.2.1,pip install scipy==1.2.1,虽然成功了,但还是提示scipy.misc.imread()被弃用,应该用imageio.imwrite()来替代
3、修改配置文件,在src/lib/datasets/dataset里面新建一个“ped. py”,文件内容先把文件夹下coco.py全部复制过来再进行修改
a:类COCO改为自己设置的文件名ped

class Ped(data.Dataset):

b:15行num_classes=80改成自己的类别数
c:17行的mean和std改成自己图片数据集的均值和标准差

    num_classes = 6
    default_resolution = [512, 512]
    mean = np.array([0.304048, 0.300987, 0.298292],
                    dtype=np.float32).reshape(1, 1, 3)
    std = np.array([0.326341, 0.326124, 0.325749],
                   dtype=np.float32).reshape(1, 1, 3)

d:修改数据和图片路径,data_dir 输入的是咱们之前建立的数据集文件夹的名字,img_dir 输入的是 images 图片文件夹,super后的类改为Ped

    def __init__(self, opt, split):
        super(Ped, self).__init__()
        self.data_dir = os.path.join(opt.data_dir, 'headout')
        self.img_dir = os.path.join(self.data_dir, 'images')

e:修改Json文件路径,并且把test的也加上,后续测试时需要用到

       if split == 'val':
            self.annot_path = os.path.join(
                self.data_dir, 'annotations',
                'val.json').format(split)
        else:
            if opt.task == 'exdet':
                self.annot_path = os.path.join(
                    self.data_dir, 'annotations',
                    'train.json').format(split)
            if split == 'test':
                self.annot_path = os.path.join(
                    self.data_dir, 'annotations',
                    'test.json').format(split)
            else:
                self.annot_path = os.path.join(
                    self.data_dir, 'annotations',
                    'train.json').format(split)

f:类别名和类别id。这里得根据生成的json文件里面的类别和id一一对应,不然后续测试的结果类别会弄混。

		self.max_objs = 128
        self.class_name = [
            '__background__', 'openwindow', 'stand_2', 'no_brake', 'leave_2', 'sit_2', 'opendoor'
            ]
        self._valid_ids = [0, 1, 2, 3, 4, 5, 6]

4、将数据集加入src/lib/datasets/dataset_factory.py里面,导入自己的类Ped以及在dataset_factory字典里加入自己的数据集名字

from .dataset.ped import Ped


dataset_factory = {
  'coco': COCO,
  'pascal': PascalVOC,
  'kitti': KITTI,
  'coco_hp': COCOHP,
  'ped': Ped
}

5、修改/src/lib/opts.py
a:将自己的数据集设为默认数据集,加入到help里面

    self.parser.add_argument('--dataset', default='ped',
                             help='coco | kitti | coco_hp | pascal | ped')

b:修改ctdet任务使用的默认数据集改为新添加的数据集,修改分辨率,类别数,均值,标准差,数据集名字

  def init(self, args=''):
    default_dataset_info = {
      'ctdet': {'default_resolution': [512, 512], 'num_classes': 6,
                'mean': [0.304, 0.300, 0.298], 'std': [0.326, 0.326, 0.325],
                'dataset': 'ped'},

c:修改batch_size,出现内存溢出的报错,就把batchsize改小一点

    self.parser.add_argument('--batch_size', type=int, default=4,
                             help='batch size')

6、修改src/lib/utils/debugger.py文件(变成自己数据的类别和名字,前后数据集名字一定保持一致)
a:46行加上这两行

    elif num_classes == 6 or dataset == 'ped':
      self.names = ped_class_name

b:460行加上自己的类(不要背景)

ped_class_name = [
            'openwindow', 'no_brake', 'opendoor', 'sit_2', 'stand_2',
            'leave_2']

2、搭建环境

我的环境是win10+cuda10.0+cudnn7.6.2+pytorch1.0.1+1660ti
cuda10和cudnn的搭建就不提了,网上的教程也非常多
主要提一下pytorch的安装,我是离线安装的:教程
第二部分已经搭建好了centernet的环境了,接下来就是训练数据了

3、开始训练

在.Centernet/src/目录下,运行main.py文件,这里head改成你自己要保存的实验结果文件夹名称即可:
1、不加载预训练权重

python main.py ctdet --exp_id head --batch_size 4 --lr 1.25e-4  --gpus 0

2、加载预训练权重

python main.py ctdet --exp_id head --batch_size 4 --lr 1.25e-4  --gpus 0 --load_model ../models/ctdet_coco_dla_2x.pth

3、断点恢复训练

python main.py ctdet --exp_id head --batch_size 4 --lr 1.25e-4  --gpus 0 --resume

成功开始训练
详解Centernet训练自己的数据集(win10 + cuda10 + pytorch1.0.1)_第3张图片
训练完成后,在./exp/ctdet/head/文件夹下会出现一堆文件;
其中,model_last是最后一次epoch的模型;model_best是val最好的模型;
详解Centernet训练自己的数据集(win10 + cuda10 + pytorch1.0.1)_第4张图片

4、测试

运行test.py文件

python test.py --exp_id head --not_prefetch_test ctdet --load_model ../exp/ctdet/food/model_best.pth

输出结果(我是在自己的笔记本上运行的,样本只用100张,epoch也只有40个,只是为了跑通算法哈,所以结果0.0)
详解Centernet训练自己的数据集(win10 + cuda10 + pytorch1.0.1)_第5张图片
测试单张图片(因为效果并不理想,图就不放出来了)

python demo.py ctdet --demo ../data/head/images/image1.jpg  --load_model ../exp/ctdet/food/model_best.pth

如果需要保存你的预测结果,可以到目录 /src/lib/detecors/ctdet.py下,在show_results函数中的末尾加入这句:

debugger.save_all_imgs(path='/CenterNet-master/outputs', genID=True)

5、绘制loss曲线

写个py文件将F:\CenterNet\exp\ctdet\head\logs_2020-01-13-09-42目录下的log.txt里的loss值绘制成曲线
详解Centernet训练自己的数据集(win10 + cuda10 + pytorch1.0.1)_第6张图片

6、问题及解决

其实过程并没有这么简单,遇到了非常多的问题,筛选几个多数会碰到的问题
1、训练阶段一开始就报错,错误如下:

AttributeError: Can't pickle local object 'get_dataset.<locals>.Dataset'

(tensorflow-gpu) F:\CenterNet\src>Traceback (most recent call last):
  File "", line 1, in <module>
  File "D:\Anaconda\envs\tensorflow-gpu\lib\multiprocessing\spawn.py", line 105, in spawn_main
    exitcode = _main(fd)
  File "D:\Anaconda\envs\tensorflow-gpu\lib\multiprocessing\spawn.py", line 115, in _main
    self = reduction.pickle.load(from_parent)
EOFError: Ran out of input

大多数都遇到了这个问题,有些论坛上说在main.py文件前面加上

torch.backends.cudnn.enabled = False

但还是报错了,尝试好多方法后最终发现是num_workers的问题,修改方法是在/src/lib/opts.py把num_workers修改为0

    self.parser.add_argument('--num_workers', type=int, default=0,
                             help='dataloader threads. 0 for single-thread.')

之后在训练第五步后进行val的时候又会报这个错误,在main.py文件修改为0

  val_loader = torch.utils.data.DataLoader(
      Dataset(opt, 'val'), 
      batch_size=1, 
      shuffle=False,
      num_workers=0,
      pin_memory=True
  )

理由:官方解释:DataLoader的函数定义中num_workers是使用多进程加载的进程数,0代表不使用多进程;使用dataloader读取数据时,如果设置num_workers为0,也就是用主进程读取数据,模型训练程序运行正常。如果设置num_workers为其他任何一个大于0的整数,也就是使用子进程读取数据时,训练程序会卡住,卡在训练之前,GPU使用率和显存占用率都为0,导致图片读取不了而报错。
2、报错信息如下

AttributeError: 'NoneType' object has no attribute 'shape'

训练阶段报错,在datasets/sample/ctdet.py文件添加

file_name = self.coco.loadImgs(ids=[img_id])[0]['file_name']
self.img_dir = 'F:/CenterNet/data/headout/images/'
file_name = file_name + ".png"
img_path = os.path.join(self.img_dir, file_name)

之后在测试阶段也报报错,在test.py文件修改
第32行

    img_id = self.images[index]
    img_info = self.load_image_func(ids=[img_id])[0]['file_name']
    # print(img_info)
    self.img_dir = 'F:/CenterNet/data/headout/images/'
    img_info = img_info + ".png"
    img_path = os.path.join(self.img_dir, img_info)

第63行,num_workers改为0,同第一个问题

  data_loader = torch.utils.data.DataLoader(
    PrefetchDataset(opt, dataset, detector.pre_process), 
    batch_size=1, shuffle=False, num_workers=0, pin_memory=True)

第103行,这里要添加两次路径,第二处一开始没看到,找了好久好久才发现这里还要改

  for ind in range(num_iters):
    img_id = dataset.images[ind]
    img_info = dataset.coco.loadImgs(ids=[img_id])[0]['file_name']
    dataset.img_dir = 'F:/CenterNet/data/headout/images/'
    img_info = img_info + ".png"
    img_path = os.path.join(dataset.img_dir, img_info)

理由:这个错误是查了好多资料才发现的,报错原因是没有加载到图片,图片的路径不对,原来的self.img_dir定义了一个路径,但与本机的不符,所以在上面加一行,重新定义一下图片路径。
3、cocoapi报错 pycocotools
这个在配置centernet环境的时候用不到,但在训练的时候却需要,按照官方https://github.com/cocodataset/cocoapi的安装方法并不能成功,报错找不到对象;
在https://github.com/philferriere/cocoapi 上下载源码,这也是支持在windows系统的版本,并且要安装VS2017,之后在cmd上编译就可以了。

python setup.py install

4、测试的时候,运行test时,会将网络预测的测试集图片的结果保存为results.json ,然后与标签test.json进行比较来计算AP,然后我们发现,results里面目标的中心点坐标就为框的中心点坐标,而之前生成的test里面框的中心点坐标实际为框的左上角坐标,因此重新生成一下test.json,修改其标签信息与results相对应才可。

7、总结

经过好多天的奋斗,也总算把centernet跑通了,了解的并不深入,希望可以和大家一起学习!!!

你可能感兴趣的:(#,centernet,#,pytorch,#,python)