改進(jìn)YOLO和OpenCV的交通信號(hào)燈識(shí)別系統(tǒng)(部署教程&源碼)

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è)方面:

  1. 提高交通信號(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ù)支持,從而提高交通效率和安全性裙椭。

  2. 提高交通信號(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)駕駛的安全性和可靠性扫尺。

  3. 提供部署教程和源碼:通過(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.圖片演示

2.png
3.png
4.png

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)
如下圖所示:


image.png

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ì)算。


image.png

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ò);


image.png

其次陋气,我們添加一個(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

image.png

圖中藍(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界面

1.png

參考博客《改進(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.

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末惭笑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子生真,更是在濱河造成了極大的恐慌沉噩,老刑警劉巖铺敌,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異屁擅,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)产弹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)派歌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人痰哨,你說(shuō)我怎么就攤上這事胶果。” “怎么了斤斧?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵早抠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我撬讽,道長(zhǎng)蕊连,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任游昼,我火速辦了婚禮甘苍,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘烘豌。我一直安慰自己载庭,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布廊佩。 她就那樣靜靜地躺著囚聚,像睡著了一般。 火紅的嫁衣襯著肌膚如雪标锄。 梳的紋絲不亂的頭發(fā)上顽铸,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音鸯绿,去河邊找鬼跋破。 笑死,一個(gè)胖子當(dāng)著我的面吹牛瓶蝴,可吹牛的內(nèi)容都是我干的毒返。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼舷手,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拧簸!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起男窟,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盆赤,失蹤者是張志新(化名)和其女友劉穎贾富,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體牺六,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡颤枪,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了淑际。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片畏纲。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖春缕,靈堂內(nèi)的尸體忽然破棺而出盗胀,到底是詐尸還是另有隱情,我是刑警寧澤锄贼,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布票灰,位于F島的核電站,受9級(jí)特大地震影響宅荤,放射性物質(zhì)發(fā)生泄漏屑迂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一冯键、第九天 我趴在偏房一處隱蔽的房頂上張望屈糊。 院中可真熱鬧,春花似錦琼了、人聲如沸逻锐。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)昧诱。三九已至,卻和暖如春所袁,著一層夾襖步出監(jiān)牢的瞬間盏档,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工燥爷, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留蜈亩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓前翎,卻偏偏與公主長(zhǎng)得像稚配,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子港华,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容