From d18adf8df9f5b884cee60a3c5c0eefd1aa52e10a Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Wed, 24 Jun 2020 15:53:34 -0400 Subject: [PATCH 01/42] Add files via upload --- data/VOC2007.sh | 42 +++++++++++++++++++++++++++++++ data/VOC2012.sh | 38 ++++++++++++++++++++++++++++ data/VOC_split.py | 60 ++++++++++++++++++++++++++++++++++++++++++++ data/organize_VOC.py | 47 ++++++++++++++++++++++++++++++++++ data/voc.yml | 10 ++++++++ 5 files changed, 197 insertions(+) create mode 100644 data/VOC2007.sh create mode 100644 data/VOC2012.sh create mode 100644 data/VOC_split.py create mode 100644 data/organize_VOC.py create mode 100644 data/voc.yml diff --git a/data/VOC2007.sh b/data/VOC2007.sh new file mode 100644 index 000000000000..69bad20bd86c --- /dev/null +++ b/data/VOC2007.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# Ellis Brown + +start=`date +%s` + +# handle optional download dir +if [ -z "$1" ] + then + # navigate to ~/data + echo "navigating to ../data/ ..." + mkdir -p ./data + cd ./data/ + else + # check if is valid directory + if [ ! -d $1 ]; then + echo $1 "is not a valid directory" + exit 0 + fi + echo "navigating to" $1 "..." + cd $1 +fi + +echo "Downloading VOC2007 trainval ..." +# Download the data. +curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar +echo "Downloading VOC2007 test data ..." +curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar +echo "Done downloading." + +# Extract data +echo "Extracting trainval ..." +tar -xvf VOCtrainval_06-Nov-2007.tar +echo "Extracting test ..." +tar -xvf VOCtest_06-Nov-2007.tar +echo "removing tars ..." +rm VOCtrainval_06-Nov-2007.tar +rm VOCtest_06-Nov-2007.tar + +end=`date +%s` +runtime=$((end-start)) + +echo "Completed in" $runtime "seconds" \ No newline at end of file diff --git a/data/VOC2012.sh b/data/VOC2012.sh new file mode 100644 index 000000000000..945f8d742ca9 --- /dev/null +++ b/data/VOC2012.sh @@ -0,0 +1,38 @@ +#!/bin/bash +# Ellis Brown + +start=`date +%s` + +# handle optional download dir +if [ -z "$1" ] + then + # navigate to ~/data + echo "navigating to ~/data/ ..." + mkdir -p ./data + cd ./data/ + else + # check if is valid directory + if [ ! -d $1 ]; then + echo $1 "is not a valid directory" + exit 0 + fi + echo "navigating to" $1 "..." + cd $1 +fi + +echo "Downloading VOC2012 trainval ..." +# Download the data. +curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar +echo "Done downloading." + + +# Extract data +echo "Extracting trainval ..." +tar -xvf VOCtrainval_11-May-2012.tar +echo "removing tar ..." +rm VOCtrainval_11-May-2012.tar + +end=`date +%s` +runtime=$((end-start)) + +echo "Completed in" $runtime "seconds" \ No newline at end of file diff --git a/data/VOC_split.py b/data/VOC_split.py new file mode 100644 index 000000000000..69405a31d345 --- /dev/null +++ b/data/VOC_split.py @@ -0,0 +1,60 @@ + +# Run this file with folder VOCdevkit. +import xml.etree.ElementTree as ET +import pickle +import os +from os import listdir, getcwd +from os.path import join + +sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')] + +classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"] + + +def convert(size, box): + dw = 1./(size[0]) + dh = 1./(size[1]) + x = (box[0] + box[1])/2.0 - 1 + y = (box[2] + box[3])/2.0 - 1 + w = box[1] - box[0] + h = box[3] - box[2] + x = x*dw + w = w*dw + y = y*dh + h = h*dh + return (x,y,w,h) + +def convert_annotation(year, image_id): + in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id)) + out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), 'w') + tree=ET.parse(in_file) + root = tree.getroot() + size = root.find('size') + w = int(size.find('width').text) + h = int(size.find('height').text) + + for obj in root.iter('object'): + difficult = obj.find('difficult').text + cls = obj.find('name').text + if cls not in classes or int(difficult)==1: + continue + cls_id = classes.index(cls) + xmlbox = obj.find('bndbox') + b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)) + bb = convert((w,h), b) + out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') + +wd = getcwd() + +for year, image_set in sets: + if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)): + os.makedirs('VOCdevkit/VOC%s/labels/'%(year)) + image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split() + list_file = open('%s_%s.txt'%(year, image_set), 'w') + for image_id in image_ids: + list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id)) + convert_annotation(year, image_id) + list_file.close() + +os.system("cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt > train.txt") +os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt") \ No newline at end of file diff --git a/data/organize_VOC.py b/data/organize_VOC.py new file mode 100644 index 000000000000..d4ccf3a41a54 --- /dev/null +++ b/data/organize_VOC.py @@ -0,0 +1,47 @@ +print(os.path.exists('../data/train.txt')) +f = open('../data/train.txt', 'r') +lines = f.readlines() + +for line in lines: + #print(line.split('/')[-1][:-1]) + line = "/".join(line.split('/')[2:]) + + if (os.path.exists(line[:-1])): + os.system("cp "+ line[:-1] + " VOC/images/train") + +print(os.path.exists('../data/train.txt')) +f = open('../data/train.txt', 'r') +lines = f.readlines() + +for line in lines: + #print(line.split('/')[-1][:-1]) + line = "/".join(line.split('/')[2:]) + line = line.replace('JPEGImages', 'labels') + line = line.replace('jpg', 'txt') + #print(line) + if (os.path.exists(line[:-1])): + os.system("cp "+ line[:-1] + " VOC/labels/train") + +print(os.path.exists('../data/2007_test.txt')) +f = open('../data/2007_test.txt', 'r') +lines = f.readlines() + +for line in lines: + #print(line.split('/')[-1][:-1]) + line = "/".join(line.split('/')[2:]) + + if (os.path.exists(line[:-1])): + os.system("cp "+ line[:-1] + " VOC/images/val") + +print(os.path.exists('../data/2007_test.txt')) +f = open('../data/2007_test.txt', 'r') +lines = f.readlines() + +for line in lines: + #print(line.split('/')[-1][:-1]) + line = "/".join(line.split('/')[2:]) + line = line.replace('JPEGImages', 'labels') + line = line.replace('jpg', 'txt') + #print(line) + if (os.path.exists(line[:-1])): + os.system("cp "+ line[:-1] + " VOC/labels/val") \ No newline at end of file diff --git a/data/voc.yml b/data/voc.yml new file mode 100644 index 000000000000..d531fa815911 --- /dev/null +++ b/data/voc.yml @@ -0,0 +1,10 @@ + +# train and val datasets (image directory or *.txt file with image paths) +train: ../VOC/images/train/ +val: ../VOC/images/val// + +# number of classes +nc: 20 + +# class names +names: ['aeroplane', 'bicycle','bird','boat','bottle','bus','car','cat','chair','cow','diningtable','dog','horse','motorbike','person','pottedplant','sheep','sofa','train','tvmonitor'] \ No newline at end of file From a8d17ce7124e81907020d5d8e7737a22a46657f0 Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Thu, 2 Jul 2020 14:25:28 -0400 Subject: [PATCH 02/42] Update train.py --- train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/train.py b/train.py index 2dfe78112721..700cddd668af 100644 --- a/train.py +++ b/train.py @@ -37,7 +37,7 @@ 'obj_pw': 1.0, # obj BCELoss positive_weight 'iou_t': 0.20, # iou training threshold 'anchor_t': 4.0, # anchor-multiple threshold - 'fl_gamma': 0.0, # focal loss gamma (efficientDet default is gamma=1.5) + 'fl_gamma': 1.5, # focal loss gamma (efficientDet default is gamma=1.5) 'hsv_h': 0.014, # image HSV-Hue augmentation (fraction) 'hsv_s': 0.68, # image HSV-Saturation augmentation (fraction) 'hsv_v': 0.36, # image HSV-Value augmentation (fraction) From 9705c15da1763c885990427139b1724758e4b8ce Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Thu, 2 Jul 2020 11:37:48 -0700 Subject: [PATCH 03/42] Update train.py --- train.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/train.py b/train.py index 700cddd668af..2dfe78112721 100644 --- a/train.py +++ b/train.py @@ -37,7 +37,7 @@ 'obj_pw': 1.0, # obj BCELoss positive_weight 'iou_t': 0.20, # iou training threshold 'anchor_t': 4.0, # anchor-multiple threshold - 'fl_gamma': 1.5, # focal loss gamma (efficientDet default is gamma=1.5) + 'fl_gamma': 0.0, # focal loss gamma (efficientDet default is gamma=1.5) 'hsv_h': 0.014, # image HSV-Hue augmentation (fraction) 'hsv_s': 0.68, # image HSV-Saturation augmentation (fraction) 'hsv_v': 0.36, # image HSV-Value augmentation (fraction) From 570dcf05a92512bf863158447631d2653466c59e Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Thu, 2 Jul 2020 17:04:54 -0400 Subject: [PATCH 04/42] Delete VOC2007.sh --- data/VOC2007.sh | 42 ------------------------------------------ 1 file changed, 42 deletions(-) delete mode 100644 data/VOC2007.sh diff --git a/data/VOC2007.sh b/data/VOC2007.sh deleted file mode 100644 index 69bad20bd86c..000000000000 --- a/data/VOC2007.sh +++ /dev/null @@ -1,42 +0,0 @@ -#!/bin/bash -# Ellis Brown - -start=`date +%s` - -# handle optional download dir -if [ -z "$1" ] - then - # navigate to ~/data - echo "navigating to ../data/ ..." - mkdir -p ./data - cd ./data/ - else - # check if is valid directory - if [ ! -d $1 ]; then - echo $1 "is not a valid directory" - exit 0 - fi - echo "navigating to" $1 "..." - cd $1 -fi - -echo "Downloading VOC2007 trainval ..." -# Download the data. -curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar -echo "Downloading VOC2007 test data ..." -curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar -echo "Done downloading." - -# Extract data -echo "Extracting trainval ..." -tar -xvf VOCtrainval_06-Nov-2007.tar -echo "Extracting test ..." -tar -xvf VOCtest_06-Nov-2007.tar -echo "removing tars ..." -rm VOCtrainval_06-Nov-2007.tar -rm VOCtest_06-Nov-2007.tar - -end=`date +%s` -runtime=$((end-start)) - -echo "Completed in" $runtime "seconds" \ No newline at end of file From 2bcf3da84835183e9e7796ed5400dd12754b1634 Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Thu, 2 Jul 2020 17:05:02 -0400 Subject: [PATCH 05/42] Delete VOC2012.sh --- data/VOC2012.sh | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 data/VOC2012.sh diff --git a/data/VOC2012.sh b/data/VOC2012.sh deleted file mode 100644 index 945f8d742ca9..000000000000 --- a/data/VOC2012.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -# Ellis Brown - -start=`date +%s` - -# handle optional download dir -if [ -z "$1" ] - then - # navigate to ~/data - echo "navigating to ~/data/ ..." - mkdir -p ./data - cd ./data/ - else - # check if is valid directory - if [ ! -d $1 ]; then - echo $1 "is not a valid directory" - exit 0 - fi - echo "navigating to" $1 "..." - cd $1 -fi - -echo "Downloading VOC2012 trainval ..." -# Download the data. -curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar -echo "Done downloading." - - -# Extract data -echo "Extracting trainval ..." -tar -xvf VOCtrainval_11-May-2012.tar -echo "removing tar ..." -rm VOCtrainval_11-May-2012.tar - -end=`date +%s` -runtime=$((end-start)) - -echo "Completed in" $runtime "seconds" \ No newline at end of file From 389ed1f9d686a5638692fc0848460ace0e934ed8 Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Thu, 2 Jul 2020 17:05:09 -0400 Subject: [PATCH 06/42] Delete VOC_split.py --- data/VOC_split.py | 60 ----------------------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 data/VOC_split.py diff --git a/data/VOC_split.py b/data/VOC_split.py deleted file mode 100644 index 69405a31d345..000000000000 --- a/data/VOC_split.py +++ /dev/null @@ -1,60 +0,0 @@ - -# Run this file with folder VOCdevkit. -import xml.etree.ElementTree as ET -import pickle -import os -from os import listdir, getcwd -from os.path import join - -sets=[('2012', 'train'), ('2012', 'val'), ('2007', 'train'), ('2007', 'val'), ('2007', 'test')] - -classes = ["aeroplane", "bicycle", "bird", "boat", "bottle", "bus", "car", "cat", "chair", "cow", "diningtable", "dog", "horse", "motorbike", "person", "pottedplant", "sheep", "sofa", "train", "tvmonitor"] - - -def convert(size, box): - dw = 1./(size[0]) - dh = 1./(size[1]) - x = (box[0] + box[1])/2.0 - 1 - y = (box[2] + box[3])/2.0 - 1 - w = box[1] - box[0] - h = box[3] - box[2] - x = x*dw - w = w*dw - y = y*dh - h = h*dh - return (x,y,w,h) - -def convert_annotation(year, image_id): - in_file = open('VOCdevkit/VOC%s/Annotations/%s.xml'%(year, image_id)) - out_file = open('VOCdevkit/VOC%s/labels/%s.txt'%(year, image_id), 'w') - tree=ET.parse(in_file) - root = tree.getroot() - size = root.find('size') - w = int(size.find('width').text) - h = int(size.find('height').text) - - for obj in root.iter('object'): - difficult = obj.find('difficult').text - cls = obj.find('name').text - if cls not in classes or int(difficult)==1: - continue - cls_id = classes.index(cls) - xmlbox = obj.find('bndbox') - b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text), float(xmlbox.find('ymax').text)) - bb = convert((w,h), b) - out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n') - -wd = getcwd() - -for year, image_set in sets: - if not os.path.exists('VOCdevkit/VOC%s/labels/'%(year)): - os.makedirs('VOCdevkit/VOC%s/labels/'%(year)) - image_ids = open('VOCdevkit/VOC%s/ImageSets/Main/%s.txt'%(year, image_set)).read().strip().split() - list_file = open('%s_%s.txt'%(year, image_set), 'w') - for image_id in image_ids: - list_file.write('%s/VOCdevkit/VOC%s/JPEGImages/%s.jpg\n'%(wd, year, image_id)) - convert_annotation(year, image_id) - list_file.close() - -os.system("cat 2007_train.txt 2007_val.txt 2012_train.txt 2012_val.txt > train.txt") -os.system("cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt") \ No newline at end of file From 0bd619b5aaaae5102c5fde110276c700d400d31a Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Thu, 2 Jul 2020 17:05:25 -0400 Subject: [PATCH 07/42] Delete voc.yml --- data/voc.yml | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 data/voc.yml diff --git a/data/voc.yml b/data/voc.yml deleted file mode 100644 index d531fa815911..000000000000 --- a/data/voc.yml +++ /dev/null @@ -1,10 +0,0 @@ - -# train and val datasets (image directory or *.txt file with image paths) -train: ../VOC/images/train/ -val: ../VOC/images/val// - -# number of classes -nc: 20 - -# class names -names: ['aeroplane', 'bicycle','bird','boat','bottle','bus','car','cat','chair','cow','diningtable','dog','horse','motorbike','person','pottedplant','sheep','sofa','train','tvmonitor'] \ No newline at end of file From 4503d804490bdb1875c15807cf107fc1d3310401 Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Thu, 2 Jul 2020 17:05:33 -0400 Subject: [PATCH 08/42] Delete organize_VOC.py --- data/organize_VOC.py | 47 -------------------------------------------- 1 file changed, 47 deletions(-) delete mode 100644 data/organize_VOC.py diff --git a/data/organize_VOC.py b/data/organize_VOC.py deleted file mode 100644 index d4ccf3a41a54..000000000000 --- a/data/organize_VOC.py +++ /dev/null @@ -1,47 +0,0 @@ -print(os.path.exists('../data/train.txt')) -f = open('../data/train.txt', 'r') -lines = f.readlines() - -for line in lines: - #print(line.split('/')[-1][:-1]) - line = "/".join(line.split('/')[2:]) - - if (os.path.exists(line[:-1])): - os.system("cp "+ line[:-1] + " VOC/images/train") - -print(os.path.exists('../data/train.txt')) -f = open('../data/train.txt', 'r') -lines = f.readlines() - -for line in lines: - #print(line.split('/')[-1][:-1]) - line = "/".join(line.split('/')[2:]) - line = line.replace('JPEGImages', 'labels') - line = line.replace('jpg', 'txt') - #print(line) - if (os.path.exists(line[:-1])): - os.system("cp "+ line[:-1] + " VOC/labels/train") - -print(os.path.exists('../data/2007_test.txt')) -f = open('../data/2007_test.txt', 'r') -lines = f.readlines() - -for line in lines: - #print(line.split('/')[-1][:-1]) - line = "/".join(line.split('/')[2:]) - - if (os.path.exists(line[:-1])): - os.system("cp "+ line[:-1] + " VOC/images/val") - -print(os.path.exists('../data/2007_test.txt')) -f = open('../data/2007_test.txt', 'r') -lines = f.readlines() - -for line in lines: - #print(line.split('/')[-1][:-1]) - line = "/".join(line.split('/')[2:]) - line = line.replace('JPEGImages', 'labels') - line = line.replace('jpg', 'txt') - #print(line) - if (os.path.exists(line[:-1])): - os.system("cp "+ line[:-1] + " VOC/labels/val") \ No newline at end of file From 8c3b829214fb8cdae96c61091c0636c497be76bb Mon Sep 17 00:00:00 2001 From: ChristopherSTAN <497592613@qq.com> Date: Thu, 2 Jul 2020 17:05:57 -0400 Subject: [PATCH 09/42] Add files via upload --- data/get_voc.sh | 206 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) create mode 100644 data/get_voc.sh diff --git a/data/get_voc.sh b/data/get_voc.sh new file mode 100644 index 000000000000..f035bb4c2450 --- /dev/null +++ b/data/get_voc.sh @@ -0,0 +1,206 @@ + +start=`date +%s` + +# handle optional download dir +if [ -z "$1" ] + then + # navigate to ~/data + echo "navigating to ../data/ ..." + mkdir -p ../data + cd ../data/ + else + # check if is valid directory + if [ ! -d $1 ]; then + echo $1 "is not a valid directory" + exit 0 + fi + echo "navigating to" $1 "..." + cd $1 +fi + +echo "Downloading VOC2007 trainval ..." +# Download the data. +curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtrainval_06-Nov-2007.tar +echo "Downloading VOC2007 test data ..." +curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2007/VOCtest_06-Nov-2007.tar +echo "Done downloading." + +# Extract data +echo "Extracting trainval ..." +tar -xvf VOCtrainval_06-Nov-2007.tar +echo "Extracting test ..." +tar -xvf VOCtest_06-Nov-2007.tar +echo "removing tars ..." +rm VOCtrainval_06-Nov-2007.tar +rm VOCtest_06-Nov-2007.tar + +end=`date +%s` +runtime=$((end-start)) + +echo "Completed in" $runtime "seconds" + +start=`date +%s` + +# handle optional download dir +if [ -z "$1" ] + then + # navigate to ~/data + echo "navigating to ../data/ ..." + mkdir -p ../data + cd ../data/ + else + # check if is valid directory + if [ ! -d $1 ]; then + echo $1 "is not a valid directory" + exit 0 + fi + echo "navigating to" $1 "..." + cd $1 +fi + +echo "Downloading VOC2012 trainval ..." +# Download the data. +curl -LO http://host.robots.ox.ac.uk/pascal/VOC/voc2012/VOCtrainval_11-May-2012.tar +echo "Done downloading." + + +# Extract data +echo "Extracting trainval ..." +tar -xvf VOCtrainval_11-May-2012.tar +echo "removing tar ..." +rm VOCtrainval_11-May-2012.tar + +end=`date +%s` +runtime=$((end-start)) + +echo "Completed in" $runtime "seconds" + +cd ../data +echo "Spliting dataset..." +python3 - "$@" < train.txt +cat 2007_train.txt 2007_val.txt 2007_test.txt 2012_train.txt 2012_val.txt > train.all.txt + +python3 - "$@" < Date: Thu, 2 Jul 2020 17:06:59 -0400 Subject: [PATCH 10/42] Create voc.yaml --- data/voc.yaml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 data/voc.yaml diff --git a/data/voc.yaml b/data/voc.yaml new file mode 100644 index 000000000000..e2641d6af1f4 --- /dev/null +++ b/data/voc.yaml @@ -0,0 +1,10 @@ + +# train and val datasets (image directory or *.txt file with image paths) +train: ../VOC/images/train/ +val: ../VOC/images/val// + +# number of classes +nc: 20 + +# class names +names: ['aeroplane', 'bicycle','bird','boat','bottle','bus','car','cat','chair','cow','diningtable','dog','horse','motorbike','person','pottedplant','sheep','sofa','train','tvmonitor'] From 569fbc63c8d41ca9810ca8f3c5d9ed44cebd9323 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 11:29:53 -0700 Subject: [PATCH 11/42] export.py update --- models/export.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/models/export.py b/models/export.py index bb310f3f89a0..42fd577acb0c 100644 --- a/models/export.py +++ b/models/export.py @@ -30,6 +30,7 @@ # TorchScript export try: + print('\nStarting TorchScript export with torch %s...' % torch.__version) f = opt.weights.replace('.pt', '.torchscript') # filename ts = torch.jit.trace(model, img) ts.save(f) @@ -41,6 +42,7 @@ try: import onnx + print('\nStarting ONNX export with onnx %s...' % onnx.__version__) f = opt.weights.replace('.pt', '.onnx') # filename model.fuse() # only for ONNX torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'], @@ -49,7 +51,7 @@ # Checks onnx_model = onnx.load(f) # load onnx model onnx.checker.check_model(onnx_model) # check onnx model - print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable representation of the graph + print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable model print('ONNX export success, saved as %s\nView with https://github.com/lutzroeder/netron' % f) except Exception as e: print('ONNX export failed: %s' % e) From a9d20eba3e61ac5b7e77e0a7c07828fb2cf84bb5 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 11:50:59 -0700 Subject: [PATCH 12/42] export.py update --- models/export.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/models/export.py b/models/export.py index 42fd577acb0c..1ff95266317b 100644 --- a/models/export.py +++ b/models/export.py @@ -30,7 +30,7 @@ # TorchScript export try: - print('\nStarting TorchScript export with torch %s...' % torch.__version) + print('\nStarting TorchScript export with torch %s...' % torch.__version__) f = opt.weights.replace('.pt', '.torchscript') # filename ts = torch.jit.trace(model, img) ts.save(f) @@ -52,6 +52,9 @@ onnx_model = onnx.load(f) # load onnx model onnx.checker.check_model(onnx_model) # check onnx model print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable model - print('ONNX export success, saved as %s\nView with https://github.com/lutzroeder/netron' % f) + print('ONNX export success, saved as %s' % f) except Exception as e: print('ONNX export failed: %s' % e) + + # Finish + print('\nExports complete. Visualize with https://github.com/lutzroeder/netron.') \ No newline at end of file From df224a0d8fb115c2cedc369cd03f506efc25bbdd Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 11:56:14 -0700 Subject: [PATCH 13/42] EMA bug fix #279 --- train.py | 4 ++-- utils/torch_utils.py | 21 +++++++++------------ 2 files changed, 11 insertions(+), 14 deletions(-) diff --git a/train.py b/train.py index 08a0fe5db6be..1c8ff7ddd891 100644 --- a/train.py +++ b/train.py @@ -294,7 +294,7 @@ def train(hyp): batch_size=batch_size, imgsz=imgsz_test, save_json=final_epoch and opt.data.endswith(os.sep + 'coco.yaml'), - model=ema.ema.module if hasattr(model, 'module') else ema.ema, + model=ema.ema, single_cls=opt.single_cls, dataloader=testloader) @@ -324,7 +324,7 @@ def train(hyp): ckpt = {'epoch': epoch, 'best_fitness': best_fitness, 'training_results': f.read(), - 'model': ema.ema.module if hasattr(model, 'module') else ema.ema, + 'model': ema.ema, 'optimizer': None if final_epoch else optimizer.state_dict()} # Save last, best and delete diff --git a/utils/torch_utils.py b/utils/torch_utils.py index a62adc9cf122..3cbec8ba1d33 100644 --- a/utils/torch_utils.py +++ b/utils/torch_utils.py @@ -175,8 +175,8 @@ class ModelEMA: """ def __init__(self, model, decay=0.9999, device=''): - # make a copy of the model for accumulating moving average of weights - self.ema = deepcopy(model) + # Create EMA + self.ema = deepcopy(model.module if is_parallel(model) else model).half() # FP16 EMA self.ema.eval() self.updates = 0 # number of EMA updates self.decay = lambda x: decay * (1 - math.exp(-x / 2000)) # decay exponential ramp (to help early epochs) @@ -187,22 +187,19 @@ def __init__(self, model, decay=0.9999, device=''): p.requires_grad_(False) def update(self, model): - self.updates += 1 - d = self.decay(self.updates) + # Update EMA parameters with torch.no_grad(): - if is_parallel(model): - msd, esd = model.module.state_dict(), self.ema.module.state_dict() - else: - msd, esd = model.state_dict(), self.ema.state_dict() + self.updates += 1 + d = self.decay(self.updates) - for k, v in esd.items(): + msd = model.module.state_dict() if is_parallel(model) else model.state_dict() # model state_dict + for k, v in self.ema.state_dict().items(): if v.dtype.is_floating_point: v *= d v += (1. - d) * msd[k].detach() def update_attr(self, model): - # Update class attributes - ema = self.ema.module if is_parallel(model) else self.ema + # Update EMA attributes for k, v in model.__dict__.items(): if not k.startswith('_') and k != 'module': - setattr(ema, k, v) + setattr(self.ema, k, v) From 655895a8382fcafd0e346794f401c081f57f2dc4 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 13:46:12 -0700 Subject: [PATCH 14/42] module updates --- models/experimental.py | 22 ++++++---------------- models/yolo.py | 2 +- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/models/experimental.py b/models/experimental.py index cff9d141446d..539e7f970ac3 100644 --- a/models/experimental.py +++ b/models/experimental.py @@ -4,12 +4,13 @@ class CrossConv(nn.Module): - # Cross Convolution - def __init__(self, c1, c2, shortcut=True, g=1, e=0.5): # ch_in, ch_out, shortcut, groups, expansion + # Cross Convolution Downsample + def __init__(self, c1, c2, k=3, s=1, g=1, e=1.0, shortcut=False): + # ch_in, ch_out, kernel, stride, groups, expansion, shortcut super(CrossConv, self).__init__() c_ = int(c2 * e) # hidden channels - self.cv1 = Conv(c1, c_, (1, 3), 1) - self.cv2 = Conv(c_, c2, (3, 1), 1, g=g) + self.cv1 = Conv(c1, c_, (1, k), (1, s)) + self.cv2 = Conv(c_, c2, (k, 1), (s, 1), g=g) self.add = shortcut and c1 == c2 def forward(self, x): @@ -27,7 +28,7 @@ def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5): # ch_in, ch_out, nu self.cv4 = Conv(2 * c_, c2, 1, 1) self.bn = nn.BatchNorm2d(2 * c_) # applied to cat(cv2, cv3) self.act = nn.LeakyReLU(0.1, inplace=True) - self.m = nn.Sequential(*[CrossConv(c_, c_, shortcut, g, e=1.0) for _ in range(n)]) + self.m = nn.Sequential(*[CrossConv(c_, c_, 3, 1, g, 1.0, shortcut) for _ in range(n)]) def forward(self, x): y1 = self.cv3(self.m(self.cv1(x))) @@ -84,17 +85,6 @@ def forward(self, x): return self.conv(x) + self.shortcut(x) -class ConvPlus(nn.Module): - # Plus-shaped convolution - def __init__(self, c1, c2, k=3, s=1, g=1, bias=True): # ch_in, ch_out, kernel, stride, groups - super(ConvPlus, self).__init__() - self.cv1 = nn.Conv2d(c1, c2, (k, 1), s, (k // 2, 0), groups=g, bias=bias) - self.cv2 = nn.Conv2d(c1, c2, (1, k), s, (0, k // 2), groups=g, bias=bias) - - def forward(self, x): - return self.cv1(x) + self.cv2(x) - - class MixConv2d(nn.Module): # Mixed Depthwise Conv https://arxiv.org/abs/1907.09595 def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): diff --git a/models/yolo.py b/models/yolo.py index c9e6c493d2a2..7cc86b2bc296 100644 --- a/models/yolo.py +++ b/models/yolo.py @@ -161,7 +161,7 @@ def parse_model(md, ch): # model_dict, input_channels(3) pass n = max(round(n * gd), 1) if n > 1 else n # depth gain - if m in [nn.Conv2d, Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, ConvPlus, BottleneckCSP]: + if m in [nn.Conv2d, Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, BottleneckCSP, CrossConv]: c1, c2 = ch[f], args[0] # Normal From f767023c56b9a8154c8fbf5b702dc54eb6eaef7f Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 14:07:00 -0700 Subject: [PATCH 15/42] offset and balance update --- utils/utils.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/utils/utils.py b/utils/utils.py index 305486a5f6a3..d9ffaacbe53b 100755 --- a/utils/utils.py +++ b/utils/utils.py @@ -438,6 +438,7 @@ def compute_loss(p, targets, model): # predictions, targets, model # per output nt = 0 # targets + balance = [1.0, 1.0, 1.0] for i, pi in enumerate(p): # layer index, layer predictions b, a, gj, gi = indices[i] # image, anchor, gridy, gridx tobj = torch.zeros_like(pi[..., 0]) # target obj @@ -467,11 +468,12 @@ def compute_loss(p, targets, model): # predictions, targets, model # with open('targets.txt', 'a') as file: # [file.write('%11.5g ' * 4 % tuple(x) + '\n') for x in torch.cat((txy[i], twh[i]), 1)] - lobj += BCEobj(pi[..., 4], tobj) # obj loss + lobj += BCEobj(pi[..., 4], tobj) * balance[i] # obj loss - lbox *= h['giou'] - lobj *= h['obj'] - lcls *= h['cls'] + s = 3 / (i + 1) # output count scaling + lbox *= h['giou'] * s + lobj *= h['obj'] * s + lcls *= h['cls'] * s bs = tobj.shape[0] # batch size if red == 'sum': g = 3.0 # loss gain @@ -508,16 +510,15 @@ def build_targets(p, targets, model): a, t = at[j], t.repeat(na, 1, 1)[j] # filter # overlaps + g = 0.5 # offset gxy = t[:, 2:4] # grid xy z = torch.zeros_like(gxy) if style == 'rect2': - g = 0.2 # offset j, k = ((gxy % 1. < g) & (gxy > 1.)).T a, t = torch.cat((a, a[j], a[k]), 0), torch.cat((t, t[j], t[k]), 0) offsets = torch.cat((z, z[j] + off[0], z[k] + off[1]), 0) * g elif style == 'rect4': - g = 0.5 # offset j, k = ((gxy % 1. < g) & (gxy > 1.)).T l, m = ((gxy % 1. > (1 - g)) & (gxy < (gain[[2, 3]] - 1.))).T a, t = torch.cat((a, a[j], a[k], a[l], a[m]), 0), torch.cat((t, t[j], t[k], t[l], t[m]), 0) @@ -764,11 +765,11 @@ def print_results(k): wh0 = np.concatenate([l[:, 3:5] * s for s, l in zip(shapes, dataset.labels)]) # wh # Filter - i = (wh0 < 4.0).any(1).sum() + i = (wh0 < 3.0).any(1).sum() if i: print('WARNING: Extremely small objects found. ' - '%g of %g labels are < 4 pixels in width or height.' % (i, len(wh0))) - wh = wh0[(wh0 >= 4.0).any(1)] # filter > 2 pixels + '%g of %g labels are < 3 pixels in width or height.' % (i, len(wh0))) + wh = wh0[(wh0 >= 2.0).any(1)] # filter > 2 pixels # Kmeans calculation from scipy.cluster.vq import kmeans From af41083d52992d86da7cc74734b4cf14bdb56e04 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 16:57:08 -0700 Subject: [PATCH 16/42] EMA FP16 fix #279 --- utils/torch_utils.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/utils/torch_utils.py b/utils/torch_utils.py index 3cbec8ba1d33..dd2e6e75ff97 100644 --- a/utils/torch_utils.py +++ b/utils/torch_utils.py @@ -176,13 +176,13 @@ class ModelEMA: def __init__(self, model, decay=0.9999, device=''): # Create EMA - self.ema = deepcopy(model.module if is_parallel(model) else model).half() # FP16 EMA + self.ema = deepcopy(model.module if is_parallel(model) else model) # FP32 EMA self.ema.eval() self.updates = 0 # number of EMA updates self.decay = lambda x: decay * (1 - math.exp(-x / 2000)) # decay exponential ramp (to help early epochs) self.device = device # perform ema on different device from model if set if device: - self.ema.to(device=device) + self.ema.to(device) for p in self.ema.parameters(): p.requires_grad_(False) From ce0c58f6781609ad11d99481999c9b9aa87cb91b Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 18:56:07 -0700 Subject: [PATCH 17/42] update compute_loss() --- utils/utils.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/utils/utils.py b/utils/utils.py index d9ffaacbe53b..85253a68d8da 100755 --- a/utils/utils.py +++ b/utils/utils.py @@ -437,7 +437,8 @@ def compute_loss(p, targets, model): # predictions, targets, model BCEcls, BCEobj = FocalLoss(BCEcls, g), FocalLoss(BCEobj, g) # per output - nt = 0 # targets + nt = 0 # number of targets + np = len(p) # number of outputs balance = [1.0, 1.0, 1.0] for i, pi in enumerate(p): # layer index, layer predictions b, a, gj, gi = indices[i] # image, anchor, gridy, gridx @@ -470,7 +471,7 @@ def compute_loss(p, targets, model): # predictions, targets, model lobj += BCEobj(pi[..., 4], tobj) * balance[i] # obj loss - s = 3 / (i + 1) # output count scaling + s = 3 / np # output count scaling lbox *= h['giou'] * s lobj *= h['obj'] * s lcls *= h['cls'] * s @@ -517,7 +518,6 @@ def build_targets(p, targets, model): j, k = ((gxy % 1. < g) & (gxy > 1.)).T a, t = torch.cat((a, a[j], a[k]), 0), torch.cat((t, t[j], t[k]), 0) offsets = torch.cat((z, z[j] + off[0], z[k] + off[1]), 0) * g - elif style == 'rect4': j, k = ((gxy % 1. < g) & (gxy > 1.)).T l, m = ((gxy % 1. > (1 - g)) & (gxy < (gain[[2, 3]] - 1.))).T From 7bd7b2c090cbeb7d5ff4e678d093c258d34368ee Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Fri, 3 Jul 2020 20:05:50 -0700 Subject: [PATCH 18/42] update export.py --- models/export.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/models/export.py b/models/export.py index 1ff95266317b..54d7a12f6841 100644 --- a/models/export.py +++ b/models/export.py @@ -26,7 +26,7 @@ model = torch.load(opt.weights, map_location=torch.device('cpu'))['model'].float() model.eval() model.model[-1].export = True # set Detect() layer export=True - _ = model(img) # dry run + y = model(img) # dry run # TorchScript export try: @@ -36,7 +36,7 @@ ts.save(f) print('TorchScript export success, saved as %s' % f) except Exception as e: - print('TorchScript export failed: %s' % e) + print('TorchScript export failure: %s' % e) # ONNX export try: @@ -46,7 +46,7 @@ f = opt.weights.replace('.pt', '.onnx') # filename model.fuse() # only for ONNX torch.onnx.export(model, img, f, verbose=False, opset_version=12, input_names=['images'], - output_names=['output']) # output_names=['classes', 'boxes'] + output_names=['classes', 'boxes'] if y is None else ['output']) # Checks onnx_model = onnx.load(f) # load onnx model @@ -54,7 +54,7 @@ print(onnx.helper.printable_graph(onnx_model.graph)) # print a human readable model print('ONNX export success, saved as %s' % f) except Exception as e: - print('ONNX export failed: %s' % e) + print('ONNX export failure: %s' % e) # Finish - print('\nExports complete. Visualize with https://github.com/lutzroeder/netron.') \ No newline at end of file + print('\nExport complete. Visualize with https://github.com/lutzroeder/netron.') From f83521ab9a63b7728cd562194afa7d9caf66971f Mon Sep 17 00:00:00 2001 From: NanoCode012 Date: Sat, 4 Jul 2020 16:50:33 +0700 Subject: [PATCH 19/42] Fix save error for multi-gpu --- utils/torch_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/torch_utils.py b/utils/torch_utils.py index dd2e6e75ff97..d697a06187c6 100644 --- a/utils/torch_utils.py +++ b/utils/torch_utils.py @@ -201,5 +201,5 @@ def update(self, model): def update_attr(self, model): # Update EMA attributes for k, v in model.__dict__.items(): - if not k.startswith('_') and k != 'module': + if not k.startswith('_') and k not in ["module", "process_group", "reducer"]: setattr(self.ema, k, v) From ccfa9373ae784d9bcda19d47973af9b9c15d7906 Mon Sep 17 00:00:00 2001 From: NanoCode012 Date: Sat, 4 Jul 2020 23:12:57 +0700 Subject: [PATCH 20/42] Update to remove "module" attribute check --- utils/torch_utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/utils/torch_utils.py b/utils/torch_utils.py index d697a06187c6..fd00b8bde080 100644 --- a/utils/torch_utils.py +++ b/utils/torch_utils.py @@ -201,5 +201,5 @@ def update(self, model): def update_attr(self, model): # Update EMA attributes for k, v in model.__dict__.items(): - if not k.startswith('_') and k not in ["module", "process_group", "reducer"]: + if not k.startswith('_') and k not in ["process_group", "reducer"]: setattr(self.ema, k, v) From c0c5fd812188f9561965fe2313ae06ab0246de84 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 16:11:14 -0700 Subject: [PATCH 21/42] Update voc.yaml --- data/voc.yaml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/data/voc.yaml b/data/voc.yaml index e2641d6af1f4..9275cf2a57fd 100644 --- a/data/voc.yaml +++ b/data/voc.yaml @@ -1,7 +1,14 @@ +# PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/ +# Download command: bash yolov5/data/get_voc.sh +# Train command: python train.py --data voc.yaml +# Dataset should be placed next to yolov5 folder: +# /parent_folder +# /VOC +# /yolov5 # train and val datasets (image directory or *.txt file with image paths) train: ../VOC/images/train/ -val: ../VOC/images/val// +val: ../VOC/images/val/ # number of classes nc: 20 From 7df4fcebb1e32d0fdb973d61bca32814fa254769 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 16:12:17 -0700 Subject: [PATCH 22/42] Update voc.yaml --- data/voc.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/data/voc.yaml b/data/voc.yaml index 9275cf2a57fd..ba4eff648418 100644 --- a/data/voc.yaml +++ b/data/voc.yaml @@ -14,4 +14,5 @@ val: ../VOC/images/val/ nc: 20 # class names -names: ['aeroplane', 'bicycle','bird','boat','bottle','bus','car','cat','chair','cow','diningtable','dog','horse','motorbike','person','pottedplant','sheep','sofa','train','tvmonitor'] +names: ['aeroplane', 'bicycle','bird','boat','bottle','bus','car','cat','chair','cow','diningtable','dog','horse', + 'motorbike','person','pottedplant','sheep','sofa','train','tvmonitor'] From 72a1746938710a8548c54b2c034b4cb582dcc28c Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 16:12:52 -0700 Subject: [PATCH 23/42] Update get_voc.sh --- data/get_voc.sh | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/data/get_voc.sh b/data/get_voc.sh index f035bb4c2450..f1ad41007658 100644 --- a/data/get_voc.sh +++ b/data/get_voc.sh @@ -1,4 +1,3 @@ - start=`date +%s` # handle optional download dir @@ -203,4 +202,4 @@ for line in lines: END -rm -rf ../data \ No newline at end of file +rm -rf ../data From 55e48603bf917968a72e5aa65f832517d4f06e81 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 16:25:48 -0700 Subject: [PATCH 24/42] udpate dataset download instructions --- data/get_coco2017.sh | 10 +++++++--- data/get_voc.sh | 8 ++++++++ 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/data/get_coco2017.sh b/data/get_coco2017.sh index fed57473d5c0..03b2c7e89301 100755 --- a/data/get_coco2017.sh +++ b/data/get_coco2017.sh @@ -1,7 +1,11 @@ #!/bin/bash -# Zip coco folder -# zip -r coco.zip coco -# tar -czvf coco.tar.gz coco +# 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 # Download labels from Google Drive, accepting presented query filename="coco2017labels.zip" diff --git a/data/get_voc.sh b/data/get_voc.sh index f1ad41007658..61edbacbecb9 100644 --- a/data/get_voc.sh +++ b/data/get_voc.sh @@ -1,3 +1,11 @@ +# PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/ +# Download command: bash yolov5/data/get_voc.sh +# Train command: python train.py --data voc.yaml +# Dataset should be placed next to yolov5 folder: +# /parent_folder +# /VOC +# /yolov5 + start=`date +%s` # handle optional download dir From 45abf0c74f52566003169b86012837c6f386dfa7 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 16:42:12 -0700 Subject: [PATCH 25/42] remove verbose tar unzip --- data/get_voc.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/get_voc.sh b/data/get_voc.sh index 61edbacbecb9..166370fbebff 100644 --- a/data/get_voc.sh +++ b/data/get_voc.sh @@ -34,9 +34,9 @@ echo "Done downloading." # Extract data echo "Extracting trainval ..." -tar -xvf VOCtrainval_06-Nov-2007.tar +tar -xf VOCtrainval_06-Nov-2007.tar echo "Extracting test ..." -tar -xvf VOCtest_06-Nov-2007.tar +tar -xf VOCtest_06-Nov-2007.tar echo "removing tars ..." rm VOCtrainval_06-Nov-2007.tar rm VOCtest_06-Nov-2007.tar @@ -73,7 +73,7 @@ echo "Done downloading." # Extract data echo "Extracting trainval ..." -tar -xvf VOCtrainval_11-May-2012.tar +tar -xf VOCtrainval_11-May-2012.tar echo "removing tar ..." rm VOCtrainval_11-May-2012.tar From fd0b1cc924e0ce47db7ca51e4be229dc1e3c0220 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 16:50:11 -0700 Subject: [PATCH 26/42] update voc.yaml --- data/voc.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/data/voc.yaml b/data/voc.yaml index ba4eff648418..efe22308ad47 100644 --- a/data/voc.yaml +++ b/data/voc.yaml @@ -1,5 +1,5 @@ # PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/ -# Download command: bash yolov5/data/get_voc.sh +# Download command: bash ./data/get_voc.sh # Train command: python train.py --data voc.yaml # Dataset should be placed next to yolov5 folder: # /parent_folder @@ -14,5 +14,5 @@ val: ../VOC/images/val/ nc: 20 # class names -names: ['aeroplane', 'bicycle','bird','boat','bottle','bus','car','cat','chair','cow','diningtable','dog','horse', - 'motorbike','person','pottedplant','sheep','sofa','train','tvmonitor'] +names: ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', + 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor'] From 659ad748c810bf0d6f5b5178e6aa1048780683ba Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 17:13:43 -0700 Subject: [PATCH 27/42] update get_voc.sh --- Dockerfile | 2 +- data/get_voc.sh | 2 +- detect.py | 6 +++--- models/export.py | 12 ++++++++++++ 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Dockerfile b/Dockerfile index 01551a0e49e4..bda78c028d0c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Start FROM Nvidia PyTorch image https://ngc.nvidia.com/catalog/containers/nvidia:pytorch -FROM nvcr.io/nvidia/pytorch:20.06-py3 +FROM nvcr.io/nvidia/pytorch:20.03-py3 RUN pip install -U gsutil # Create working directory diff --git a/data/get_voc.sh b/data/get_voc.sh index 166370fbebff..949e0cc9aa3a 100644 --- a/data/get_voc.sh +++ b/data/get_voc.sh @@ -1,5 +1,5 @@ # PASCAL VOC dataset http://host.robots.ox.ac.uk/pascal/VOC/ -# Download command: bash yolov5/data/get_voc.sh +# Download command: bash ./data/get_voc.sh # Train command: python train.py --data voc.yaml # Dataset should be placed next to yolov5 folder: # /parent_folder diff --git a/detect.py b/detect.py index 2650c202d49d..f9b12eb08273 100644 --- a/detect.py +++ b/detect.py @@ -102,7 +102,7 @@ def detect(save_img=False): if save_img or view_img: # Add bbox to image label = '%s %.2f' % (names[int(cls)], conf) - plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3) + plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1) # Print time (inference + NMS) print('%sDone. (%.3fs)' % (s, t2 - t1)) @@ -139,10 +139,10 @@ def detect(save_img=False): if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('--weights', type=str, default='weights/yolov5s.pt', help='model.pt path') + parser.add_argument('--weights', type=str, default='weights/yolov5m.pt', help='model.pt path') parser.add_argument('--source', type=str, default='inference/images', help='source') # file/folder, 0 for webcam parser.add_argument('--output', type=str, default='inference/output', help='output folder') # output folder - parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)') + parser.add_argument('--img-size', type=int, default=1024, help='inference size (pixels)') parser.add_argument('--conf-thres', type=float, default=0.4, help='object confidence threshold') parser.add_argument('--iou-thres', type=float, default=0.5, help='IOU threshold for NMS') parser.add_argument('--fourcc', type=str, default='mp4v', help='output video codec (verify ffmpeg support)') diff --git a/models/export.py b/models/export.py index 54d7a12f6841..c11c0a391197 100644 --- a/models/export.py +++ b/models/export.py @@ -56,5 +56,17 @@ except Exception as e: print('ONNX export failure: %s' % e) + # CoreML export + try: + import coremltools as ct + + print('\nStarting CoreML export with coremltools %s...' % ct.__version__) + model = ct.convert(ts, inputs=[ct.ImageType(name='images', shape=img.shape)]) # convert + f = opt.weights.replace('.pt', '.mlmodel') # filename + model.save(f) + print('CoreML export success, saved as %s' % f) + except Exception as e: + print('CoreML export failure: %s' % e) + # Finish print('\nExport complete. Visualize with https://github.com/lutzroeder/netron.') From 11ba5294ddb9423654c36ac056673b984d9a59dc Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 17:36:53 -0700 Subject: [PATCH 28/42] update get_voc.sh --- data/get_voc.sh | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/data/get_voc.sh b/data/get_voc.sh index 949e0cc9aa3a..b7e66d003133 100644 --- a/data/get_voc.sh +++ b/data/get_voc.sh @@ -11,10 +11,10 @@ start=`date +%s` # handle optional download dir if [ -z "$1" ] then - # navigate to ~/data - echo "navigating to ../data/ ..." - mkdir -p ../data - cd ../data/ + # navigate to ~/tmp + echo "navigating to ../tmp/ ..." + mkdir -p ../tmp + cd ../tmp/ else # check if is valid directory if [ ! -d $1 ]; then @@ -51,10 +51,10 @@ start=`date +%s` # handle optional download dir if [ -z "$1" ] then - # navigate to ~/data - echo "navigating to ../data/ ..." - mkdir -p ../data - cd ../data/ + # navigate to ~/tmp + echo "navigating to ../tmp/ ..." + mkdir -p ../tmp + cd ../tmp/ else # check if is valid directory if [ ! -d $1 ]; then @@ -82,7 +82,7 @@ runtime=$((end-start)) echo "Completed in" $runtime "seconds" -cd ../data +cd ../tmp echo "Spliting dataset..." python3 - "$@" < Date: Sat, 4 Jul 2020 17:44:53 -0700 Subject: [PATCH 29/42] update detect.py --- detect.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/detect.py b/detect.py index f9b12eb08273..2650c202d49d 100644 --- a/detect.py +++ b/detect.py @@ -102,7 +102,7 @@ def detect(save_img=False): if save_img or view_img: # Add bbox to image label = '%s %.2f' % (names[int(cls)], conf) - plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=1) + plot_one_box(xyxy, im0, label=label, color=colors[int(cls)], line_thickness=3) # Print time (inference + NMS) print('%sDone. (%.3fs)' % (s, t2 - t1)) @@ -139,10 +139,10 @@ def detect(save_img=False): if __name__ == '__main__': parser = argparse.ArgumentParser() - parser.add_argument('--weights', type=str, default='weights/yolov5m.pt', help='model.pt path') + parser.add_argument('--weights', type=str, default='weights/yolov5s.pt', help='model.pt path') parser.add_argument('--source', type=str, default='inference/images', help='source') # file/folder, 0 for webcam parser.add_argument('--output', type=str, default='inference/output', help='output folder') # output folder - parser.add_argument('--img-size', type=int, default=1024, help='inference size (pixels)') + parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)') parser.add_argument('--conf-thres', type=float, default=0.4, help='object confidence threshold') parser.add_argument('--iou-thres', type=float, default=0.5, help='IOU threshold for NMS') parser.add_argument('--fourcc', type=str, default='mp4v', help='output video codec (verify ffmpeg support)') From bb3c34691639b2aafb1a1f567e1dfb551a9a2563 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 17:51:54 -0700 Subject: [PATCH 30/42] model.yaml nc inherited from dataset.yaml --- models/yolo.py | 3 ++- train.py | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/models/yolo.py b/models/yolo.py index 7cc86b2bc296..5d96c6043cba 100644 --- a/models/yolo.py +++ b/models/yolo.py @@ -52,7 +52,8 @@ def __init__(self, model_cfg='yolov5s.yaml', ch=3, nc=None): # model, input cha self.md = yaml.load(f, Loader=yaml.FullLoader) # model dict # Define model - if nc: + if nc and nc != self.md['nc']: + print('Overriding %s nc=%g with nc=%g' % (model_cfg, self.md['nc'], nc)) self.md['nc'] = nc # override yaml value self.model, self.save = parse_model(self.md, ch=[ch]) # model, savelist, ch_out # print([x.shape for x in self.forward(torch.zeros(1, ch, 64, 64))]) diff --git a/train.py b/train.py index 1c8ff7ddd891..e07cbb158b25 100644 --- a/train.py +++ b/train.py @@ -77,8 +77,7 @@ def train(hyp): os.remove(f) # Create model - model = Model(opt.cfg).to(device) - assert model.md['nc'] == nc, '%s nc=%g classes but %s nc=%g classes' % (opt.data, nc, opt.cfg, model.md['nc']) + model = Model(opt.cfg, nc=data_dict['nc']).to(device) # Image sizes gs = int(max(model.stride)) # grid size (max stride) From 4e2d24602d246231694ba1b4d3bf3bd01f027ea4 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sat, 4 Jul 2020 23:07:26 -0700 Subject: [PATCH 31/42] update yolo.py --- models/yolo.py | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/models/yolo.py b/models/yolo.py index 7cc86b2bc296..66bdb7d5cd8e 100644 --- a/models/yolo.py +++ b/models/yolo.py @@ -59,10 +59,14 @@ def __init__(self, model_cfg='yolov5s.yaml', ch=3, nc=None): # model, input cha # Build strides, anchors m = self.model[-1] # Detect() - m.stride = torch.tensor([128 / x.shape[-2] for x in self.forward(torch.zeros(1, ch, 128, 128))]) # forward - m.anchors /= m.stride.view(-1, 1, 1) - check_anchor_order(m) - self.stride = m.stride + if isinstance(m, Detect): + s = 128 # 2x min stride + m.stride = torch.tensor([s / x.shape[-2] for x in self.forward(torch.zeros(1, ch, s, s))]) # forward + m.anchors /= m.stride.view(-1, 1, 1) + check_anchor_order(m) + self.stride = m.stride + self._initialize_biases() # only run once + # print('Strides: %s' % m.stride.tolist()) # Init weights, biases torch_utils.initialize_weights(self) @@ -146,7 +150,7 @@ def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers def parse_model(md, ch): # model_dict, input_channels(3) - print('\n%3s%15s%3s%10s %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments')) + print('\n%3s%18s%3s%10s %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments')) anchors, nc, gd, gw = md['anchors'], md['nc'], md['depth_multiple'], md['width_multiple'] na = (len(anchors[0]) // 2) # number of anchors no = na * (nc + 5) # number of outputs = anchors * (classes + 5) @@ -161,7 +165,7 @@ def parse_model(md, ch): # model_dict, input_channels(3) pass n = max(round(n * gd), 1) if n > 1 else n # depth gain - if m in [nn.Conv2d, Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, BottleneckCSP, CrossConv]: + if m in [nn.Conv2d, Conv, Bottleneck, SPP, DWConv, MixConv2d, Focus, CrossConv, BottleneckCSP, C3]: c1, c2 = ch[f], args[0] # Normal @@ -182,7 +186,7 @@ def parse_model(md, ch): # model_dict, input_channels(3) # c2 = make_divisible(c2, 8) if c2 != no else c2 args = [c1, c2, *args[1:]] - if m is BottleneckCSP: + if m in [BottleneckCSP, C3]: args.insert(2, n) n = 1 elif m is nn.BatchNorm2d: @@ -198,7 +202,7 @@ def parse_model(md, ch): # model_dict, input_channels(3) t = str(m)[8:-2].replace('__main__.', '') # module type np = sum([x.numel() for x in m_.parameters()]) # number params m_.i, m_.f, m_.type, m_.np = i, f, t, np # attach index, 'from' index, type, number params - print('%3s%15s%3s%10.0f %-40s%-30s' % (i, f, n, np, t, args)) # print + print('%3s%18s%3s%10.0f %-40s%-30s' % (i, f, n, np, t, args)) # print save.extend(x % i for x in ([f] if isinstance(f, int) else f) if x != -1) # append to savelist layers.append(m_) ch.append(c2) From 956511dafd7451423be9c10f9c38f8e21631ba52 Mon Sep 17 00:00:00 2001 From: Laughing <61612323+Laughing-q@users.noreply.github.com> Date: Sun, 5 Jul 2020 15:08:24 +0800 Subject: [PATCH 32/42] fix LR bug --- train.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/train.py b/train.py index e07cbb158b25..61ed84ea8fa0 100644 --- a/train.py +++ b/train.py @@ -101,6 +101,9 @@ def train(hyp): optim.SGD(pg0, lr=hyp['lr0'], momentum=hyp['momentum'], nesterov=True) optimizer.add_param_group({'params': pg1, 'weight_decay': hyp['weight_decay']}) # add pg1 with weight_decay optimizer.add_param_group({'params': pg2}) # add pg2 (biases) + # Scheduler https://arxiv.org/pdf/1812.01187.pdf + lf = lambda x: (((1 + math.cos(x * math.pi / epochs)) / 2) ** 1.0) * 0.9 + 0.1 # cosine + scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) print('Optimizer groups: %g .bias, %g conv.weight, %g other' % (len(pg2), len(pg1), len(pg0))) del pg0, pg1, pg2 @@ -144,9 +147,7 @@ def train(hyp): if mixed_precision: model, optimizer = amp.initialize(model, optimizer, opt_level='O1', verbosity=0) - # Scheduler https://arxiv.org/pdf/1812.01187.pdf - lf = lambda x: (((1 + math.cos(x * math.pi / epochs)) / 2) ** 1.0) * 0.9 + 0.1 # cosine - scheduler = lr_scheduler.LambdaLR(optimizer, lr_lambda=lf) + scheduler.last_epoch = start_epoch - 1 # do not move # https://discuss.pytorch.org/t/a-problem-occured-when-resuming-an-optimizer/28822 # plot_lr_scheduler(optimizer, scheduler, epochs) From 2541f77946530c6afebc5f2400701a220e2954a9 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 11:57:48 -0700 Subject: [PATCH 33/42] update detect.py --- detect.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/detect.py b/detect.py index 2650c202d49d..268b5dfcd32a 100644 --- a/detect.py +++ b/detect.py @@ -123,10 +123,11 @@ def detect(save_img=False): if isinstance(vid_writer, cv2.VideoWriter): vid_writer.release() # release previous video writer + fourcc = 'mp4v' # output video codec fps = vid_cap.get(cv2.CAP_PROP_FPS) w = int(vid_cap.get(cv2.CAP_PROP_FRAME_WIDTH)) h = int(vid_cap.get(cv2.CAP_PROP_FRAME_HEIGHT)) - vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*opt.fourcc), fps, (w, h)) + vid_writer = cv2.VideoWriter(save_path, cv2.VideoWriter_fourcc(*fourcc), fps, (w, h)) vid_writer.write(im0) if save_txt or save_img: @@ -145,20 +146,20 @@ def detect(save_img=False): parser.add_argument('--img-size', type=int, default=640, help='inference size (pixels)') parser.add_argument('--conf-thres', type=float, default=0.4, help='object confidence threshold') parser.add_argument('--iou-thres', type=float, default=0.5, help='IOU threshold for NMS') - parser.add_argument('--fourcc', type=str, default='mp4v', help='output video codec (verify ffmpeg support)') parser.add_argument('--device', default='', help='cuda device, i.e. 0 or 0,1,2,3 or cpu') parser.add_argument('--view-img', action='store_true', help='display results') parser.add_argument('--save-txt', action='store_true', help='save results to *.txt') parser.add_argument('--classes', nargs='+', type=int, help='filter by class') parser.add_argument('--agnostic-nms', action='store_true', help='class-agnostic NMS') parser.add_argument('--augment', action='store_true', help='augmented inference') + parser.add_argument('--update', action='store_true', help='update all models') opt = parser.parse_args() print(opt) with torch.no_grad(): - detect() - - # # Update all models - # for opt.weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt', 'yolov3-spp.pt']: - # detect() - # create_pretrained(opt.weights, opt.weights) + if opt.update: # update all models (to fix SourceChangeWarning) + for opt.weights in ['yolov5s.pt', 'yolov5m.pt', 'yolov5l.pt', 'yolov5x.pt', 'yolov3-spp.pt']: + detect() + create_pretrained(opt.weights, opt.weights) + else: + detect() From 8c43a6906d32e96002b8d6aa6b5cfb8571fda3ce Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 12:33:51 -0700 Subject: [PATCH 34/42] Update README.md --- README.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 1e29d1835196..d97be1597184 100755 --- a/README.md +++ b/README.md @@ -41,9 +41,13 @@ $ pip install -U -r requirements.txt ## Tutorials * [Notebook](https://github.com/ultralytics/yolov5/blob/master/tutorial.ipynb) Open In Colab +* [Kaggle](https://www.kaggle.com/ultralytics/yolov5-tutorial) * [Train Custom Data](https://github.com/ultralytics/yolov5/wiki/Train-Custom-Data) -* [Google Cloud Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/GCP-Quickstart) -* [Docker Quickstart Guide](https://github.com/ultralytics/yolov5/wiki/Docker-Quickstart) ![Docker Pulls](https://img.shields.io/docker/pulls/ultralytics/yolov5?logo=docker) +* [PyTorch Hub](https://github.com/ultralytics/yolov5/issues/36) +* [ONNX and TorchScript Export](https://github.com/ultralytics/yolov5/issues/251) +* [Test-Time Augmentation (TTA)](https://github.com/ultralytics/yolov5/issues/303) +* [Google Cloud Quickstart](https://github.com/ultralytics/yolov5/wiki/GCP-Quickstart) +* [Docker Quickstart](https://github.com/ultralytics/yolov5/wiki/Docker-Quickstart) ![Docker Pulls](https://img.shields.io/docker/pulls/ultralytics/yolov5?logo=docker) ## Inference From 997ba7b346beede15009b187b6d1d47367f94434 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 12:50:04 -0700 Subject: [PATCH 35/42] import yaml in yolo.py --- models/yolo.py | 1 + 1 file changed, 1 insertion(+) diff --git a/models/yolo.py b/models/yolo.py index 7961fabbd62d..b2d09cc51ea9 100644 --- a/models/yolo.py +++ b/models/yolo.py @@ -48,6 +48,7 @@ def __init__(self, model_cfg='yolov5s.yaml', ch=3, nc=None): # model, input cha if type(model_cfg) is dict: self.md = model_cfg # model dict else: # is *.yaml + import yaml with open(model_cfg) as f: self.md = yaml.load(f, Loader=yaml.FullLoader) # model dict From 38f5c1ad1d0f4d391544e302498eb81ff234e175 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 13:41:21 -0700 Subject: [PATCH 36/42] pruning and sparsity initial commit --- models/yolo.py | 2 +- utils/torch_utils.py | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/models/yolo.py b/models/yolo.py index b2d09cc51ea9..9617f5b3aaad 100644 --- a/models/yolo.py +++ b/models/yolo.py @@ -48,7 +48,7 @@ def __init__(self, model_cfg='yolov5s.yaml', ch=3, nc=None): # model, input cha if type(model_cfg) is dict: self.md = model_cfg # model dict else: # is *.yaml - import yaml + import yaml # for torch hub with open(model_cfg) as f: self.md = yaml.load(f, Loader=yaml.FullLoader) # model dict diff --git a/utils/torch_utils.py b/utils/torch_utils.py index fd00b8bde080..35ef0116d263 100644 --- a/utils/torch_utils.py +++ b/utils/torch_utils.py @@ -76,6 +76,26 @@ def find_modules(model, mclass=nn.Conv2d): return [i for i, m in enumerate(model.module_list) if isinstance(m, mclass)] +def sparsity(model): + # Return global model sparsity + a, b = 0., 0. + for p in model.parameters(): + a += p.numel() + b += (p == 0).sum() + return b / a + + +def prune(model, amount=0.3): + # Prune model to requested global sparsity + import torch.nn.utils.prune as prune + print('Pruning model... ', end='') + for name, m in model.named_modules(): + if isinstance(m, torch.nn.Conv2d): + prune.l1_unstructured(m, name='weight', amount=amount) # prune + prune.remove(m, 'weight') # make permanent + print(' %.3g global sparsity' % sparsity(model)) + + def fuse_conv_and_bn(conv, bn): # https://tehnokv.com/posts/fusing-batchnorm-and-conv/ with torch.no_grad(): From 5ba1de0cdcc414c69ceb7a4c45eb1e3895eca32a Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 15:02:56 -0700 Subject: [PATCH 37/42] update experimental.py with Ensemble() module --- models/experimental.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/models/experimental.py b/models/experimental.py index 539e7f970ac3..146a61b67a44 100644 --- a/models/experimental.py +++ b/models/experimental.py @@ -107,3 +107,15 @@ def __init__(self, c1, c2, k=(1, 3), s=1, equal_ch=True): def forward(self, x): return x + self.act(self.bn(torch.cat([m(x) for m in self.m], 1))) + + +class Ensemble(nn.ModuleList): + # Ensemble of models + def __init__(self): + super(Ensemble, self).__init__() + + def forward(self, x, augment=False): + y = [] + for module in self: + y.append(module(x, augment)[0]) + return torch.cat(y, 1), None # ensembled inference output, train output From 04bdbe4104728dac15937ad06dbb9071ae3bebf9 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 23:16:50 -0700 Subject: [PATCH 38/42] fuse update --- detect.py | 9 +++------ models/yolo.py | 4 ++-- test.py | 2 +- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/detect.py b/detect.py index 268b5dfcd32a..44cd64e53164 100644 --- a/detect.py +++ b/detect.py @@ -21,13 +21,10 @@ def detect(save_img=False): # Load model google_utils.attempt_download(weights) - model = torch.load(weights, map_location=device)['model'].float() # load to FP32 - # torch.save(torch.load(weights, map_location=device), weights) # update model if SourceChangeWarning - # model.fuse() - model.to(device).eval() - imgsz = check_img_size(imgsz, s=model.model[-1].stride.max()) # check img_size + model = torch.load(weights, map_location=device)['model'].float().eval() # load FP32 model + imgsz = check_img_size(imgsz, s=model.stride.max()) # check img_size if half: - model.half() # to FP16 + model.float() # to FP16 # Second-stage classifier classify = False diff --git a/models/yolo.py b/models/yolo.py index 9617f5b3aaad..3fd87a336c68 100644 --- a/models/yolo.py +++ b/models/yolo.py @@ -142,14 +142,14 @@ def _print_biases(self): # print('%10.3g' % (m.w.detach().sigmoid() * 2)) # shortcut weights def fuse(self): # fuse model Conv2d() + BatchNorm2d() layers - print('Fusing layers...') + print('Fusing layers... ', end='') for m in self.model.modules(): if type(m) is Conv: m.conv = torch_utils.fuse_conv_and_bn(m.conv, m.bn) # update conv m.bn = None # remove batchnorm m.forward = m.fuseforward # update forward torch_utils.model_info(self) - + return self def parse_model(md, ch): # model_dict, input_channels(3) print('\n%3s%18s%3s%10s %-40s%-30s' % ('', 'from', 'n', 'params', 'module', 'arguments')) diff --git a/test.py b/test.py index 259d44444bcd..644f6b977b3c 100644 --- a/test.py +++ b/test.py @@ -22,6 +22,7 @@ def test(data, # Initialize/load model and set device if model is None: training = False + merge = opt.merge # use Merge NMS device = torch_utils.select_device(opt.device, batch_size=batch_size) # Remove previous @@ -59,7 +60,6 @@ def test(data, # Dataloader if dataloader is None: # not training - merge = opt.merge # use Merge NMS img = torch.zeros((1, 3, imgsz, imgsz), device=device) # init img _ = model(img.half() if half else img) if device.type != 'cpu' else None # run once path = data['test'] if opt.task == 'test' else data['val'] # path to val/test images From a40f615c6f0c9b3acf3a39b3e58841c006010af2 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 23:24:53 -0700 Subject: [PATCH 39/42] .half() bug fix --- detect.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/detect.py b/detect.py index 44cd64e53164..d02f0a922817 100644 --- a/detect.py +++ b/detect.py @@ -24,7 +24,7 @@ def detect(save_img=False): model = torch.load(weights, map_location=device)['model'].float().eval() # load FP32 model imgsz = check_img_size(imgsz, s=model.stride.max()) # check img_size if half: - model.float() # to FP16 + model.half() # to FP16 # Second-stage classifier classify = False From a62333e8084301721fa66797e8968b11b2204bbc Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 23:27:03 -0700 Subject: [PATCH 40/42] test.py .fuse() update --- test.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/test.py b/test.py index 644f6b977b3c..2d6ab5454b51 100644 --- a/test.py +++ b/test.py @@ -31,11 +31,8 @@ def test(data, # Load model google_utils.attempt_download(weights) - model = torch.load(weights, map_location=device)['model'].float() # load to FP32 - torch_utils.model_info(model) - model.fuse() - model.to(device) - imgsz = check_img_size(imgsz, s=model.model[-1].stride.max()) # check img_size + model = torch.load(weights, map_location=device)['model'].float().fuse() # load to FP32 + imgsz = check_img_size(imgsz, s=model.stride.max()) # check img_size # Multi-GPU disabled, incompatible with .half() https://github.com/ultralytics/yolov5/issues/99 # if device.type != 'cpu' and torch.cuda.device_count() > 1: From 6b95d6d2c06879f0f64f004e1f166246f7e90093 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Sun, 5 Jul 2020 23:37:36 -0700 Subject: [PATCH 41/42] .to(device) bug fix --- test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test.py b/test.py index 2d6ab5454b51..1cfae9591287 100644 --- a/test.py +++ b/test.py @@ -31,7 +31,7 @@ def test(data, # Load model google_utils.attempt_download(weights) - model = torch.load(weights, map_location=device)['model'].float().fuse() # load to FP32 + model = torch.load(weights, map_location=device)['model'].float().fuse().to(device) # load to FP32 imgsz = check_img_size(imgsz, s=model.stride.max()) # check img_size # Multi-GPU disabled, incompatible with .half() https://github.com/ultralytics/yolov5/issues/99 From 121d90b3f2ffe085176ca3a21bbbc87260667655 Mon Sep 17 00:00:00 2001 From: Glenn Jocher Date: Mon, 6 Jul 2020 11:46:10 -0700 Subject: [PATCH 42/42] update fuse_conv_and_bn() --- utils/torch_utils.py | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/utils/torch_utils.py b/utils/torch_utils.py index 35ef0116d263..6baa9d5061e5 100644 --- a/utils/torch_utils.py +++ b/utils/torch_utils.py @@ -90,7 +90,7 @@ def prune(model, amount=0.3): import torch.nn.utils.prune as prune print('Pruning model... ', end='') for name, m in model.named_modules(): - if isinstance(m, torch.nn.Conv2d): + if isinstance(m, nn.Conv2d): prune.l1_unstructured(m, name='weight', amount=amount) # prune prune.remove(m, 'weight') # make permanent print(' %.3g global sparsity' % sparsity(model)) @@ -100,12 +100,12 @@ def fuse_conv_and_bn(conv, bn): # https://tehnokv.com/posts/fusing-batchnorm-and-conv/ with torch.no_grad(): # init - fusedconv = torch.nn.Conv2d(conv.in_channels, - conv.out_channels, - kernel_size=conv.kernel_size, - stride=conv.stride, - padding=conv.padding, - bias=True) + fusedconv = nn.Conv2d(conv.in_channels, + conv.out_channels, + kernel_size=conv.kernel_size, + stride=conv.stride, + padding=conv.padding, + bias=True).to(conv.weight.device) # prepare filters w_conv = conv.weight.clone().view(conv.out_channels, -1) @@ -113,10 +113,7 @@ def fuse_conv_and_bn(conv, bn): fusedconv.weight.copy_(torch.mm(w_bn, w_conv).view(fusedconv.weight.size())) # prepare spatial bias - if conv.bias is not None: - b_conv = conv.bias - else: - b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) + b_conv = torch.zeros(conv.weight.size(0), device=conv.weight.device) if conv.bias is None else conv.bias b_bn = bn.bias - bn.weight.mul(bn.running_mean).div(torch.sqrt(bn.running_var + bn.eps)) fusedconv.bias.copy_(torch.mm(w_bn, b_conv.reshape(-1, 1)).reshape(-1) + b_bn) @@ -159,8 +156,8 @@ def load_classifier(name='resnet101', n=2): # Reshape output to n classes filters = model.fc.weight.shape[1] - model.fc.bias = torch.nn.Parameter(torch.zeros(n), requires_grad=True) - model.fc.weight = torch.nn.Parameter(torch.zeros(n, filters), requires_grad=True) + model.fc.bias = nn.Parameter(torch.zeros(n), requires_grad=True) + model.fc.weight = nn.Parameter(torch.zeros(n, filters), requires_grad=True) model.fc.out_features = n return model