[嵌入式AI从0开始到入土]8_在线Gpu环境训练(基于启智ai协作平台)


前言

一、注册

二、导入项目

三、修改代码适配在线环境

1、下载预训练模型

2、上传模型

3、下载数据集

4、修改数据集

5、上传数据集

四、编写启动文件

1、修改train.py

2、单独启动文件

五、创建调试任务

1、路径说明

2、创建任务

3、开始调试

4、导出镜像

六、创建训练任务

1、路径说明

2、创建任务

3、开始训练

4、下载训练结果

问题

1、无法解压代码

总结

前言

当前启智平台每天能够稳定白嫖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 #不然没法安装软件


总结

具体细节直接点击平台首页右上角的问号,查看帮助。好像过段时间会统一各个集群的环境内代码等存放位置,请关注公告。


请使用浏览器的分享功能分享到微信等