目录
1. 硬软件配置
1.1 ubuntu16.04 + GTX1650
笔者的电脑配置为GTX1650和R7-5800的8核心16线程处理器, GTX1650只有75w的功率,跑一般YOLOv5这种深度学习GPU温度维持在65°。
1.1.1 ubuntu16.04的弊病
1.1.1.1 输入法 Bug
ubuntu16.04没有中文配置输入法。我使用的ibus输入法的配置坎坷不断,同样的操作一会儿可以,一会儿又会报错无法启动GUI界面,需要卸载重装ibus输入法才行,比较折腾。
1.1.1.2 Nvidia 驱动 Bug
最初笔者使用的是 ubuntu16.04版本,一进去16.04的系统发现没有自带Nvidia驱动 (ubuntu20.04是自带对应机型的Nvidia驱动的),于是尝试了以图形界面方式进行GTX1650对应驱动版本的安装:
进入英伟达官网,官网上可以根据你的显卡型号选择对应版本的最新驱动,跟着网页上的图形交互提示,最后点击Downloads就可以下载最新驱动了,下载完后也装了,但是安装之后报错了,因为提示类似ubuntu内核不支持之类的问题,并且命令行输入nvidia-smi
还是提示没有相关包。
1.1.1.3 内核 Bug
进入系统后发现内核是老的(4.15大概,最新的内核版本已经有5.17了),于是到官网上更新内核。
把官方以往发布版本的汇总网页往下拉到最后,看到了2021年8月31号的最新ubuntu内核,下载了对应amd64后缀的3、4个deb文件,下载之后输入:
sudo dpkg -i *deb
对所有deb文件进行安装,这次没报错。
再次查看内核版本发现已经安装了一个最新的内核,此时再执行1.1.1.2的步骤,发现可以正常安装最新驱动了。
1.1.2 ubuntu16.04的界面丢失
1.1.2.1 首次丢失界面后找回
就在我使用ubuntu装完pytorch并且运行成功一个YOLOv5项目之后,我重启了电脑,发现此时我再次正确输入密码屏幕总是循环验证,后来按网上说法按:
Alt + Ctrl +F1
的组合键进入无界面ubuntu, 执行了一些命令后发现总算成功进入了有图标的界面。
1.1.2.2 再次丢失界面后找回
在完成一些事情后,我再次重启电脑,发现屏幕输完密码之后直接进入一个无图标的只有背景的桌面,此时鼠标可以右键创建终端,但只能创建一个且位于背景左上方不可移动的终端,我按照网上的解决办法输入:
sudo apt-get install gdam
sudo apt-get install ubuntu-desktop
sudo apt-get install gnome
sudo reboot now
重启之后果然是正常了。
1.1.2.3 最后一次丢失界面再也找不回
命运好像在捉弄人,因为前面感觉16.04挺落后的,刚好系统又自动检测到可以升级至18.04,于是我手贱升级并再次重启了一下并输入正确密码进入系统之后发现这次直接进入到一个连鼠标都没有的界面,没办法我只能按网上各种办法试,以及卸载并重装1.1.2.2上叙述的包,结果还是不行,就这样僵持了几个小时,我放弃16.04了,决定重装真正的18.04。
1.2 ubuntu18.04安装失败
1.2.1 制作U盘启动盘
在win10上下载UltralSO和ubuntu18.04.iso镜像制作了一个启动盘。
1.2.2 进入bios设置U盘启动项
把kingston 设为第一项之后重启电脑发现竟然花屏了,我把U盘拔下来重新写了一遍镜像,发现还是花屏,这时候我想不明白是显卡驱动问题、显卡问题、还是显示器问题呢??僵持了有几十分钟,于是一咬牙决定直接装ubuntu20.04这个比较新的稳定版。
1.3 ubuntu20.04安装
这次和安装18.04的步骤一样,一次就成功了,我选的直接覆盖,所以连分区的事儿都不用干。
1.4 安装pytorch(GPU版本)
1.4.1 查看并确认Nvidia驱动
ubuntu20.04果然大有改观,不但有自带的根据你系统语言琐配对的输入法,还自动给你装了比较新的Nvidia驱动,这个驱动是可以直接用的,你自己不用再下了。
1.4.2 安装Miniconda
ubuntu20.04本身就有虚拟环境:刚打开的命令行是用户环境,但执行su root
后并输入密码可以进入root
下的(base)
环境,安装完Miniconda的deb包后一路Enter
和Yes
完成后执行创建pytorch的虚拟环境:
conda create -n pytorch python=3.6
激活并进入该虚拟环境:
conda activate pytorch
1.4.3 安装pytorch
使用清华源安装会很快,我用宿舍有线网发现只有不到10KB/S. 简直在草菅人命。
命令:
pip install pytorch==1.7.0 -i http://XXXXX./
pip install torchvision==0.8.1 -i http://XXXXX./
pip install torchaudio -i http://XXXXX./
1.4.4 安装CUDA
进入Nvidia官网,找到过去发行版本,选择CUDA11.1和ubuntu20.04, 可以看到一些最新版本的CUDA已经不支持ubuntu16.04了,所以坚守老版本真没必要。
CUDA的安装有三种方式,我选择的是runfile这种最保险、出错率最低的方式:
weget http://xxx.run
sudo sh ./xxx.run
就这两句话,直接就把CUDA安装到你的/usr/local/cuda
目录下了,此时根据你选择的CUDA版本,在cuda的同目录下有个cuda-xx.x,即/usr/local/cuda-xx.x
的包,里面东西和cuda
里面一模一样。实际使用的是带cuda-xx.x版本号的而不是单纯的cuda包, 因为你的环境变量是这样加的:
输入:
sudo ~/.bashrc
在文件的最后面加上这两行,根据你的cuda版本改一下名字:
XXXX
XXXX
保存并退出后执行:
source ~/.bashrc
确保环境变量打通:
nvcc -V
这时候就能看到CUDA版本号了。
1.4.5 安装Cudnn-8.2.3
英伟达官网直接下载cudnn的tgz文件,然后提取到本目录就行,解压出来后把里面cuda文件夹下面的/cuda/include/
包里面的cudnn.h
复制到/usr/local/cuda-XX.X/
里面,把/cuda/lib64/
里面的带cudnn*
的文件也全部复制到/usr/local/cuda-XX.X/
里面,然后象征性查看一下cudnn版本号, 发现出来版本号就说明环境变量配对了,不出的话就说明环境变量没有严格按要求来,等于做了无用功。
1.5 YOLOv5测试pytorch(GPU)环境
1.5.1 查看显卡信息
输入:
nvidia-smi
出现显卡信息图表,说明一切正常。
1.5.2 准备数据
保证你的数据格式是YOLO的,不是的话先转成YOLO:
1.5.2.1 split.py
import os
import random
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--xml_path', default='9.2dataset/Annotations', type=str, help='input xml label path')
parser.add_argument('--txt_path', default='9.2dataset/labels', type=str, help='output txt label path')
opt = parser.parse_args()
trainval_percent = 1.0
train_percent = 0.9
xmlfilepath = opt.xml_path
txtsavepath = opt.txt_path
total_xml = os.listdir(xmlfilepath)
if not os.path.exists(txtsavepath):
os.makedirs(txtsavepath)
num = len(total_xml)
list_index = range(num)
tv = int(num * trainval_percent)
tr = int(tv * train_percent)
trainval = random.sample(list_index, tv)
train = random.sample(trainval, tr)
file_trainval = open(txtsavepath + '/trainval.txt', 'w')
file_test = open(txtsavepath + '/test.txt', 'w')
file_train = open(txtsavepath + '/train.txt', 'w')
file_val = open(txtsavepath + '/val.txt', 'w')
for i in list_index:
name = total_xml[i][:-4] + '\n'
if i in trainval:
file_trainval.write(name)
if i in train:
file_train.write(name)
else:
file_val.write(name)
else:
file_test.write(name)
file_trainval.close()
file_train.close()
file_val.close()
file_test.close()
1.5.2.2 txt2yolo_label.py
# -*- coding: utf-8 -*-
import xml.etree.ElementTree as ET
from tqdm import tqdm
import os
from os import getcwd
sets = ['train', 'val', 'test']
classes = ['adgai',
'apple',
'baisuishan',
'banana',
'biangtangxueli',
'binghongcha',
'bingtangxueli',
'chapi',
'chouzhi_blue_weida',
'chouzhi_green_xinxiangyin',
'chouzhi_yellow_qingfeng',
'd',
'fenda',
'guolicheng',
'hengdabingquan',
'hongniu',
'hongshaoniuroumian',
'hongshaoniuroumian_daizhuang',
'hongshaoniuroumian_tongzhuan',
'hongshaoniuroumian_tongzhuang',
'ilid',
'jiaduobao',
'juanzhi_blue_weida',
'juanzhi_green_xinxiangyin',
'kangshifu_water',
'kebike',
'kele',
'kiwi',
'leshidaizhuang',
'leshitongzhuang',
'lvcha',
'lvjian',
'lvjian_daizhuang',
'lvjian_guanzhuang',
'mengniu',
'nongfushanquan',
'pear',
'telunsu',
'telunsuw',
'tip',
'wahaha_congee',
'wahaha_water',
'wanglaoji',
'wangzainiunai',
'wangzainiunia',
'wangzianiunai',
'wanzimian',
'wwwwwww',
'xiangzao_olay',
'xiangzao_olay_box',
'xiangzao_shufujai',
'xiangzao_shufujia',
'xiangzao_shufujia_box',
'xianxiayujimian_tongzhuang',
'xiiangzao_olay',
'xishouye_lanyueliang',
'xuebi',
'yaundianmuchang',
'yibao',
'yida',
'yili',
'yinlu_congee',
'yuandianmuchang']
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(image_id):
try:
in_file = open('9.2dataset/Annotations/%s.xml' % (image_id), encoding='utf-8')
out_file = open('9.2dataset/labels/%s.txt' % (image_id), 'w', encoding='utf-8')
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))
b1, b2, b3, b4 = b
# 标注越界修正
if b2 > w:
b2 = w
if b4 > h:
b4 = h
b = (b1, b2, b3, b4)
bb = convert((w, h), b)
out_file.write(str(cls_id) + " " +
" ".join([str(a) for a in bb]) + '\n')
except Exception as e:
print(e, image_id)
wd = getcwd()
for image_set in sets:
if not os.path.exists('9.2dataset/labels/'):
os.makedirs('9.2dataset/labels/')
image_ids = open('9.2dataset/labels/%s.txt' %
(image_set)).read().strip().split('\n')
list_file = open('9.2dataset/%s.txt' % (image_set), 'w')
for image_id in tqdm(image_ids):
# print(image_id)
list_file.write('9.2dataset/images/%s.jpg\n' % (image_id))
convert_annotation(image_id)
list_file.close()
保证数据是这样的:
1.5.3 开始训练
python train.py --img 640 --batch 6 --epoch 135 --data ./data/myvoc.yaml --cfg ./models/yolov5s.yaml --weights weights/yolov5s.pt --workers 4
运行python代码,可以发现显卡显存不断被python程序占用,成功测试。
1.5.4 预测推理
python detect.py --weights weights/best.pt --source NO1_1057.jpg