YOLOv3改进方法增加特征尺度和训练层数
YOLOv3改進(jìn)方法
YOLOv3的改進(jìn)方法有很多,本文講述的是增加一個(gè)特征尺度。
以YOLOv3-darknet53(ALexeyAB版本)為基礎(chǔ),增加了第4個(gè)特征尺度:104*104。原版YOLOv3網(wǎng)絡(luò)結(jié)構(gòu):
YOLOv3-4l網(wǎng)絡(luò)結(jié)構(gòu):
即,在經(jīng)過(guò)2倍上采樣后,輸出的特征尺度由52x52提升至104x104,再通過(guò)route層將第109層與特征提取網(wǎng)絡(luò)的第11層特征進(jìn)行特征融合,以充分利用深層特征和淺層特征。其余的特征融合分別為:2倍上采樣后輸出的第85層和第97層。通過(guò)route層分別將第85層與第61層,第97層與第36層的特征圖進(jìn)行特征融合。四個(gè)特征尺度分別為:104x104,52x52,26x26和13x13。
具體的步驟為:
(1)修改配置文件cfg
再增加一個(gè)檢測(cè)尺度(在原yolov3的最后一層yolo層的后面,再增加一個(gè)檢測(cè)層:在下方鏈接里的cfg文件最后的“#######”注釋行之后的部分,便是增加的檢測(cè)層結(jié)構(gòu))。
yolo-4l的cfg下載地址
鏈接:https://pan.baidu.com/s/1b92jmcAPTgzxua4Pat7p4A
提取碼:xji2
注意:網(wǎng)盤里提供的cfg配置文件,需要進(jìn)行相應(yīng)參數(shù)修改(修改示例見(jiàn)鏈接:yolov3的cfg配置文件注釋及修改示例)。
(2)重新計(jì)算anchors
由于原先是3個(gè)檢測(cè)尺度共9個(gè)anchors,此時(shí)是4層共12個(gè)anchors。且不同數(shù)據(jù)庫(kù)的anchors值不一樣(比如自行構(gòu)建的數(shù)據(jù)庫(kù)),所以必須重新計(jì)算anchors,并更新到cfg文件中。如果選取的先驗(yàn)框維度比較合適,那么模型就會(huì)更容易學(xué)習(xí),更易收斂,從而做出更好的預(yù)測(cè),預(yù)測(cè)框與標(biāo)注真實(shí)框的IOU就會(huì)更好。
計(jì)算數(shù)據(jù)庫(kù)的anchors的命令為:
./darknet detector calc_anchors /usr/cx/darknetalexeyAB/darknet-master/names_data/voc.data -num_of_clusters 12 -width 416 -height 416 -show 1注意:/usr/cx/darknetalexeyAB/darknet-master/names_data/voc.data是我們自己的voc.data的路徑,根據(jù)自己的項(xiàng)目自行進(jìn)行修改。
結(jié)果如下圖所示:
計(jì)算多次,每次的anchors值會(huì)不一樣,但基本相差無(wú)幾。其實(shí)這些anchors值,就是先驗(yàn)框,就是樣本庫(kù)里最經(jīng)常出現(xiàn)的幾類邊界框。通過(guò)選取專屬于實(shí)際數(shù)據(jù)庫(kù)的anchors,將會(huì)加速收斂,更容易學(xué)習(xí),提高IOU值。
這里還有另外1種計(jì)算anchors的方法, 通過(guò)腳本文件來(lái)計(jì)算anchors錨點(diǎn)值。腳本文件如下所示:
# coding=utf-8 # 通過(guò)k-means ++ 算法獲取anchors的尺寸 import numpy as np# 定義Box類,描述bounding box的坐標(biāo) class Box():def __init__(self, x, y, w, h):self.x = xself.y = yself.w = wself.h = h# 計(jì)算兩個(gè)box在某個(gè)軸上的重疊部分 # x1是box1的中心在該軸上的坐標(biāo) # len1是box1在該軸上的長(zhǎng)度 # x2是box2的中心在該軸上的坐標(biāo) # len2是box2在該軸上的長(zhǎng)度 # 返回值是該軸上重疊的長(zhǎng)度 def overlap(x1, len1, x2, len2):len1_half = len1 / 2len2_half = len2 / 2left = max(x1 - len1_half, x2 - len2_half)right = min(x1 + len1_half, x2 + len2_half)return right - left# 計(jì)算box a 和box b 的交集面積 # a和b都是Box類型實(shí)例 # 返回值area是box a 和box b 的交集面積 def box_intersection(a, b):w = overlap(a.x, a.w, b.x, b.w)h = overlap(a.y, a.h, b.y, b.h)if w < 0 or h < 0:return 0area = w * hreturn area# 計(jì)算 box a 和 box b 的并集面積 # a和b都是Box類型實(shí)例 # 返回值u是box a 和box b 的并集面積 def box_union(a, b):i = box_intersection(a, b)u = a.w * a.h + b.w * b.h - ireturn u# 計(jì)算 box a 和 box b 的 iou # a和b都是Box類型實(shí)例 # 返回值是box a 和box b 的iou def box_iou(a, b):return box_intersection(a, b) / box_union(a, b)# 使用k-means ++ 初始化 centroids,減少隨機(jī)初始化的centroids對(duì)最終結(jié)果的影響 # boxes是所有bounding boxes的Box對(duì)象列表 # n_anchors是k-means的k值 # 返回值centroids 是初始化的n_anchors個(gè)centroid def init_centroids(boxes,n_anchors):centroids = []boxes_num = len(boxes)centroid_index = np.random.choice(boxes_num, 1)centroids.append(boxes[centroid_index])print(centroids[0].w,centroids[0].h)for centroid_index in range(0,n_anchors-1):sum_distance = 0distance_thresh = 0distance_list = []cur_sum = 0for box in boxes:min_distance = 1for centroid_i, centroid in enumerate(centroids):distance = (1 - box_iou(box, centroid))if distance < min_distance:min_distance = distancesum_distance += min_distancedistance_list.append(min_distance)distance_thresh = sum_distance*np.random.random()for i in range(0,boxes_num):cur_sum += distance_list[i]if cur_sum > distance_thresh:centroids.append(boxes[i])print(boxes[i].w, boxes[i].h)breakreturn centroids# 進(jìn)行 k-means 計(jì)算新的centroids # boxes是所有bounding boxes的Box對(duì)象列表 # n_anchors是k-means的k值 # centroids是所有簇的中心 # 返回值new_centroids 是計(jì)算出的新簇中心 # 返回值groups是n_anchors個(gè)簇包含的boxes的列表 # 返回值loss是所有box距離所屬的最近的centroid的距離的和 def do_kmeans(n_anchors, boxes, centroids):loss = 0groups = []new_centroids = []for i in range(n_anchors):groups.append([])new_centroids.append(Box(0, 0, 0, 0))for box in boxes:min_distance = 1group_index = 0for centroid_index, centroid in enumerate(centroids):distance = (1 - box_iou(box, centroid))if distance < min_distance:min_distance = distancegroup_index = centroid_indexgroups[group_index].append(box)loss += min_distancenew_centroids[group_index].w += box.wnew_centroids[group_index].h += box.hfor i in range(n_anchors):new_centroids[i].w /= len(groups[i])new_centroids[i].h /= len(groups[i])return new_centroids, groups, loss# 計(jì)算給定bounding boxes的n_anchors數(shù)量的centroids # label_path是訓(xùn)練集列表文件地址 # n_anchors 是anchors的數(shù)量 # loss_convergence是允許的loss的最小變化值 # grid_size * grid_size 是柵格數(shù)量 # iterations_num是最大迭代次數(shù) # plus = 1時(shí)啟用k means ++ 初始化centroids def compute_centroids(label_path,n_anchors,loss_convergence,grid_size,iterations_num,plus):boxes = []label_files = []f = open(label_path)for line in f:label_path = line.rstrip().replace('images', 'labels')label_path = label_path.replace('JPEGImages', 'labels')label_path = label_path.replace('.jpg', '.txt')label_path = label_path.replace('.JPEG', '.txt')label_files.append(label_path)f.close()for label_file in label_files:f = open(label_file)for line in f:temp = line.strip().split(" ")if len(temp) > 1:boxes.append(Box(0, 0, float(temp[3]), float(temp[4])))if plus:centroids = init_centroids(boxes, n_anchors)else:centroid_indices = np.random.choice(len(boxes), n_anchors)centroids = []for centroid_index in centroid_indices:centroids.append(boxes[centroid_index])# iterate k-meanscentroids, groups, old_loss = do_kmeans(n_anchors, boxes, centroids)iterations = 1while (True):centroids, groups, loss = do_kmeans(n_anchors, boxes, centroids)iterations = iterations + 1print("loss = %f" % loss)if abs(old_loss - loss) < loss_convergence or iterations > iterations_num:breakold_loss = lossfor centroid in centroids:print(centroid.w * grid_size, centroid.h * grid_size)# print resultfor centroid in centroids:print("k-means result:\n")print(centroid.w * grid_size, centroid.h * grid_size) #只需修改這里的參數(shù)n_anchors和grid_size;得到的9個(gè)預(yù)選框的參數(shù)復(fù)制到cfg即可 #要修改的路徑--訓(xùn)練集train.txt的路徑 #label_path = "/home/chris/darknet/scripts/2007_train.txt" label_path = "/usr/cx/darknetalexeyAB/names_data/2007_train.txt" n_anchors = 9 #預(yù)選框anchors的個(gè)數(shù),6,9,12,15,根據(jù)自己的實(shí)際項(xiàng)目進(jìn)行設(shè)置; loss_convergence = 1e-6 grid_size = 416 #柵格的尺寸 iterations_num = 100 #迭代的步數(shù) plus = 0 #開(kāi)關(guān);=1時(shí),使用k-means++算法,一般=0。 compute_centroids(label_path,n_anchors,loss_convergence,grid_size,iterations_num,plus)腳本文件(命名為k-means.py)
運(yùn)行python k-means.py即可。
注意修改路徑。代碼注釋中已經(jīng)標(biāo)出。
(3)anchors值替換
在cfg文件的每個(gè)yolo層,進(jìn)行如下修改:
1)mask取值變?yōu)?~11,3個(gè)為一組,最前面一層yolo層的mask賦值為9,10,11
2)將第二行的anchors值更新替換成步驟(2)中計(jì)算得到的anchors值;
3)classes是類別數(shù),此項(xiàng)目?jī)H有1個(gè)類別,根據(jù)自己的項(xiàng)目修改classes的值;
3)將num=9改成num=12;
(4)模型訓(xùn)練
經(jīng)過(guò)增加cfg配置文件的檢測(cè)層,計(jì)算anchors,并將其更新到cfg配置文件中之后,接下來(lái)就可以進(jìn)行模型的訓(xùn)練了。
注意:由于我們沒(méi)有對(duì)backbone基礎(chǔ)網(wǎng)絡(luò)進(jìn)行修改,所以,可以使用darknet53.conv.74預(yù)訓(xùn)練權(quán)重進(jìn)行訓(xùn)練。
darknet53.conv.74下載鏈接如下:
darknet53.conv.74權(quán)重文件
鏈接:https://pan.baidu.com/s/14Hwqqsp_ua28Xu27gaQk6g
提取碼:dnai
總結(jié)
以上是生活随笔為你收集整理的YOLOv3改进方法增加特征尺度和训练层数的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: matlab for循环太慢,Matla
- 下一篇: commonjs 和 es6模块化开发入