Yolov8:训练模子并部署到安卓端

打印 上一主题 下一主题

主题 1003|帖子 1003|积分 3009

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
目次
1. 训练前准备
1.1 网络训练数据,完成分类标记
1.2 划分训练集、验证集和测试集
1.3 定义训练参数文件
2. 训练模子
2.1训练数据
​2.2 验证数据
2.3 预测数据
3.安卓端部署
3.1 模子转换
3.2 将onnx文件转换成param、bin文件
3.3 修改相关配置参数
3.4 运行测试


上一篇 yolov8的完整部署 讲解了Yolov8模子的部署和运行
实际生活中,我们需要的检索目标,许多并不在官方给出的模子之中。那我们就需要训练本身的数据模子。
1. 训练前准备

1.1 网络训练数据,完成分类标记

首先,我们在根目次datasets/文件夹下像存放coco128数据的格式创建fire文件,在fire/文件下创建Myimages和Annotations文件夹,这个背面我们会用到。

这里我们要用到一个工具 labelimg,通过下面下令安装这个工具:
  1. pip install labelimg -i https://pypi.tuna.tsinghua.edu.cn/simple
复制代码
 安装好以后,我们在fire文件夹下创建一个predefined_classed.txt文件, 在这个文件中,我们可以写入你本身定义的种别名称。每一个种别换一行。这里我指定以了三个分类 smog、fire、person

在下令管理器里输入下面下令,进入labelimg界面
  1. labelimg predefined_classed.txt
复制代码

把网络的训练图片全部输入在fire目次下创建的Myimages文件夹
待标注图片数据的路径文件夹,选择Myimages文件夹
保存种别标签的路径文件夹,选择Annotations 文件夹
这个按键可以说明我们标注的标签为voc格式,点击可以换成yolo或者createML格式。
点击View,会出现如图红色框框中的选项。最好和我一样把勾勾勾上。

点击 Create RectBox,框选图片中的目标。保存名称为分类名。这里是smog、fire,然后点击Next Image切换写一个图片进行标记。

关闭labelimg,我们可以在Annotations 文件夹里,看到新天生的.xml文件。

1.2 划分训练集、验证集和测试集

在datasets目次下创建程序 xml2txt.py并运行,将XML格式转yolo_txt格式,代码如下(地址可修改)
  1. import xml.etree.ElementTree as ET
  2. import os, cv2
  3. import numpy as np
  4. from os import listdir
  5. from os.path import join
  6. classes = []
  7. def convert(size, box):
  8.     dw = 1. / (size[0])
  9.     dh = 1. / (size[1])
  10.     x = (box[0] + box[1]) / 2.0 - 1
  11.     y = (box[2] + box[3]) / 2.0 - 1
  12.     w = box[1] - box[0]
  13.     h = box[3] - box[2]
  14.     x = x * dw
  15.     w = w * dw
  16.     y = y * dh
  17.     h = h * dh
  18.     return (x, y, w, h)
  19. def convert_annotation(xmlpath, xmlname):
  20.     with open(xmlpath, "r", encoding='utf-8') as in_file:
  21.         txtname = xmlname[:-4] + '.txt'
  22.         txtfile = os.path.join(txtpath, txtname)
  23.         tree = ET.parse(in_file)
  24.         root = tree.getroot()
  25.         filename = root.find('filename')
  26.         img = cv2.imdecode(np.fromfile('{}/{}.{}'.format(imgpath, xmlname[:-4], postfix), np.uint8), cv2.IMREAD_COLOR)
  27.         h, w = img.shape[:2]
  28.         res = []
  29.         for obj in root.iter('object'):
  30.             cls = obj.find('name').text
  31.             if cls not in classes:
  32.                 classes.append(cls)
  33.             cls_id = classes.index(cls)
  34.             xmlbox = obj.find('bndbox')
  35.             b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
  36.                  float(xmlbox.find('ymax').text))
  37.             bb = convert((w, h), b)
  38.             res.append(str(cls_id) + " " + " ".join([str(a) for a in bb]))
  39.         if len(res) != 0:
  40.             with open(txtfile, 'w+') as f:
  41.                 f.write('\n'.join(res))
  42. if __name__ == "__main__":
  43.     postfix = 'jpg'
  44.     imgpath = 'fire/Myimages'
  45.     xmlpath = 'fire/Annotations'
  46.     txtpath = 'fire/Mylabels'
  47.     if not os.path.exists(txtpath):
  48.         os.makedirs(txtpath, exist_ok=True)
  49.     list = os.listdir(xmlpath)
  50.     error_file_list = []
  51.     for i in range(0, len(list)):
  52.         try:
  53.             path = os.path.join(xmlpath, list[i])
  54.             if ('.xml' in path) or ('.XML' in path):
  55.                 convert_annotation(path, list[i])
  56.                 print(f'file {list[i]} convert success.')
  57.             else:
  58.                 print(f'file {list[i]} is not xml format.')
  59.         except Exception as e:
  60.             print(f'file {list[i]} convert error.')
  61.             print(f'error message:\n{e}')
  62.             error_file_list.append(list[i])
  63.     print(f'this file convert failure\n{error_file_list}')
  64.     print(f'Dataset Classes:{classes}')
复制代码
运行完成后再Mylabels文件下会天生xml文件对应的txt文件,保存不同图像的标注文件,每个图像对应一个txt文件,文件每一行为一个目标的信息,分别为class, x_center, y_center, width, height,这种为 yolo_txt格式。

将Myimages和Mylabels中的文件划分训练、验证、测试集,创建split_data.py并运行可完成此操作,代码如下(更改val_size和test_size控制验证和测试集比例)
  1. import os, shutil
  2. from sklearn.model_selection import train_test_split
  3. val_size = 0.1
  4. test_size = 0.2
  5. postfix = 'jpg'
  6. imgpath = 'fire/Myimages'
  7. txtpath = 'fire/Mylabels'
  8. os.makedirs('fire/images/train', exist_ok=True)
  9. os.makedirs('fire/images/val', exist_ok=True)
  10. os.makedirs('fire/images/test', exist_ok=True)
  11. os.makedirs('fire/labels/train', exist_ok=True)
  12. os.makedirs('fire/labels/val', exist_ok=True)
  13. os.makedirs('fire/labels/test', exist_ok=True)
  14. listdir = [i for i in os.listdir(txtpath) if 'txt' in i]
  15. train, test = train_test_split(listdir, test_size=test_size, shuffle=True, random_state=0)
  16. train, val = train_test_split(train, test_size=val_size, shuffle=True, random_state=0)
  17. print(f'train set size:{len(train)} val set size:{len(val)} test set size:{len(test)}')
  18. for i in train:
  19.     shutil.copy('{}/{}.{}'.format(imgpath, i[:-4], postfix), 'Myimages/train/{}.{}'.format(i[:-4], postfix))
  20.     shutil.copy('{}/{}'.format(txtpath, i), 'Mylabels/train/{}'.format(i))
  21. for i in val:
  22.     shutil.copy('{}/{}.{}'.format(imgpath, i[:-4], postfix), 'Myimages/val/{}.{}'.format(i[:-4], postfix))
  23.     shutil.copy('{}/{}'.format(txtpath, i), 'Mylabels/val/{}'.format(i))
  24. for i in test:
  25.     shutil.copy('{}/{}.{}'.format(imgpath, i[:-4], postfix), 'Myimages/test/{}.{}'.format(i[:-4], postfix))
  26.     shutil.copy('{}/{}'.format(txtpath, i), 'Mylabels/test/{}'.format(i))
复制代码
运行完目次

1.3 定义训练参数文件

在  fire 文件夹下 新建一个 fire.yaml文件,具体内容如下


  • train: 训练图片路径
  • val:验证图片路径
  • test: 测试图片路径
  • names: 训练的类名称聚集

修改yolov8.yaml,nc背面的参数改为训练的类数量

2. 训练模子

2.1训练数据

输入训练下令
  1. yolo task=detect mode=train model=yolov8s.pt data=datasets/fire/fire.yaml epochs=100 batch=4
复制代码
训练好的模子会被保存在 yolov8 目次下的 runs/detect/train/weights/ 下,天生 best.pt,last.pt文件。
2.2 验证数据


输入验证下令,用训练好的模子去验证
  1. yolo task=detect mode=val model=runs/detect/train4/weights/best.pt  data=datasets/fire/fire.yaml device=cpu
复制代码
验证数据会被保存在 yolov8 目次下的 runs/detect/val/下,

2.3 预测数据

  1. yolo task=detect mode=predict model=runs/detect/train4/weights/best.pt source=datasets/fire/images/val  device=cpu
复制代码
测试输出的数据会被保存在 yolov8 目次下的 runs/detect/predict/下

3.安卓端部署

上一篇yolov8模子安卓端部署具体先容了部署步骤,现在只需将yolov8模子替换成本身训练的模子并修改相关参数即可
3.1 模子转换

转换本身训练的pt权重为ncnn格式
我们接纳.pt ->onnx->ncnn的门路来转换本身训练的模子
(1)修改ultralytics/ultralytics/nn/modules/block.py中的class C2f(nn.Module)如下:
  1. def forward(self, x):
  2.         """Forward pass through C2f layer."""
  3.         # y = list(self.cv1(x).chunk(2, 1))   #注释
  4.         # y.extend(m(y[-1]) for m in self.m)  #注释
  5.         # return self.cv2(torch.cat(y, 1))    #注释
  6.         x = self.cv1(x)
  7.         x = [x, x[:, self.c:, ...]]
  8.         x.extend(m(x[-1]) for m in self.m)
  9.         x.pop(1)
  10.         return self.cv2(torch.cat(x, 1))
复制代码

(2)修改ultralytics/ultralytics/nn/modules/head.py中的class Detect(nn.Module)改动如下:
  1. def forward(self, x):
  2.         """Concatenates and returns predicted bounding boxes and class probabilities."""
  3.         # if self.end2end:
  4.         #     return self.forward_end2end(x)
  5.         #
  6.         # for i in range(self.nl):
  7.         #     x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
  8.         # if self.training:  # Training path
  9.         #     return x
  10.         # y = self._inference(x)
  11.         # return y if self.export else (y, x)
  12.         shape = x[0].shape  # BCHW
  13.         for i in range(self.nl):
  14.             x[i] = torch.cat((self.cv2[i](x[i]), self.cv3[i](x[i])), 1)
  15.         if self.training:
  16.             return x
  17.         elif self.dynamic or self.shape != shape:
  18.             self.anchors, self.strides = (x.transpose(0, 1) for x in make_anchors(x, self.stride, 0.5))
  19.             self.shape = shape
  20.         # 中间部分注释掉,return语句替换为
  21.         return torch.cat([xi.view(shape[0], self.no, -1) for xi in x], 2).permute(0, 2, 1)
复制代码

(3)安装onnx包,在终端运行下面下令
  1. pip install onnx coremltools onnx-simplifier
复制代码
(4)根据官方文档,创建demo.py,将pt权重文件转换成onnx格式,代码如下
  1. # 将模型导出为 ONNX 格式
  2. from ultralytics import YOLO
  3. model = YOLO("runs/detect/train4/weights/best.pt")
  4. success = model.export(format="onnx", simplify=True, opset=11)
复制代码
opset的参数(11、12、13皆可),使用其他会造成手机上运行出现许多框重叠错误识别
(5)对onnx文件进行压缩,进入到onnx文件所在目次,运行下面下令
  1. python -m onnxsim best.onnx best-sim.onnx
复制代码
压缩完之后会天生一个best-sim.onnx的文件,这一步是必须的,假如这一步不做,背面ONNX转NCNN大概会报错
完成上面步骤包含pt文件的weights目次下会天生对应文件

3.2 将onnx文件转换成param、bin文件

将best.onnx转成param和bin文件,可以下载以下文件之一 
地址:Releases · Tencent/ncnn · GitHub

解压后,打开文件夹,并将best.onnx复制到该文件夹的对应位置,地址栏输入cmd后,在打开的下令行窗口输入onnx2ncnn.exe best-sim.onnx best.param best.bin回车即可,原文件夹天生了需要的bin文件和param文件


3.3 修改相关配置参数

(1)替换本身的模子到assets下

(2)修改strings.xml文件

把item替换为本身模子的名字
(3)修改yolov8ncnn.cpp文件
修改modeltypes变量为本身模子的名字,target_sizes为320,具体代码如下:

(4)修改yolo.cpp文件
1)Yolo::load方法中,模子名字修改,代码如下图:

2)Yolo::detect方法中,输入输出的名字修改。
可以使用onnx,通过网站link进行输入输出名字的查察。



具体修改代码内容如下:

这里修改了输入为Myimages,输出为output0
3)Yolo::draw方法中,修改class_names为你的种别名。这个模子中,只有一个种别,所以这里设置为smog、fire、person

4)修改yolo.cpp中的方法generate_proposals
修改num_class为你对应的种别数量,我们这里是一类,所以是3,具体代码如下:

完成上述步骤后,直接运行
3.4 运行测试

连上手机,点击run按钮,编译安装调试,运行成功,手机端已经安装好这个APP



免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万有斥力

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表