目標(biāo)檢測(cè)模型NanoDet(超輕量,速度很快)介紹和PyTorch版本實(shí)踐
前言
YOLO、SSD、Fast R-CNN等模型在目標(biāo)檢測(cè)方面速度較快和精度較高,但是這些模型比較大,不太適合移植到移動(dòng)端或嵌入式設(shè)備;輕量級(jí)模型 NanoDet-m,對(duì)單階段檢測(cè)模型三大模塊(Head、Neck、Backbone)進(jìn)行輕量化,目標(biāo)加檢測(cè)速度很快;模型文件大小僅幾兆(小于4M)。
NanoDet作者開源代碼地址:https://github.com/RangiLyu/nanodet (致敬)
先看一下NanoDet目標(biāo)檢測(cè)的效果:
同時(shí)檢測(cè)多輛汽車:
查看多目標(biāo)、目標(biāo)之間重疊、同時(shí)存在小目標(biāo)和大目標(biāo)的檢測(cè)效果:
NanoDet 模型介紹
NanoDet 是一種 FCOS 式的單階段 anchor-free 目標(biāo)檢測(cè)模型,它使用 ATSS 進(jìn)行目標(biāo)采樣,使用 Generalized Focal Loss 損失函數(shù)執(zhí)行分類和邊框回歸(box regression)。
1)NanoDet 模型性能
NanoDet-m模型和YoloV3-Tiny、YoloV4-Tiny作對(duì)比:
ModelResolutionCOCO mAPLatency(ARM 4xCore)FLOPSParams|Model Size(ncnn bin) |------ NanoDet-m320*32020.610.23ms0.72B0.95M|1.8mb NanoDet-m416*41621.716.44ms1.2B0.95M|1.8mb YoloV3-Tiny416*41616.637.6ms5.62B8.86M|33.7mb YoloV4-Tiny416*41621.732.81ms6.96B6.06M|23.0mb備注:以上性能基于 ncnn 和麒麟 980 (4xA76+4xA55) ARM CPU 獲得的。使用 COCO mAP (0.5:0.95) 作為評(píng)估指標(biāo),兼顧檢測(cè)和定位的精度,在 COCO val 5000 張圖片上測(cè)試,并且沒(méi)有使用 Testing-Time-Augmentation。
NanoDet作者將 ncnn 部署到手機(jī)(基于 ARM 架構(gòu)的 CPU 麒麟 980,4 個(gè) A76 核心和 4 個(gè) A55 核心)上之后跑了一下 benchmark,模型前向計(jì)算時(shí)間只要 10 毫秒左右,而 yolov3 和 v4 tiny 均在 30 毫秒的量級(jí)。在安卓攝像頭 demo app 上,算上圖片預(yù)處理、檢測(cè)框后處理以及繪制檢測(cè)框的時(shí)間,NanoDet 也能輕松跑到 40+FPS。
2)NanoDet 模型架構(gòu)
3)NanoDet損失函數(shù)
NanoDet 使用了李翔等人提出的 Generalized Focal Loss 損失函數(shù)。該函數(shù)能夠去掉 FCOS 的 Centerness 分支,省去這一分支上的大量卷積,從而減少檢測(cè)頭的計(jì)算開銷,非常適合移動(dòng)端的輕量化部署。
詳細(xì)請(qǐng)參考:
4)NanoDet 優(yōu)勢(shì)
NanoDet 是一個(gè)速度超快和輕量級(jí)的移動(dòng)端 Anchor-free 目標(biāo)檢測(cè)模型。該模型具備以下優(yōu)勢(shì):
-
超輕量級(jí):模型文件大小僅幾兆(小于4M——nanodet_m.pth);- 速度超快:在移動(dòng) ARM CPU 上的速度達(dá)到 97fps(10.23ms);- 訓(xùn)練友好:GPU 內(nèi)存成本比其他模型低得多。GTX1060 6G 上的 Batch-size 為 80 即可運(yùn)行;- 方便部署:提供了基于 ncnn 推理框架的 C++ 實(shí)現(xiàn)和 Android demo。
基于PyTorch 實(shí)現(xiàn)NanoDet
基于NanoDet項(xiàng)目進(jìn)行小裁剪,專門用來(lái)實(shí)現(xiàn)Python語(yǔ)言、PyTorch 版本的代碼地址:
1)NanoDet目標(biāo)檢測(cè)效果
1)檢測(cè)出一只小鳥
2)同時(shí)檢測(cè)出四位少年
3)在復(fù)雜街道中,檢測(cè)出行人、汽車:
通過(guò)測(cè)試發(fā)現(xiàn)NanoDet確實(shí)很快,但識(shí)別精度和效果比YOLOv4差不少的。
下圖是使用 YOLOv4檢測(cè)的效果:
文章鏈接:
大家可以看到大部分的行人、汽車是被檢測(cè)出來(lái)了,存在小部分沒(méi)有被檢測(cè)出來(lái);
看左上角的統(tǒng)計(jì)信息,能看到汽車檢測(cè)到5輛,行人檢測(cè)到14位,自行車檢測(cè)到1輛,交通燈設(shè)備3臺(tái),太陽(yáng)傘3把,手提包1個(gè)。
2)環(huán)境參數(shù)
測(cè)試環(huán)境參數(shù)
系統(tǒng):Windows ** 編程語(yǔ)言**:Python 3.8 整合開發(fā)環(huán)境:Anaconda深度學(xué)習(xí)框架:PyTorch1.7.0+cu101 (torch>=1.3 即可) 開發(fā)代碼IDE:PyCharm
開發(fā)具體環(huán)境要求如下:
-
Cython- termcolor- numpy- torch>=1.3- torchvision- tensorboard- pycocotools- matplotlib- pyaml- opencv-python- tqdm
通常測(cè)試感覺(jué)GPU加速(顯卡驅(qū)動(dòng)、cudatoolkit 、cudnn)、PyTorch、pycocotools相對(duì)難裝一點(diǎn)
Windows開發(fā)環(huán)境安裝可以參考:
安裝cudatoolkit 10.1、cudnn7.6請(qǐng)參考
安裝PyTorch請(qǐng)參考
安裝pycocotools請(qǐng)參考
3)體驗(yàn)NanoDet目標(biāo)檢測(cè)
下載代碼,打開工程
先到githug下載代碼,然后解壓工程,然后使用PyCharm工具打開工程;
githug代碼下載地址:
說(shuō)明:該代碼是基于NanoDet項(xiàng)目進(jìn)行小裁剪,專門用來(lái)實(shí)現(xiàn)Python語(yǔ)言、PyTorch 版本的代碼NanoDet作者開源代碼地址: (致敬)
使用PyCharm工具打開工程
打開后的頁(yè)面是這樣的:
【選擇開發(fā)環(huán)境】
文件(file)——>設(shè)置(setting)——>項(xiàng)目(Project)——>Project Interpreters 選擇搭建的開發(fā)環(huán)境;
然后先點(diǎn)擊Apply,等待加載完成,再點(diǎn)擊OK;
進(jìn)行目標(biāo)檢測(cè)
具體命令請(qǐng)參考:
'''目標(biāo)檢測(cè)-圖片'''
python detect_main.py image --config ./config/nanodet-m.yml --model model/nanodet_m.pth --path street.png
'''目標(biāo)檢測(cè)-視頻文件'''
python detect_main.py video --config ./config/nanodet-m.yml --model model/nanodet_m.pth --path test.mp4
'''目標(biāo)檢測(cè)-攝像頭'''
python detect_main.py webcam --config ./config/nanodet-m.yml --model model/nanodet_m.pth --path 0
【目標(biāo)檢測(cè)-圖片】
【目標(biāo)檢測(cè)-視頻文件】
檢測(cè)的是1080*1920的圖片,很流暢毫不卡頓,就是目前識(shí)別精度不太高
4)調(diào)用模型的核心代碼
detect_main.py 代碼:
import cv2
import os
import time
import torch
import argparse
from nanodet.util import cfg, load_config, Logger
from nanodet.model.arch import build_model
from nanodet.util import load_model_weight
from nanodet.data.transform import Pipeline
image_ext = ['.jpg', '.jpeg', '.webp', '.bmp', '.png']
video_ext = ['mp4', 'mov', 'avi', 'mkv']
'''目標(biāo)檢測(cè)-圖片'''
# python detect_main.py image --config ./config/nanodet-m.yml --model model/nanodet_m.pth --path street.png
'''目標(biāo)檢測(cè)-視頻文件'''
# python detect_main.py video --config ./config/nanodet-m.yml --model model/nanodet_m.pth --path test.mp4
'''目標(biāo)檢測(cè)-攝像頭'''
# python detect_main.py webcam --config ./config/nanodet-m.yml --model model/nanodet_m.pth --path 0
def parse_args():
parser = argparse.ArgumentParser()
parser.add_argument('demo', default='image', help='demo type, eg. image, video and webcam')
parser.add_argument('--config', help='model config file path')
parser.add_argument('--model', help='model file path')
parser.add_argument('--path', default='./demo', help='path to images or video')
parser.add_argument('--camid', type=int, default=0, help='webcam demo camera id')
args = parser.parse_args()
return args
class Predictor(object):
def __init__(self, cfg, model_path, logger, device='cuda:0'):
self.cfg = cfg
self.device = device
model = build_model(cfg.model)
ckpt = torch.load(model_path, map_location=lambda storage, loc: storage)
load_model_weight(model, ckpt, logger)
self.model = model.to(device).eval()
self.pipeline = Pipeline(cfg.data.val.pipeline, cfg.data.val.keep_ratio)
def inference(self, img):
img_info = {}
if isinstance(img, str):
img_info['file_name'] = os.path.basename(img)
img = cv2.imread(img)
else:
img_info['file_name'] = None
height, width = img.shape[:2]
img_info['height'] = height
img_info['width'] = width
meta = dict(img_info=img_info,
raw_img=img,
img=img)
meta = self.pipeline(meta, self.cfg.data.val.input_size)
meta['img'] = torch.from_numpy(meta['img'].transpose(2, 0, 1)).unsqueeze(0).to(self.device)
with torch.no_grad():
results = self.model.inference(meta)
return meta, results
def visualize(self, dets, meta, class_names, score_thres, wait=0):
time1 = time.time()
self.model.head.show_result(meta['raw_img'], dets, class_names, score_thres=score_thres, show=True)
print('viz time: {:.3f}s'.format(time.time()-time1))
def get_image_list(path):
image_names = []
for maindir, subdir, file_name_list in os.walk(path):
for filename in file_name_list:
apath = os.path.join(maindir, filename)
ext = os.path.splitext(apath)[1]
if ext in image_ext:
image_names.append(apath)
return image_names
def main():
args = parse_args()
torch.backends.cudnn.enabled = True
torch.backends.cudnn.benchmark = True
load_config(cfg, args.config)
logger = Logger(-1, use_tensorboard=False)
predictor = Predictor(cfg, args.model, logger, device='cuda:0')
logger.log('Press "Esc", "q" or "Q" to exit.')
if args.demo == 'image':
if os.path.isdir(args.path):
files = get_image_list(args.path)
else:
files = [args.path]
files.sort()
for image_name in files:
meta, res = predictor.inference(image_name)
predictor.visualize(res, meta, cfg.class_names, 0.35)
ch = cv2.waitKey(0)
if ch == 27 or ch == ord('q') or ch == ord('Q'):
break
elif args.demo == 'video' or args.demo == 'webcam':
cap = cv2.VideoCapture(args.path if args.demo == 'video' else args.camid)
while True:
ret_val, frame = cap.read()
meta, res = predictor.inference(frame)
predictor.visualize(res, meta, cfg.class_names, 0.35)
ch = cv2.waitKey(1)
if ch == 27 or ch == ord('q') or ch == ord('Q'):
break
if __name__ == '__main__':
main()


Python“寶藏級(jí)”公眾號(hào)【Python之王】專注于Python領(lǐng)域,會(huì)爬蟲,數(shù)分,C++,tensorflow和Pytorch等等。
近 2年共原創(chuàng) 100+ 篇技術(shù)文章。創(chuàng)作的精品文章系列有:
日常收集整理了一批不錯(cuò)的 Python 學(xué)習(xí)資料,有需要的小伙可以自行免費(fèi)領(lǐng)取。
獲取方式如下:公眾號(hào)回復(fù)資料。領(lǐng)取Python等系列筆記,項(xiàng)目,書籍,直接套上模板就可以用了。資料包含算法、python、算法小抄、力扣刷題手冊(cè)和 C++ 等學(xué)習(xí)資料!
