1.研究背景與意義
隨著城市交通的不斷發(fā)展和車(chē)輛數(shù)量的不斷增加,交通信號(hào)燈的識(shí)別系統(tǒng)變得越來(lái)越重要。交通信號(hào)燈的準(zhǔn)確識(shí)別對(duì)于交通管理检眯、智能交通系統(tǒng)以及自動(dòng)駕駛等領(lǐng)域具有重要意義关炼。然而,由于交通信號(hào)燈的形狀贸桶、顏色和光照條件的變化舅逸,以及復(fù)雜的交通場(chǎng)景,交通信號(hào)燈的準(zhǔn)確識(shí)別一直是一個(gè)具有挑戰(zhàn)性的問(wèn)題皇筛。
目前琉历,基于深度學(xué)習(xí)的目標(biāo)檢測(cè)算法YOLO(You Only Look Once)已經(jīng)在圖像識(shí)別領(lǐng)域取得了顯著的成果。然而,對(duì)于交通信號(hào)燈的識(shí)別旗笔,YOLO算法在準(zhǔn)確性和實(shí)時(shí)性方面仍然存在一定的局限性彪置。另外,OpenCV作為一個(gè)開(kāi)源的計(jì)算機(jī)視覺(jué)庫(kù)蝇恶,提供了豐富的圖像處理和計(jì)算機(jī)視覺(jué)算法拳魁,但是在交通信號(hào)燈識(shí)別方面的應(yīng)用還比較有限。
因此撮弧,本研究旨在改進(jìn)YOLO和OpenCV的交通信號(hào)燈識(shí)別系統(tǒng)潘懊,提高其準(zhǔn)確性和實(shí)時(shí)性,并提供部署教程和源碼想虎,以便更多的研究人員和工程師能夠使用和改進(jìn)該系統(tǒng)卦尊。
本研究的意義主要體現(xiàn)在以下幾個(gè)方面:
提高交通信號(hào)燈識(shí)別的準(zhǔn)確性:通過(guò)改進(jìn)YOLO算法,引入更多的訓(xùn)練數(shù)據(jù)和優(yōu)化網(wǎng)絡(luò)結(jié)構(gòu)舌厨,可以提高交通信號(hào)燈識(shí)別的準(zhǔn)確性岂却。準(zhǔn)確的交通信號(hào)燈識(shí)別可以為交通管理和智能交通系統(tǒng)提供更可靠的數(shù)據(jù)支持,從而提高交通效率和安全性裙椭。
提高交通信號(hào)燈識(shí)別的實(shí)時(shí)性:通過(guò)優(yōu)化算法和使用高性能的硬件設(shè)備躏哩,可以提高交通信號(hào)燈識(shí)別系統(tǒng)的實(shí)時(shí)性。實(shí)時(shí)的交通信號(hào)燈識(shí)別可以為自動(dòng)駕駛系統(tǒng)提供及時(shí)的決策依據(jù)揉燃,從而提高自動(dòng)駕駛的安全性和可靠性扫尺。
提供部署教程和源碼:通過(guò)提供部署教程和源碼,可以幫助更多的研究人員和工程師快速理解和使用該交通信號(hào)燈識(shí)別系統(tǒng)炊汤。這將促進(jìn)交通信號(hào)燈識(shí)別技術(shù)的傳播和應(yīng)用正驻,為交通管理和智能交通系統(tǒng)的發(fā)展做出貢獻(xiàn)。
總之抢腐,改進(jìn)YOLO和OpenCV的交通信號(hào)燈識(shí)別系統(tǒng)具有重要的實(shí)際意義和應(yīng)用價(jià)值姑曙。通過(guò)提高識(shí)別準(zhǔn)確性和實(shí)時(shí)性,并提供部署教程和源碼迈倍,可以推動(dòng)交通信號(hào)燈識(shí)別技術(shù)的發(fā)展伤靠,為交通管理、智能交通系統(tǒng)和自動(dòng)駕駛等領(lǐng)域提供更好的解決方案啼染。
2.圖片演示
3.視頻演示
改進(jìn)YOLO和OpenCV的交通信號(hào)燈識(shí)別系統(tǒng)(部署教程&源碼)_嗶哩嗶哩_bilibili
4.BiFPN簡(jiǎn)介
在本節(jié)中宴合,我們首先闡述了多尺度特征融合問(wèn)題,然后介紹了我們提出的BiFPN的主要思想:有效的雙向交叉尺度連接和加權(quán)特征融合迹鹅。
4.1 問(wèn)題闡述
多尺度特征融合旨在對(duì)不同分辨率下的特征進(jìn)行聚合卦洽。形式上,給出了多尺度特征的列表
in = (Pin, Pin ,..)
pl代表所在層的特征斜棚,in表示輸出逐样,out表示輸出
我們的目標(biāo)是找到一個(gè)能有效聚合不同特征并輸出新特征列表的變換
f:out = f( pin)
如下圖所示:
4.2 跨尺度連接
該博客在CVPR 2017的FPN指出了不同層之間特征融合的重要性,并且以一種比較簡(jiǎn)單,Heuristic的方法把底層的特征乘兩倍和淺層相加來(lái)融合脂新。之后人們也試了各種別的融合方法挪捕,比如PANet先從底?上連,再自頂向下連回去(上圖b);NAS-FPN采用神經(jīng)架構(gòu)搜索來(lái)搜索更好的跨尺度特征網(wǎng)絡(luò)難拓?fù)湔悖窃谒阉鬟^(guò)程中需要花費(fèi)數(shù)千個(gè)GPU小時(shí)级零,并且發(fā)現(xiàn)的網(wǎng)絡(luò)不規(guī)則,難以解釋或修改滞乙,如上圖( c )所示奏纪。總之上述都是一些人工各種連接的設(shè)計(jì)斩启,包含Conv序调,Sum,Concatenate,Resize,Skip Connection等候選操作兔簇。很明顯使用哪些操作发绢、操作之間的順序是可以用NAS搜的。
通過(guò)研究這三種網(wǎng)絡(luò)的性能和效率(如下表)垄琐,我們觀察到PANet比FPN和NAS-FPN具有更好的精度边酒,但需要更多的參數(shù)和計(jì)算。
PANet效果好于FPN和NAS-FPN,計(jì)算代價(jià)也更高;
為了提高模型的效率狸窘,該論文提出了幾種跨尺度連接的優(yōu)化方法:
首先墩朦,我們刪除那些只有一個(gè)輸入邊的節(jié)點(diǎn)。我們認(rèn)為:如果一個(gè)節(jié)點(diǎn)只有一條輸入邊而沒(méi)有特征融合翻擒,那么它對(duì)融合不同特征的特征網(wǎng)絡(luò)的貢獻(xiàn)就會(huì)更小氓涣。所以我們移除了PANet中P3,P7的中間結(jié)點(diǎn),這就導(dǎo)致了一個(gè)簡(jiǎn)化的雙向網(wǎng)絡(luò);
其次陋气,我們添加一個(gè)跳躍連接春哨,在同一尺度的輸入節(jié)點(diǎn)到輸出節(jié)點(diǎn)之間加一個(gè)跳躍連接,因?yàn)樗鼈冊(cè)谙嗤瑢佣魉牛诓辉黾犹嘤?jì)算成本的同時(shí),融合了更多的特征椰拒。得到上圖(d);
最后晶渠,與PANet僅有一個(gè)自頂向下和一個(gè)自底向上的路徑不同,我們將每個(gè)雙向(自頂向下和自底向上)路徑看作一個(gè)特征網(wǎng)絡(luò)層(repeated blocks),并多次重復(fù)同一層燃观,以實(shí)現(xiàn)更高層次的特征融合褒脯。
第7節(jié)將討論如何使用YOLOv5引入BIFPN。經(jīng)過(guò)這些優(yōu)化缆毁,我們將新的特征網(wǎng)絡(luò)命名為雙向特征金字塔網(wǎng)絡(luò)( BiFPN )番川。
加權(quán)特征融合(Weighted Feature Fusion)
當(dāng)融合具有不同分辨率的特征時(shí),一種常見(jiàn)的方法是首先將它們調(diào)整到相同的分辨率,然后對(duì)它們進(jìn)行融合颁督。金字塔注意網(wǎng)絡(luò)[Hanchao Li, Pengfei Xiong, Jie An, and Lingxue Wang. Pyramidattention networks.BMVC, 2018]引入全局自注意上采樣來(lái)恢復(fù)像素定位践啄,在Nas-fpn中也有進(jìn)一步研究。以前的所有方法都一視同仁地對(duì)待所有輸入特性沉御。然而屿讽,我們觀察到,由于不同的輸入
特征具有不同的分辨率吠裆,它們通常對(duì)輸出特征的貢獻(xiàn)是不平等的伐谈。為了解天這個(gè)口熱,華有建以種每個(gè)輸入增加一個(gè)額外的權(quán)重试疙,并讓網(wǎng)絡(luò)學(xué)習(xí)每個(gè)輸入特性的重要性诵棵。基于這一思想祝旷,考慮了三種加權(quán)融合方法履澳。
5.核心代碼講解
5.1 Interface.py
class YOLOv5Detector:
def __init__(self, weights, data, device='', half=False, dnn=False):
self.weights = weights
self.data = data
self.device = device
self.half = half
self.dnn = dnn
def load_model(self):
FILE = Path(__file__).resolve()
ROOT = FILE.parents[0] # YOLOv5 root directory
if str(ROOT) not in sys.path:
sys.path.append(str(ROOT)) # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
# Load model
device = self.select_device(self.device)
model = self.DetectMultiBackend(self.weights, device=device, dnn=self.dnn, data=self.data)
stride, names, pt, jit, onnx, engine = model.stride, model.names, model.pt, model.jit, model.onnx, model.engine
# Half
half &= (pt or jit or onnx or engine) and device.type != 'cpu' # FP16 supported on limited backends with CUDA
if pt or jit:
model.model.half() if half else model.model.float()
return model, stride, names, pt, jit, onnx, engine
def run(self, model, img, stride, pt, imgsz=(640, 640), conf_thres=0.25, iou_thres=0.45, max_det=1000,
device='', classes=None, agnostic_nms=False, augment=False, half=False):
cal_detect = []
device = self.select_device(device)
names = model.module.names if hasattr(model, 'module') else model.names # get class names
# Set Dataloader
im = self.letterbox(img, imgsz, stride, pt)[0]
# Convert
im = im.transpose((2, 0, 1))[::-1] # HWC to CHW, BGR to RGB
im = np.ascontiguousarray(im)
im = torch.from_numpy(im).to(device)
im = im.half() if half else im.float() # uint8 to fp16/32
im /= 255 # 0 - 255 to 0.0 - 1.0
if len(im.shape) == 3:
im = im[None] # expand for batch dim
pred = model(im, augment=augment)
pred = self.non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det=max_det)
# Process detections
for i, det in enumerate(pred): # detections per image
if len(det):
# Rescale boxes from img_size to im0 size
det[:, :4] = self.scale_coords(im.shape[2:], det[:, :4], img.shape).round()
# Write results
for *xyxy, conf, cls in reversed(det):
c = int(cls) # integer class
label = f'{names[c]}'
cal_detect.append([label, xyxy])
return cal_detect
def detect(self):
model, stride, names, pt, jit, onnx, engine = self.load_model() # 加載模型
image = cv2.imread("./images/1.jpg") # 讀取識(shí)別對(duì)象
results = self.run(model, image, stride, pt) # 識(shí)別, 返回多個(gè)數(shù)組每個(gè)第一個(gè)為結(jié)果缓屠,第二個(gè)為坐標(biāo)位置
for i in results:
box = i[1]
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
print(i[0])
cv2.rectangle(image, p1, p2, (0, 255, 0), thickness=3, lineType=cv2.LINE_AA)
cv2.imshow('image', image)
cv2.waitKey(0)
@staticmethod
def select_device(device):
# ...
pass
@staticmethod
def DetectMultiBackend(weights, device, dnn, data):
# ...
pass
@staticmethod
def letterbox(img, imgsz, stride, pt):
# ...
pass
@staticmethod
def non_max_suppression(pred, conf_thres, iou_thres, classes, agnostic_nms, max_det):
# ...
pass
@staticmethod
def scale_coords(shape, coords, img_shape):
# ...
pass
該程序文件名為Interface.py奇昙,主要功能是加載YOLOv5模型并進(jìn)行目標(biāo)檢測(cè)。
程序首先導(dǎo)入了必要的庫(kù)和模塊敌完,包括os储耐、sys、pathlib滨溉、cv2什湘、torch等。然后定義了一些常量和全局變量晦攒。
load_model函數(shù)用于加載模型闽撤,其中weights參數(shù)指定了模型權(quán)重文件的路徑,data參數(shù)指定了數(shù)據(jù)集配置文件的路徑脯颜,device參數(shù)指定了設(shè)備類(lèi)型哟旗,half參數(shù)指定是否使用FP16半精度推理,dnn參數(shù)指定是否使用OpenCV DNN進(jìn)行ONNX推理栋操。該函數(shù)返回加載的模型以及一些相關(guān)信息闸餐。
run函數(shù)用于運(yùn)行模型進(jìn)行目標(biāo)檢測(cè),其中model參數(shù)為加載的模型矾芙,img參數(shù)為輸入的圖像舍沙,stride參數(shù)為模型的步長(zhǎng),pt參數(shù)指定是否使用PyTorch模型剔宪,imgsz參數(shù)指定推理的圖像尺寸拂铡,conf_thres參數(shù)指定置信度閾值壹无,iou_thres參數(shù)指定NMS的IOU閾值,max_det參數(shù)指定每張圖像的最大檢測(cè)數(shù)感帅,device參數(shù)指定設(shè)備類(lèi)型斗锭,classes參數(shù)指定需要過(guò)濾的類(lèi)別,agnostic_nms參數(shù)指定是否使用類(lèi)別不可知的NMS留瞳,augment參數(shù)指定是否進(jìn)行數(shù)據(jù)增強(qiáng)拒迅,half參數(shù)指定是否使用FP16半精度推理。該函數(shù)返回檢測(cè)到的目標(biāo)結(jié)果她倘。
detect函數(shù)用于加載模型并進(jìn)行目標(biāo)檢測(cè)璧微,其中調(diào)用了load_model函數(shù)加載模型,然后讀取待檢測(cè)的圖像硬梁,最后調(diào)用run函數(shù)進(jìn)行目標(biāo)檢測(cè)前硫,并將結(jié)果繪制在圖像上顯示出來(lái)。
最后調(diào)用detect函數(shù)進(jìn)行目標(biāo)檢測(cè)荧止。
5.2 main.py
class ObjectDetector:
def __init__(self, weights_path, data_path):
self.weights_path = weights_path
self.data_path = data_path
self.model = None
self.stride = None
self.names = None
self.pt = None
self.jit = None
self.onnx = None
self.engine = None
def load_model(self):
device = self.select_device('')
model = self.DetectMultiBackend(self.weights_path, device=device, dnn=False, data=self.data_path)
self.model = model
self.stride, self.names, self.pt, self.jit, self.onnx, self.engine = model.stride, model.names, model.pt, model.jit, model.onnx, model.engine
def run(self, img_path):
img = cv2.imread(img_path)
cal_detect = []
device = self.select_device('')
names = self.model.module.names if hasattr(self.model, 'module') else self.model.names
im = self.letterbox(img, (640, 640), self.stride, self.pt)[0]
im = im.transpose((2, 0, 1))[::-1]
im = np.ascontiguousarray(im)
im = torch.from_numpy(im).to(device)
im = im.half() if False else im.float()
im /= 255
if len(im.shape) == 3:
im = im[None]
pred = self.model(im, augment=False)
pred = self.non_max_suppression(pred, 0.35, 0.05, classes=None, agnostic_nms=False, max_det=1000)
for i, det in enumerate(pred):
if len(det):
det[:, :4] = self.scale_coords(im.shape[2:], det[:, :4], img.shape).round()
for *xyxy, conf, cls in reversed(det):
c = int(cls)
label = f'{names[c]}'
lbl = names[int(cls)]
cal_detect.append([label, xyxy, float(conf)])
return cal_detect
def select_device(self, device=''):
pass
def DetectMultiBackend(self, weights, device='', dnn=False, data=None):
pass
def letterbox(self, img, new_shape=(640, 640), stride=32, auto=True, scaleFill=False, scaleup=True, color=(114, 114, 114)):
pass
def non_max_suppression(self, pred, conf_thres=0.35, iou_thres=0.05, classes=None, agnostic_nms=False, max_det=1000):
pass
def scale_coords(self, img1_shape, coords, img0_shape, ratio_pad=None):
pass
# Example usage
weights_path = Path(__file__).resolve().parents[0] / 'best.pt'
data_path = Path(__file__).resolve().parents[0] / 'data/coco128.yaml'
img_path = './images/4.bmp'
detector = ObjectDetector(weights_path, data_path)
detector.load_model()
results = detector.run(img_path)
image = cv2.imread(img_path)
for i in results:
box = i[1]
p1, p2 = (int(box[0]), int(box[1])), (int(box[2]), int(box[3]))
color = [0, 0, 255]
cv2.rectangle(image, p1, p2, color, thickness=2, lineType=cv2.LINE_AA)
cv2.putText(image, str(i[0]), (int(box[0]), int(box[1]) - 10),
cv2.FONT_HERSHEY_SIMPLEX, 2, color, 3)
print(i)
cv2.imshow('image', image)
cv2.waitKey(0)
這個(gè)程序文件是一個(gè)使用YOLOv5模型進(jìn)行目標(biāo)檢測(cè)的程序屹电。它首先導(dǎo)入了一些必要的庫(kù)和模塊,然后定義了一些函數(shù)和變量。
在load_model
函數(shù)中,它加載了YOLOv5模型铲掐,并返回了模型厅翔、步長(zhǎng)郑兴、類(lèi)別名稱(chēng)等信息。
在run
函數(shù)中,它接受一個(gè)圖像作為輸入,并使用加載的模型進(jìn)行目標(biāo)檢測(cè)偷线。它將圖像進(jìn)行預(yù)處理,然后將其傳遞給模型進(jìn)行推理沽甥。最后声邦,它對(duì)檢測(cè)到的目標(biāo)進(jìn)行后處理,并返回目標(biāo)的類(lèi)別摆舟、坐標(biāo)和置信度亥曹。
在主程序中,它首先加載了模型恨诱,然后讀取一張圖像媳瞪,并調(diào)用run
函數(shù)進(jìn)行目標(biāo)檢測(cè)。最后胡野,它將檢測(cè)結(jié)果在圖像上進(jìn)行可視化,并顯示出來(lái)痕鳍。
這個(gè)程序文件使用了很多庫(kù)和模塊硫豆,包括argparse龙巨、platform、shutil熊响、time旨别、numpy、cv2汗茄、torch秸弛、PyQt5等。它還使用了YOLOv5模型和一些輔助函數(shù)和工具類(lèi)來(lái)實(shí)現(xiàn)目標(biāo)檢測(cè)功能洪碳。
5.3 torch_utils.py
@contextmanager
def torch_distributed_zero_first(local_rank: int):
"""
Decorator to make all processes in distributed training wait for each local_master to do something.
"""
if local_rank not in [-1, 0]:
dist.barrier(device_ids=[local_rank])
yield
if local_rank == 0:
dist.barrier(device_ids=[0])
def date_modified(path=__file__):
# return human-readable file modification date, i.e. '2021-3-26'
t = datetime.datetime.fromtimestamp(Path(path).stat().st_mtime)
return f'{t.year}-{t.month}-{t.day}'
def git_describe(path=Path(__file
這個(gè)程序文件是一個(gè)PyTorch工具文件递览,包含了一些常用的函數(shù)和類(lèi)。文件名為torch_utils.py瞳腌。
該文件中的函數(shù)和類(lèi)的功能如下:
1. `torch_distributed_zero_first(local_rank: int)`:用于在分布式訓(xùn)練中绞铃,使所有進(jìn)程等待每個(gè)本地主進(jìn)程執(zhí)行某個(gè)操作。
2. `date_modified(path=__file__)`:返回文件的人類(lèi)可讀的修改日期嫂侍。
3. `git_describe(path=Path(__file__).parent)`:返回人類(lèi)可讀的git描述儿捧。
4. `select_device(device='', batch_size=None)`:選擇設(shè)備(CPU或GPU)進(jìn)行訓(xùn)練。
5. `time_sync()`:返回PyTorch準(zhǔn)確的時(shí)間挑宠。
6. `profile(input, ops, n=10, device=None)`:用于對(duì)YOLOv5模型的速度菲盾、內(nèi)存和FLOPs進(jìn)行分析。
5.4 train.py
class YOLOTrainer:
def __init__(self, hyp, opt, device, tb_writer=None):
self.hyp = hyp
self.opt = opt
self.device = device
self.tb_writer = tb_writer
self.logger = logging.getLogger(__name__)
def train(self):
logger.info(colorstr('hyperparameters: ') + ', '.join(f'{k}={v}' for k, v in self.hyp.items()))
save_dir, epochs, batch_size, total_batch_size, weights, rank, freeze = \
Path(self.opt.save_dir), self.opt.epochs, self.opt.batch_size, self.opt.total_batch_size, self.opt.weights, self.opt.global_rank, self.opt.freeze
# Directories
wdir = save_dir / 'weights'
wdir.mkdir(parents=True, exist_ok=True) # make dir
last = wdir / 'last.pt'
best = wdir / 'best.pt'
results_file = save_dir / 'results.txt'
# Save run settings
with open(save_dir / 'hyp.yaml', 'w') as f:
yaml.dump(self.hyp, f, sort_keys=False)
with open(save_dir / 'opt.yaml', 'w') as f:
yaml.dump(vars(self.opt), f, sort_keys=False)
# Configure
plots = not self.opt.evolve # create plots
cuda = self.device.type != 'cpu'
init_seeds(2 + rank)
with open(self.opt.data) as f:
data_dict = yaml.load(f, Loader=yaml.SafeLoader) # data dict
is_coco = self.opt.data.endswith('coco.yaml')
# Logging- Doing this before checking the dataset. Might update data_dict
loggers = {'wandb': None} # loggers dict
if rank in [-1, 0]:
self.opt.hyp = self.hyp # add hyperparameters
run_id = torch.load(weights, map_location=self.device).get('wandb_id') if weights.endswith('.pt') and os.path.isfile(weights) else None
wandb_logger = WandbLogger(self.opt, Path(self.opt.save_dir).stem, run_id, data_dict)
loggers['wandb'] = wandb_logger.wandb
data_dict = wandb_logger.data_dict
if wandb_logger.wandb:
weights, epochs, self.hyp = self.opt.weights, self.opt.epochs, self.opt.hyp # WandbLogger might update weights, epochs if resuming
nc = 1 if self.opt.single_cls else int(data_dict['nc']) # number of classes
names = ['item'] if self.opt.single_cls and len(data_dict['names']) != 1 else data_dict['names'] # class names
assert len(names) == nc, '%g names found for nc=%g dataset in %s' % (len(names), nc, self.opt.data) # check
# Model
pretrained = weights.endswith('.pt')
if pretrained:
with torch_distributed_zero_first(rank):
attempt_download(weights) # download if not found locally
ckpt = torch.load(weights, map_location=self.device) # load checkpoint
model = Model(self.opt.cfg or ckpt['model'].yaml, ch=3, nc=nc, anchors=self.hyp.get('anchors')).to(self.device) # create
exclude = ['anchor'] if (self.opt.cfg or self.hyp.get('anchors')) and not self.opt.resume else [] # exclude keys
state_dict = ckpt['model'].float().state_dict() # to FP32
state_dict = intersect_dicts(state_dict, model.state_dict(), exclude=exclude) # intersect
model.load_state_dict(state_dict, strict=False) # load
logger.info('Transferred %g/%g items from %s' % (len(state_dict), len(model.state_dict()), weights)) # report
else:
model = Model(self.opt.cfg, ch=3, nc=nc, anchors=self.hyp.get('anchors')).to(self.device) # create
with torch_distributed_zero_first(rank):
check_dataset(data_dict) # check
train_path = data_dict['train']
test_path = data_dict['val']
# Freeze
freeze = [f'model.{x}.' for x in (freeze if len(freeze) > 1 else range(freeze[0]))] # parameter names to freeze (full or partial)
for k, v in model.named_parameters():
v.requires_grad = True # train all layers
if any(x in k for x in freeze):
print('freezing %s' % k)
v.requires_grad = False
# Optimizer
nbs = 64 # nominal batch size
accumulate = max(round(nbs / total_batch_size), 1) # accumulate loss before optimizing
self.hyp['weight_decay'] *= total_batch_size * accumulate / nbs # scale weight_decay
logger.info(f"Scaled weight_decay = {self.hyp['weight_decay']}")
pg0, pg1, pg2 = [], [], [] # optimizer parameter groups
for k, v in model.named_modules():
if hasattr(v, 'bias') and isinstance(v.bias, nn.Parameter):
pg2.append(v.bias) # biases
if isinstance(v, nn.BatchNorm2d):
pg0.append(v.weight) # no decay
elif hasattr(v, 'weight') and isinstance(v.weight, nn.Parameter):
pg1.append(v.weight) # apply decay
if hasattr(v, 'im'):
if hasattr(v.im, 'implicit'):
pg0.append(v.im.implicit)
else:
for iv in v.im:
pg0.append(iv.implicit)
if hasattr(v, 'imc'):
if hasattr(v.imc, 'implicit'):
pg0.append(v.imc.implicit)
else:
for iv in v.imc:
pg0.append(iv.implicit)
if hasattr(v, 'imb'):
if hasattr
這個(gè)程序文件是一個(gè)用于訓(xùn)練模型的腳本各淀。它包含了導(dǎo)入必要的庫(kù)和模塊懒鉴,定義了訓(xùn)練函數(shù)train()
,以及一些輔助函數(shù)和工具函數(shù)揪阿。
在train()
函數(shù)中疗我,首先讀取了一些配置參數(shù),如超參數(shù)南捂、保存路徑吴裤、訓(xùn)練輪數(shù)等。然后創(chuàng)建了模型溺健,并加載了預(yù)訓(xùn)練權(quán)重麦牺。接著配置了優(yōu)化器和學(xué)習(xí)率調(diào)度器,并定義了損失函數(shù)鞭缭。最后剖膳,開(kāi)始進(jìn)行訓(xùn)練循環(huán),每個(gè)epoch中進(jìn)行前向傳播岭辣、計(jì)算損失吱晒、反向傳播和參數(shù)更新等操作。
整個(gè)訓(xùn)練過(guò)程中還包括了一些輔助功能沦童,如日志記錄仑濒、模型保存叹话、模型評(píng)估等。
總的來(lái)說(shuō)墩瞳,這個(gè)程序文件實(shí)現(xiàn)了一個(gè)完整的模型訓(xùn)練過(guò)程驼壶,包括了模型的初始化、數(shù)據(jù)加載喉酌、優(yōu)化器的配置热凹、損失函數(shù)的定義、訓(xùn)練循環(huán)的實(shí)現(xiàn)等泪电。
6.系統(tǒng)整體結(jié)構(gòu)
根據(jù)以上分析般妙,整體來(lái)看,這個(gè)程序是一個(gè)使用YOLOv5模型進(jìn)行目標(biāo)檢測(cè)的工程歪架。它包含了多個(gè)文件股冗,每個(gè)文件都有不同的功能,用于實(shí)現(xiàn)不同的模塊和功能和蚪。
下面是每個(gè)文件的功能整理:
文件路徑 | 功能概述 |
---|---|
F:\project6\200deng(f13lw)h\code\Interface.py | 加載YOLOv5模型并進(jìn)行目標(biāo)檢測(cè) |
F:\project6\200deng(f13lw)h\code\main.py | 主程序止状,調(diào)用模型進(jìn)行目標(biāo)檢測(cè) |
F:\project6\200deng(f13lw)h\code\torch_utils.py | 包含一些PyTorch工具函數(shù)和類(lèi) |
F:\project6\200deng(f13lw)h\code\train.py | 訓(xùn)練模型的腳本 |
F:\project6\200deng(f13lw)h\code\models\common.py | 包含一些常用的模塊和函數(shù) |
F:\project6\200deng(f13lw)h\code\models\experimental.py | 包含一些實(shí)驗(yàn)性的模型和功能 |
F:\project6\200deng(f13lw)h\code\models\tf.py | 包含一些與TensorFlow相關(guān)的模型和功能 |
F:\project6\200deng(f13lw)h\code\models\yolo.py | 包含YOLOv5模型的定義和相關(guān)函數(shù) |
F:\project6\200deng(f13lw)h\code\models_init_.py | 模型初始化文件 |
F:\project6\200deng(f13lw)h\code\tools\activations.py | 包含一些激活函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\tools\augmentations.py | 包含一些數(shù)據(jù)增強(qiáng)的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\autoanchor.py | 包含自動(dòng)錨框生成的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\autobatch.py | 包含自動(dòng)批處理大小調(diào)整的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\callbacks.py | 包含一些回調(diào)函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\tools\datasets.py | 包含數(shù)據(jù)集處理的函數(shù)和類(lèi) |
F:\project6\200deng(f13lw)h\code\tools\downloads.py | 包含下載數(shù)據(jù)集和權(quán)重的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\general.py | 包含一些通用的輔助函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\loss.py | 包含一些損失函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\tools\metrics.py | 包含一些評(píng)估指標(biāo)的定義 |
F:\project6\200deng(f13lw)h\code\tools\plots.py | 包含繪圖函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\tools\torch_utils.py | 包含一些PyTorch工具函數(shù)和類(lèi) |
F:\project6\200deng(f13lw)h\code\tools_init_.py | 工具初始化文件 |
F:\project6\200deng(f13lw)h\code\tools\aws\resume.py | 包含AWS訓(xùn)練恢復(fù)的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\aws_init_.py | AWS工具初始化文件 |
F:\project6\200deng(f13lw)h\code\tools\flask_rest_api\example_request.py | 包含F(xiàn)lask REST API示例請(qǐng)求的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\flask_rest_api\restapi.py | 包含F(xiàn)lask REST API的實(shí)現(xiàn) |
F:\project6\200deng(f13lw)h\code\tools\loggers_init_.py | 日志記錄器初始化文件 |
F:\project6\200deng(f13lw)h\code\tools\loggers\wandb\log_dataset.py | 包含使用WandB記錄數(shù)據(jù)集的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\loggers\wandb\sweep.py | 包含使用WandB進(jìn)行超參數(shù)搜索的函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\loggers\wandb\wandb_utils.py | 包含與WandB相關(guān)的輔助函數(shù) |
F:\project6\200deng(f13lw)h\code\tools\loggers\wandb_init_.py | WandB日志記錄器初始化文件 |
F:\project6\200deng(f13lw)h\code\utils\activations.py | 包含一些激活函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\utils\augmentations.py | 包含一些數(shù)據(jù)增強(qiáng)的函數(shù) |
F:\project6\200deng(f13lw)h\code\utils\autoanchor.py | 包含自動(dòng)錨框生成的函數(shù) |
F:\project6\200deng(f13lw)h\code\utils\autobatch.py | 包含自動(dòng)批處理大小調(diào)整的函數(shù) |
F:\project6\200deng(f13lw)h\code\utils\callbacks.py | 包含一些回調(diào)函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\utils\datasets.py | 包含數(shù)據(jù)集處理的函數(shù)和類(lèi) |
F:\project6\200deng(f13lw)h\code\utils\downloads.py | 包含下載數(shù)據(jù)集和權(quán)重的函數(shù) |
F:\project6\200deng(f13lw)h\code\utils\general.py | 包含一些通用的輔助函數(shù) |
F:\project6\200deng(f13lw)h\code\utils\loss.py | 包含一些損失函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\utils\metrics.py | 包含一些評(píng)估指標(biāo)的定義 |
F:\project6\200deng(f13lw)h\code\utils\plots.py | 包含繪圖函數(shù)的定義 |
F:\project6\200deng(f13lw)h\code\utils\torch_utils.py | 包含一些PyTorch工具函數(shù)和類(lèi) |
F:\project6\200deng(f13lw)h\code\utils_init_.py | 工具初始化文件 |
F:\project6\200deng(f13lw)h\code\utils\aws\resume.py | 包含AWS訓(xùn)練恢復(fù)的函數(shù) |
F:\project6\200deng(f13lw)h\code\utils\aws_init_.py | AWS工具初始化文件 |
F:\project6\200deng(f13lw)h\code\utils\flask_rest_api\example_request.py | 包含F(xiàn)lask REST API示例請(qǐng)求的函數(shù) |
7.改進(jìn)YOLO-BIFPN
圖中藍(lán)色部分為自頂向下的通路,傳遞的是高層特征的語(yǔ)義信息;紅色部分是自底向上的通路攒霹,傳遞的是低層特征的位置信息;紫色部分是同一層在輸入節(jié)點(diǎn)和輸入節(jié)點(diǎn)間新加的一條邊怯疤。
。我們刪除那些只有一條輸入邊的節(jié)點(diǎn)催束。這么做的思路很簡(jiǎn)單:如果一個(gè)節(jié)點(diǎn)只有一條輸入邊而沒(méi)有特征融合集峦,那么它對(duì)旨在融合不同特征的特征網(wǎng)絡(luò)的貢獻(xiàn)就會(huì)很小。刪除它對(duì)我們的網(wǎng)絡(luò)影響不大抠刺,同時(shí)簡(jiǎn)化了雙向網(wǎng)絡(luò);如上圖d的P7右邊第一個(gè)節(jié)點(diǎn)
·如果原始輸入節(jié)點(diǎn)和輸出節(jié)點(diǎn)處于同一層塔淤,我們會(huì)在原始輸入節(jié)點(diǎn)和輸出節(jié)點(diǎn)之間添加一條額外的邊。思路:以在不增加太多成本的情況下融合更多的特性;
速妖。與只有一個(gè)自頂向下和一個(gè)自底向上路徑的PANet不同高蜂,我們處理每個(gè)雙向路徑(自頂向下和自底而上)路徑作為一個(gè)特征網(wǎng)絡(luò)層,并重復(fù)同一層多次罕容,以實(shí)現(xiàn)更高層次的特征融合备恤。如下圖EificientNet的網(wǎng)絡(luò)結(jié)構(gòu)所示,我們對(duì)BiFPN是重復(fù)使用多次的锦秒。而這個(gè)使用次數(shù)也不是我們認(rèn)為設(shè)定的露泊,而是作為參數(shù)一起加入網(wǎng)絡(luò)的設(shè)計(jì)當(dāng)中,使用NAS技術(shù)算出來(lái)的旅择。
# 結(jié)合BiFPN 設(shè)置可學(xué)習(xí)參數(shù) 學(xué)習(xí)不同分支的權(quán)重
# 兩個(gè)分支concat操作
......
class BiFPN_Concat2(nn.Module):
def __init__(self, dimension=1):
super(BiFPN_Concat2, self).__init__()
self.d = dimension
self.w = nn.Parameter(torch.ones(2, dtype=torch.float32), requires_grad=True)
self.epsilon = 0.0001
# 設(shè)置可學(xué)習(xí)參數(shù) nn.Parameter的作用是:將一個(gè)不可訓(xùn)練的類(lèi)型Tensor轉(zhuǎn)換成可以訓(xùn)練的類(lèi)型
parameter
# 并且會(huì)向宿主模型注冊(cè)該參數(shù) 成為其一部分 即model.parameters()會(huì)包含這個(gè)parameter
# 從而在參數(shù)優(yōu)化的時(shí)候可以自動(dòng)一起優(yōu)化
def forward(self, x):
w = self.w
weight = w / (torch.sum(w, dim=0) + self.epsilon) # 將權(quán)重進(jìn)行歸一化
# Fast normalized fusion
x = [weight[0] * x[0], weight[1] * x[1]]
return torch.cat(x, self.d)
# 三個(gè)分支concat操作
class BiFPN_Concat3(nn.Module):
def __init__(self, dimension=1):
super(BiFPN_Concat3, self).__init__()
self.d = dimension
self.w = nn.Parameter(torch.ones(3, dtype=torch.float32), requires_grad=True)
self.epsilon = 0.0001
def forward(self, x):
w = self.w
weight = w / (torch.sum(w, dim=0) + self.epsilon) # 將權(quán)重進(jìn)行歸一化
# Fast normalized fusion
x = [weight[0] * x[0], weight[1] * x[1], weight[2] * x[2]]
return torch.cat(x, self.d)
......
8.系統(tǒng)整合
下圖完整源碼&環(huán)境部署視頻教程&數(shù)據(jù)集&自定義UI界面
參考博客《改進(jìn)YOLO和OpenCV的交通信號(hào)燈識(shí)別系統(tǒng)(部署教程&源碼)》
9.參考文獻(xiàn)
[1]祝朝坤,李宗賢.基于OpenCV的交通燈綠信比智能調(diào)節(jié)裝置的設(shè)計(jì)[J].電子產(chǎn)品世界.2020,(1).
[2]林曉予.基于MATLAB仿真的手勢(shì)識(shí)別系統(tǒng)及其應(yīng)用[J].信息技術(shù)與信息化.2020,(9).DOI:10.3969/j.issn.1672-9528.2020.09.065.
[3]周沛,熊文華.交叉口信號(hào)控制評(píng)價(jià)指標(biāo)體系[J].公路交通技術(shù).2020,(1).DOI:10.13607/j.cnki.gljt.2020.01.020.
[4]張立立,王力.新一代人工智能交通信號(hào)控制器架構(gòu)研究[J].重慶交通大學(xué)學(xué)報(bào)(自然科學(xué)版).2019,(11).DOI:10.3969/j.issn.1674-0696.2019.11.02.
[5]韓宇,張磊,吳澤民,等.基于嵌入式樹(shù)莓派和OpenCV的運(yùn)動(dòng)檢測(cè)與跟蹤系統(tǒng)[J].電視技術(shù).2017,(2).DOI:10.16280/j.videoe.2017.02.002.
[6]高美蓉.基于車(chē)流量的智能交通控制系統(tǒng)的設(shè)計(jì)[J].電氣自動(dòng)化.2017,(5).DOI:10.3969/j.issn.1000-3886.2017.05.026.
[7]方熙霞.智能交通信號(hào)線(xiàn)控模型研究[J].公路交通技術(shù).2004,(5).DOI:10.3969/j.issn.1009-6477.2004.05.039.
[8]舒朗,張智杰,雷波.一種針對(duì)紅外目標(biāo)檢測(cè)的Dense-Yolov5算法研究[J].光學(xué)與光電技術(shù).2021,(1).
[9]徐星.基于深度學(xué)習(xí)的太陽(yáng)能電池片缺陷檢測(cè)[D].2019.
[10]李劉劉.基于生成式對(duì)抗網(wǎng)絡(luò)的室內(nèi)家居場(chǎng)景生成算法[D].2019.