第一部分:mmdetection的安装
1. 首先查看一下gcc的版本,有的服务器gcc版本较低,导致后续安装出现问题(大部分无此问题)
gcc --version
官网v1.0rc0 (27/07/2019)版本要求GCC: 4.9/5.3/5.4/7.3
2.创建基本运行环境
注:这里使用anaconda管理项目环境,不建议使用Anaconda3-2019.03-Linux-x86_64.sh版本的anaconda,个人实验证明这个版本的anaconda在进行conda安装某些包时存在bug,需要更新conda.使用 Anaconda3-2019.07-Linux-x86_64.sh版本的anaconda时一切顺利。(mmdetection安装时可以不注意这个,不影响)
conda create -n open-mmlab python=3.7 -y
conda activate open-mmlab
conda install cython
安装pytorch官方链接:https://pytorch.org/
有的时候因网速过慢无法使用官方方式直接安装,这里提供了一个使用清华镜像的安装方式:
### 设置清华源镜像
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/free/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/
conda config --set show_channel_urls yes
### 设置pytorch镜像
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/pytorch/
conda config --add channels https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/peterjc123/
添加镜像后将官方安装的命令-c pytorch去掉,例如:
conda install pytorch torchvision cudatoolkit=9.0
3.安装mmdetection
安装mmcv:
git clone https://github.com/open-mmlab/mmcv.git
cd mmcv
pip install -e .
安装mmdetection:
git clone https://github.com/open-mmlab/mmdetection.git
cd mmdetection
python setup.py develop # or "pip install -v -e ."
安装mmdetection时若出现警告:cc1plus: warning: command line option ‘-Wstrict-prototypes’ is valid for C/ObjC but not for C++,按照https://note.qidong.name/2018/03/setup-warning-strict-prototypes/ 方式可以解决,当然你也可以忽略。
安装mmdetection时若出现此错误:libSM.so.6: cannot open shared,按照https://blog.csdn.net/fanzonghao/article/details/85112476。方式可以解决,注意把cuda-8改成你的cuda版本。
apt-get install tk-dev python-tk
sudo ldconfig /usr/local/cuda-9.0/lib64
第二部分:使用mmdetection训练自己的数据
1.mmdetection提控了一个CustomDataset数据格式用于训练自己的数据,首先附上自己csv格式数据转CustomDataset格式的代码:
'''
CSV annotation file transferred to mmdetection annotation file
@author: hobart
'''
# -*- coding: UTF-8 -*-
import os
import cv2
import numpy as np
import pandas as pd
def read_csv(ROOT_DIR, FILE_NAME):
''' Read the CSV file , put the image csv in data.
Args:
ROOT_DIR (str): csv file root_path
FILE_NAME (str): csv file name
Return (dict):
a dictionary contains ...
{"name1" : [[[x1],[y1],[x2],[y2]]],
"name2" : [[[x1],[y1],[x2],[y2]],[[x1],[y1],[x2],[y2]]]
}
'''
anns_train = pd.read_csv(os.path.join(ROOT_DIR, FILE_NAME))
data = {}
for _, row in anns_train.iterrows():
if row["name"] in data and row["label"] == "1":
data[row["name"]].append([row["x1"], row["y1"], row["x2"], row["y2"]])
else:
if row["label"] == "1":
data[row["name"]] = [[row["x1"], row["y1"], row["x2"], row["y2"]]]
else:
data[row["name"]] = []
return data
def generate_dataset(ROOT_DIR, annotations):
'''
Args (type):
ROOT_DIR (str)
annotations (dictionary):
{"name1" : [[[x1],[y1],[x2],[y2]]],
"name2" : [[[x1],[y1],[x2],[y2]],[[x1],[y1],[x2],[y2]]]
}
Return (type):
dataset (list):
[
{
'filename': 'a.jpg',
'width': 1280,
'height': 720,
'ann': {
'bboxes': <np.ndarray> (n, 4),
'labels': <np.ndarray> (n, ),
'bboxes_ignore': <np.ndarray> (k, 4),
'labels_ignore': <np.ndarray> (k, ) (optional field)
}
},
...
]
'''
dataset = []
for record in annotations:
image_path = os.path.join(ROOT_DIR, record)
image_name = os.path.basename(image_path)
img = cv2.imread(image_path)
height, width = img.shape[:2]
if len(annotations[record]) > 0:
labels = [1] * len(annotations[record])
labels = np.array(labels)
bboxes = annotations[record]
bboxes = np.array(bboxes)
else:
bboxes = np.zeros((0, 4))
labels = np.zeros((0,))
bboxes_ignore = np.zeros((0, 4))
labels_ignore = np.zeros((0,))
annotation = {
'filename': image_name,
'width': height,
'height': width,
'ann': {
'bboxes': bboxes.astype(np.float32),
'labels': labels.astype(np.int64),
'bboxes_ignore': bboxes_ignore.astype(np.float32),
'labels_ignore': labels_ignore.astype(np.int64)
}
}
dataset.append(annotation)
return dataset
def save_pkl(dataset, ROOT_DIR, OUTPUT_DIR, OUTPUT_FILENAME):
folder = os.path.join(ROOT_DIR, OUTPUT_DIR)
if not os.path.isdir(folder):
os.makedirs(folder)
out_file = os.path.join(ROOT_DIR, OUTPUT_DIR, OUTPUT_FILENAME)
import mmcv
mmcv.dump(dataset, out_file)
def main():
# parameter definition
ROOT_DIR = "csv/" # The root of all file
FILE_NAME = "test.csv"
OUTPUT_DIR = "pkl/"
OUTPUT_FILENAME = "val.pkl"
annotations = read_csv(ROOT_DIR, FILE_NAME)
dataset = generate_dataset(ROOT_DIR, annotations)
save_pkl(dataset, ROOT_DIR, OUTPUT_DIR, OUTPUT_FILENAME)
if __name__ == "__main__":
main()
print("do")
2.修改configs文件下的配置文件:
这里以retinanet_r50_fpn_1x.py为例。主要修改dataset_type以及所用的训练集验证集测试集的标注数据以及图像的地址,还有算法分类个数(目标类别书+背景类)。附上自己的配置文件
# model settings
model = dict(
type='RetinaNet',
pretrained='modelzoo://resnet50',
backbone=dict(
type='ResNet',
depth=50,
num_stages=4,
out_indices=(0, 1, 2, 3),
frozen_stages=1,
style='pytorch'),
neck=dict(
type='FPN',
in_channels=[256, 512, 1024, 2048],
out_channels=256,
start_level=1,
add_extra_convs=True,
num_outs=5),
bbox_head=dict(
type='RetinaHead',
num_classes=2,
in_channels=256,
stacked_convs=4,
feat_channels=256,
octave_base_scale=4,
scales_per_octave=3,
anchor_ratios=[0.5, 1.0, 2.0],
anchor_strides=[8, 16, 32, 64, 128],
target_means=[.0, .0, .0, .0],
target_stds=[1.0, 1.0, 1.0, 1.0],
loss_cls=dict(
type='FocalLoss',
use_sigmoid=True,
gamma=2.0,
alpha=0.25,
loss_weight=1.0),
loss_bbox=dict(type='SmoothL1Loss', beta=0.11, loss_weight=1.0)))
# training and testing settings
train_cfg = dict(
assigner=dict(
type='MaxIoUAssigner',
pos_iou_thr=0.5,
neg_iou_thr=0.4,
min_pos_iou=0,
ignore_iof_thr=-1),
allowed_border=-1,
pos_weight=-1,
debug=False)
test_cfg = dict(
nms_pre=1000,
min_bbox_size=0,
score_thr=0.05,
nms=dict(type='nms', iou_thr=0.5),
max_per_img=100)
# dataset settings
dataset_type = "CustomDataset"#'CocoDataset'
data_root = 'data'
img_norm_cfg = dict(
mean=[123.675, 116.28, 103.53], std=[58.395, 57.12, 57.375], to_rgb=True)
data = dict(
imgs_per_gpu=2,
workers_per_gpu=2,
train=dict(
type=dataset_type,
ann_file=data_root + 'pkl/txt.pkl',
img_prefix=data_root + 'picture/train/',
img_scale=(1333, 800),
img_norm_cfg=img_norm_cfg,
size_divisor=32,
flip_ratio=0.5,
with_mask=False,
with_crowd=False,
with_label=True),
val=dict(
type=dataset_type,
ann_file=data_root + 'pkl/val.pkl',
img_prefix=data_root + 'picture/val/',
img_scale=(1333, 800),
img_norm_cfg=img_norm_cfg,
size_divisor=32,
flip_ratio=0,
with_mask=False,
with_crowd=False,
with_label=True),
test=dict(
type=dataset_type,
ann_file=data_root + 'pkl/test.pkl',
img_prefix=data_root + 'picture/test/',
img_scale=(1333, 800),
img_norm_cfg=img_norm_cfg,
size_divisor=32,
flip_ratio=0,
with_mask=False,
with_crowd=False,
with_label=False,
test_mode=True))
# optimizer
optimizer = dict(type='SGD', lr=0.01, momentum=0.9, weight_decay=0.0001)
optimizer_config = dict(grad_clip=dict(max_norm=35, norm_type=2))
# learning policy
lr_config = dict(
policy='step',
warmup='linear',
warmup_iters=500,
warmup_ratio=1.0 / 3,
step=[8, 11])
checkpoint_config = dict(interval=1)
# yapf:disable
log_config = dict(
interval=50,
hooks=[
dict(type='TextLoggerHook'),
# dict(type='TensorboardLoggerHook')
])
# yapf:enable
# runtime settings
total_epochs = 12
device_ids = range(8)
dist_params = dict(backend='nccl')
log_level = 'INFO'
work_dir = './work_dirs/retinanet_r50_fpn_1x'
load_from = None
resume_from = None
workflow = [('train', 1)]
注意,如果实在docker上进行训练,若出现以下错误:ERROR: Unexpected bus error encountered in worker. This might be caused by insufficient shared memory (shm)
解决:在服务器上的docker中运行训练代码时,batch size设置得过大,shared memory不够(因为docker限制了shm).解决方法是,将Dataloader的num_workers设置为0.
3.训练
python tools/train.py ${CONFIG_FILE}