YOLOv3改进方法增加特征尺度和训练层数
YOLOv3改進方法
YOLOv3的改進方法有很多,本文講述的是增加一個特征尺度。
以YOLOv3-darknet53(ALexeyAB版本)為基礎,增加了第4個特征尺度:104*104。原版YOLOv3網絡結構:
YOLOv3-4l網絡結構:
即,在經過2倍上采樣后,輸出的特征尺度由52x52提升至104x104,再通過route層將第109層與特征提取網絡的第11層特征進行特征融合,以充分利用深層特征和淺層特征。其余的特征融合分別為:2倍上采樣后輸出的第85層和第97層。通過route層分別將第85層與第61層,第97層與第36層的特征圖進行特征融合。四個特征尺度分別為:104x104,52x52,26x26和13x13。
具體的步驟為:
(1)修改配置文件cfg
再增加一個檢測尺度(在原yolov3的最后一層yolo層的后面,再增加一個檢測層:在下方鏈接里的cfg文件最后的“#######”注釋行之后的部分,便是增加的檢測層結構)。
yolo-4l的cfg下載地址
鏈接:https://pan.baidu.com/s/1b92jmcAPTgzxua4Pat7p4A
提取碼:xji2
注意:網盤里提供的cfg配置文件,需要進行相應參數修改(修改示例見鏈接:yolov3的cfg配置文件注釋及修改示例)。
(2)重新計算anchors
由于原先是3個檢測尺度共9個anchors,此時是4層共12個anchors。且不同數據庫的anchors值不一樣(比如自行構建的數據庫),所以必須重新計算anchors,并更新到cfg文件中。如果選取的先驗框維度比較合適,那么模型就會更容易學習,更易收斂,從而做出更好的預測,預測框與標注真實框的IOU就會更好。
計算數據庫的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的路徑,根據自己的項目自行進行修改。
結果如下圖所示:
計算多次,每次的anchors值會不一樣,但基本相差無幾。其實這些anchors值,就是先驗框,就是樣本庫里最經常出現的幾類邊界框。通過選取專屬于實際數據庫的anchors,將會加速收斂,更容易學習,提高IOU值。
這里還有另外1種計算anchors的方法, 通過腳本文件來計算anchors錨點值。腳本文件如下所示:
# coding=utf-8 # 通過k-means ++ 算法獲取anchors的尺寸 import numpy as np# 定義Box類,描述bounding box的坐標 class Box():def __init__(self, x, y, w, h):self.x = xself.y = yself.w = wself.h = h# 計算兩個box在某個軸上的重疊部分 # x1是box1的中心在該軸上的坐標 # len1是box1在該軸上的長度 # x2是box2的中心在該軸上的坐標 # len2是box2在該軸上的長度 # 返回值是該軸上重疊的長度 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# 計算box a 和box b 的交集面積 # a和b都是Box類型實例 # 返回值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# 計算 box a 和 box b 的并集面積 # a和b都是Box類型實例 # 返回值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# 計算 box a 和 box b 的 iou # a和b都是Box類型實例 # 返回值是box a 和box b 的iou def box_iou(a, b):return box_intersection(a, b) / box_union(a, b)# 使用k-means ++ 初始化 centroids,減少隨機初始化的centroids對最終結果的影響 # boxes是所有bounding boxes的Box對象列表 # n_anchors是k-means的k值 # 返回值centroids 是初始化的n_anchors個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# 進行 k-means 計算新的centroids # boxes是所有bounding boxes的Box對象列表 # n_anchors是k-means的k值 # centroids是所有簇的中心 # 返回值new_centroids 是計算出的新簇中心 # 返回值groups是n_anchors個簇包含的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# 計算給定bounding boxes的n_anchors數量的centroids # label_path是訓練集列表文件地址 # n_anchors 是anchors的數量 # loss_convergence是允許的loss的最小變化值 # grid_size * grid_size 是柵格數量 # iterations_num是最大迭代次數 # plus = 1時啟用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) #只需修改這里的參數n_anchors和grid_size;得到的9個預選框的參數復制到cfg即可 #要修改的路徑--訓練集train.txt的路徑 #label_path = "/home/chris/darknet/scripts/2007_train.txt" label_path = "/usr/cx/darknetalexeyAB/names_data/2007_train.txt" n_anchors = 9 #預選框anchors的個數,6,9,12,15,根據自己的實際項目進行設置; loss_convergence = 1e-6 grid_size = 416 #柵格的尺寸 iterations_num = 100 #迭代的步數 plus = 0 #開關;=1時,使用k-means++算法,一般=0。 compute_centroids(label_path,n_anchors,loss_convergence,grid_size,iterations_num,plus)腳本文件(命名為k-means.py)
運行python k-means.py即可。
注意修改路徑。代碼注釋中已經標出。
(3)anchors值替換
在cfg文件的每個yolo層,進行如下修改:
1)mask取值變為0~11,3個為一組,最前面一層yolo層的mask賦值為9,10,11
2)將第二行的anchors值更新替換成步驟(2)中計算得到的anchors值;
3)classes是類別數,此項目僅有1個類別,根據自己的項目修改classes的值;
3)將num=9改成num=12;
(4)模型訓練
經過增加cfg配置文件的檢測層,計算anchors,并將其更新到cfg配置文件中之后,接下來就可以進行模型的訓練了。
注意:由于我們沒有對backbone基礎網絡進行修改,所以,可以使用darknet53.conv.74預訓練權重進行訓練。
darknet53.conv.74下載鏈接如下:
darknet53.conv.74權重文件
鏈接:https://pan.baidu.com/s/14Hwqqsp_ua28Xu27gaQk6g
提取碼:dnai
總結
以上是生活随笔為你收集整理的YOLOv3改进方法增加特征尺度和训练层数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab for循环太慢,Matla
- 下一篇: commonjs 和 es6模块化开发入