Docker部署Nvidia Cuda环境并运行yolo

一、Docker安装

我的Docker部署于Windows WSL环境中,可参考制作自己的Docker镜像并发布进行配置。

如果不使用Docker,也可以直接跑在WSL或虚拟机中。如果使用WSL,请跳过安装直接进入2.5 部署anaconda环境。

二、搭建Nvidia Cuda环境

硬件环境:3700x+3060ti

2.1 镜像页面:

https://hub.docker.com/r/nvidia/cuda/tags?page=&page_size=&ordering=&name=11.4

(注意此页面可能需要科学上网才能打开)

2.2 拉取镜像:

1
docker pull nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04

2.3 运行容器:

1
docker run -i -t --gpus all --shm-size 8g nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04  /bin/bash
Tips: 关于docker run 参数及含义

docker run 参数意义:
-d, --detach=false, 指定容器运行于前台还是后台,默认为false
-i, --interactive=false, 打开STDIN,用于控制台交互
-t, --tty=false, 分配tty设备,该可以支持终端登录,默认为false
-u, --user=“”, 指定容器的用户
-a, --attach=[], 登录容器(必须是以docker run -d启动的容器)
-w, --workdir=“”, 指定容器的工作目录
-c, --cpu-shares=0, 设置容器CPU权重,在CPU共享场景使用
-e, --env=[], 指定环境变量,容器中可以使用该环境变量
-m, --memory=“”, 指定容器的内存上限
-P, --publish-all=false, 指定容器暴露的端口
-p, --publish=[], 指定容器暴露的端口
-h, --hostname=“”, 指定容器的主机名
-v, --volume=[], 给容器挂载存储卷,挂载到容器的某个目录
–volumes-from=[], 给容器挂载其他容器上的卷,挂载到容器的某个目录
–cap-add=[], 添加权限,权限清单详见:http://linux.die.net/man/7/capabilities
–cap-drop=[], 删除权限,权限清单详见:http://linux.die.net/man/7/capabilities
–cidfile=“”, 运行容器后,在指定文件中写入容器PID值,一种典型的监控系统用法
–cpuset=“”, 设置容器可以使用哪些CPU,此参数可以用来容器独占CPU
–device=[], 添加主机设备给容器,相当于设备直通
–dns=[], 指定容器的dns服务器
–dns-search=[], 指定容器的dns搜索域名,写入到容器的/etc/resolv.conf文件
–entrypoint=“”, 覆盖image的入口点
–env-file=[], 指定环境变量文件,文件格式为每行一个环境变量
–expose=[], 指定容器暴露的端口,即修改镜像的暴露端口
–link=[], 指定容器间的关联,使用其他容器的IP、env等信息
–lxc-conf=[], 指定容器的配置文件,只有在指定–exec-driver=lxc时使用
–name=“”, 指定容器名字,后续可以通过名字进行容器管理,links特性需要使用名字
–net=“bridge”, 容器网络设置:
bridge 使用docker daemon指定的网桥
host //容器使用主机的网络
container:NAME_or_ID >//使用其他容器的网路,共享IP和PORT等网络资源
none 容器使用自己的网络(类似–net=bridge),但是不进行配置
–privileged=false, 指定容器是否为特权容器,特权容器拥有所有的capabilities
–restart=“no”, 指定容器停止后的重启策略:
no:容器退出时不重启
on-failure:容器故障退出(返回值非零)时重启
always:容器退出时总是重启
–rm=false, 指定容器停止后自动删除容器(不支持以docker run -d启动的容器)
–sig-proxy=true, 设置由代理接受并处理信号,但是SIGCHLD、SIGSTOP和SIGKILL不能被代理
示例
运行一个在后台执行的容器,同时,还能用控制台管理:docker run -i -t -d ubuntu:latest
运行一个带命令在后台不断执行的容器,不直接展示容器内部信息:docker run -d ubuntu:latest ping www.docker.com
运行一个在后台不断执行的容器,同时带有命令,程序被终止后还能重启继续跑,还能用控制台管理,docker run -d --restart=always ubuntu:latest ping www.docker.com
为容器指定一个名字,docker run -d --name=ubuntu_server ubuntu:latest
容器暴露80端口,并指定宿主机80端口与其通信(: 之前是宿主机端口,之后是容器需暴露的端口),docker run -d --name=ubuntu_server -p 80:80 ubuntu:latest
指定容器内目录与宿主机目录共享(: 之前是宿主机文件夹,之后是容器需共享的文件夹),docker run -d --name=ubuntu_server -v /etc/www:/var/www ubuntu:latest

2.4 查看CUDA版本

通常查看CUDA环境的命令是nvidia-smi,但在容器中运行会显示宿主机的CUDA版本。在容器中确认版本的命令是nvcc -V:

1
2
3
4
5
6
root@882d2efb2bf9:/# nvcc -V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2021 NVIDIA Corporation
Built on Mon_May__3_19:15:13_PDT_2021
Cuda compilation tools, release 11.3, V11.3.109
Build cuda_11.3.r11.3/compiler.29920130_0

2.5 部署anaconda环境

从清华源下载对应安装脚本:

1
wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/Anaconda3-2024.02-1-Linux-x86_64.sh

执行安装:

1
sh Anaconda3-2024.02-1-Linux-x86_64.sh

安装过程:

  1. 输入回车键确认安装;

  2. 阅读Anaconda最终用户许可协议,按下回车浏览完信息;

  3. 问我们是否接受该协议,只能接受了,输入yes;(Do you accept the license terms? [yes|no])

  4. 提示安装到以下位置,回车确认即可;(Anaconda3 will now be installed into this location:)

  5. 是否加入环境变量,通常是选择yes的;这个根据自己情况选择,如果经常用conda环境开发,建议选择yes(Do you wish the installer to initialize Anaconda3 in your /home/linuxidc/.bashrc ? [yes|no])

创建虚拟环境:

1
2
3
#ingenic_magik可替换为任意自定义名称
#python=3.7为需要安装的python环境版本
conda create -n ingenic_magik python=3.8

激活创建的虚拟环境:

1
2
3
4
5
6
conda init
#此处需要关闭Shell重新打开
conda activate ingenic_magik
(base) root@882d2efb2bf9:/# conda activate ingenic_magik
(ingenic_magik) root@882d2efb2bf9:/#
#此处可以看到开头多了一个括号,里面是Anaconda环境的名称。

查看python的版本:

1
2
(ingenic_magik) root@882d2efb2bf9:/# python3 --version
Python 3.8.19

安装运行yolo5所需的依赖包:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#-i 参数表示使用国内源,速度会更快
pip3 install pandas -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install requests -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install opencv-python -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install pyyaml -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install tqdm -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install matplotlib -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install seaborn -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install tensorboard -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
pip3 install scipy -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
#或者:
pip3 install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
#解决找不到openGL库问题
pip3 install opencv-python-headless -i https://pypi.tuna.tsinghua.edu.cn/simple some-package --force
#numpy 1.22中弃用了一些函数, yolo5的代码会报错.只能安装旧版本
pip3 install numpy==1.20.3 -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
#同上面类似的问题。安装旧版setuptools
pip install setuptools==59.5.0 --force

安装君正magik使用的依赖包:

1
2
3
4
5
#Magik_py38_torch1.10_cuda-11.3
pip install torch==1.10+cu113 torchvision==0.11.0+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html

#安装君正插件,需要注意和环境中的依赖包版本一致
pip3 install TrainingKit/pytorch/magik_whl/Magik_py38_torch1.10_cuda-11.3/magik_trainingkit_torch_1.10.0_cuda11.3-2.1.0-py3-none-any.whl

Tips: 更多版本的pytorch参见这里

检查是否安装成功:

1
2
3
4
5
6
7
8
9
10
(ingenic_magik) root@882d2efb2bf9:/magik-toolkit# python3
Python 3.8.19 (default, Mar 20 2024, 19:58:24)
[GCC 11.2.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> print(torch.version.cuda)
11.3
>>> from ingenic_magik_trainingkit.QuantizationTrainingPlugin.python import *
INFO(magik): trainingkit version:2.1.0(00020100_ea259ad) built:20231102-1844(5.5.0 pytorch)
>>>

注意检查此处打印的torch.version.cuda版本号是11.3是否与环境相符。如果打印出绿色的INFO(magik)且没有报错则已经安装成功。

开发环境至此已搭建完成。

2.6 额外内容——清理Windows中DockerDesktop和WSL的空间

清理Docker空间占用(在Windows命令行中执行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#查看docker 占用的空间
docker system df

#TYPE 列出了docker 使用磁盘的 4 种类型:
#Images:所有镜像占用的空间,包括拉取下来的镜像,和本地构建的。
#Containers:运行的容器占用的空间,表示每个容器的读写层的空间。
#Local Volumes:容器挂载本地数据卷的空间。
#Build Cache:镜像构建过程中产生的缓存空间(只有在使用 BuildKit 时才有,Docker 18.09 以后可用)。
#RECLAIMABLE :可回收大小。

#列出所有悬挂状态的镜像:
docker image ls -f dangling=true
#删除镜像
docker image prune 或者 docker image rm $(docker image ls -f dangling=true -q)
#删除不再使用的数据卷
docker volume prune 或者 docker volume rm $(docker volume ls -q)
#删除 build cache磁盘占用
docker builder prune
#一键清理
docker system prune

清理WSL空间占用(在Windows命令行中执行):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#关闭所有实例
wsl.exe --shutdown
#运行磁盘工具
diskpart.exe
#选择目标磁盘
select vdisk file="Docker镜像的路径或其他wsl虚拟机镜像路径"
#例如,我的路径是:E:\Docker_images\DockerDesktopWSL\data\ext4.vhdx
select vdisk file="E:\Docker_images\DockerDesktopWSL\data\ext4.vhdx"
#以只读模式连接虚拟磁盘文件
attach vdisk readonly
#开始压缩虚拟磁盘文件
compact vdisk
#分离虚拟磁盘文件
detach vdisk
#退出diskpart工具
exit

三、Yolo5s使用

代码地址:https://github.com/ultralytics/yolov5.git

君正代码库中包含了一个修改好的yolo5s-person,修改了Magik插件对应接口。建议先用官方Yolo测试一下环境是否正常,之后再使用君正修改后的yolo。

3.1 准备数据集

下载COCO2017:

1
2
3
4
5
6
wget http://images.cocodataset.org/zips/train2017.zip
wget http://images.cocodataset.org/zips/test2017.zip
wget http://images.cocodataset.org/zips/val2017.zip
wget http://images.cocodataset.org/annotations/stuff_annotations_trainval2017.zip
wget http://images.cocodataset.org/annotations/image_info_test2017.zip
wget http://images.cocodataset.org/annotations/annotations_trainval2017.zip

或者在其他地方下载后传输到容器中:

1
docker cp [文件名] [容器id]:[目标路径]

注意: 跨容器传输文件速度仅约20MB/s,如果下载文件速度超过这个速度可以考虑直接wget。

(可选1)分割数据集:

1
2
3
4
5
6
#coco数据分割工具
pip3 install pycocotools -i https://pypi.tuna.tsinghua.edu.cn/simple some-package
#执行君正的分割脚本,执行前记得修改程序中的coco数据集路径coco_data_dir

#移动到君正目录下
mv /COCO_Data_set/person/data /magik-toolkit/Models/training/pytorch/

(可选2)使用coco128:
下文训练参数中–data data/coco-person.yaml\改为–data data/coco128.yaml, 会自动下载一个小型的coco数据。

3.2 网络训练

训练配置:

默认训练参数设置在models/common.py中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
bita = 4

if bita==32:
bitw = 32
is_quantize = 0
clip_max_value = 6.0
shortcut_clip_max_value = 2.0
elif bita==8:
bitw = 8
is_quantize = 1
clip_max_value = 6.0
shortcut_clip_max_value = 2.0
elif bita==4:
bitw = 4
is_quantize = 1
clip_max_value = 6.0
shortcut_clip_max_value = 2.0

32bit, 设置: bita=32

8bit, 设置: bita=8

4bit, 设置: bita=4

其中,is_quantize -是否进行量化,0-不量化,1-量化,bita为32时,注意该值设为0

bitw- weight的位宽

magik2.0不需要修改这个脚本。直接训练时增加--bit 4参数即可。

训练脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#nvidia多卡交火相关变量. 单卡可删除
export NCCL_IB_DISABLE=1
export NCCL_DEBUG=info
GPUS=6

#32bit:
#torch.distributed.launch表示使用多卡分布式训练,
#后面的参数nproc_per_node、master_port、--device都与此相关。
#如果只有单卡,训练时直接用python3 train.py加参数即可。
python3 -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=60051 train.py \
--data data/coco-person.yaml\
--cfg models/yolov5s.yaml \
--weights ' ' \
--batch-size 132 \
--hyp data/hyp.scratch.yaml \
--project ./runs/train/yolov5s-person-32bit \
--epochs 300 \
--device 0,1,2,3,4,5

#8bit:
# python3 -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=60051 train.py \
# --data data/coco-person.yaml\
# --cfg models/yolov5s.yaml \
# --weights './runs/train/yolov5s-person-32bit/weights/best.pt' \
# --batch-size 132 \
# --hyp data/hyp.scratch-8bit.yaml \
# --project ./runs/train/yolov5s-person-8bit \
# --epochs 300 \
# --device 0,1,2,3,4,5


#4bit:
# python3 -m torch.distributed.launch --nproc_per_node=$GPUS --master_port=60051 train.py \
# --data data/coco-person.yaml\
# --cfg models/yolov5s.yaml \
# --weights './runs/train/yolov5s-person-8bit/weights/best.pt' \
# --batch-size 132 \
# --hyp data/hyp.scratch-4bit.yaml \
# --project ./runs/train/yolov5s-person-4bit \
# --epochs 300 \
# --device 0,1,2,3,4,5
# --adam

参数解析:

–data:数据集路径
–cfg:yolo权重参数配置文件
–weights:预训练模型, 比如8bit训练时可以加载32bit已经训练好的模型, 4bit再加载8bit的模型. 层层递进效果更好.
–hyp:超参数配置. yolo算法使用的参数配置.
–project:此次训练的工作目录.后续输出模型也在这里
–epoch:深度学习的一个参数.可以理解为迭代次数.但这个次数并不是越多越好, 太多会造成过拟合.
–device:与前面多卡训练相关.单卡可删除.

训练结果(验证集):