在SeaShips数据集上训练CenterNet网络

在SeaShips数据集上训练并测试CenterNet网络

本文主要记录我在seaships数据集上训练CenterNet网络进行船只检测过程中遇到的问题和解决办法

首先,介绍一下SeaShips数据集,
该版本数据集共有7000张图片,图片分辨率均为1920*1080,分为六类船只,主要是一些内河航道中船只的图片。
在pytorch实现的centernet的基础上进行修改,centernet作者的源代码链接:CenterNet

本文主要讲述运行工程的过程及遇到的bug和解决方法

数据集处理

在上一篇文章里面已经提到把标签转为txt文件,但是本工程需要把数据集转为coco数据集的格式才可以使用。
下载工程zip解压后可到许多文件夹,第一个是data文件夹。我先把上篇文章处理过的SeaShips数据集文件夹放在data文件夹下,然后seaships文件夹下新建images文件夹,把所有图片放在这里面。清空原来的annotations文件下,现在这个文件下需要放两个文件: train.json 和 val.json,分别是训练集和验证集的信息。7000张图片,我选取前5000张为训练集,1000张为测试集,1000张作为验证集。在最后修改后的工程里面的seaships_to_cocoformat.py文件里面定义了coco数据集格式并用来生成json文件。这里在data[“annotations”]中的img_id =“00xxxx”,均为字符串形式,在data[“annotations”]中的bbox定义了框的信息,x,y,w,h, 对应的均为1920*1080分辨率大小。
然后在src/lib/datasets/dataset里面新建一个“seaships. py”,文件内容照着文件夹下coco.py改一改既可。建立完毕后,将数据集加入src/lib/datasets/dataset_factory里面,加入也很简单,打开这个文件,一看便知。

添加数据集

修改opt.py文件

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

要把新的数据集加入help里面,可以修改一下默认数据集,这样方便一些。

 def init(self, args=''):
        default_dataset_info = {
            'ctdet': {'default_resolution': [512, 512], 'num_classes': 11,
                      'mean': [0.408, 0.447, 0.470], 'std': [0.289, 0.274, 0.278],
                      'dataset': 'Annapolis'},

修改ctdet任务使用的默认数据集为新添加的数据集,如上。
修改debugger.py文件:

 elif num_classes == 11 or dataset == 'Annapolis':
            self.names = Annapolis_class_name

把新添加的数据集加入elif,这句在Debugger这个类里面。
然后在debugger.py文件下面添加newdataset_class_name。

修改test.py文件
由于图片路径问题,要在test函数里面修改一下数据集图片的绝对路径

# 修改test函数图片的绝对路径
        dataset.img_dir = '/home/czb/CenterNet-master/data/Annapolis/JPEGImages/'
        img_path = os.path.join(dataset.img_dir, img_info['filename'])

修改sample/ctdet.py文件,35行左右,self.img_dir,更改包含全部图片的文件夹的绝对路径

搭建环境

原工程中体到的环境为Ubuntu+pytorch0.4.1,我原来的环境为Ubuntu+pytorch1.1.0,按照原工程readme文件夹中INSTALL.md里面执行,

cd $CenterNet_ROOT/src/lib/models/networks/DCNv2
./make.sh
问题一:

在执行上面这一步时报错:"ImportError: torch.utils.ffi is deprecated. Please use cpp extensions instead", 原因是pytorch1.1中,torch.utils.ffi被弃用,最简单有效的解决方法是回退到pytorch0.4.x版本。于是我新建anaconda虚拟环境,虚拟环境为python3.6 + pytorch0.4.1,本问题解决。

问题二:

配置好环境后,欢天喜地继续make,迎接我的却是下一个错误:"cffi.error.VerificationError:LinkError:command 'gcc' failed with exit status 1"
查了许多种方法,有效的解决方案是打开CenterNet-master/src/lib/models/networks/DCNv2/make.sh文件,在文件最上面加上如下几句:

export CUDA_PATH=/usr/local/cuda/
export CXXFLAGS="-std=c++11"
export CFLAGS="-std=c99"

export PATH=/usr/local/cuda-9.0/bin${PATH:+:${PATH}}
export CPATH=/usr/local/cuda-9.0/include${CPATH:+:${CPATH}}
export LD_LIBRARY_PATH=/usr/local/cuda-9.0/lib64${LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}}

cd src/cuda

加上以上几句话后就make成功了,我也不知道为什么,玄学吧…

接下来:

cd $CenterNet_ROOT/src/lib/external
make

然后MODEL_ZOO.md里面下载参数,ctdet_coco_dla_2x,下载完毕后放在models文件夹里面。
到这里,环境基本搭建成功,接下来可以跑一下代码了

运行demo.py文件

首先要进入虚拟环境:

source activate CenterNet-master
cd src/

然后在终端输入:

python demo.py ctdet --demo /home/czb/CenterNet-master/images/ --load_model /home/czb/CenterNet-master/models/ctdet_coco_dla_2x.pth

第一个问题:

运行demo.py后发现,已经下载好了参数,可是它还是会重新开始下载参数,经过排查发现,在pose_dla_dcn.py文件里面,第309行开始的一个函数:

def dla34(pretrained=True, **kwargs):  # DLA-34
    model = DLA([1, 1, 1, 2, 2, 1],
                [16, 32, 64, 128, 256, 512],
                block=BasicBlock, **kwargs)
    pretrained = False
    if pretrained:
        model.load_pretrained_model(data='imagenet', name='dla34', hash='ba72cf86')
    return model

我加了一句pretrained = False后,便可正常load我们前面下载的参数了。

第二个问题:

开始运行后,终端显示:

(CenterNet-master) czb@server:~/CenterNet-master/src$ python demo.py ctdet --demo /home/czb/CenterNet-master/images/ --load_model /home/czb/CenterNet-master/models/ctdet_coco_dla_2x.pth
Fix size testing.
training chunk_sizes: [32]
The output will be saved to  /home/czb/CenterNet-master/src/lib/../../exp/ctdet/default
Creating model...
loaded /home/czb/CenterNet-master/models/ctdet_coco_dla_2x.pth, epoch 230
Drop parameter base.fc.weight.
Drop parameter base.fc.bias.

会弹出第一张照片,然后就再也没有任何反应了。经过测试后发现问题是在弹出一张照片后,不要用鼠标关闭这张图片,而是按键盘除了 ESC以外任意键就可以继续运行了。

第三个问题:

看着demo.py运行成功,也能显示图片后,本以为万事大吉可以开始train了,但是我发现demo运行完毕后并没有保存检测后的图片,后来发现,原因很简单,因为运行的时候根本就没有调用保存图片的函数。只需要在src/lib/detectors/cdet.py文件中:

    def show_results(self, debugger, image, results):  # demo文件會調用這個函數,本函`python main.py ctdet --exp_id coco_dla --batch_size 32 --master_batch 1 --lr 1.25e-4  --gpus 0,1`數是demo時顯示圖片並保存圖片
        debugger.add_img(image, img_id='ctdet')
        for j in range(1, self.num_classes + 1):
            for bbox in results[j]:
                if bbox[4] > self.opt.vis_thresh:
                    debugger.add_coco_bbox(bbox[:4], j - 1, bbox[4], img_id='ctdet')
        debugger.show_all_imgs(pause=self.pause)
        debugger.save_all_imgs(path='/home/czb/CenterNet-master/output/', genID=True)

加上一行代码,就是最后一行debugger.save_all_imgs(path='/home/czb/CenterNet-master/output/', genID=True) ,path是输出路径,需要在CenterNet-master文件夹下新建一个文件夹output,然后再运行一遍发现检测后的图片就会保存在这个文件夹里面了。当然,去掉倒数第二行show_all_imgs,那么运行的时候就不会弹出照片了。

开始训练

首先将opts.py文件里面,大概第十几行的地方,把dataset默认值改为seaships,将默认task改为ctdet:

self.parser.add_argument('task', default='ctdet',
                                 help='ctdet | ddd | multi_pose | exdet')
self.parser.add_argument('--dataset', default='seaships',
                                 help='coco | kitti | coco_hp | pascal|seaships')

然后终端输入:

python main.py ctdet --exp_id coco_dla --batch_size 32 --master_batch 1 --lr 1.25e-4  --gpus 0,1
问题一:

发现不管是val还是train都导入的是5000张图片,val应该是1000张才对,定位一下发现前面自己建立的seaships.py文件的27行:

        if split == 'val':
            self.annot_path = os.path.join(
                self.data_dir, 'annotations',
                'val.json').format(split) # 修改test的json文件位置
        else:
            if opt.task == 'exdet':
                self.annot_path = os.path.join(
                    self.data_dir, 'annotations',
                    'train.json').format(split)
            else:
                self.annot_path = os.path.join(
                    self.data_dir, 'annotations',
                    'train.json').format(split) # 这才是train文件

要把第一行if split 改为 ==‘val’,这样validate时就会调用val.json文件,1000张图片。把最后一行要调用的文件改为‘train.json’,这样训练的时候才会调用train.json文件,训练5000张图片。改完之后数据集导入就正常了。

问题二:

报错如下:

Starting training...
ctdet/coco_dlaTraceback (most recent call last):
  File "main.py", line 104, in <module>
    main(opt)
  File "main.py", line 71, in main
    log_dict_train, _ = trainer.train(epoch, train_loader)
  File "/home/czb/CenterNet-master/src/lib/trains/base_trainer.py", line 120, in train
    return self.run_epoch('train', epoch, data_loader)
  File "/home/czb/CenterNet-master/src/lib/trains/base_trainer.py", line 62, in run_epoch
    for iter_id, batch in enumerate(data_loader):
  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/utils/data/dataloader.py", line 336, in __next__
    return self._process_next_batch(batch)
  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/utils/data/dataloader.py", line 357, in _process_next_batch
    raise batch.exc_type(batch.exc_msg)
KeyError: 'Traceback (most recent call last):\n  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/utils/data/dataloader.py", line 106, in _worker_loop\n    samples = collate_fn([dataset[i] for i in batch_indices])\n  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/utils/data/dataloader.py", line 106, in <listcomp>\n    samples = collate_fn([dataset[i] for i in batch_indices])\n  File "/home/czb/CenterNet-master/src/lib/datasets/sample/ctdet.py", line 32, in __getitem__\n    file_name = self.coco.loadImgs(ids=[img_id])[0][\'file_name\']\nKeyError: \'file_name\'\n'

定位后发现是”file_name"的问题,datasets/sample/ctdet.py32行左右:

img_id = self.images[index]  # 返回img的id,即00xxxx
file_name = self.coco.loadImgs(ids=[img_id])[0]['filename'] 

通过输出发现self.coco.loadImgs(ids=img_id)[0]返回的是一张图片的信息,字典形式,其中字典中对应图片名字的key是“filename”,而源代码通过file_name = self.coco.loadImgs(ids=[img_id])[0]['file_name']来读取只会返回空值,所以报错。把"file_name" 改为"filename"即可,这个问题后面还会遇到好几次,处理方法均相同。(其实这是因为我在生成json文件时定义的就是filename可以在生成json文件时把filename改为file_name,这样就不用修改代码了)
接下来这几行使用cv2通过路径读取图片,但是我发现图片路径有问题,即:

self.img_dir = '/home/czb/CenterNet-master/data/Seaships/images2/  # 用于CV2根据路径读取图片
img_path = os.path.join(self.img_dir, file_name)  

原来的self.img_dir定义了一个路径,但与本机的不符,所以在上面加一行,重新定义一下图片路径。

问题三

在计算loss的时候我们发现了一个怪问题,送进去的batch的大小为[32 * 3 * 512 * 512],但是计算loss的时候输出一下batch大小却为[1 * 3 * 512 * 512],batch被分为了两部分,感到很奇怪。后来发现,这是因为使用了两个GPU的原因,不是什么bug,是我们太菜了…
不过这里一个问题但是先跳过了,就是数据集分别在两个GPU上训练,但是数据集的划分极为不均,第一个小GPU训练了31/32的训练集,而第二个只分了1/32的,这样很容易out of memory的。没错,就是下面遇到的问题…

问题四:
  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/nn/modules/module.py", line 477, in __call__
    result = self.forward(*input, **kwargs)
  File "/home/czb/CenterNet-master/src/lib/models/networks/pose_dla_dcn.py", line 216, in forward
    x2 = self.tree2(x1)
  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/nn/modules/module.py", line 477, in __call__
    result = self.forward(*input, **kwargs)
  File "/home/czb/CenterNet-master/src/lib/models/networks/pose_dla_dcn.py", line 54, in forward
    out = self.bn2(out)
  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/nn/modules/module.py", line 477, in __call__
    result = self.forward(*input, **kwargs)
  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/nn/modules/batchnorm.py", line 66, in forward
    exponential_average_factor, self.eps)
  File "/home/czb/anaconda3/envs/CenterNet-master/lib/python3.6/site-packages/torch/nn/functional.py", line 1254, in batch_norm
    training, momentum, eps, torch.backends.cudnn.enabled
RuntimeError: CUDA error: out of memory

batch_size原来设为32,现在改为16,用两个GPU,显存分别是6G和12G,发现还是会报相同的问题。在报out of memory之前(报错之后会清空该程序占用的显存),检查显卡占用情况发现,batch_size为16时占用显存约9个G,但是用两个显卡时它却将大部分数据放在6G的显卡上训练,这样尽管12G显卡有很多内存剩余,还是会out of memory,暂时解决方法是只用12G的那一个显卡进行训练就暂时没问题了。

问题五

首先训练了10个epoch,训练得到的权重在/home/czb/CenterNet-master/exp/ctdet/coco_dla文件夹下,分为model_best.pth和model_last.pth,这里先运行一下demo.py看一下船只检测的效果如何,发现报错。问题主要在于,第一点:在opt.py文件里面修改ctdet网络默认使用的数据集,改为seaships,并修改num_classes=6,大概在340行,如下:

    def init(self, args=''):    # 修改默认数据集
        default_dataset_info = {
            'ctdet': {'default_resolution': [512, 512], 'num_classes': 6,
                      'mean': [0.408, 0.447, 0.470], 'std': [0.289, 0.274, 0.278],
                      'dataset': 'seaships'},

第二点,修改utils文件夹下的debugger.py文件,第48行左右,添加seships数据集基本信息,如下:

        elif num_classes == 20 or dataset == 'pascal':
            self.names = pascal_class_name
        elif num_classes == 6 or dataset == 'seaships':  # 添加seaships数据集
        	self.names = seaships_class_name

注意我这里直接修改opt.py文件中load_model的默认值,这样在终端运行时不用加上模型路径。

问题六

训练了10个epoch后观察一下输出,发现基本所有预测框都偏向船的右下方,怀疑是处理数据集时给的标签有问题。如下,是根据训练了10个epoch后得到的权重参数预测的框:
在这里插入图片描述
现在将原来标签中心点的坐标向左上方移动二分之一个框,重新生成train.json 和 val.json文件。
修改后重新训练十个epoch,效果已经很不错了:
在这里插入图片描述

运行test文件:

修改的地方一:
    for ind, (img_id, pre_processed_images) in enumerate(data_loader):
        ret = detector.run(pre_processed_images)
        img_id = np.array(img_id)

        results[img_id.astype(np.int32)[0]] = ret['results']

要加一句img_id = np.array(img_id),将其转为numpy类型,同时,删去上面最后一句[img_id.numpy().astype(np.int32)[0]]中的.numpy。

修改的地方二:
        if split == 'val':
            self.annot_path = os.path.join(
                self.data_dir, 'annotations',
                'val.json').format(split) # 修改test的json文件位置
        elif split == 'test':
            self.annot_path = os.path.join(
                self.data_dir, 'annotations',
                'test.json').format(split)  # 修改test的json文件位置

在前面新建的seaships.py文件中,加入elif split == ‘test’:…,作用是当test时指定一个要读取的标签文件。

修改的地方三
    for image_id in all_bboxes:
        image_id = str(image_id)
        image_id = image_id.zfill(6)
        for cls_ind in all_bboxes[int(image_id)]:
            category_id = self._valid_ids[cls_ind - 1]
            for bbox in all_bboxes[int(image_id)][cls_ind]:

为了保证两个json文件image_id相同,因此将image_id转换为6位整数型,并在接下来的循环处改成int类型.

修改的地方四

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

一个很小却又很重要的问题

这个不起眼的问题困扰了我很久,花费了我们十几个小时才注意到这个地方,真是一把辛酸泪啊…
那就是运行test.py时,不要加上 --keep_res,加上这句会保持图片的原始分辨率,与我们训练的权重参数不符,这样导致测试的结果明显有问题。而最初我们以为是标签的问题,但是检查了好几遍都是没有问题的,最后无奈把yolov3工程中test改变一下放在这里使用,修改的过程又是几个小时。悲催的是即使用了yolov3的test,所测结果依然几乎为0。因为跑demo效果很好,test效果很差,经过详细比较发现没什么区别啊,最后输出发现,对于同一张图片,它们输出的results竟然不一样!!!终于发现了这个不起眼的keep_res,因为原工程里readme里面给的例子测试的时候有这句,我测试的时候就没多想直接加上了,结果给我们了许多的问题,所以一定不要拿来主义直接去用!

这里指出https://github.com/ultralytics/yolov3这个工程里面的一个小错误

在ultralytics/yolov3/utils/datasets.py文件中,第328行:
在这里插入图片描述应改为如下所示:

          if x.size > 0:
                # Normalized xywh to pixel xyxy format
                labels = x.copy()
                # labels[:, 1] = ratio * w * (x[:, 1] - x[:, 3] / 2) + padw
                # labels[:, 2] = ratio * h * (x[:, 2] - x[:, 4] / 2) + padh
                # labels[:, 3] = ratio * w * (x[:, 1] + x[:, 3] / 2) + padw
                # labels[:, 4] = ratio * h * (x[:, 2] + x[:, 4] / 2) + padh
                labels[:, 1] = (x[:, 1] - x[:, 3] / 2)
                labels[:, 2] = (x[:, 2] - x[:, 4] / 2)
                labels[:, 3] = (x[:, 1] + x[:, 3] / 2)
                labels[:, 4] = (x[:, 2] + x[:, 4] / 2)

        # Augment image and labels
        if self.augment:
            img, labels = random_affine(img, labels, degrees=(-5, 5), translate=(0.10, 0.10), scale=(0.90, 1.10))

        nL = len(labels)  # number of labels
        if nL:
            # convert xyxy to xywh
            labels[:, 1:5] = xyxy2xywh(labels[:, 1:5])

            # Normalize coordinates 0 - 1
            # labels[:, [2, 4]] /= img.shape[0]  
            # labels[:, [1, 3]] /= img.shape[1] 
        if self.augment:

把4行labels坐标转换修改一下,并把下面的normalize coordinates的两行注释掉就可以了。
原因: 其实这几行是对标签坐标进行修改,一方面是把xywh改为x1y1x2y2,另一方面是根据图片的分辨率进行修改。以seaships数据集为例,原图大小为1920 * 1080,标签为class,x,y,w,h,其中xywh都已经是归一化的,而原来代码这里把归一化的坐标转为网络需要的分辨率如 416 * 416大小,再把x,w除以宽416,y,h除以高416,首先有点多此一举,因为标签是归一化的,那么就与分辨率大小无关了。
其次有点小错误。这里的ratio=max(new_shape)/max(shape)=416/1920,见原代码385行letterbox函数。那么shape * ratio对于宽1920正常,但是高1080*ratio小于416,于是原代码加了一个修正,即padh = (416 - 1080 * ratio)/2,然后在原来代码330和332行,对于416 * 416大小图片标签 y1=ratio(=416/1920) * h(=1080) * y1(原标签归一化的y1) + padh(416-1080 * 416/1920)/2,这里padh是应该对1080转416的修正的1/2,而不是1080转416再乘以一个小数后的修正,所以会有误差。应该为y1=(ratio * h+padh * 2) * y1 = (416/1920 * 1080 + 416-1080 * 416/1920) * y1 = 416 * y1, 而下面labels[:, [1, 3]] /= img.shape[1] 相当于 416 * y1 / 416 = y1,这也是这个过程多此一举的原因。

记录一下main.py大概执行流程

大概在main.py的70行:

    for epoch in range(start_epoch + 1, opt.num_epochs + 1):
    	mark = epoch if opt.save_all else 'last'
    	log_dict_train, _ = trainer.train(epoch, train_loader) 

开始进行一个epoch训练,调用trainer.train函数会跳转到base_trainer.py中的run_epoch函数,大概在五十五行。run_epoch()函数:

        for iter_id, batch in enumerate(data_loader):  # dataloader 长度为156,大概是一个batch32,总需156个batch
            if iter_id >= num_iters:
                break
            data_time.update(time.time() - end)

data_loader为训练集容器,因为训练集5000张图片,batch_size=32,那么一个epoch要分为156个batch(我是这样理解的)。
代码里面batch包含了一个batch的全部内容,是一个字典形式,有6个key,分别是:input, heatmap, reg_msk, ind, wh, reg,它们的大小分别为:
#torch.Size([32, 3, 512, 512]) input
# torch.Size([32, 6, 128, 128]) heatmap
# torch.Size([32, 128]) reg_mask
# torch.Size([32, 128]) ind
# torch.Size([32, 128, 2]) wh
# torch.Size([32, 128, 2]) reg
然后要计算损失,将batch送入下面的函数:

output, loss, loss_stats = model_with_loss(batch)  # loss为总的loss,loss_stats即为一个字典,是loss的类型,三部分,output即网络输出

这个函数经过层层调用,最终调用的是class ModleWithLoss:

class ModleWithLoss(torch.nn.Module):
    def __init__(self, model, loss):
        super(ModleWithLoss, self).__init__()
        self.model = model  # model为网络框架
        self.loss = loss    # self.loss = 总的loss大小,还有一个loss_stats

    def forward(self, batch):
        # batch为一个批次输入的全部内容,字典形式,key为input即为输入的图片,是一个32*3*512*512的张量
        outputs = self.model(batch['input'])
        loss, loss_stats = self.loss(outputs, batch)  # 总的loss、大小, 和loss、 states
        return outputs[-1], loss, loss_stats

input即输入图片,shape为torch.Size([32, 3, 512, 512]), outputs为网络输出, self.loss来计算损失。
测试得到,outputs是一个长度为1的list,outputs[0]长度为3的字典,outputs[0]的value的shape分别为:
torch.Size([1, 6, 128, 128]) heatmap
torch.Size([1, 2, 128, 128]) box的wh
torch.Size([1, 2, 128, 128]) box的偏置
self.loss()函数计算loss,两个返回值,第一个loss即为总的loss,第二个loss_stats为每部分的loss大小(包括关键点估计,框的大小回归,偏置损失三部分),如下:

loss tensor(307.9406, device='cuda:0', grad_fn=<ThAddBackward>)
{'loss': tensor(307.9406, device='cuda:0', grad_fn=<ThAddBackward>), 'hm_loss': tensor(306.8193, device='cuda:0', grad_fn=<AddBackward>), 'wh_loss': tensor(7.4733, device='cuda:0', grad_fn=<AddBackward>), 'off_loss': tensor(0.3739, device='cuda:0', grad_fn=<AddBackward>)}

在上面记录的那些报错解决后,运行main.py就可以正常开始训练了。

总结一下:

加入一个新的数据集需要做什么:

1,要生成coco数据集格式的标签文件train.json 和 val.json,其中标签信息大小就以1920*1080分辨率来给出,x,y要给出box左上角的坐标。
2,仿照coco.py新建seaships.py修改一些地方就可以。
3,在dataset_factory.py里面将seaships加进去,里面就是记录了有哪些数据集。
4,修改opt.py里面opt.data_dir路径,给出本机data文件夹绝对路径。
5,修改opt.py中def init函数ctdet任务对应的默认数据集信息,修改num_classes和dataset两个点。
6,修改utils文件夹下的debugger.py文件,第48行左右,添加seships数据集基本信息,照着例子加一个elif即可。
7,修改opt.py文件大概十几行的地方,修改默认任务为ctdet,默认数据集为seaships,这样改是为了运行时方便,不改的话可以在终端运行时在指定也可,当然,要把seaships加入datasets的help里面。
8,新建的seaships.py要加上elif split == test: … 详见上方test部分。

训练:
python main.py ctdet --exp_id coco_dla --batch_size 16 --master_batch 1 --lr 1.25e-4  --gpus 0

1,opt.load_model,将默认改为空,这样就会重新初始化进行训练
2,训练完毕后,会生成model_best.pth 和 model_last.pth两个权重,在exp/ctdet/coco_dla/路径下,然后把opt.load_model的默认那里改为两个权重中一个的路径,这样继续训练或者test或者跑demo的时候就会导入先前训练的权重
3,opt.epoch修改default改变训练的epoch多少,也可不改,在终端运行时直接指定。

测试

1,这里有两个测试方法,一个是原工程就有的调用pycocotools进行测试,另一个是自己写代码测试mAP。
原方法:python test.py ctdet --exp_id coco_dla --not_prefetch_test
另一个可以测每个类别的mAP:python test_with_yolov3.py ctdet --exp_id coco_dla --not_prefetch_test
2,这里注意要生成一个包含全部测试图片路径的txt文件,每一行指向每一张图片的绝对路径。类似于yolov3测试数据集的方法,全部图片要放在JPEGImages文件下,对每一张图片生成txt标签文件,放在labels文件下,labels文件夹和JPEGImages文件夹放在同一个文件夹下。
3,test文件test函数和prefetch_test函数都有一句split = 'val' if opt.trainval else 'test',原来是split = 'val' if not opt.trainval else 'test',把not去掉,否则在终端测试时要加上--trainval

  • 30
    点赞
  • 167
    收藏
    觉得还不错? 一键收藏
  • 137
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 137
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值