admin 管理员组文章数量: 887021
YOLOX模型部署Android端-NCNN方法
- 1.YOLOX代码仓库的下载
- 2.ONNX模型转换
- 3.NCNN框架模型转换
- 4 安装Android Studio
- 5 准备Android项目文件
- 6 连接手机
将自己任务的YOLOX-nano模型和YOLOX-tiny模型通过NCNN架构的转换方式部署到Android手机端。
1.YOLOX代码仓库的下载
对于自己的任务,需要修改以下几处代码:
- 1.1数据集的准备(使用的VOC数据格式):在datasets文件夹下新建一个VOCdevkit文件夹->接着在VOCdevkit文件夹下新建VOC2007文件夹->接着在VOC2007文件夹下新建Annotations、ImageSets、JPEGImages三个文件夹,分别存储.xml标签文件、划分数据集为训练集和验证集的文本、和图片数据。
大概如下图所示:
- 1.2 根据自己的任务修改几处代码:(1)如果我们使用YOLOX-nano模型,建议在.\exps\example\yolox_voc\文件夹下新建一个yolox_voc_nano.py,该文件与yolox_voc_s.py并列。yolox_voc_nano.py的代码是参考别人的,如下所示:
# encoding: utf-8
import os
import torch
# 需要加上这个
import torch.nn as nn
import torch.distributed as dist
from yolox.data import get_yolox_datadir
from yolox.exp import Exp as MyExp
class Exp(MyExp):
def __init__(self):
super(Exp, self).__init__()
# 修改网络深度和宽度
self.depth = 0.33
self.width = 0.25
self.input_size = (416, 416)
self.mosaic_scale = (0.5, 1.5)
self.random_size = (10, 20)
self.test_size = (416, 416)
self.exp_name = os.path.split(os.path.realpath(__file__))[1].split(".")[0]
self.enable_mixup = False
# 修改类别数
self.num_classes = 1
# 之前没有加上这个get_model函数,就训练有问题
def get_model(self, sublinear=False):
def init_yolo(M):
for m in M.modules():
if isinstance(m, nn.BatchNorm2d):
m.eps = 1e-3
m.momentum = 0.03
if "model" not in self.__dict__:
from yolox.models import YOLOX, YOLOPAFPN, YOLOXHead
in_channels = [256, 512, 1024]
# NANO model use depthwise = True, which is main difference.
backbone = YOLOPAFPN(self.depth, self.width, in_channels=in_channels, depthwise=True)
head = YOLOXHead(self.num_classes, self.width, in_channels=in_channels, depthwise=True)
self.model = YOLOX(backbone, head)
self.model.apply(init_yolo)
self.model.head.initialize_biases(1e-2)
return self.model
def get_data_loader(self, batch_size, is_distributed, no_aug=False, cache_img=False):
from yolox.data import (
VOCDetection,
TrainTransform,
YoloBatchSampler,
DataLoader,
InfiniteSampler,
MosaicDetection,
worker_init_reset_seed,
)
from yolox.utils import (
wait_for_the_master,
get_local_rank,
)
local_rank = get_local_rank()
with wait_for_the_master(local_rank):
dataset = VOCDetection(
data_dir=os.path.join(get_yolox_datadir(), "VOCdevkit"),
# image_sets=[('2007', 'trainval'), ('2012', 'trainval')],
# 训练的时候只有VOC2007的数据集,所以需要改这里
image_sets=[('2007', 'trainval')],
img_size=self.input_size,
preproc=TrainTransform(
max_labels=50,
flip_prob=self.flip_prob,
hsv_prob=self.hsv_prob),
cache=cache_img,
)
dataset = MosaicDetection(
dataset,
mosaic=not no_aug,
img_size=self.input_size,
preproc=TrainTransform(
max_labels=120,
flip_prob=self.flip_prob,
hsv_prob=self.hsv_prob),
degrees=self.degrees,
translate=self.translate,
mosaic_scale=self.mosaic_scale,
mixup_scale=self.mixup_scale,
shear=self.shear,
enable_mixup=self.enable_mixup,
mosaic_prob=self.mosaic_prob,
mixup_prob=self.mixup_prob,
)
self.dataset = dataset
if is_distributed:
batch_size = batch_size // dist.get_world_size()
sampler = InfiniteSampler(
len(self.dataset), seed=self.seed if self.seed else 0
)
batch_sampler = YoloBatchSampler(
sampler=sampler,
batch_size=batch_size,
drop_last=False,
mosaic=not no_aug,
)
dataloader_kwargs = {"num_workers": self.data_num_workers, "pin_memory": True}
dataloader_kwargs["batch_sampler"] = batch_sampler
# Make sure each process has different random seed, especially for 'fork' method
dataloader_kwargs["worker_init_fn"] = worker_init_reset_seed
train_loader = DataLoader(self.dataset, **dataloader_kwargs)
return train_loader
def get_eval_loader(self, batch_size, is_distributed, testdev=False, legacy=False):
from yolox.data import VOCDetection, ValTransform
valdataset = VOCDetection(
data_dir=os.path.join(get_yolox_datadir(), "VOCdevkit"),
image_sets=[('2007', 'test')],
img_size=self.test_size,
preproc=ValTransform(legacy=legacy),
)
if is_distributed:
batch_size = batch_size // dist.get_world_size()
sampler = torch.utils.data.distributed.DistributedSampler(
valdataset, shuffle=False
)
else:
sampler = torch.utils.data.SequentialSampler(valdataset)
dataloader_kwargs = {
"num_workers": self.data_num_workers,
"pin_memory": True,
"sampler": sampler,
}
dataloader_kwargs["batch_size"] = batch_size
val_loader = torch.utils.data.DataLoader(valdataset, **dataloader_kwargs)
return val_loader
def get_evaluator(self, batch_size, is_distributed, testdev=False, legacy=False):
from yolox.evaluators import VOCEvaluator
val_loader = self.get_eval_loader(batch_size, is_distributed, testdev, legacy)
evaluator = VOCEvaluator(
dataloader=val_loader,
img_size=self.test_size,
confthre=self.test_conf,
nmsthre=self.nmsthre,
num_classes=self.num_classes,
)
return evaluator
当然yolox_voc_nano.py代码你也可以不用新建(只训练nano模型时),只需要在yolox_voc_s.py代码的基础上进行修改(nano和tiny模型都在这上面修改即可,类别数及模型大小)。self.num_classes 类别数、self.depth = 0.33、self.width = 0.25(nano模型:0.33,0.25;tiny模型:0.33,0.375)以及wait_for_the_master函数下的data_dir=路径直接修改为数据集的绝对路径,比如:data_dir=“E:\Android_studio\YOLOX\datasets\VOCdevkit”,而下面的image_sets函数修改为:image_sets=[(‘2007’, ‘trainval’)],。同理,下面的get_eval_loade函数这两处也修改绝对路径和image_sets=[(‘2007’, ‘test’)],。
(2)还需要修改.\yolox\exp\yolox_base.py文件。其中的self.num_classes 类别数、self.depth = 0.33、self.width = 0.25(nano模型:0.33,0.25;tiny模型:0.33,0.375),self.input_size =(416,416)建议为416。其中还可以根据自己的调参习惯进行其余项的修改,最大的epoch数,热重启的学习率,以及多少个epoch进行验证等。
(3)接下来就是.\tools\train.py文件的修改。其中-b为batch size的大小,-f需要修改:default=“exps/example/yolox_voc/yolox_voc_s.py”,-c是加载预训练模型default="weights/yolox_nano.pth"其余的按着自己电脑配置自行设置。
(4)最后就是模型的训练了。
2.ONNX模型转换
默认我们已经训练好了自己的模型,并得到了yolox_nano.pth和yolox_tiny.pth(就是每次得到的best_ckpt.pth进行重命名就可以)。
- 使用YOLOX官方自带的export_onnx.py进行转换。其中**–output-name需要修改转换模型的保存路径,-f同样需要进行指定文件default="exps/example/yolox_voc/yolox_voc_s.py,-c加载的就是我们训练好的.pt的权值文件。转换提示如下图:(nano和tiny模型需要修改两次,-f指定的yolox_voc_s.py文件需要按着每次转换的模型修改深度和宽度的系数)
3.NCNN框架模型转换
接下来就稍微有些麻烦了~~
- 3.1 安装protobuf:下载后解压:https://github/google/protobuf/archive/v3.4.0.zip ,然后使用
绿色终端执行命令:
cd <protobuf-root-dir>
mkdir build-vs2017
cd build-vs2017
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -
DCMAKE_INSTALL_PREFIX=%cd%/install -Dprotobuf_BUILD_TESTS=OFF -
Dprotobuf_MSVC_STATIC_RUNTIME=OFF ../cmake
nmake
nmake install
编译后执行,验证是否安装。
protoc.exe --version
- 3.2 编译NCNN,从该地址下载:https://github/Tencent/ncnn/releases。同样打开刚才的(VS2017X64)的命令终端(进入到ncnn根目录下),执行以下语句:
cd <ncnn-root-dir>
mkdir -p build-vs2017
cd build-vs2017
cmake -G"NMake Makefiles" -DCMAKE_BUILD_TYPE=Release -
DCMAKE_INSTALL_PREFIX=%cd%/install -DProtobuf_INCLUDE_DIR=D:/protobuf3.4.0/build-vs2019/install/include -DProtobuf_LIBRARIES=D:/protobuf-3.4.0/buildvs2019/install/lib/libprotobuf.lib -DProtobuf_PROTOC_EXECUTABLE=D:/protobuf3.4.0/build-vs2019/install/bin/protoc.exe -DNCNN_VULKAN=OFF ..
nmake
nmake install
编译后.\ncnn\build-vs2019\tools\onnx下有onnx2ncnn.exe。
注:这里其实还可以使用另一种官方的库,实在是忘记参考哪个博客下载的,该文件下包含转换的exe程序,而且不需要下载编译,可直接用该文件夹.\X64\bin\下的onnx2ncnn.exe,ncnnoptimize.exe:网盘地址:链接:https://pan.baidu/s/1aKNmAvApLsnKtBl0BXA22Q
提取码:8ewm
- 3.3 生成NCNN文件。拷贝yolox_nano.onnx和yolox_tiny.onnx文件到onnx2ncnn.exe,ncnnoptimize.exe文件下,生成ncnn相应的param和bin文件。(命令如下)
onnx2ncnn.exe yolox_nano.onnx yolox_nano.param yolox_nano.bin
onnx2ncnn.exe yolox_tiny.onnx yolox_tiny.param yolox_tiny.bin
因为ncnn不支持Focus模块,会有警告:(没关系,不用管)
Unsupported slice step !
Unsupported slice step !
Unsupported slice step !
- 3.4 因为ncnn不支持Focus模块,所以需要对yolox_nano.param、yolox_tiny.param进行修改。
(1)yolo_nano.param模型修改前:
7767517
295 328
Input images 0 1 images
Split splitncnn_input0 1 4 images images_splitncnn_0
images_splitncnn_1 images_splitncnn_2 images_splitncnn_3
Crop Slice_4 1 1 images_splitncnn_3 647 -23309=1,0
-23310=1,2147483647 -23311=1,1
Crop Slice_9 1 1 647 652 -23309=1,0
-23310=1,2147483647 -23311=1,2
Crop Slice_14 1 1 images_splitncnn_2 657 -23309=1,0
-23310=1,2147483647 -23311=1,1
Crop Slice_19 1 1 657 662 -23309=1,1
-23310=1,2147483647 -23311=1,2
Crop Slice_24 1 1 images_splitncnn_1 667 -23309=1,1
-23310=1,2147483647 -23311=1,1
Crop Slice_29 1 1 667 672 -23309=1,0
-23310=1,2147483647 -23311=1,2
Crop Slice_34 1 1 images_splitncnn_0 677 -23309=1,1
-23310=1,2147483647 -23311=1,1
Crop Slice_39 1 1 677 682 -23309=1,1
-23310=1,2147483647 -23311=1,2
Concat Concat_40 4 1 652 672 662 682 683 0=0
Convolution Conv_41 1 1 683 1177 0=16 1=3 11=3 2=1 12=1
3=1 13=1 4=1 14=1 15=1 16=1 5=1 6=1728
Swish Mul_43 1 1 1177 687
ConvolutionDepthWise Conv_44 1 1 687 1180 0=16 1=3 11=3 2=1
12=1 3=2 13=2 4=1 14=1 15=1 16=1 5=1 6=144 7=16
Swish Mul_46 1 1 1180 691
Convolution Conv_47 1 1 691 1183 0=32 1=1 11=1 2=1 12=1
3=1 13=1 4=0 14=0 15=0 16=0 5=1 6=512
Swish Mul_49 1 1 1183 695
把 295修改为295 - 9 = 286 (由于我们将删除 10 层并添加 1 层,因此总层数应减去 9)。然后从 Split 到 Concat 删除 10 行代码,但记住Concat一行最后倒数第二个数字:683。在输入后添加 YoloV5Focus 层(使用之前的数字 683):
YoloV5Focus focus 1 1 images 683
yolox_nano.param文件修改后:(注:YoloV5Focus一定不要写错大小写,app闪退就是因为大小写)
286 328
Input images 0 1 images
YoloV5Focus focus 1 1 images 683
(2)yolo_tiny.param模型修改前:
7767517
235 268
Input images 0 1 images
Split splitncnn_input0 1 4 images images_splitncnn_0
images_splitncnn_1 images_splitncnn_2 images_splitncnn_3
Crop Slice_4 1 1 images_splitncnn_3 467 -23309=1,0
-23310=1,2147483647 -23311=1,1
Crop Slice_9 1 1 467 472 -23309=1,0
-23310=1,2147483647 -23311=1,2
Crop Slice_14 1 1 images_splitncnn_2 477 -23309=1,0
-23310=1,2147483647 -23311=1,1
Crop Slice_19 1 1 477 482 -23309=1,1
-23310=1,2147483647 -23311=1,2
Crop Slice_24 1 1 images_splitncnn_1 487 -23309=1,1
-23310=1,2147483647 -23311=1,1
Crop Slice_29 1 1 487 492 -23309=1,0
-23310=1,2147483647 -23311=1,2
Crop Slice_34 1 1 images_splitncnn_0 497 -23309=1,1
-23310=1,2147483647 -23311=1,1
Crop Slice_39 1 1 497 502 -23309=1,1
-23310=1,2147483647 -23311=1,2
Concat Concat_40 4 1 472 492 482 502 503 0=0
Convolution Conv_41 1 1 503 877 0=24 1=3 11=3 2=1 12=1 3=1
13=1 4=1 14=1 15=1 16=1 5=1 6=2592
Swish Mul_43 1 1 877 507
Convolution Conv_44 1 1 507 880 0=48 1=3 11=3 2=1 12=1 3=2
13=2 4=1 14=1 15=1 16=1 5=1 6=10368
Swish Mul_46 1 1 880 511
Split splitncnn_0 1 2 511 511_splitncnn_0
511_splitncnn_1
Convolution Conv_47 1 1 511_splitncnn_1 883 0=24 1=1 11=1
2=1 12=1 3=1 13=1 4=0 14=0 15=0 16=0 5=1 6=1152
把 235修改为235 - 9 = 226 (由于我们将删除 10 层并添加 1 层,因此总层数应减去 9);然后从 Split 到 Concat 删除 10 行代码,记住Concat一行最后倒数第二个数字:503。在输入后添加 YoloV5Focus 层(使用之前的数字 503)。修改后为:
7767517
226 268
Input images 0 1 images
YoloV5Focus focus 1 1 images 503
(3)对算子量化。使用ncnnoptimize.exe进行模型的量化。基于刚刚修改的.param和.bin文件,在终端输入:
ncnnoptimize.exe yolox_nano.param yolox_nano.bin yolox_nano.param yolox_nano.bin 65536
yolox_nano.param文件中开头的286改280,328改310(自动改的)
ncnnoptimize.exe yolox_tiny.param yolox_tiny.bin yolox_tiny.param yolox_tiny.bin 65536
yolox_tiny.param文件中开头的226改220,268改250(自动改的)
其中.param为模型的结构文件,.bin为模型的参数文件。
4 安装Android Studio
网址:https://developer.android.google/studio/
安装时会提示安装SDK
注意:Android SDK安装路径中不要有空格
注意配置:
File->Settings->Appearance & Behavior ->System Settings->Android SDK
SDK Platforms选中面向手机的Android版本
SDK Tools选中NDK, CMake
(值得注意的是:校园网会出现加载不出来SDK Tools的选项,所以要使用手机热点)
5 准备Android项目文件
- 5.1 首先下载Android开发文件:https://github/nihui/ncnn-android-nanodet(里面已经有参考和步骤,我下载好的:链接:https://pan.baidu/s/14S05OMCx-O2BAvMx5cnJdA 提取码:5epp)
- 5.2 放置ncnn的安卓文件:https://github/Tencent/ncnn/releases
下载ncnn-YYYYMMDD-android-vulkan.zip
课程中使用ncnn-20210720-android-vulkan.zip
解压ncnn-YYYYMMDD-android-vulkan.zip后放置到app/src/main/jni 并修改 app/src/main/jni/CMakeLists.txt中的ncnn_DIR - 5.3 放置opencv的安卓文件:https://github/nihui/opencv-mobile
下载opencv-mobile-XYZ-android.zip
课程中使用opencv-mobile-4.5.3-android.zip
解压opencv-mobile-XYZ-android.zip后放置到app/src/main/jni并修改 app/src/main/jni/CMakeLists.txt中的 OpenCV_DIR - 5.4 修改CMakeLists.txt文件
project(ncnnyolox)
cmake_minimum_required(VERSION 3.10)
set(OpenCV_DIR ${CMAKE_SOURCE_DIR}/opencv-mobile-4.5.3-android/sdk/native/jni)
find_package(OpenCV REQUIRED core imgproc)
set(ncnn_DIR ${CMAKE_SOURCE_DIR}/ncnn-20210720-androidvulkan/${ANDROID_ABI}/lib/cmake/ncnn)
find_package(ncnn REQUIRED)
add_library(ncnnyolox SHARED yoloxncnn.cpp yolox.cpp ndkcamera.cpp)
target_link_libraries(ncnnyolox ncnn ${OpenCV_LIBS} camera2ndk mediandk)
- 5.5 自己数据集训练模型的部署
asset文件下,替换自己训练及转化的模型
修改yolox.cpp和yoloxncnn.cpp文件中的class_names(自己按着自己的任务修改)
6 连接手机
- 6.1 手机打开开发者模型,并打开开发者模式下的USB调试功能。
- 6.2 此时手机界面弹出提示窗口,选择允许,Android Studio就会连接到手机了。
- 6.3 点击右上方app左侧的绿色小锤子(不知道叫啥)再加载一次需要的环境,然后点击右侧的绿色三角开始键,apk就部署到手机上了。
下面是我最新的部署结果:
部署前:
自己的模型部署后:
有疑问的小伙伴可以联系我一起解决~~
本文标签: 模型 方法 YOLOX ncnn Android
版权声明:本文标题:YOLOX模型部署Android端-NCNN方法 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726386470h950235.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论