學習深度學習和計算機視覺,特別是目標檢測方向的學習者,一定聽說過Faster Rcnn;在目標檢測領域,Faster Rcnn表現出了極強的生命力,被大量的學習者學習,研究和工程應用。網上有很多版本的Faster RCNN的源碼,但是很多版本代碼太過於龐大,對新入門的學習者學習起來很不友好,在網上苦苦尋找了一番後終於找到了一個適合源碼學習的Faster Rcnn的pytorch版本代碼。
根據該版本的作者講該代碼除去注釋只有兩千行左右,並且經過小編的一番學習之後,發現該版本的代碼真的是非常的精簡幹練,讀起來「朗朗上口」,並且深刻的感覺到作者代碼功底之深厚。在此先附上源碼的地址(https://github.com/chenyuntc/simple-faster-rcnn-pytorch) ,並對源碼作者(陳雲)表示由衷的感謝和深深地敬意。
本文章主要的目的是對該版本代碼的主要框架進行梳理,希望能夠對一些想學習源碼的讀者有一定的幫助。
-data文件中主要是文件的與dataset相關的文件
-misc中有下載caffe版本預訓練模型的文件,可以不看
-model文件中主要是與構建Faster Rcnn網絡模型有關的文件
-utils中主要是一些輔助可視化和驗證的文件
-train.py是整個程序的運行文件,下面有一部分會做介紹
-trainer.py文件主要是用於訓練,模型的損失函數的計算都在這個文件中
train先來看一下train.py裡的主要內容:
def train(train(**kwargs)):
opt._parse(kwargs)
dataset = Dataset(opt)
dataloader = data_.DataLoader(dataset, \
batch_size=1, \
shuffle=True, \
num_workers=opt.num_workers)
testset = TestDataset(opt)
test_dataloader = data_.DataLoader(testset,
batch_size=1,
num_workers=opt.test_num_workers,
shuffle=False, \
pin_memory=True
)
faster_rcnn = FasterRCNNVGG16()
trainer = FasterRCNNTrainer(faster_rcnn).cuda()
for epoch in range(opt.epoch):
trainer.reset_meters()
for ii, (img, bbox_, label_, scale) in tqdm(enumerate(dataloader)):
scale = at.scalar(scale)
img, bbox, label = img.cuda().float(), bbox_.cuda(), label_.cuda()
trainer.train_step(img, bbox, label, scale)
從train.py中的主要函數可以看出,主要的步驟涉及訓練數據和測試數據的預處理,網絡模型的構建(Faster RCNN),然後就是迭代訓練,這也是通用的神經網絡搭建和訓練的過程。在Faster Rcnn網絡模型中主要包含Extractor、RPN和RoIhead三部分。網絡中Extractor主要是利用CNN進行特徵提取,網絡採用的VGG16;RPN是候選區網絡,為RoIHead模塊提供可能存在目標的候選區域(rois);RoIHead主要負責rois的分類和微調。整體的框架圖如下圖所示:
圖片來源於陳雲的知乎
Dataset在本版本的代碼中讀取的數據格式為VOC,Dataset和TestDataset類分別負責訓練數據和測試數據的讀取及預處理。在預處理部分主要的操作就是resize圖像的大小、像素值的處理以及圖像的隨機翻轉。主要的內容如下:
class Dataset:
def __init__(self, opt):
self.opt = opt
self.db = VOCBboxDataset(opt.voc_data_dir)
self.tsf = Transform(opt.min_size, opt.max_size)
def __getitem__(self, idx):
ori_img, bbox, label, difficult = self.db.get_example(idx)
img, bbox, label, scale = self.tsf((ori_img, bbox, label))
return img.copy(), bbox.copy(), label.copy(), scale
def __len__(self):
return len(self.db)
class TestDataset:
pass
下面主要介紹Extractor、RPN和RoIHead三部分結構
Extractorextractor, classifier = decom_vgg16()
Extractor部分主要使用的VGG16的網絡結構,同時使用預訓練好的模型提取圖片的特徵。論文中主要使用的是Caffe的預訓練模型,根據代碼的作者講該版本的預訓練模型效果比較好。
為了節約顯存,作者將前四層卷積層的學習率設置為0,Conv5_3的輸入作為圖片的特徵輸入到RPN網絡中。根據網絡結構,Conv5_3部分的感受野為16,也就是相較於輸入的圖片大小,feature map的尺寸為(C,H/16,W/16).該部分網絡結構圖如下所示:
具體的decom_vgg16()代碼如下:
def decom_vgg16():
if opt.caffe_pretrain:
model = vgg16(pretrained=False)
if not opt.load_path:
model.load_state_dict(t.load(opt.caffe_pretrain_path))
else:
model = vgg16(not opt.load_path)
features = list(model.features)[:30]
classifier = model.classifier
classifier = list(classifier)
del classifier[6]
if not opt.use_drop:
del classifier[5]
del classifier[2]
classifier = nn.Sequential(*classifier)
for layer in features[:10]:
for p in layer.parameters():
p.requires_grad = False
return nn.Sequential(*features), classifier
Faster RCNN中最突出的貢獻就是提出了Region Proposal Network(RPN),將候選區域提取的時間開銷幾乎降為0。該模塊的主要作用提供可能存在目標的候選區域rois。模塊結構圖如下所示:
圖片來源於陳雲的知乎
class RegionProposalNetwork(nn.Module):
def __init__():
def forward(self, x, img_size, scale=1.):
n, _, hh, ww = x.shape
anchor = _enumerate_shifted_anchor(
np.array(self.anchor_base),
self.feat_stride, hh, ww)
n_anchor = anchor.shape[0] // (hh * ww)
h = F.relu(self.conv1(x))
rpn_locs = self.loc(h)
rpn_locs = rpn_locs.permute(0, 2, 3, 1).contiguous().view(n, -1, 4)
rpn_scores = self.score(h)
rpn_scores = rpn_scores.permute(0, 2, 3, 1).contiguous()
rpn_softmax_scores = F.softmax(rpn_scores.view(n, hh, ww, n_anchor, 2), dim=4)
rpn_fg_scores = rpn_softmax_scores[:, :, :, :, 1].contiguous()
rpn_fg_scores = rpn_fg_scores.view(n, -1)
rpn_scores = rpn_scores.view(n, -1, 2)
rois = list()
roi_indices = list()
for i in range(n):
roi = self.proposal_layer(
rpn_locs[i].cpu().data.numpy(),
rpn_fg_scores[i].cpu().data.numpy(),
anchor, img_size,
scale=scale)
batch_index = i * np.ones((len(roi),), dtype=np.int32)
rois.append(roi)
roi_indices.append(batch_index)
rois = np.concatenate(rois, axis=0)
roi_indices = np.concatenate(roi_indices, axis=0)
return rpn_locs, rpn_scores, rois, roi_indices, anchor
RoIhead主要任務是對RPN網絡選出的候選框進行分類和回歸,在RoIhead中作者提出了RolPooling方法將不同尺度的候選區域全部pooling到一個尺度上。模塊結構圖如下所示:
class VGG16RoIHead(nn.Module):
def __init__(self, n_class, roi_size, spatial_scale,
classifier):
super(VGG16RoIHead, self).__init__()
self.classifier = classifier
self.cls_loc = nn.Linear(4096, n_class * 4)
self.score = nn.Linear(4096, n_class)
normal_init(self.cls_loc, 0, 0.001)
normal_init(self.score, 0, 0.01)
self.n_class = n_class
self.roi_size = roi_size
self.spatial_scale = spatial_scale
self.roi = RoIPooling2D(self.roi_size, self.roi_size, self.spatial_scale)
def forward(self, x, rois, roi_indices):
pool = self.roi(x, indices_and_rois)
pool = pool.view(pool.size(0), -1)
fc7 = self.classifier(pool)
roi_cls_locs = self.cls_loc(fc7)
roi_scores = self.score(fc7)
return roi_cls_locs, roi_scores
整體來說該版本的代碼環境相當簡單,配置起來相當容易,沒有什麼坑,認真閱讀作者的readme就好。在utils文件中有一個config.py文件,在裡邊可以修改文件讀取的路徑,學習率等參數,自己運行時根據自己的情況進行修改即可。小編運行自己的數據(非VOC2007)結果如下圖:
總結本篇文章主要的目的是推薦一個適合源碼學習的Faster rcnn版本給大家,並對代碼框架做了初步的介紹,希望對大家的源碼學習有一定的幫助,由於整個算法實現的代碼較為複雜,且細節比較多,很難通過一篇文章進行詳細的說明,如果大家對本版本的代碼感興趣,可以自己閱讀源碼學習。在學習源碼的時候我個人是有很多感想的,作為一個小白,通過源碼的學習真的學習到了很多,之前論文閱讀過幾遍,別的版本的代碼也拿來訓練過數據,但是讀了這個的源碼,又如發現了新大陸,很多算法的細節和精髓才算有了深刻的理解,真的是紙上得來終覺淺,絕知此事要coding。除了算法本身,在一些代碼的實現上也有很多的學習,真的感受到代碼作者的功力深厚,再次對作者表示深深地敬意.最後留個問題,在閱讀源碼的時候,發現作者使用了visdom進行可視化,如運行的截圖,小編還知道pytorch中一個可視化工具tensorboardX,但都不是很熟悉,還請知情人士在下方留言,詳細的講解一下兩種可視化工具的優劣。由於小編是一個剛入門(入坑)的學習者,文章中的不當之處還請大家諒解和提出,很希望能與大家一起討論學習。
最後再次放上源碼連結:
https://github.com/chenyuntc/simple-faster-rcnn-pytorch
參考:https://zhuanlan.zhihu.com/p/32404424
https://www.cnblogs.com/kerwins-AC/p/9734381.html