← 返回文章列表

“极市”平台的新手任务:安全帽识别

分类
资料库学习笔记人工智能
标签
极市学习笔记
  • 开始之前

    • 请一定要看到文档最后!本文教程援引自官方文档,并插入了我实践过程中所遇到的问题以及解决方法
  • 背景

  • 开发流程

    • 主要流程如下:

      • 1.报名参与,创建实例:开发者报名参加 安全帽识别-新手任务,创建新手任务实例,进入编码环境。
      • 2.数据预处理:编写数据集处理脚本,将数据集转化为YOLOV5格式。
      • 3.编码环境训练:使用YOLOV5训练模块,在编码环境使用样例数据集验证训练代码。
      • 4.训练:使用极市平台AI开发工作台模型开发 发起训练 功能,完成训练数据集的模型训练、保存。
      • 5.编写测试脚本:在模型开发编码环境完成测试脚本编写,验证测试脚本。
      • 6.发起模型测试:使用极市平台AI开发工作台模型开发 发起模型测试 功能,平台将使用测试数据集对算法完成自动测试,最终出分上榜。
      • 7.将模型导出为ONNX格式:使用极市平台AI开发工作台模型开发 发起训练 功能,通过挂载已经训练的模型,完成模型导出ONNX格式。
      • 官方实战视频教程:https://docs.cvmart.net/#/resources

    • 1. 报名参与

      • 报名参与 安全帽识别-新手任务 ,选择Pytorch1.10基础镜像,创建新手任务编码实例。 等待编码实例创建完成后,点击在线编码进入编码环境,开始算法开发。
        • 截屏2026-04-14 15.29.31.png
        • 注意!平台编码和封装环境实行域名白名单制度,开源镜像站推荐使用清华源(pypi.tuna.tsinghua.edu.cn # 清华大学源),字体和预训练模型建议传到【我的文件】再复制文件地址在编码环境使用。
      • 新手任务工程目录
        #编码环境IDE工程文件目录 (仅展示与新手教程相关目录)
        ev_sdk       #AI算法封装sdk工程目录
        |-- ......     
        |-- src
            |-- ......
            |-- ji.py                 #自动测试脚本,需开发者按赛道说明完成编码
        train        #AI算法开发工程目录
        |-- src_repo      #代码目录,极市平台将自动备份此目录  
            |-- yolov5               #yolov5框架目录,开发者自行下载
            |-- run.sh               #训练命令脚本
            |-- split_train_val.py   # 划分数据集脚本
            |-- voc_label.py         #数据集格式转换脚本
        |-- models       #模型保存目录,平台将保存此目录下文件至“模型列表”
        |-- result-graphs   #训练结果图片保存目录
        |-- log          #训练日志保存目录  
        
    • 2. 数据预处理

      • 极市平台AI开发系统默认将数据集挂载到 /home/data/ 下。在新手任务中,开发者可在编码环境下/home/data/831文件夹内,查看到包含100张安全帽佩戴图片及对应标记文件的 样例数据集。开发者可在新手任务的 **说明页**中,通过 赛道说明 和 数据集获取更多关于数据相关的信息。

      • 260414-18a9ds8hfwo3r.png

      • 训练数据集将在训练阶段被极市平台AI开发系统自动替换到home/data/831下。因此,开发者需要编写数据预处理脚本,将平台数据集转换为YOLO格式的数据集。

      • 温馨提示:训练/测试时,请使用绝对路径 /home/data

      • 本教程使用split_train_val.py脚本,将/home/data/831数据集划分为 训练集 和验证集。使用voc_label.py脚本,将XML标记文件转换为YOLO格式的txt文件。 代码已经附上,我手动创建后的情况: 截屏2026-04-14 15.36.40.png 注意!为避免使用不同 IDE 和操作失误带来影响,请开发者复制代码使用时检查代码缩进

      • 代码文件 split_train_val.py

        • import os  
          import random  
          import argparse
          
          parser = argparse.ArgumentParser()
          parser.add_argument('--xml_path', type=str, help='input xml label path')
          parser.add_argument('--txt_path', 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=range(num)  
          
          ftrainval = open(txtsavepath + '/trainval.txt', 'w')  
          ftest = open(txtsavepath + '/test.txt', 'w')  
          ftrain = open(txtsavepath + '/train.txt', 'w')  
          fval = open(txtsavepath + '/val.txt', 'w')  
          
          for i in list:
              name=total_xml[i][:-4]+'\n'  
              ftrainval.write(name)  
              if i%7 == 0:
                  fval.write(name)
              else:
                  ftrain.write(name)  
          
          ftrainval.close()  
          ftrain.close()  
          fval.close()  
          ftest.close()  
          
      • 代码文件 voc_label.py

        • import xml.etree.ElementTree as ET
          import pickle
          import os
          from os import listdir, getcwd
          from os.path import join
          
          sets=['train', 'val', 'test']
          classes = ['person', 'hat','head']
          
          abs_path = os.getcwd()
          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):
              in_file = open('/project/train/src_repo/dataset/Annotations/%s.xml'%( image_id))  
              out_file = open('/project/train/src_repo/dataset/labels/%s.txt'%(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 :
                      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')
          
          for image_set in sets:
              if not os.path.exists('/project/train/src_repo/dataset/labels/'):
                  os.makedirs('/project/train/src_repo/dataset/labels/')
              image_ids = open('/project/train/src_repo/dataset/ImageSets/Main/%s.txt'%(image_set)).read().strip().split()
              list_file = open('/project/train/src_repo/dataset/%s.txt'%(image_set), 'w')
              for image_id in image_ids:
                  list_file.write('/project/train/src_repo/dataset/images/%s.jpg\n'%(image_id))
                  convert_annotation(image_id)
              list_file.close()   
          
    • 3. 编码环境训练

      • 开发者需要自行将**YOLOV5**工程从其GitHub仓库下载到/project/train/src_repo目录。平台将自动备份该目录下所有文件,请勿放置大型文件,以免备份失败。主要流程如下:
        • 根据YOLOV5使用说明配置其运行环境。
           #shell
           cd /project/train/src_repo
           git clone -b v6.2  https://mirror.ghproxy.com/https://github.com/ultralytics/yolov5.git    # 使用代理方式下载v6.2版本yolov5
           cd yolov5
           rm -rf .git    #删除yolov5工程自身.git,以免与平台代码保存机制冲突
           pip install -r requirements.txt  # install
          
          • 此处出现报错,核心问题是'Connection to mirrors.cloud.tencent.com timed out. (connect timeout=15)')'
            • 最后那句“找不到版本”很多时候是假象,本质上是前面联网超时,pip 根本没有成功拉到索引数据,所以才会像“没有这个版本”一样报错

            • 先查看了一下当前的 pip 配置:pip config list
            • [root@yolov5]$ pip config list
              global.index-url='https://mirrors.cloud.tencent.com/pypi/simple/'
              install.trusted-host='mirrors.cloud.tencent.com'
              [root@yolov5]$ 
            • 然后尝试更换清华源进行安装
            • pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn --default-timeout=100
            • 截屏2026-04-14 15.55.12.png
          • 一个小插曲,我做着做着服务器崩了 :(
            • 截屏2026-04-14 16.02.12.png
            • 哎,免费的服务我也不好计较什么了,哈哈哈哈
        • yolov5/data目录内建立helmet.yaml,配置数据集路径、目标类别数量、名称。
          #helmet.yaml
          train: /project/train/src_repo/dataset/train.txt
          val: /project/train/src_repo/dataset/val.txt
          # number of classes
          nc: 3
          # class names
          names: ['person', 'hat','head']
          
        • /project/train/src_repo目录内建立训练命令脚本run.sh,执行数据集划分、转换脚本,执行YOLOV5训练模块。
          #run.sh
          rm  -r /project/train/src_repo/dataset
          #创建数据集相关文件夹
          mkdir /project/train/src_repo/dataset
          mkdir /project/train/src_repo/dataset/Annotations
          mkdir /project/train/src_repo/dataset/images
          mkdir /project/train/src_repo/dataset/ImageSets
          mkdir /project/train/src_repo/dataset/labels
          mkdir /project/train/src_repo/dataset/ImageSets/Main
          
          cp /home/data/831/*.xml /project/train/src_repo/dataset/Annotations
          cp /home/data/831/*.jpg  /project/train/src_repo/dataset/images
          #执行数据集划分、转换
          python /project/train/src_repo/split_train_val.py --xml_path /project/train/src_repo/dataset/Annotations  --txt_path /project/train/src_repo/dataset/ImageSets/Main
          cp /project/train/src_repo/voc_label.py /project/train/src_repo/dataset
          python /project/train/src_repo/dataset/voc_label.py
          #执行YOLOV5训练脚本
          python /project/train/src_repo/yolov5/train.py   --data helmet.yaml --project /project/train/models/train  
          
        • **请务必将训练模型保存到 **/project/train/models目录下,请参见模型榜>实例>常见目录

      • 在编码环境执行run.sh,验证训练代码的正确性,输出基于样例集的模型。训练完成后,可在/project/train/models目录内查看训练输出。
        • #shell
          bash /project/train/src_repo/run.sh
          
        • 此处遇到报错 urlopen error [SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: Hostname mismatch, certificate is not valid for 'ultralytics.com'. (_ssl.c:1076)
          • 训练已经真正启动了,但 YOLOv5 在检查数据集时,想自动下载字体文件 Arial.ttf

            Downloading https://ultralytics.com/assets/Arial.ttf to /project/.config/Ultralytics/Arial.ttf…
            >然后因为平台网络限制 / SSL 证书校验异常,下载失败了:
            >```bash
            ssl.SSLCertVerificationError
            certificate verify failed: Hostname mismatch

            所以这次是 YOLOv5 的一个“附加动作”卡住了:它为了后续画图、标注可视化,去下载字体

          • 所以现在只需要给 YOLO 一个路径 /project/.config/Ultralytics/Arial.ttf 的字体就好了
          • 截屏2026-04-14 16.19.31.png
          • 按照官方文档的解决方法,下载字体后,在工作台上传到我的文件里面
          • 截屏2026-04-14 16.22.21.png
          • 在右边复制地址,然后回到控制台,使用命令wget https://xxxx 下载你复制的链接,拿到字体文件
          • 然后就使用 mv 命令移动到 /project/.config/Ultralytics/目录下
            • mv Arial.ttf /project/.config/Ultralytics/
          • 问题解决

        • 又遇到报错 Downloading https://github.com/ultralytics/yolov5/releases/download/v6.1/yolov5s.p 后的 Temporary failure in name resolution;同时报兼容性错误 TypeError: unlink() got an unexpected keyword argument 'missing_ok'
          • 也就是说,真正先出错的是下载权重失败,unlink 报错只是后续兼容性问题。

          • 首先是:手动解决 yolov5s.pt 文件下载问题,如上个字体问题的解决方法一样
            • 文中给到了配置路径:weights=train/src_repo/yolov5/yolov5s.pt
            • 那就先在本地电脑上下载https://github.com/ultralytics/yolov5/releases/download/v6.1/yolov5s.pt
            • 上传到平台过后 wget 从云空间下载到虚拟机,然后 mv 到配置目录
              • wget https://xxxx...
                mv yolov5s.pt train/src_repo/yolov5/yolov5s.pt
          • 然后解决问题 2:unlink(missing_ok=True) 与 Python 3.7 不兼容
            • 打开并修改文件:/project/train/src_repo/yolov5/utils/downloads.py
            • 找到 file.unlink(missing_ok=True) 命令位置
              • 截屏2026-04-14 16.37.46.png
            • 改成兼容 Python3.7 的写法:
              if file.exists():
                  file.unlink()
            • 或者,备份文件后使用命令修改
              • cp /project/train/src_repo/yolov5/utils/downloads.py /project/train/src_repo/yolov5/utils/downloads.py.bak
                sed -i "s/file.unlink(missing_ok=True)/if file.exists(): file.unlink()/g" /project/train/src_repo/yolov5/utils/downloads.py
            • 命令修改后,我用查找命令定位到了那个语句旁边的位置,你可以看到原先 file.unlink(missing_ok=True) 被修改为了 if file.exists(): file.unlink()
              • 截屏2026-04-14 16.40.33.png
          • 解决完成
            • 截屏2026-04-14 16.42.51.png
            • 截屏2026-04-14 16.45.28.png
      • 检验输出
        • ls /project/train/models/train
        • 截屏2026-04-14 16.46.21.png
        • 我中间处理了几次报错,所以有好几个 exp 文件夹,这是正常的
    • 4. 训练

      • 当开发者在编码环境下成功完成模型训练,可使用极市平台AI开发系统,打榜工作台里面的模型开发工作卡,其中的 新建训练任务功能。提交后,平台将使用 训练数据集 执行正式的训练任务,并保存/project/train/models目录下的文件。模型训练主要流程如下:
        • 打开路径:https://cvmart.net/dev/10163/modelDevelopment/train
          • 截屏2026-04-14 16.50.16.png
        • 在 训练任务 新建训练任务,输入训练命令 bash /project/train/src_repo/run.sh,下方数据集点开选择安全帽识别的选项卡,提交训练任务,等待训练任务结束。
          • 截屏2026-04-14 16.51.51.png
          • 截屏2026-04-14 16.52.30.png
        • 训练完成后,可在 模型列表查看训练任务所保存的结果文件。
          • 截屏2026-04-15 11.39.35.png
          • 截屏2026-04-15 11.40.26.png
    • 5. 编写测试脚本

      • 极市平台AI开发系统通过自研的 自动测试 功能对开发者的AI算法进行性能评估。自动测试功能本质上就是模型推理。开发者需要按照要求,编写测试脚本,以便平台使用测试数据集评估算法性能。详情请参见**自动测试**。

        自动测试支持Python和C++两种形式。本文选择使用python编写ji.py测试脚本,完成新手任务自动测试。

      • 根据新手任务 赛道说明,查看 六、模型榜单接口说明,了解新手任务python版自动测试要求。

        • 截屏2026-04-14 16.57.45.png
      • ev_sdk目录构建自动测试相关文件

        • 开发者需将YOLOV5框架的/models/utils文件夹拷贝到/project/ev_sdk/src目录,并创建ji.py脚本。
        • 截屏2026-04-15 11.52.02.png
      • 编写ji.py脚本,完成模型推理,并输出规定格式推理结果。

        • 注意!为避免使用不同 IDE 和操作失误带来影响,请开发者复制代码使用时检查代码缩进。 ji.py可参考:https://yunyin.cn-gd.ufileos.com/ji.py

        • import json
          import torch
          import sys
          import numpy as np
          import cv2
          from pathlib import Path
          
          # from ensemble_boxes import weighted_boxes_fusion
          
          from models.experimental import attempt_load
          from utils.torch_utils import select_device
          from utils.general import check_img_size, non_max_suppression, scale_coords
          from utils.augmentations import letterbox
          
          device = torch.device("cuda:0")
          # 模型地址一定要和测试阶段选择的模型地址一致!!!
          model_path = '/project/train/models/train/exp/weights/best.pt'
          
          @torch.no_grad()
          def init():
              weights = model_path
              device = 'cuda:0'  # cuda device, i.e. 0 or 0,1,2,3 or
          
              half = True  # use FP16 half-precision inference
              device = select_device(device)
              w = str(weights[0] if isinstance(weights, list) else weights)
              model = torch.jit.load(w) if 'torchscript' in w else attempt_load(
                  weights, device=device)
              if half:
                  model.half()  # to FP16
              model.eval()
              return model
          
          def process_image(handle=None, input_image=None, args=None, **kwargs):
              half = True  # use FP16 half-precision inference
              conf_thres = 0.3  # confidence threshold
              iou_thres = 0.05  # NMS IOU threshold
          
              max_det = 1000  # maximum detections per image
              imgsz = [1024, 1024]
              names = {
                  0: 'person',
                  1: 'hat',
                  2: 'head'
              }
          
              stride = 32
              fake_result = {}
              fake_result["model_data"] = {"objects": []}
          
              img = letterbox(input_image, imgsz, stride, True)[0]
              img = img.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
              img = img/255  # 0 - 255 to 0.0 - 1.0
              img = torch.from_numpy(img)
              img = torch.unsqueeze(img, dim=0).to(device)
              img = img.type(torch.cuda.HalfTensor)
              pred = handle(img, augment=False, visualize=False)[0]
          
              pred = non_max_suppression(
                  pred, conf_thres, iou_thres, None, False, max_det=max_det)
          
              for i, det in enumerate(pred):  # per image
                  det[:, :4] = scale_coords(
                      img.shape[2:], det[:, :4], input_image.shape).round()
                  for *xyxy, conf, cls in reversed(det):
                      xyxy_list = torch.tensor(xyxy).view(1, 4).view(-1).tolist()
                      conf_list = conf.tolist()
                      label = names[int(cls)]
                      fake_result['model_data']['objects'].append({
                          "xmin": int(xyxy_list[0]),
                          "ymin": int(xyxy_list[1]),
                          "xmax": int(xyxy_list[2]),
                          "ymax": int(xyxy_list[3]),
                          "confidence": conf_list,
                          "name": label
                      })
          
              return json.dumps(fake_result, indent=4)
          
          if __name__ == '__main__':
              # Test API
              img = cv2.imread('/home/data/831/helmet_10809.jpg')
              predictor = init()
              import time
              s = time.time()
              fake_result = process_image(predictor, img)
              e = time.time()
              print(fake_result)
              print((e-s))
          
      • 加载编码环境的样例集模型,测试ji.py脚本

        • #shell
          cd /project/ev_sdk/src
          python ji.py
          
      • 运行时发生报错:FileNotFoundError: [Errno 2] No such file or directory: '/project/train/models/train/exp/weights/best.pt'

        • 其实就是我们之前在控制台训练的时候,报错调试,导致运行了多次,产生了多个 exp 文件(train/models/train/目录下,比如我这里就有四个 exp 文件夹)
        • 解决方法就是,先找一下 best.pt 这个权重文件在哪里
        • cd ~
          find . -name "best.pt"
        • 我的输出结果就只有一条:./train/models/train/exp4/weights/best.pt,也应证了我刚分析的,多个目录,最新的一个目录里面有成功训练出来的模型文件
        • 然后我们就只需要把这个路径拿着,到刚刚的 ji.py 里面去替换第 17 行的目录就行了:
        • 截屏2026-04-15 12.03.55.png
      • ji.py运行结果如下:

        • 截屏2026-04-15 12.04.54.png
    • 6. 发起模型测试

      • 当开发者在编码环境下完成ji.py脚本编写后,可使用极市平台AI开发工作台模型开发发起模型测试功能,正式执行测试数据集下的模型测试。主要流程如下:

      • 发起模型测试,选择需要挂载的模型
        • 重要:请确保ji.py代码中的模型绝对路径与选择的模型文件路径保持一致

        • 截屏2026-04-15 12.09.17.png
      • 等待测试结束,查看测试结果,出分上榜
      • 截屏2026-04-15 12.10.13.png
      • 截屏2026-04-15 12.14.53.png
      • 截屏2026-04-15 12.16.29.png
    • 7. 将模型导出 .ONNX 格式

      • 当开发完成模型测试后,可使用模型开发内发起训练功能,将模型挂载进训练任务,将yolov5原生训练产出的.pt权重,导出为ONNX格式的模型文件,以便后续的算法开发阶段使用C++语言完成evsdk算法封装。主要流程如下:

      • 安装 ONNX 环境依赖
        • pip install  onnx onnx-simplifier onnxruntime #安装ONNX环境依赖(可参考export.py)
        • 这里可能会没法访问仓库,像上面的报错一样处理,通过清华源下载即可。另外,不需要 onnx-simplifier 模块,这个模块它会触发源码编译,会额外依赖 pytest-runner库,这个库的下载也会触发白名单外的仓库:
        • pip install onnx onnxruntime -i https://pypi.tuna.tsinghua.edu.cn/simple --trusted-host pypi.tuna.tsinghua.edu.cn --default-timeout=100
        • 截屏2026-04-15 12.36.15.png
      • 修改yolov5自带export.py脚本 为了匹配后续算法开发流程中evsdk规范,平台对export脚本做出了如下改造。请开发者将如下代码添加至yolov5/export.py中。
        • #更改anchor尺寸
          grid = model.model[-1].anchor_grid
          model.model[-1].anchor_grid = [a[..., :1, :1, :] for a in grid]
        • 截屏2026-04-15 12.24.12.png
      • 编写转换脚本convert_onnx.sh 请在/project/train/src_repo下创建convert_onnx.sh脚本,并写入如下内容:
        • cd  /project/train/src_repo/yolov5
          python export.py --weights /project/train/models/train/exp/weights/best.pt --include onnx --data data/helmet.yaml --simplify
        • 截屏2026-04-15 12.38.03.png
      • 挂载模型发起模型导出 请开发者使用模型开发阶段发起训练功能,输入指令bash /project/train/src_repo/convert_onnx.sh,挂载训练任务中的模型权重文件,执行模型导出ONNX。
        • 截屏2026-04-15 12.39.19.png
      • 任务 ID 175560175561的转换任务,都出现了错误:
        • convert_onnx.sh 脚本中的路径,也要跟上面一样更改成最新的 exp 文件夹!!
          • find . -name "best.pt"
            # 假设输出是 ./train/models/train/exp4/weights/best.pt
          • # convert_onnx.sh
            cd  /project/train/src_repo/yolov5
            python export.py --weights /project/train/models/train/exp4/weights/best.pt --include onnx --data data/helmet.yaml --simplify
      • 截屏2026-04-15 12.56.55.png
      • 查看模型导出结果 模型导出任务结束后,开发者可在模型列表查看已经导出的ONNX模型文件。
        • 截屏2026-04-15 12.58.07.png
        • 可以看到在我们前面指定的 exp4 文件夹下出现了 .onnx 的文件
    • 最后的最后!

      • 本实践教程偏向展现极市平台AI开发的功能使用,尝试以最简、最直观的路径帮助平台新用户快速体验平台AI开发流程。本文不涉及数据增强、参数优化等常见AI开发工作,开发者可在平台编码环境中自行进行相关工作。

      • 以上内容绝大部分来自于 极市 平台的官方文档 基于YOLOv5的模型开发实践
      • 在此基础上,我对其整理存档,并加入了我自己在实操过程当中的经验和报错解决方法
      • 如果以上内容对您造成了侵权,请您联系我,我将在第一时间删除这边文档,并对此事造成的后果表示由衷的抱歉!