數值求和如何屏蔽掉時間
1090
2022-05-30
1、簡介
目前,目標檢測領域中主流的兩大類方法。
第一大類是 從非Deep時代就被廣泛應用的dense detector ,例如DPM,YOLO,RetinaNet,FCOS。在dense detector中,大量的object candidates例如sliding-windows,anchor-boxes, reference-points等被提前預設在圖像網格或者特征圖網格上,然后直接預測這些candidates到gt的scaling/offest和物體類別。
第二大類是 dense-to-sparse detector ,例如R-CNN家族。這類方法的特點是對一組sparse的candidates預測回歸和分類,而這組sparse的candidates來自于dense detector。
但是,dense屬性的一些固有局限總讓人難以滿意,比如:
NMS 后處理
many-to-one 正負樣本分配
prior candidates的設計
Sparse R-CNN拋棄了anchor boxes或者reference point等dense概念,直接從a sparse set of learnable proposals出發,沒有NMS后處理,整個網絡異常干凈和簡潔,可以看做是在dense(單階段),dense2sparse(二階段)之外的一個全新的檢測范式。
2、Detectron2介紹
Detectron2 是Facebook第二代檢測工具箱,支持目標檢測、實例分割、姿態估計、語義分割和全景分割等任務。
我們使用Detectron2很方便的實現模型的訓練、測試以及模型轉換。所以現在很多的新模型都是在Detectron2開發。
3、搭建Sparse R-CNN測試環境
我本地環境:
操作系統:win10、Cuda11.0。
3.1 創建虛擬環境
創建虛擬環境,并激活環境。
conda create --name sparsercnn python=3.7 activate sparsercnn conda install pytorch==1.7.1 torchvision==0.8.2 torchaudio==0.7.2 cudatoolkit=11.0 -c pytorch
1
2
3
3.2 安裝apex庫
APEX是英偉達開源的,完美支持PyTorch框架,用于改變數據格式來減小模型顯存占用的工具。其中最有價值的是amp(Automatic Mixed Precision),將模型的大部分操作都用Float16數據類型測試,一些特別操作仍然使用Float32。并且用戶僅僅通過三行代碼即可完美將自己的訓練代碼遷移到該模型。實驗證明,使用Float16作為大部分操作的數據類型,并沒有降低參數,在一些實驗中,反而由于可以增大Batch size,帶來精度上的提升,以及訓練速度上的提升。
3.2.1 下載apex庫
網址 https://github.com/NVIDIA/apex,下載到本地文件夾。解壓后進入到apex的目錄安裝依賴。在執行命令;
cd C:\Users\WH\Downloads\apex-master #進入apex目錄 pip install -r requirements.txt
1
2
3.2.2 安裝apex庫
依賴安裝完后,打開cmd,cd進入到剛剛下載完的apex-master路徑下,運行:
python setup.py install
1
然后跑了一堆東西,最后是這樣的:
安裝完成!
3.3 安裝fvcore庫
fvcore庫的簡介
fvcore是一個輕量級的核心庫,它提供了在各種計算機視覺框架(如Detectron2)中共享的最常見和最基本的功能。這個庫基于Python 3.6+和PyTorch。這個庫中的所有組件都經過了類型注釋、測試和基準測試。Facebook 的人工智能實驗室即FAIR的計算機視覺組負責維護這個庫。
github地址:https://github.com/facebookresearch/fvcore
執行命令
conda install -c fvcore -c iopath -c conda-forge fvcore
1
3.4 安裝Detectron2環境需要其他庫
安裝pycocotools
pip install pycocotools
1
安裝cv2
pip install opencv-python
1
安裝 antlr4
pip install antlr4-python3-runtime
1
安裝future
pip install future
1
安裝protobuf
pip install protobuf
1
安裝absl
pip install absl-py
1
安裝tensorboard
pip install tensorboard
1
安裝pydot
pip install pydot
1
安裝scipy
pip install scipy -i https://pypi.tuna.tsinghua.edu.cn/simple
1
3.5 編譯Sparse R-CNN
進入Sparse R-CNN目錄,目錄根據自己的實際情況更改。
cd D:\SparseR-CNN-main
1
編譯Detectron2和Sparse R-CNN
python setup.py build develop
1
看到如下信息,則表明安裝完成,如果缺少庫的情況,則需要安裝庫,再編譯,直到編譯成功!
4、測試環境
新建input_img和output_img文件夾,imgs文件夾存放待測試的圖片。
圖片如下:
下載模型“r50_100pro_3x_model.pth”(注:如果不能下載,關注我的公眾號獲取連接。),將其放到“projects/SparseRCNN”目錄下面。
執行命令:
python demo/demo.py --config-file projects/SparseRCNN/configs/sparsercnn.res50.100pro.3x.yaml --input imgs/*.jpg --output imgout --opts MODEL.WEIGHTS projects/SparseRCNN/r50_100pro_3x_model.pth
1
運行結果:
能夠運行demo說明環境已經沒有問題了。
5、制作數據集
本次采用的數據集是Labelme標注的數據集,地址:鏈接:https://pan.baidu.com/s/1nxo9-NpNWKK4PwDZqwKxGQ 提取碼:kp4e,需要將其轉為COCO格式的數據集。轉換代碼如下:
新建labelme2coco.py
import argparse import json import matplotlib.pyplot as plt import skimage.io as io import cv2 from labelme import utils import numpy as np import glob import PIL.Image REQUIRE_MASK = False labels = {'aircraft': 1, 'oiltank': 2} class labelme2coco(object): def __init__(self, labelme_json=[], save_json_path='./new.json'): ''' :param labelme_json: the list of all labelme json file paths :param save_json_path: the path to save new json ''' self.labelme_json = labelme_json self.save_json_path = save_json_path self.images = [] self.categories = [] self.annotations = [] # self.data_coco = {} self.label = [] self.annID = 1 self.height = 0 self.width = 0 self.require_mask = REQUIRE_MASK self.save_json() def data_transfer(self): for num, json_file in enumerate(self.labelme_json): if not json_file == self.save_json_path: with open(json_file, 'r') as fp: data = json.load(fp) self.images.append(self.image(data, num)) for shapes in data['shapes']: print("label is ") print(shapes['label']) label = shapes['label'] # if label[1] not in self.label: if label not in self.label: print("find new category: ") self.categories.append(self.categorie(label)) print(self.categories) # self.label.append(label[1]) self.label.append(label) points = shapes['points'] self.annotations.append(self.annotation(points, label, num)) self.annID += 1 def image(self, data, num): image = {} img = utils.img_b64_to_arr(data['imageData']) height, width = img.shape[:2] img = None image['height'] = height image['width'] = width image['id'] = num + 1 image['file_name'] = data['imagePath'].split('/')[-1] self.height = height self.width = width return image def categorie(self, label): categorie = {} categorie['supercategory'] = label # categorie['supercategory'] = label categorie['id'] = labels[label] # 0 默認為背景 categorie['name'] = label return categorie def annotation(self, points, label, num): annotation = {} print(points) x1 = points[0][0] y1 = points[0][1] x2 = points[1][0] y2 = points[1][1] contour = np.array([[x1, y1], [x2, y1], [x2, y2], [x1, y2]]) # points = [[x1, y1], [x2, y2]] for rectangle contour = contour.astype(int) area = cv2.contourArea(contour) print("contour is ", contour, " area = ", area) annotation['segmentation'] = [list(np.asarray([[x1, y1], [x2, y1], [x2, y2], [x1, y2]]).flatten())] # [list(np.asarray(contour).flatten())] annotation['iscrowd'] = 0 annotation['area'] = area annotation['image_id'] = num + 1 if self.require_mask: annotation['bbox'] = list(map(float, self.getbbox(points))) else: x1 = points[0][0] y1 = points[0][1] width = points[1][0] - x1 height = points[1][1] - y1 annotation['bbox'] = list(np.asarray([x1, y1, width, height]).flatten()) annotation['category_id'] = self.getcatid(label) annotation['id'] = self.annID return annotation def getcatid(self, label): for categorie in self.categories: # if label[1]==categorie['name']: if label == categorie['name']: return categorie['id'] return -1 def getbbox(self, points): polygons = points mask = self.polygons_to_mask([self.height, self.width], polygons) return self.mask2box(mask) def mask2box(self, mask): # np.where(mask==1) index = np.argwhere(mask == 1) rows = index[:, 0] clos = index[:, 1] left_top_r = np.min(rows) # y left_top_c = np.min(clos) # x right_bottom_r = np.max(rows) right_bottom_c = np.max(clos) return [left_top_c, left_top_r, right_bottom_c - left_top_c, right_bottom_r - left_top_r] def polygons_to_mask(self, img_shape, polygons): mask = np.zeros(img_shape, dtype=np.uint8) mask = PIL.Image.fromarray(mask) xy = list(map(tuple, polygons)) PIL.ImageDraw.Draw(mask).polygon(xy=xy, outline=1, fill=1) mask = np.array(mask, dtype=bool) return mask def data2coco(self): data_coco = {} data_coco['images'] = self.images data_coco['categories'] = self.categories data_coco['annotations'] = self.annotations return data_coco def save_json(self): print("in save_json") self.data_transfer() self.data_coco = self.data2coco() print(self.save_json_path) json.dump(self.data_coco, open(self.save_json_path, 'w'), indent=4) labelme_json = glob.glob('LabelmeData/*.json') from sklearn.model_selection import train_test_split trainval_files, test_files = train_test_split(labelme_json, test_size=0.2, random_state=55) import os if not os.path.exists("projects/SparseRCNN/datasets/coco/annotations"): os.makedirs("projects/SparseRCNN/datasets/coco/annotations/") if not os.path.exists("projects/SparseRCNN/datasets/coco/train2017"): os.makedirs("projects/SparseRCNN/datasets/coco/train2017") if not os.path.exists("projects/SparseRCNN/datasets/coco/val2017"): os.makedirs("projects/SparseRCNN/datasets/coco/val2017") labelme2coco(trainval_files, 'projects/SparseRCNN/datasets/coco/annotations/instances_train2017.json') labelme2coco(test_files, 'projects/SparseRCNN/datasets/coco/annotations/instances_val2017.json') import shutil for file in trainval_files: shutil.copy(os.path.splitext(file)[0] + ".jpg", "projects/SparseRCNN/datasets/coco/train2017/") for file in test_files: shutil.copy(os.path.splitext(file)[0] + ".jpg", "projects/SparseRCNN/datasets/coco/val2017/")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
6、配置訓練環境
6.1 更改預訓練模型的size
在projects/SparseRCNN目錄,新建change_model_size.py文件
import torch import numpy as np import pickle num_class = 2 pretrained_weights = torch.load('r50_100pro_3x_model.pth') pretrained_weights["head.head_series.0.class_logits.weight"].resize_(num_class,256) pretrained_weights["head.head_series.0.class_logits.bias"].resize_(num_class) pretrained_weights["head.head_series.1.class_logits.weight"].resize_(num_class,256) pretrained_weights["head.head_series.1.class_logits.bias"].resize_(num_class) pretrained_weights["head.head_series.2.class_logits.weight"].resize_(num_class,256) pretrained_weights["head.head_series.2.class_logits.bias"].resize_(num_class) pretrained_weights["head.head_series.3.class_logits.weight"].resize_(num_class,256) pretrained_weights["head.head_series.3.class_logits.bias"].resize_(num_class) pretrained_weights["head.head_series.4.class_logits.weight"].resize_(num_class,256) pretrained_weights["head.head_series.4.class_logits.bias"].resize_(num_class) pretrained_weights["head.head_series.5.class_logits.weight"].resize_(num_class,256) pretrained_weights["head.head_series.5.class_logits.bias"].resize_(num_class) torch.save(pretrained_weights, "model_%d.pth"%num_class)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
這個文件的目的是修改模型輸出的size,numclass按照本次打算訓練的數據集的類別設置。
6.2 修改config參數
路徑:“detectron2/engine/defaults.py”
–config-file:模型的配置文件,SparseRCNN的模型配置文件放在“projects/SparseRCNN/configs”下面。名字和預訓練模型對應。
parser.add_argument("--config-file", default="./configs/sparsercnn.res50.100pro.3x.yaml", metavar="FILE", help="path to config file")
1
resume 是否再次,訓練,如果設置為true,則接著上次訓練的結果訓練。所以第一次訓練不用設置。
parser.add_argument( "--resume", action="store_true", help="Whether to attempt to resume from the checkpoint directory. " "See documentation of `DefaultTrainer.resume_or_load()` for what it means.", )
1
2
3
4
5
6
–num-gpus,gpu的個數,如果只有一個設置為1,如果有多個,可以自己設置想用的個數。
parser.add_argument("--num-gpus", type=int, default=1, help="number of gpus *per machine*")
1
opts指的是yaml文件的參數。
上面的參數可以設置,也可以不設置,設置之后可以直接運行不用再考慮設置參數,如果不設置每次訓練的時候配置一次參數。
修改類別,文件路徑“projects/SparseRCNN/config.py”,
cfg.MODEL.SparseRCNN.NUM_CLASSES = 2
1
修改yaml文件參數
sparsercnn.res50.100pro.3x.yaml中修改預訓練模型的路徑。
WEIGHTS: "model_2.pth"
1
BASE_LR:設置學習率。
STEPS:設置訓練多少步之后調整學習率。
MAX_ITER:最大迭代次數。
CHECKPOINT_PERIOD:設置迭代多少次保存一次模型
IMS_PER_BATCH:batchsize的大小,根據顯存大小設置。
NUM_CLASSES:數據集中物體類別的種類。
NUM_PROPOSALS:提議框的個數。
BASE_LR: 0.00025 #在Base-SparseRCNN.yaml中 IMS_PER_BATCH: 2#在Base-SparseRCNN.yaml中 NUM_CLASSES:2 STEPS: (21000, 25000) MAX_ITER: 54000 CHECKPOINT_PERIOD: 5000
1
2
3
4
5
6
6.3 修改train_net.py
主要修改該setup函數,增加數據集注冊。
NUM_CLASSES=2 def setup(args): """ Create configs and perform basic setups. """ register_coco_instances("train", {}, "datasets/coco/annotations/instances_train2017.json", "datasets/coco/train2017") register_coco_instances("test", {}, "datasets/coco/annotations/instances_val2017.json", "datasets/coco/val2017") cfg = get_cfg() add_sparsercnn_config(cfg) cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) cfg.DATASETS.TRAIN = ("train",) cfg.DATASETS.TEST = ("test",) cfg.MODEL.SparseRCNN.NUM_CLASSES = NUM_CLASSES cfg.MODEL.ROI_HEADS.NUM_CLASSES=NUM_CLASSES cfg.freeze() default_setup(cfg, args) return cfg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
還要修改detectron2/engine/launch.py,在launch函數下面增加一句
dist.init_process_group('gloo', init_method='file://tmp/somefile', rank=0, world_size=1)
1
如下圖:
這句話的作用是初始化分布式訓練,因為我們沒有使用分布式,所以沒有初始化,但是不初始化就會報錯,所以加上這句。
7、訓練
兩種啟動方式:
第一種,命令行:進入“projects/SparseRCNN/”目錄下,執行:
python train_net.py
1
第二種,直接在pycharm 直接運行train_net.py.
訓練結果:
從訓練結果上看,效果確實不錯,和CenterNet2的結果相差不大,不過模型很大,大約有1.2G,比CenterNet2的模型大了一倍多。
8、測試
修改demo/demo.py
8.1 修改setup_cfg函數
在紅框的位置增加代碼,詳細如下面的代碼。
NUM_CLASSES=2 def setup_cfg(args): # load config from file and command-line arguments cfg = get_cfg() from projects.SparseRCNN.sparsercnn import add_sparsercnn_config add_sparsercnn_config(cfg) cfg.MODEL.SparseRCNN.NUM_CLASSES = NUM_CLASSES cfg.MODEL.ROI_HEADS.NUM_CLASSES = NUM_CLASSES cfg.merge_from_file(args.config_file) cfg.merge_from_list(args.opts) # Set score_threshold for builtin models cfg.MODEL.RETINANET.SCORE_THRESH_TEST = args.confidence_threshold cfg.MODEL.ROI_HEADS.SCORE_THRESH_TEST = args.confidence_threshold cfg.MODEL.PANOPTIC_FPN.COMBINE.INSTANCES_CONFIDENCE_THRESH = args.confidence_threshold cfg.freeze() return cfg
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
8.2 修改顯示類別
在demo/predictor.py
代碼:
visualizer.metadata.thing_classes[:10] = ["aircraft", "oiltank"]
1
然后進入SparseR-CNN-main目錄,執行如下命令:
python demo/demo.py --config-file projects/SparseRCNN/configs/sparsercnn.res50.100pro.3x.yaml --input img/*jpg --output out --opts MODEL.WEIGHTS projects/Spa rseRCNN/output/model_final.pth
1
2
3
運行結果:
機器學習 深度學習 神經網絡
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。