[嵌入式AI从0开始到入土]嵌入式AI系列教程
注:等我摸完鱼再把链接补上
可以关注我的B站号工具人呵呵的个人空间,后期会考虑出视频教程,务必催更,以防我变身鸽王。
第1期 昇腾Altas 200 DK上手
第2期 下载昇腾案例并运行
第3期 官方模型适配工具使用
第4期 炼丹炉的搭建(基于Ubuntu23.04 Desktop)
第5期 炼丹炉的搭建(基于wsl2_Ubuntu22.04)
第6期 Ubuntu远程桌面配置
第7期 下载yolo源码及样例运行验证
第8期 在线Gpu环境训练(基于启智ai协作平台)
第9期 转化为昇腾支持的om离线模型
第10期 jupyter lab的使用
第11期 yolov5在昇腾上推理
第12期 yolov5在昇腾上应用
第13期_orangepi aipro开箱测评
第14期 orangepi_aipro小修补含yolov7多线程案例
未完待续…
文章目录
前言
当前启智平台每天能够稳定白嫖5个小时Nvidia T4、5小时ascend 910或者其他算力。
一、注册
点击这里进入注册链接。
填写相关信息,完成注册。
二、导入项目
在首页右上角选择迁移外部项目。
如下图所示,填写相关信息
- url:https://github.com/ultralytics/yolov5.git
- 迁移类型:因为我们要做修改,因此不勾选
点击迁移,耐心等待迁移结束。
三、修改代码适配在线环境
1、下载预训练模型
平台访问github有一定几率失败,因此我们需要提前上传。
我个人使用的是yolov5s的预训练模型,这里贴出下载地址:https://github.com/ultralytics/yolov5/releases/download/v7.0/yolov5s.pt
其他几个模型只需要将yolov5s的s修改为n,s,m,l,x,n6,s,6,m6,l6,x6
即可。老规矩,下不动丢迅雷。
区别如下:
2、上传模型
因为我目前只用yolov5s,且模型不大,因此我将其上传到仓库的models文件夹内了。
其他模型请按如下步骤上传
如果是你自己的模型,可以设置为私有
3、下载数据集
这里使用coco128数据集做演示。
下载地址:https://ultralytics.com/assets/coco128.zip
4、修改数据集
为方便接下来的操作,请将数据集调整到这样
coco128.yaml内设置好相关信息,以下仅供参考
# YOLOv5 🚀 by Ultralytics, AGPL-3.0 license
# COCO128 dataset https://www.kaggle.com/ultralytics/coco128 (first 128 images from COCO train2017) by Ultralytics
# Example usage: python train.py --data coco128.yaml
# parent
# ├── yolov5
# └── datasets
# └── coco128 ← downloads here (7 MB)
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ../datasets/coco128 # dataset root dir
train: images/train2017 # train images (relative to 'path') 128 images
val: images/train2017 # val images (relative to 'path') 128 images
test: # test images (optional)
# Classes
names:
0: person
1: bicycle
2: car
3: motorcycle
4: airplane
5: bus
6: train
7: truck
8: boat
9: traffic light
10: fire hydrant
11: stop sign
12: parking meter
13: bench
14: bird
15: cat
16: dog
17: horse
18: sheep
19: cow
20: elephant
21: bear
22: zebra
23: giraffe
24: backpack
25: umbrella
26: handbag
27: tie
28: suitcase
29: frisbee
30: skis
31: snowboard
32: sports ball
33: kite
34: baseball bat
35: baseball glove
36: skateboard
37: surfboard
38: tennis racket
39: bottle
40: wine glass
41: cup
42: fork
43: knife
44: spoon
45: bowl
46: banana
47: apple
48: sandwich
49: orange
50: broccoli
51: carrot
52: hot dog
53: pizza
54: donut
55: cake
56: chair
57: couch
58: potted plant
59: bed
60: dining table
61: toilet
62: tv
63: laptop
64: mouse
65: remote
66: keyboard
67: cell phone
68: microwave
69: oven
70: toaster
71: sink
72: refrigerator
73: book
74: clock
75: vase
76: scissors
77: teddy bear
78: hair drier
79: toothbrush
最后打包压缩,名称要和coco128.yaml中的path一致,否则解压数据的时候会比较麻烦。
为了简单,假设数据集名称为xxx,请将xxx.yaml(path路径为…/datasets/xxx)和相关数据打包到xxx.zip
5、上传数据集
注意:此处应分为CPU/GPU或NPU两种,且互相不能调用
这里我已经在另一个项目中上传了,因此上传失败,你自己的数据集是不会这样的。
四、编写启动文件
启智在训练时会自动加载数据集和模型到指定文件夹,结果也需要保存到指定文件夹。当然,你可以通过调试去训练,但是麻烦程度+999.
这里,如果你在导入的时候没有勾选镜像的话,按照法一直接修改train.py即可。否则建议单独建立分支或者单独编写一个启动文件(法二)。
1、修改train.py
在模块导入最下方添加代码,创建输出目录
import zipfile
import shutil
import subprocess
if not os.path.exists("/tmp/output"): # 判断是否存在文件夹如果不存在则创建为文件夹
os.makedirs("/tmp/output")
print("create '/tmp/output' successed!")
在train函数return结果之前添加代码,保存结果到输出目录
def zip_runs_folder():
folder_path = "runs"
zip_path = "runs.zip"
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(folder_path):
for file in files:
file_path = os.path.join(root, file)
zipf.write(file_path, os.path.relpath(file_path, folder_path))
print("Successfully compressed the 'runs' folder and its contents!")
zip_runs_folder()
shutil.copy('runs.zip','/tmp/output')
shutil.copy('runs/train/exp/weights/best.pt','/tmp/output')
print("Save training output successfully!")
print("Start export onnx model!")
subprocess.call(f"python export.py --weights runs/train/exp/weights/best.pt --data {opt.data} --include onnx --opset=12 --simplify --device 0", shell=True)
shutil.copy('runs/train/exp/weights/best.onnx','/tmp/output')
print("Export onnx model successfully!")
在main函数开头添加代码,解压数据集
#Copy dataset and unzip
data_param = os.path.basename(opt.data) # 获取--data参数的文件名
data_name = data_param.split('.')[0] # 获取数据集名称,去除文件后缀
dataset_dir = "../datasets" # 数据集目录
dataset_path = f"/tmp/dataset/{data_name}.zip" # 数据集压缩文件路径
if not os.path.exists(os.path.join(dataset_dir, data_name)):
print("start unzip dataset,please wait some times!")
f = zipfile.ZipFile(os.path.join(dataset_path),'r')
for file in f.namelist():
f.extract(file,"../datasets") # 解压到的位置
f.close()
print("Dataset decompression completed!")
2、单独启动文件
在代码根目录新建start.py文件
import os
import zipfile
import shutil
import subprocess
import argparse
from pathlib import Path
import sys
FILE = Path(__file__).resolve()
ROOT = FILE.parents[0] # YOLOv5 root directory
if str(ROOT) not in sys.path:
sys.path.append(str(ROOT)) # add ROOT to PATH
ROOT = Path(os.path.relpath(ROOT, Path.cwd())) # relative
# Parse command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--weights", type=str)
parser.add_argument("--cfg", type=str)
parser.add_argument("--data", type=str)
parser.add_argument("--hyp", type=str)
parser.add_argument("--epochs", type=int)
parser.add_argument("--batch-size", type=int)
parser.add_argument("--imgsz", "--img", "--img-size", type=int)
parser.add_argument("--rect", action="store_true")
parser.add_argument("--resume", nargs="?", const=True)
parser.add_argument("--nosave", action="store_true")
parser.add_argument("--noval", action="store_true")
parser.add_argument("--noautoanchor", action="store_true")
parser.add_argument("--noplots", action="store_true")
parser.add_argument("--evolve", type=int, nargs="?", const=300)
parser.add_argument("--evolve_population", type=str)
parser.add_argument("--resume_evolve", type=str)
parser.add_argument("--bucket", type=str)
parser.add_argument("--cache", type=str, nargs="?", const="ram")
parser.add_argument("--image-weights", action="store_true")
parser.add_argument("--device")
parser.add_argument("--multi-scale", action="store_true")
parser.add_argument("--single-cls", action="store_true")
parser.add_argument("--optimizer", type=str, choices=["SGD", "Adam", "AdamW"])
parser.add_argument("--sync-bn", action="store_true")
parser.add_argument("--workers", type=int)
parser.add_argument("--project")
parser.add_argument("--name")
parser.add_argument("--exist-ok", action="store_true")
parser.add_argument("--quad", action="store_true")
parser.add_argument("--cos-lr", action="store_true")
parser.add_argument("--label-smoothing", type=float)
parser.add_argument("--patience", type=int)
parser.add_argument("--freeze", nargs="+", type=int)
parser.add_argument("--save-period", type=int)
parser.add_argument("--seed", type=int)
parser.add_argument("--local_rank", type=int)
parser.add_argument("--entity")
parser.add_argument("--upload_dataset", nargs="?", const=True)
parser.add_argument("--bbox_interval", type=int)
parser.add_argument("--artifact_alias", type=str)
parser.add_argument("--ndjson-console", action="store_true")
parser.add_argument("--ndjson-file", action="store_true")
opt = parser.parse_args()
if not os.path.exists("/tmp/output"): # 判断是否存在文件夹如果不存在则创建为文件夹
os.makedirs("/tmp/output")
print("create '/tmp/output' successed!")
#Copy dataset and unzip
data_param = os.path.basename(opt.data) # 获取--data参数的文件名
data_name = data_param.split('.')[0] # 获取数据集名称,去除文件后缀
dataset_dir = "../datasets" # 数据集目录
dataset_path = f"/tmp/dataset/{data_name}.zip" # 数据集压缩文件路径
if not os.path.exists(os.path.join(dataset_dir, data_name)):
print("start unzip dataset,please wait some times!")
f = zipfile.ZipFile(os.path.join(dataset_path),'r')
for file in f.namelist():
f.extract(file,"../datasets") # 解压到的位置
f.close()
print("Dataset decompression completed!")
# zip runs folder
def zip_runs_folder():
folder_path = "runs"
zip_path = "runs.zip"
with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
for root, dirs, files in os.walk(folder_path):
for file in files:
file_path = os.path.join(root, file)
zipf.write(file_path, os.path.relpath(file_path, folder_path))
print("Successfully compressed the 'runs' folder and its contents!")
#start train
print("Start train model!")
subprocess.call(f"python train.py --weights {opt.weights} --cfg {opt.cfg} --data {opt.data} --hyp {opt.hyp} --epochs {opt.epochs} --batch-size {opt.batch_size} --imgsz {opt.imgsz} --device {opt.device} --name {opt.name} --exist-ok --rect {'--resume' if opt.resume else ''} {'--nosave' if opt.nosave else ''} {'--noval' if opt.noval else ''} {'--noautoanchor' if opt.noautoanchor else ''} {'--noplots' if opt.noplots else ''} {'--evolve' if opt.evolve else ''} {'--resume_evolve' if opt.resume_evolve else ''} {'--cache' if opt.cache else ''} {'--image-weights' if opt.image_weights else ''} {'--multi-scale' if opt.multi_scale else ''} {'--single-cls' if opt.single_cls else ''} --optimizer {opt.optimizer} {'--sync-bn' if opt.sync_bn else ''} --workers {opt.workers} --project {opt.project} --patience {opt.patience} --freeze {' '.join(map(str, opt.freeze))} --save-period {opt.save_period} --seed {opt.seed} --local_rank {opt.local_rank}", shell=True)
print("Training is complete, start saving results!")
# Save training output
zip_runs_folder()
shutil.copy('runs.zip','/tmp/output')
shutil.copy('runs/train/exp/weights/best.pt','/tmp/output')
print("Save training output successfully!")
# Export onnx model
print("Start export onnx model!")
subprocess.call(f"python export.py --weights runs/train/exp/weights/best.pt --data {opt.data} --include onnx --opset=12 --simplify --device 0", shell=True)
shutil.copy('runs/train/exp/weights/best.onnx','/tmp/output')
print("Export and save onnx model successfully!")
五、创建调试任务
以英伟达GPU环境为例
1、路径说明
第四步中的路径是英伟达gpu训练环境的路径,调试时需要删除/tmp
- /code:本目录对应控制台左侧的文件菜单栏,已经存放代码仓为 master.zip,需要用户手动解压
- /dataset: 本目录下可以找到创建调试任务时选取的数据集,平台已解压
- /pretrainmodel: 本目录下将加载创建调试任务时选取的预训练模型
2、创建任务
如下图所示
建议使用T4卡做前期调试,这样比较节省积分
镜像可以使用这个,这样需要配置的东西就少一些
数据集可以提前下载coco2017val,上传到数据集,节省调试时下载时间。
3、开始调试
不幸的是,笔者在写本文时,英伟达集群似乎都无法创建任务,这里没法放进图了。
首先,打开终端执行以下命令
su
apt update #这一步很重要
apt install unzip
cd /code
unzip master.zip
之后我们只需要打开对应的jupyter文件进行调试即可。
4、导出镜像
在调试完成后,我们需要导出镜像以便我们在训练时使用
注意,必须在任务处于运行状态时提交镜像
提交成功后,在下次创建任务的时候,在我的镜像内就可以看到。
六、创建训练任务
以英伟达GPU环境为例
1、路径说明
- /tmp/code:存放训练脚本
- /tmp/dataset:存放训练数据集
- /tmp/pretrainmodel:存放预训练模型
- /tmp/output:存放训练结果
2、创建任务
如下图所示
注意事项:
- 代码分支:选择存放你训练代码的分支,我这没有去修改master分支,选择从master分支派生出来的适配启智GPU平台的train_gpu分支
- 镜像:请选择你在上一步中提交的镜像,否则可能需要在开始运行前安装缺少的依赖(没有命令交互界面,需要写入main函数)
可以使用我的环境(仅保证GPU环境下可以正常训练和导出onnx模型):192.168.204.22:5000/default-workspace/99280a9940ae44ca8f5892134386fddb/image:yolov5_gpu_240124 - 启动文件:一般为train.py,如果你需要在训练开始前做一些配置的话,可能需要使用start.sh等
- 数据集:训练使用的数据集
- 运行参数:详见train.py内opt函数,和本地训练一致,只需要写与默认参数不同的参数即可
3、开始训练
新建任务后,等待其状态变为running,可以点击任务,日志查看其输出(可能需要手动刷新才会显示新的日志)
下图为任务开始时的日志
下图为训练结束后的日志
4、下载训练结果
点击结果下载,如果使用我的代码,将生成以下三个文件。
文件说明:
- best.onnx:导出的onnx模型
- best.pt:训练获得的原始pt模型
- runs.zip:包含所有训练结果的压缩包
问题
1、无法解压代码
一定要先打开终端执行
su #这里是切换到root用户
apt update #不然没法安装软件
总结
具体细节直接点击平台首页右上角的问号,查看帮助。好像过段时间会统一各个集群的环境内代码等存放位置,请关注公告。