1 前言
?? 優(yōu)質(zhì)競賽項(xiàng)目系列,今天要分享的是
?? 基于深度學(xué)習(xí)YOLO抽煙行為檢測
該項(xiàng)目較為新穎,適合作為競賽課題方向,學(xué)長非常推薦!
??學(xué)長這里給一個(gè)題目綜合評分(每項(xiàng)滿分5分)
- 難度系數(shù):3分
- 工作量:3分
- 創(chuàng)新點(diǎn):4分
?? 更多資料, 項(xiàng)目分享:文章來源:http://www.zghlxwxcb.cn/news/detail-834837.html
https://gitee.com/dancheng-senior/postgraduate文章來源地址http://www.zghlxwxcb.cn/news/detail-834837.html
1 課題背景
公共場合抽煙的危害很大,國家也相應(yīng)地出臺了在公共場合禁煙的政策。以前實(shí)行相關(guān)的政策都是靠工作人員巡邏發(fā)現(xiàn)并出言禁止,這樣做效率很低下。計(jì)算機(jī)視覺領(lǐng)域發(fā)展迅速,而抽煙檢測也屬于一種計(jì)算機(jī)視覺目標(biāo)檢測的行為,可以采用目標(biāo)檢測的方法來實(shí)現(xiàn)。目前,目標(biāo)檢測在很多領(lǐng)域都取得顯著成就,但是在抽煙檢測領(lǐng)域方面進(jìn)行研究卻幾乎沒有。該研究可以有效節(jié)省成本,對公共場合禁煙政策的實(shí)行有很大的推動作用。
2 實(shí)現(xiàn)效果
左圖為原圖,右圖為推理后的圖片,以圖片方式展示,視頻流和實(shí)時(shí)流也能達(dá)到這個(gè)效果,由于視頻轉(zhuǎn)GIF大小原因,這里暫不演示。
3 Yolov5算法
3.1 簡介
YOLO系列是基于深度學(xué)習(xí)的回歸方法。該系列陸續(xù)誕生出YOLOv1、YOLOv2、YOLOv3、YOLOv4、YOLOv5。YOLOv5算法,它是一種單階段目標(biāo)檢測的算法,該算法可以根據(jù)落地要求靈活地通過chaneel和layer的控制因子來配置和調(diào)節(jié)模型,所以在比賽和落地中應(yīng)用比較多。同時(shí)它有YOLOv5x、YOLOv5l、YOLOv5m、YOLOv5s四種模型。
具有以下優(yōu)點(diǎn):
- 在pytorch環(huán)境下編寫;
- 可以很容易編譯成ON?NX和Core ML;
- 運(yùn)行速度很快,每秒可以達(dá)到140FPS的速度;
- 模型精度高;
- 集成了YOLOv3和YOLOv4的部分優(yōu)秀特性,進(jìn)行了推陳出新的改進(jìn)。
3.2 相關(guān)技術(shù)
Mosaic數(shù)據(jù)增強(qiáng)
Mosaic數(shù)據(jù)增強(qiáng)技術(shù)采用了四張圖片的隨機(jī)縮放、隨機(jī)剪裁、隨機(jī)排布的方式對數(shù)據(jù)進(jìn)行拼接,相比CutMix數(shù)據(jù)增強(qiáng)多用了兩張圖片。在目標(biāo)識別過程中,要識別的目標(biāo)有大目標(biāo)、中等目標(biāo)、小目標(biāo),并且三種目標(biāo)的占比例不均衡,其中,小目標(biāo)的數(shù)量是最多的,但是出現(xiàn)的頻率很低,這種情況就會導(dǎo)致在bp時(shí)對小目標(biāo)的優(yōu)化不足,模型正確識別小目標(biāo)的難度比識別中、大目標(biāo)的難度要大很多,于是對于小目標(biāo)來說很容易出現(xiàn)誤檢和漏檢的情況。Mosaic數(shù)據(jù)增強(qiáng)技術(shù)做出改進(jìn)后,上述的問題得到有效的解決。
該技術(shù)的優(yōu)點(diǎn)是:
- 豐富了數(shù)據(jù)集,采用“三個(gè)隨機(jī)”的方式對數(shù)據(jù)進(jìn)行拼接豐富了檢測的數(shù)據(jù)集,尤其是隨機(jī)縮放增加了很多小目標(biāo),克服了小目標(biāo)的不足,讓網(wǎng)絡(luò)的魯棒性得到提高;
- 減少GPU的使用,在Mosaic增強(qiáng)訓(xùn)練時(shí),四張圖片拼接在一起,GPU可以直接計(jì)算四張圖片的數(shù)據(jù),讓Mini-batch的大小減少了很多,這使得一個(gè)GPU就可以達(dá)到比較可觀的效果。
自適應(yīng)anchor
自適應(yīng)anchor是check_anchors函數(shù)通過遺傳算法與Kmeans迭代算出的最大可能召回率的anchor組合。在網(wǎng)絡(luò)模型的訓(xùn)練過程中,網(wǎng)絡(luò)在初始化的錨框的基礎(chǔ)上輸出預(yù)測框,然后與真實(shí)框groundtruth進(jìn)行對比,計(jì)算兩個(gè)框之間的差值,再根據(jù)差值進(jìn)行反向更新,迭代網(wǎng)絡(luò)參數(shù),最后求出最佳的錨框值。自適應(yīng)的anchor能夠更好地配合網(wǎng)絡(luò)訓(xùn)練,提高模型的精度,減少對anchor的設(shè)計(jì)難度,具有很好的實(shí)用性。
自適應(yīng)圖片縮放
為了提高模型的推理速度,YOLOv5提出自適應(yīng)圖片縮放,根據(jù)長寬比對圖像進(jìn)行縮放,并添加最少的黑邊,減少計(jì)算量。該方法是用縮放后的長邊減去短邊再對32進(jìn)行取余運(yùn)算,求出padding。在訓(xùn)練時(shí)并沒有采用縮減黑邊的方法,該方法只是在測試模型推理的時(shí)候才使用,這樣提高了目標(biāo)檢測的準(zhǔn)確率和速度。
Focus結(jié)構(gòu)
該結(jié)構(gòu)采用切片操作,將特征切片成四份,每一份將當(dāng)成下采樣的特征,然后在channel維度進(jìn)行concat。例如:原始608 608
3的數(shù)據(jù)圖片,經(jīng)過切片操作先變成304 304 12的特征圖,再經(jīng)過一次32個(gè)卷積核的卷積操作,變成304 304 32的特征圖。
CSP結(jié)構(gòu)
YOLOv5中的CSP[5]結(jié)構(gòu)應(yīng)用于兩處,一處是CSP1_X結(jié)構(gòu)應(yīng)用于Backbone的主干網(wǎng)絡(luò)中,另一處的CSP2_X結(jié)構(gòu)應(yīng)用于Neck中,用于加強(qiáng)網(wǎng)絡(luò)的特征融合的能力。CSPNet主要從網(wǎng)絡(luò)結(jié)構(gòu)設(shè)計(jì)的角度解決推理中從計(jì)算量很大的問題。該結(jié)構(gòu)的優(yōu)點(diǎn)有:1)增強(qiáng)CNN的學(xué)習(xí)能力,使得模型在輕量化的同時(shí)保持較高的準(zhǔn)確性;2)減低計(jì)算的瓶頸問題;3)減低內(nèi)存的分險(xiǎn)。
PFN+PAN結(jié)構(gòu)
這個(gè)結(jié)構(gòu)是FPN和PAN的聯(lián)合。FPN是自頂向下的,將高層的特征信息通過上采樣的方式進(jìn)行傳遞融合,得到進(jìn)行預(yù)測的特征圖,而PAN正好與FPN的方向是相反的方向,它是自底向上地采取特征信息。兩個(gè)結(jié)構(gòu)各自從不同的主干層對不同的檢測層進(jìn)行參數(shù)聚合。兩個(gè)結(jié)構(gòu)的強(qiáng)強(qiáng)聯(lián)合讓得到的特征圖的特征更加明顯和清楚。
Bounding box的損失函數(shù)
Bounding
box損失函數(shù)[6]增加了相交尺度的衡量方式,有效緩解了當(dāng)兩個(gè)框不相交和兩個(gè)框大小完全相同的兩種特殊情況。因?yàn)楫?dāng)預(yù)測框和目標(biāo)框不相交時(shí),IOU=0,無法反應(yīng)兩個(gè)框距離的遠(yuǎn)近的時(shí)候,此時(shí)的損失函數(shù)不可導(dǎo);兩個(gè)框大小完全相同,兩個(gè)IOU也相同,IOU_LOSS無法區(qū)分以上兩種特殊情況。
nms非極大值抑制
在目標(biāo)檢測過程的后續(xù)處理中,對于大量的目標(biāo)框的篩選問題,通常會進(jìn)行nms操作,以此來達(dá)到一個(gè)不錯(cuò)的效果。YO?LOv5算法同樣采用了加權(quán)的nms操作。
4 數(shù)據(jù)集處理及實(shí)驗(yàn)
數(shù)據(jù)集準(zhǔn)備
由于目前針對吸煙圖片并沒有現(xiàn)成的數(shù)據(jù)集,我們使用Python爬蟲利用關(guān)鍵字在互聯(lián)網(wǎng)上獲得的圖片數(shù)據(jù),編寫程序爬了1w張,篩選下來有近1000張可用,以及其他途徑獲取到的,暫時(shí)可用數(shù)據(jù)集有5k張,
深度學(xué)習(xí)圖像標(biāo)注軟件眾多,按照不同分類標(biāo)準(zhǔn)有多中類型,本文使用LabelImg單機(jī)標(biāo)注軟件進(jìn)行標(biāo)注。LabelImg是基于角點(diǎn)的標(biāo)注方式產(chǎn)生邊界框,對圖片進(jìn)行標(biāo)注得到xml格式的標(biāo)注文件,由于邊界框?qū)z測精度的影響較大因此采用手動標(biāo)注,并沒有使用自動標(biāo)注軟件。
考慮到有的朋友時(shí)間不足,博主提供了標(biāo)注好的數(shù)據(jù)集和訓(xùn)練好的模型,需要請聯(lián)系。
數(shù)據(jù)標(biāo)注簡介
通過pip指令即可安裝
?
pip install labelimg
在命令行中輸入labelimg即可打開
5 部分核心代碼
?
# data/smoke.yaml
# COCO 2017 dataset http://cocodataset.org
# Download command: bash yolov5/data/get_coco2017.sh
# Train command: python train.py --data ./data/coco.yaml
# Dataset should be placed next to yolov5 folder:
# /parent_folder
# /coco
# /yolov5
# train and val datasets (image directory or *.txt file with image paths)
train: data\train.txt # 上面我們生成的train,根據(jù)自己的路徑進(jìn)行更改
val: data\test.txt # 上面我們生成的test
#test: ../coco/test-dev2017.txt # 20k images for submission to https://competitions.codalab.org/competitions/20794
# number of classes
nc: 1 #訓(xùn)練的類別
# class names
names: ['smoke']
# Print classes
# with open('data/coco.yaml') as f:
# d = yaml.load(f, Loader=yaml.FullLoader) # dict
# for i, x in enumerate(d['names']):
# print(i, x)
# model/yolov5s.yaml
# parameters
nc: 1 # number of classes
depth_multiple: 0.33 # model depth multiple
width_multiple: 0.50 # layer channel multiple
# anchors
anchors:
- [116,90, 156,198, 373,326] # P5/32
- [30,61, 62,45, 59,119] # P4/16
- [10,13, 16,30, 33,23] # P3/8
# YOLOv5 backbone
backbone:
# [from, number, module, args]
[[-1, 1, Focus, [64, 3]], # 0-P1/2
[-1, 1, Conv, [128, 3, 2]], # 1-P2/4
[-1, 3, BottleneckCSP, [128]],
[-1, 1, Conv, [256, 3, 2]], # 3-P3/8
[-1, 9, BottleneckCSP, [256]],
[-1, 1, Conv, [512, 3, 2]], # 5-P4/16
[-1, 9, BottleneckCSP, [512]],
[-1, 1, Conv, [1024, 3, 2]], # 7-P5/32
[-1, 1, SPP, [1024, [5, 9, 13]]],
]
# YOLOv5 head
head:
[[-1, 3, BottleneckCSP, [1024, False]], # 9
[-1, 1, Conv, [512, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 6], 1, Concat, [1]], # cat backbone P4
[-1, 3, BottleneckCSP, [512, False]], # 13
[-1, 1, Conv, [256, 1, 1]],
[-1, 1, nn.Upsample, [None, 2, 'nearest']],
[[-1, 4], 1, Concat, [1]], # cat backbone P3
[-1, 3, BottleneckCSP, [256, False]],
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 18 (P3/8-small)
[-2, 1, Conv, [256, 3, 2]],
[[-1, 14], 1, Concat, [1]], # cat head P4
[-1, 3, BottleneckCSP, [512, False]],
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 22 (P4/16-medium)
[-2, 1, Conv, [512, 3, 2]],
[[-1, 10], 1, Concat, [1]], # cat head P5
[-1, 3, BottleneckCSP, [1024, False]],
[-1, 1, nn.Conv2d, [na * (nc + 5), 1, 1]], # 26 (P5/32-large)
[[], 1, Detect, [nc, anchors]], # Detect(P5, P4, P3)
]
# 訓(xùn)練部分主函數(shù)
if __name__ == '__main__':
check_git_status()
parser = argparse.ArgumentParser()
parser.add_argument('--epochs', type=int, default=300)
parser.add_argument('--batch-size', type=int, default=16)
parser.add_argument('--cfg', type=str, default='models/yolov5s.yaml', help='*.cfg path')
parser.add_argument('--data', type=str, default='data/smoke.yaml', help='*.data path')
parser.add_argument('--img-size', nargs='+', type=int, default=[640, 640], help='train,test sizes')
parser.add_argument('--rect', action='store_true', help='rectangular training')
parser.add_argument('--resume', action='store_true', help='resume training from last.pt')
parser.add_argument('--nosave', action='store_true', help='only save final checkpoint')
parser.add_argument('--notest', action='store_true', help='only test final epoch')
parser.add_argument('--noautoanchor', action='store_true', help='disable autoanchor check')
parser.add_argument('--evolve', action='store_true', help='evolve hyperparameters')
parser.add_argument('--bucket', type=str, default='', help='gsutil bucket')
parser.add_argument('--cache-images', action='store_true', help='cache images for faster training')
parser.add_argument('--weights', type=str, default='', help='initial weights path')
parser.add_argument('--name', default='', help='renames results.txt to results_name.txt if supplied')
parser.add_argument('--device', default='0', help='cuda device, i.e. 0 or 0,1,2,3 or cpu')
parser.add_argument('--adam', action='store_true', help='use adam optimizer')
parser.add_argument('--multi-scale', action='store_true', help='vary img-size +/- 50%')
parser.add_argument('--single-cls', action='store_true', help='train as single-class dataset')
opt = parser.parse_args()
opt.weights = last if opt.resume else opt.weights
opt.cfg = check_file(opt.cfg) # check file
opt.data = check_file(opt.data) # check file
print(opt)
opt.img_size.extend([opt.img_size[-1]] * (2 - len(opt.img_size))) # extend to 2 sizes (train, test)
device = torch_utils.select_device(opt.device, apex=mixed_precision, batch_size=opt.batch_size)
if device.type == 'cpu':
mixed_precision = False
# Train
if not opt.evolve:
tb_writer = SummaryWriter(comment=opt.name)
print('Start Tensorboard with "tensorboard --logdir=runs", view at http://localhost:6006/')
train(hyp)
# Evolve hyperparameters (optional)
else:
tb_writer = None
opt.notest, opt.nosave = True, True # only test/save final epoch
if opt.bucket:
os.system('gsutil cp gs://%s/evolve.txt .' % opt.bucket) # download evolve.txt if exists
for _ in range(10): # generations to evolve
if os.path.exists('evolve.txt'): # if evolve.txt exists: select best hyps and mutate
# Select parent(s)
parent = 'single' # parent selection method: 'single' or 'weighted'
x = np.loadtxt('evolve.txt', ndmin=2)
n = min(5, len(x)) # number of previous results to consider
x = x[np.argsort(-fitness(x))][:n] # top n mutations
w = fitness(x) - fitness(x).min() # weights
if parent == 'single' or len(x) == 1:
# x = x[random.randint(0, n - 1)] # random selection
x = x[random.choices(range(n), weights=w)[0]] # weighted selection
elif parent == 'weighted':
x = (x * w.reshape(n, 1)).sum(0) / w.sum() # weighted combination
# Mutate
mp, s = 0.9, 0.2 # mutation probability, sigma
npr = np.random
npr.seed(int(time.time()))
g = np.array([1, 1, 1, 1, 1, 1, 1, 0, .1, 1, 0, 1, 1, 1, 1, 1, 1, 1]) # gains
ng = len(g)
v = np.ones(ng)
while all(v == 1): # mutate until a change occurs (prevent duplicates)
v = (g * (npr.random(ng) < mp) * npr.randn(ng) * npr.random() * s + 1).clip(0.3, 3.0)
for i, k in enumerate(hyp.keys()): # plt.hist(v.ravel(), 300)
hyp[k] = x[i + 7] * v[i] # mutate
# Clip to limits
keys = ['lr0', 'iou_t', 'momentum', 'weight_decay', 'hsv_s', 'hsv_v', 'translate', 'scale', 'fl_gamma']
limits = [(1e-5, 1e-2), (0.00, 0.70), (0.60, 0.98), (0, 0.001), (0, .9), (0, .9), (0, .9), (0, .9), (0, 3)]
for k, v in zip(keys, limits):
hyp[k] = np.clip(hyp[k], v[0], v[1])
# Train mutation
results = train(hyp.copy())
# Write mutation results
print_mutation(hyp, results, opt.bucket)
# Plot results
# plot_evolution_results(hyp)
?
6 最后
?? 更多資料, 項(xiàng)目分享:
https://gitee.com/dancheng-senior/postgraduate
到了這里,關(guān)于計(jì)算機(jī)設(shè)計(jì)大賽 深度學(xué)習(xí)YOLO抽煙行為檢測 - python opencv的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!