目录
1.关于使用环境
2.训练非jpg的数据
3.关于cuda out of memory
4.多尺度训练理解
截止到2019年底,一共三台机器在跑:
1、第一台台式机:anaconda + 1080ti + python 3.7 + cuda9.0 + pytorch 1.2 跑起来ok
2、第二台台式机: anaconda + 2080ti + python 3.6 + cuda10.0 + pytorch 1.3 跑起来ok
3、第三台老式笔记本: 无anaconda + 1050ti + python 3.5 + cuda9.0 + pytorch 1.3 因为内存限制 只能infer
建议用anaconda创建一个环境安装mmdetection比较顺利,建议python 3.6+ ,python 3.5在编译的过程中配套的一些包需要手动降级安装,如matplotlib,scipy-image等等,比较麻烦,笔记本就是这么安装的。
pytorch的安装直接可以通过官网,百度pytorch+ github即可
mmdetection默认是训练jpg的数据,要训练其他格式的数据更改pipelines/loading代码:
class LoadImageFromFile(object):
def __init__(self, to_float32=False):
self.to_float32 = to_float32
def __call__(self, results):
if results['img_prefix'] is not None:
filename = osp.join(results['img_prefix'],
results['img_info']['filename'])
filename = filename.replace('jpg', 'png') #替换为自己的格式
#print(filename)
else:
filename = results['img_info']['filename']
print(filename)
img = mmcv.imread(filename)
if self.to_float32:
img = img.astype(np.float32)
results['filename'] = filename
results['img'] = img
results['img_shape'] = img.shape
results['ori_shape'] = img.shape
return results
这一点在截止到2019年12月已经得到部分修正,官方修正主要在max_iou_assigner.py文件中,增添了self.gpu_assign_thr。
因为mmdetecton默认将gt求交求并的运算全部放在gpu进行,因此一旦gt数量太多,gpu的内存就爆了,例如DOTA数据集。因此可以设置self.gpu_assign_thr,当gt的数量大于self.gpu_assign_thr时,将gt的运算放在cpu。
if bboxes.shape[0] == 0 or gt_bboxes.shape[0] == 0:
raise ValueError('No gt or bboxes')
assign_on_cpu = True if (self.gpu_assign_thr > 0) and (
gt_bboxes.shape[0] > self.gpu_assign_thr) else False
# compute overlap and assign gt on CPU when number of GT is large
if assign_on_cpu:
device = bboxes.device
bboxes = bboxes.cpu()
gt_bboxes = gt_bboxes.cpu()
if gt_bboxes_ignore is not None:
gt_bboxes_ignore = gt_bboxes_ignore.cpu()
if gt_labels is not None:
gt_labels = gt_labels.cpu()
本人一直在训练dota数据集所以将self.gpu_assign_thr设置在300-400之间。但是一旦使用到多尺度的训练或者anchor free的算法时,还是会爆掉,将self.gpu_assign_thr设置到100也不能幸免,主要爆炸的地方在geometry.py中bbox_overlaps函数(其实有点费解,按道理前面的设置self.gpu_assign_thr,就不会在产生求交内存爆炸的的问题了,不知道为何还会产生,希望高人解答)
为了解决这个问题,我在geometry.py中bbox_overlaps函数增添了代码,也是模拟上面添加了gpu_assign_thr。
但是有一个问题是,如果在这里设置了gpu_assign_thr,那么上面的self.gpu_assign_thr必须设置为-1,否则会报错。毕竟这个是非官方实现,与官方是有冲突的。
建议如果存在内存爆炸问题,先按照官方的设置self.gpu_assign_thr,如果不行再试试我这个三脚猫的方法吧。
if bboxes1.shape[0] == 0 or bboxes2.shape[0] == 0:
raise ValueError('No gt or bboxes')
gpu_assign_thr = 400
assign_on_cpu = True if (gpu_assign_thr > 0) and (
bboxes1.shape[0] > gpu_assign_thr) else False
# compute overlap and assign gt on CPU when number of GT is large
if assign_on_cpu:
bboxes1 = bboxes1.cpu().detach().numpy()
bboxes2 = bboxes2.cpu().detach().numpy()
tl = np.maximum(bboxes1[:, None, :2], bboxes2[:, :2])
br = np.minimum(bboxes1[:, None, 2:], bboxes2[:, 2:])
iw = (br - tl + 1)[:, :, 0]
ih = (br - tl + 1)[:, :, 1]
iw[iw < 0] = 0
ih[ih < 0] = 0
overlaps = iw * ih
area1 = (bboxes1[:, 2] - bboxes1[:, 0] + 1) * (
bboxes1[:, 3] - bboxes1[:, 1] + 1)
if mode == 'iou':
area2 = (bboxes2[:, 2] - bboxes2[:, 0] + 1) * (
bboxes2[:, 3] - bboxes2[:, 1] + 1)
ious = overlaps / (area1[:, None] + area2 - overlaps)
else:
ious = overlaps / (area1[:, None])
ious = torch.from_numpy(ious).cuda()
else:
lt = torch.max(bboxes1[:, None, :2], bboxes2[:, :2]) # [rows, cols, 2]
rb = torch.min(bboxes1[:, None, 2:], bboxes2[:, 2:]) # [rows, cols, 2]
wh = (rb - lt + 1).clamp(min=0) # [rows, cols, 2]
overlap = wh[:, :, 0] * wh[:, :, 1]
area1 = (bboxes1[:, 2] - bboxes1[:, 0] + 1) * (
bboxes1[:, 3] - bboxes1[:, 1] + 1)
if mode == 'iou':
area2 = (bboxes2[:, 2] - bboxes2[:, 0] + 1) * (
bboxes2[:, 3] - bboxes2[:, 1] + 1)
ious = overlap / (area1[:, None] + area2 - overlap)
else:
ious = overlap / (area1[:, None])
mmdetection的多尺度训练与其他开源框架的多尺度训练不同。以典型的detectron为例,在训练中人为指定几个尺度如
(512,768,1024),再设置最长边为1024,其缩放的步骤是:
(1)首先以样本的短边为基准缩放到(512,768,1024)中的一个尺度,这时候会有一个缩放比例。
(2)以这个比例缩放样本的长边,计算缩放后的样本长边。
(3)如果这个长边大于1024,那么就以样本的长边为基准缩放到1024,此时还会有一个缩放比例,以这个比例缩放短边。
总之缩放的尺度是有限的
mmdetection的缩放代码在piplines/transforms.py,如下,在config文件中设置的时候只能设置两个尺度如(800,1200),(900,1300)
def random_sample(img_scales):
assert mmcv.is_list_of(img_scales, tuple) and len(img_scales) == 2
img_scale_long = [max(s) for s in img_scales]
img_scale_short = [min(s) for s in img_scales]
long_edge = np.random.randint(
min(img_scale_long),
max(img_scale_long) + 1)
short_edge = np.random.randint(
min(img_scale_short),
max(img_scale_short) + 1)
img_scale = (long_edge, short_edge)
return img_scale, None
步骤如下:
(1)算法会在(800,1200)和(900,1300)这两个尺度中找到每个尺度的最大值,作为长边的缩放范围,即此时是(1200,1300);
(2)同样地,会在这两个尺度中找到每个尺度的最小值,作为短边的缩放范围,即(800,900);
(3)从长边的范围中随机选一个作为长边的最大尺度,同样地在短的范围中随机选一个作为短边的尺度
(4)之后就与前面detectron的缩放步骤一致了
可见由于随机性,mmdetection可以缩放到指定范围的任意一个尺度