diff --git a/config/config_en.json b/config/config_en.json index 33c8fb10..01b5e939 100755 --- a/config/config_en.json +++ b/config/config_en.json @@ -61,6 +61,10 @@ { "url": "/soft/Lichee/zh/index.html", "label": "Lichee" + }, + { + "url": "/ai/en/index.html", + "label": "AI Guide" } ] }, diff --git a/config/config_zh.json b/config/config_zh.json index 2dbb228b..26e6a01e 100755 --- a/config/config_zh.json +++ b/config/config_zh.json @@ -60,6 +60,10 @@ { "url": "/soft/Lichee/zh/index.html", "label": "Lichee" + }, + { + "url": "/ai/zh/index.html", + "label": "AI 指南" } ] }, diff --git a/docs/soft/ai/assets/dnn.jpg b/docs/soft/ai/assets/dnn.jpg new file mode 100644 index 00000000..ecb93bf7 Binary files /dev/null and b/docs/soft/ai/assets/dnn.jpg differ diff --git a/docs/soft/ai/en/README.md b/docs/soft/ai/en/README.md new file mode 100644 index 00000000..37cbf2de --- /dev/null +++ b/docs/soft/ai/en/README.md @@ -0,0 +1,31 @@ +--- +title: AI Guide +date: 2022-09-15 +--- + + +## About this document + +`Sipeed` has launched a series of `AI` development boards, including: +* `Maix-I(M1)` series of `MaixBit`, `Maixduino` `M1s-Dock` and other microcontroller development boards with hardware `AI` acceleration. +* Cost-effective `SOC` development boards with `AI` hardware acceleration, such as `M2-Dock` `MaixSense` of the `Maix-II(M2)` series. +* High-performance `SOC` development boards such as `M3 AX-Pi` of the `Maix-III(M3)` series. + +In order to popularize the application of `AI` on edge devices, `Sipeed` has developed easy-to-use `MaixPy` and `MaixPy3` SDKs, and supplied [MaixHub](https://maixhub.com/) platform to make developers can easily train models without AI programming foundation and AI training experience. + +On this basis, this document is dedicated to providing developers with an `AI` development guide, the purpose is to allow novices to quickly get started with `AI` applications, or developers who have mastered `AI` related knowledge to quickly apply the results to the device or on the product. +The content includes but is not limited to: +* Basic knowledge of AI +* Model training guide +* AI tutorial recommendation +* AI interesting project recommendation +* Commonly used tools +* Edge Device Deployment Guide + + +## Participate in contribution + +The content of the document will be continuously updated, and everyone is welcome to participate in the content writing. How to participate: + +* You can directly click the `Edit this page` button in the upper right corner of the document to jump to `GitHub` to edit and submit a `PR` (for specific methods, see [Contribution Documentation](/share_docs/zh/) or search engine search `GitHub' how to submit a PR`). +* You can also directly send suggestions for changes or submit your manuscript to `support@sipeed.com`, the title should start with `[WiKi contribution]`, the text needs to indicate the author, the modified content and where to be modified, so that we can quickly transfer your Content updated to documentation. \ No newline at end of file diff --git a/docs/soft/ai/en/basic/what_is_ai.md b/docs/soft/ai/en/basic/what_is_ai.md new file mode 100644 index 00000000..1c9ca598 --- /dev/null +++ b/docs/soft/ai/en/basic/what_is_ai.md @@ -0,0 +1,59 @@ +--- +title: What is AI and machine learning? +date: 2022-09-15 +--- + +>! This document is not translate yet, translation is welcome + +AI(Artificial Intelligence) 想必大家都听之极多了,有人觉得 AI 要毁灭人类了;有人认为 AI 只是擅长某些特殊场景不必惊慌(比如 alpha Go 打败了人类围棋大师);但大多数人接触到的 AI 可能更愿意称之为“人工智障”,比如手机里面的助手;对于很多工程师来说, AI 可能更多的是指机器学习,比如图像识别,语音识别,自然语言处理等等。 + +作为一个开发者, 首先我们需要了解大家经常听到的 `AI`,`机器学习`,`神经网络`等概念以及区别: +* AI: 人工智能,是指让机器具有人类智能的能力,比如人类可以看到一张照片,然后判断出这是一只猫,这是一个人,这是一只狗等等,而机器也可以做到这一点,这就是人工智能。 +* 机器学习: 一般是指让机器通过大量的数据,然后通过算法,让机器自己学习,比如通过大量的猫的图片,让机器自己学习,然后判断出一张图片是不是猫。 +* 神经网络: 一般是指在机器学习中用到的一种数据结构,因为其类似人大脑的神经网络,各个数据节点互相连接互相通信和影响,故称之为神经网络。 +* 模型: 指用来承载和表示机器学习过程中的相关参数的数据结构,一般可以保存为一个文件,可以将神经网络的结构和参数都存储在这个数据结构里面,方便用数学或者编程语言将其解析,比如取名叫`.model`格式的文件 + +所以可以理解为三者是包含关系: 神经网络 ∈ 机器学习 ∈ 人工智能, 模型文件则一般为机器学习中的产物, 另外你可能还听说过“深度神经网络”,其实也是属于神经网络,只不过是网络层数有深度不同一说。 + +而本文也大多阐述了如何利用各种神经网络模型和机器学习的方法来实现 AI 应用。 + +## 机器学习过程简介 + +这里首先对机器学习的过程做一个通俗的介绍,不涉及数学公式,只是简单的介绍一下机器学习的过程。 + +### 训练 + +以让机器区分猫咪和狗为例: +和教(训练)人类婴儿一样,可以把模型比作婴儿,为了让这个模型能认识猫和狗,我们需要一遍一遍地让它看各种猫猫狗狗,并让它去识别,错了我们就告诉它错了,对了我们就告诉它对了,这样一遍一遍地让它看,让它学习,最终它就能区分猫和狗了。 + +从这段话我们分析出训练的时候几个关键点: +* 模型: 一个工具或者黑盒,给它一个输入,它能给我们一个输出结果。 +* 输入: 这里是图像,猫或者狗的图像。 +* 输出: 猫或者狗 +* 判断错误的方法:也就是它的输出和真实的结果是否一样,这里是靠教学者判断正误的,也就是判断错误的方法是教学者。 +* 学习方法:就是当我们告诉模型输出结果是错的时候,它如何去改进。 + +得到这几个关键点后,就可以很好地理解这个机器学习的过程了: +* 定义模型的输入输出, 输入是图像,输出是猫或者狗。 +* 为了让这个模型能够有学习的本领,也就是和人一样有足够的脑容量, 我们定义一个属于模型的“脑子”,也就是一个看起来和人脑突触结构类似的神经网络结构: +![神经网络](../../assets/dnn.jpg) +可以看到输入和中间每个节点间都有线连起来,每条线都是一个计算公式,具体是什么样的公式以及具体如何设计一个这样的结构这里先不细究,先有个概念就行。 +* 然后就是判断错误和误差的方法,一般在代码中称之为损失函数,也就是模型的输出结果是否正确。 +* 然后就是学习方法,比如结果不正确,如何去微调模型内部的参数,让下一次的输出结果更接近正确的结果。 + +### 验证 + +经过很多数据的反复训练后,我们发现好像基本都能识别正确了,但是我们还是担心我们用的图片种类是否不够多,这个模型是否真的能够识别所有的猫和狗或者其它猫和狗,这个时候就需要验证了,验证的方法就是把模型拿出来,给它一些新的数据,即在训练的时候从来没用到过的数据,让它去识别,看看它的识别结果是否正确,如果正确,那我们就认为这个模型泛化效果不错,可以放心的使用这个模型去识别毛毛狗狗了。 + +一般我们会在训练是一段时间后拿出`验证集`(也就是用来验证的数据集,对应训练的数据叫`训练集`)来测试一下模型的效果,如果效果不错就可以停止训练了,如果效果不好,则需要继续训练或者考虑是不是训练集有问题,或者模型结构、损失函数、学习方法等有问题了。 + + +### 测试 + +验证效果的好坏决定了我们合适停止训练,也就是说模型效果如何和`验证集`紧密相关,相当于**验证集也变相地参与了模型的训练过程**, 所以在结束训练后,我们用一个新的数据集`测试集`来测试一下模型的效果,这个数据集是在训练和验证的时候从来没用过的,这样就可以更加客观地评估模型的效果了。 + +这里共提到了`训练集`,`验证集`,`测试集`,需要注意他们三个数据集的区别!前两者在训练过程参与,后者在训练过程不参与,只是用来评估模型的效果,并且三者互相不重合,防止训练过程中模型只对一小部分数据有效,到了新的场景就无法识别(也就是所谓的`过拟合`)。 + +### 总结 + +这里简单阐述了机器学习的过程的通俗解释,你也可以到 [MaixHub](https://maixhub.com) 注册登录后体验自己体验一遍在线训练过程加深理解,无需懂代码,懂得这里描述的机器学习的过程就可以了,然后再进行进一步学习。 diff --git a/docs/soft/ai/en/config.json b/docs/soft/ai/en/config.json new file mode 100644 index 00000000..2aef6500 --- /dev/null +++ b/docs/soft/ai/en/config.json @@ -0,0 +1,4 @@ +{ + "import": "config_en", + "name": "AI Guide" +} \ No newline at end of file diff --git a/docs/soft/ai/en/deploy/k210.md b/docs/soft/ai/en/deploy/k210.md new file mode 100644 index 00000000..81d4d773 --- /dev/null +++ b/docs/soft/ai/en/deploy/k210.md @@ -0,0 +1,160 @@ +--- +title: Deploy model to Maix-I(M1) K210 series development boards +date: 2022-09-15 +--- + +>! This document is not translate yet, translation is welcome + +> 欢迎修改和补充 + +一般使用 `tensorflow` 训练出浮点模型, 再使用转换工具将其转换成 `K210` 所支持的 `Kmodel` 模型,然后将模型部署到 `K210` 开发板上。 + + +## K210 上的 KPU + +`K210` 上的 AI 硬件加速单元取名为`KPU`,`KPU` 实现了 卷积、批归一化、激活、池化 这 4 种基础操作的硬件加速, 但是它们不能分开单独使用,是一体的加速模块。 + +所以, 在 KPU 上面推理模型, 以下要求: + +### 内存限制 + + K210 有 6MB 通用 RAM 和 2MB KPU 专用 RAM。模型的输入和输出特征图存储在 2MB KPU RAM 中。权重和其他参数存储在 6MB 通用 RAM 中,在转换模型时,会打印模型使用的内存大小以及临时最大内存使用情况。 + +### 哪些算子可以被 KPU 完全加速? + +nncase 支持的算子: + * nncase v0.2.0 支持的算子: https://github.com/kendryte/nncase/blob/master/docs/tflite_ops.md + * nncase v0.1.0 支持的算子: https://github.com/kendryte/nncase/tree/v0.1.0-rc5 + +下面的约束需要全部满足。 + + * 特征图尺寸:输入特征图小于等于 320x240 (宽x高) 同时输出特征图大于等于 4x4 (宽x高),通道数在 1 到 1024。 + * Same 对称 paddings (TensorFlow 在 stride=2 同时尺寸为偶数时使用非对称 paddings)。 + * 普通 Conv2D 和 DepthwiseConv2D,卷积核为 1x1 或 3x3,stride 为 1 或 2。 + * 最大池化 MaxPool (2x2 或 4x4) 和 平均池化 AveragePool (2x2 或 4x4)。 + * 任意逐元素激活函数 (ReLU, ReLU6, LeakyRelu, Sigmoid...), KPU 不支持 PReLU。 + +### 哪些算子可以被 KPU 部分加速? + + * 非对称 paddings 或 valid paddings 卷积, nncase 会在其前后添加必要的 Pad 和 Crop(可理解为 边框 与 裁切)。 + * 普通 Conv2D 和 DepthwiseConv2D,卷积核为 1x1 或 3x3,但 stride 不是 1 或 2。 nncase 会把它分解为 KPUConv2D 和一个 StridedSlice (可能还需要 Pad)。 + * MatMul 算子, nncase 会把它替换为一个 Pad(到 4x4)+ KPUConv2D(1x1 卷积和) + Crop(到 1x1)。 + * TransposeConv2D 算子, nncase 会把它替换为一个 SpaceToBatch + KPUConv2D + BatchToSpace。 + +> 以上说明来自[这里](https://github.com/kendryte/nncase/blob/master/docs/FAQ_ZH.md) + + +## 训练出浮点模型 + +对于 K210, 建议使用 TensorFlow,因为它的转换工具对其支持最好。 + +tensorflow 举个例子, 两分类模型, 这里是随便叠的层结构 + +```python +import tensorflow as tf +from tensorflow import keras +from tensorflow.keras import layers + +input_shape = (240, 320, 3) + +model = tf.keras.models.Sequential() + +model.add(layers.ZeroPadding2D(input_shape = input_shape, padding=((1, 1), (1, 1)))) +model.add(layers.Conv2D(32, (3,3), padding = 'valid', strides = (2, 2)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); #model.add(MaxPool2D()); + + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (2, 2)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (2, 2)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(64, (3,3), padding = 'valid',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(64, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(64, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.Flatten()) +model.add(layers.Dropout(0.5)) +model.add(layers.Dense(2)) +model.add(layers.Activation('softmax')) + +model.summary() + +model.compile( + loss ='sparse_categorical_crossentropy', + optimizer = 'adam', + metrics =['accuracy']) + +mode.fit(...) +``` + +这里你可能注意到了, 在 `conv` 层中`stride != 1` 时, 都加了一个 `zeropadding` 层, 这是 K210 硬件支持的模式, 如果不这样做, 转换成 V3 模型时(使用 nncase v0.1.0 RC5) 则直接报错, 使用 V4 模型(nncase V0.2.0转换)可以通过,但是是使用软件运算的, 会消耗大量内存和时间, 会发现内存占用大了很多!!! 所以设计模型时也需要注意 + + +## 转换工具 + +使用 K210 芯片官方提供的 [nncase](https://github.com/kendryte/nncase) 工具来进行转换。 + +需要注意的是,工具版本更新迭代比较多, `K210`属于第一代芯片,算子支持有限,并且内存只有`6MiB(通用)+2MiB(AI专用)`内存,所以最新版本的工具可能并不是最好的选择,根据需求选择合适的版本。 + +由于代码更新, 在过程中**模型格式**产生了两个大版本, `V3` 和 `V4`, 其中 `V3` 模型是指用 [nncase v0.1.0 RC5](https://github.com/kendryte/nncase/releases/tag/v0.1.0-rc5) 转换出来的模型; `V4`模型指用 [nncase v0.2.0](https://github.com/kendryte/nncase/releases/tag/v0.2.0-beta4) 转换出来的模型,以及 V5 或更新版本等等。 + +两者有一定的不同,所以现在两者共存, `V3` 代码量更少,占用内存小,效率也高,但是支持的算子少; `V4` 支持的算子更多,但是都是软件实现的,没有硬件加速,内存使用更多,所以各有所长。 `MaixPy` 的固件也可以选择是否支持 `V4`, 如果你的模型 `V3` 能够满足算子支持,强烈建议用 `V3`,在遇到算子不支持而且一定要用那个算子时再用`V4`。 + + + +## 运行测试模型 + +使用 [MaixPy](/maixpy) 来运行模型,也可以用 [C SDK](https://github.com/sipeed/LicheeDan_K210_examples) 写。 + + +比如使用 `MaixPy`固件, 将模型放到 SD 卡, 然后使用代码加载 + + ```python + import KPU as kpu + import image + m = kpu.load("/sd/test.kmodel") + img = image.Image("/sd/test.jpg") + img = img.resize(224, 224) + img.pix_to_ai() + feature_map = kpu.forward(m, img) + p_list = feature_map[:] + ``` + +## 更多参考 + +* [K210 MaixPy 从入门到飞升--AI视觉篇--完全教程(以及一些小问题处理比如内存不足)](https://neucrack.com/p/325) +* [MaixPy AI 硬件加速基本知识](/soft/maixpy/zh/course/ai/basic/maixpy_hardware_ai_basic.html) + + +## 上传分享到 MaixHub + +可以上传分享你的模型到到 [MaixHub](https://maixhub.com/) 的模型库,可以让更多人发现并使用你的模型~ 一起做出更多有趣的项目吧!(K210 模型支持加密分享) + +另外你也可以使用 [MaixHub](https://maixhub.com/) 的模型库,下载别人分享的模型,或者使用在线训练出的模型,直接使用或者参考模型结构。 + + diff --git a/docs/soft/ai/en/deploy/tinymaix.md b/docs/soft/ai/en/deploy/tinymaix.md new file mode 100644 index 00000000..7c7a76df --- /dev/null +++ b/docs/soft/ai/en/deploy/tinymaix.md @@ -0,0 +1,14 @@ +--- +title: Use TinyMaix to deploy AI models to MCU +date: 2022-09-15 +--- + + +[TinyMaix](https://github.com/sipeed/TinyMaix) is a model inference runtime designed for limited memory and compute resources MCU, can even run `MNIST` on `2KB` RAM's MCU `Arduino ATmega328`, especially optimized for many architecture like RISC-V, ARM Cortex-M etc. + + +More details see [TinyMaix official repository](https://github.com/sipeed/TinyMaix) + + + + diff --git a/docs/soft/ai/en/deploy/v831.md b/docs/soft/ai/en/deploy/v831.md new file mode 100644 index 00000000..94091954 --- /dev/null +++ b/docs/soft/ai/en/deploy/v831.md @@ -0,0 +1,349 @@ +--- +title: Deploy models to V831 +date: 2022-09-15 +--- + +>! This document is not translate yet, translation is welcome + +## 制作浮点模型 + +对于 V831, 强烈推荐使用`Pytorch`训练模型,因为模型转换工具对其支持较好。 + +这里直接使用 pytorch hub 的预训练模型为例。 + +这里省略了模型定义和训练过程, 直接使用 pytorch hub 的 resnet18 预训练模型进行简单介绍: +https://pytorch.org/hub/pytorch_vision_resnet/ + + +注意 V831 支持的算子有限,具体请在 [MaixHub](https://maixhub.com/) 点击`工具箱->模型转换->v831`中的文档查看。 + + +## 在 PC 端测试模型推理 + + +根据上面链接的使用说明, 使用如下代码可以运行模型 + +其中, label 下载: https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt + +```python +import os +import torch +from torchsummary import summary + + +## model +model = torch.hub.load('pytorch/vision:v0.6.0', 'resnet18', pretrained=True) + +model.eval() + +input_shape = (3, 224, 224) +summary(model, input_shape, device="cpu") + +## test image +filename = "out/dog.jpg" +if not os.path.exists(filename): + if not os.path.exists("out"): + os.makedirs("out") + import urllib + url, filename = ("https://github.com/pytorch/hub/raw/master/images/dog.jpg", filename) + try: urllib.URLopener().retrieve(url, filename) + except: urllib.request.urlretrieve(url, filename) + +print("test image:", filename) + +## preparing input data +from PIL import Image +import numpy as np +from torchvision import transforms +input_image = Image.open(filename) +# input_image.show() +preprocess = transforms.Compose([ + transforms.Resize(max(input_shape[1:3])), + transforms.CenterCrop(input_shape[1:3]), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), +]) +input_tensor = preprocess(input_image) + +print("input data max value: {}, min value: {}".format(torch.max(input_tensor), torch.min(input_tensor))) + +input_batch = input_tensor.unsqueeze(0) # create a mini-batch as expected by the model + +## forward model +# move the input and model to GPU for speed if available +if torch.cuda.is_available(): + input_batch = input_batch.to('cuda') + model.to('cuda') + +with torch.no_grad(): + output = model(input_batch) + +## result +# Tensor of shape 1000, with confidence scores over Imagenet's 1000 classes +# print(output[0]) +# The output has unnormalized scores. To get probabilities, you can run a softmax on it. +max_1000 = torch.nn.functional.softmax(output[0], dim=0) +max_idx = int(torch.argmax(max_1000)) +with open("imagenet_classes.txt") as f: + labels = f.read().split("\n") +print("result: idx:{}, name:{}".format(max_idx, labels[max_idx])) +``` + +运行后 结果: +``` +Using cache found in /home/neucrack/.cache/torch/hub/pytorch_vision_v0.6.0 +---------------------------------------------------------------- + Layer (type) Output Shape Param # +================================================================ + Conv2d-1 [-1, 64, 112, 112] 9,408 + BatchNorm2d-2 [-1, 64, 112, 112] 128 + ReLU-3 [-1, 64, 112, 112] 0 + MaxPool2d-4 [-1, 64, 56, 56] 0 + Conv2d-5 [-1, 64, 56, 56] 36,864 + BatchNorm2d-6 [-1, 64, 56, 56] 128 + ReLU-7 [-1, 64, 56, 56] 0 + Conv2d-8 [-1, 64, 56, 56] 36,864 + BatchNorm2d-9 [-1, 64, 56, 56] 128 + ReLU-10 [-1, 64, 56, 56] 0 + BasicBlock-11 [-1, 64, 56, 56] 0 + Conv2d-12 [-1, 64, 56, 56] 36,864 + BatchNorm2d-13 [-1, 64, 56, 56] 128 + ReLU-14 [-1, 64, 56, 56] 0 + Conv2d-15 [-1, 64, 56, 56] 36,864 + BatchNorm2d-16 [-1, 64, 56, 56] 128 + ReLU-17 [-1, 64, 56, 56] 0 + BasicBlock-18 [-1, 64, 56, 56] 0 + Conv2d-19 [-1, 128, 28, 28] 73,728 + BatchNorm2d-20 [-1, 128, 28, 28] 256 + ReLU-21 [-1, 128, 28, 28] 0 + Conv2d-22 [-1, 128, 28, 28] 147,456 + BatchNorm2d-23 [-1, 128, 28, 28] 256 + Conv2d-24 [-1, 128, 28, 28] 8,192 + BatchNorm2d-25 [-1, 128, 28, 28] 256 + ReLU-26 [-1, 128, 28, 28] 0 + BasicBlock-27 [-1, 128, 28, 28] 0 + Conv2d-28 [-1, 128, 28, 28] 147,456 + BatchNorm2d-29 [-1, 128, 28, 28] 256 + ReLU-30 [-1, 128, 28, 28] 0 + Conv2d-31 [-1, 128, 28, 28] 147,456 + BatchNorm2d-32 [-1, 128, 28, 28] 256 + ReLU-33 [-1, 128, 28, 28] 0 + BasicBlock-34 [-1, 128, 28, 28] 0 + Conv2d-35 [-1, 256, 14, 14] 294,912 + BatchNorm2d-36 [-1, 256, 14, 14] 512 + ReLU-37 [-1, 256, 14, 14] 0 + Conv2d-38 [-1, 256, 14, 14] 589,824 + BatchNorm2d-39 [-1, 256, 14, 14] 512 + Conv2d-40 [-1, 256, 14, 14] 32,768 + BatchNorm2d-41 [-1, 256, 14, 14] 512 + ReLU-42 [-1, 256, 14, 14] 0 + BasicBlock-43 [-1, 256, 14, 14] 0 + Conv2d-44 [-1, 256, 14, 14] 589,824 + BatchNorm2d-45 [-1, 256, 14, 14] 512 + ReLU-46 [-1, 256, 14, 14] 0 + Conv2d-47 [-1, 256, 14, 14] 589,824 + BatchNorm2d-48 [-1, 256, 14, 14] 512 + ReLU-49 [-1, 256, 14, 14] 0 + BasicBlock-50 [-1, 256, 14, 14] 0 + Conv2d-51 [-1, 512, 7, 7] 1,179,648 + BatchNorm2d-52 [-1, 512, 7, 7] 1,024 + ReLU-53 [-1, 512, 7, 7] 0 + Conv2d-54 [-1, 512, 7, 7] 2,359,296 + BatchNorm2d-55 [-1, 512, 7, 7] 1,024 + Conv2d-56 [-1, 512, 7, 7] 131,072 + BatchNorm2d-57 [-1, 512, 7, 7] 1,024 + ReLU-58 [-1, 512, 7, 7] 0 + BasicBlock-59 [-1, 512, 7, 7] 0 + Conv2d-60 [-1, 512, 7, 7] 2,359,296 + BatchNorm2d-61 [-1, 512, 7, 7] 1,024 + ReLU-62 [-1, 512, 7, 7] 0 + Conv2d-63 [-1, 512, 7, 7] 2,359,296 + BatchNorm2d-64 [-1, 512, 7, 7] 1,024 + ReLU-65 [-1, 512, 7, 7] 0 + BasicBlock-66 [-1, 512, 7, 7] 0 +AdaptiveAvgPool2d-67 [-1, 512, 1, 1] 0 + Linear-68 [-1, 1000] 513,000 +================================================================ +Total params: 11,689,512 +Trainable params: 11,689,512 +Non-trainable params: 0 +---------------------------------------------------------------- +Input size (MB): 0.57 +Forward/backward pass size (MB): 62.79 +Params size (MB): 44.59 +Estimated Total Size (MB): 107.96 +---------------------------------------------------------------- +out/dog.jpg +tensor(2.6400) tensor(-2.1008) +idx:258, name:Samoyed, Samoyede +``` + +可以看到模型有 `11,689,512`的参数, 即 `11MiB`左右, 这个大小也就几乎是实际在 831 上运行的模型的大小了 + +## 将模型转换为 V831 能使用的模型文件 + +转换过程如下: + +### 使用 Pytorch 将模型导出为 `onnx`模型, 得到`onnx`文件 + +```python +def torch_to_onnx(net, input_shape, out_name="out/model.onnx", input_names=["input0"], output_names=["output0"], device="cpu"): + batch_size = 1 + if len(input_shape) == 3: + x = torch.randn(batch_size, input_shape[0], input_shape[1], input_shape[2], dtype=torch.float32, requires_grad=True).to(device) + elif len(input_shape) == 1: + x = torch.randn(batch_size, input_shape[0], dtype=torch.float32, requires_grad=False).to(device) + else: + raise Exception("not support input shape") + print("input shape:", x.shape) + # torch.onnx._export(net, x, "out/conv0.onnx", export_params=True) + torch.onnx.export(net, x, out_name, export_params=True, input_names = input_names, output_names=output_names) + + +onnx_out="out/resnet_1000.onnx" +ncnn_out_param = "out/resnet_1000.param" +ncnn_out_bin = "out/resnet_1000.bin" +input_img = filename + +torch_to_onnx(model, input_shape, onnx_out, device="cuda:0") + +``` + +如果你不是使用 pytorch 转换的, 而是使用了现成的 ncnn 模型, 不知道输出层的名字, 可以在 https://netron.app/ 打开模型查看输出层的名字 + +## 使用 `onnx2ncnn` 工具将`onnx`转成`ncnn`模型,得到一个`.param`文件和一个`.bin`文件 + +>! 这一步可以跳过。 + +> 按照[ncnn项目](https://github.com/Tencent/ncnn)的编译说明编译,在`build/tools/onnx`目录下得到`onnx2ncnn`可执行文件 + +```python +def onnx_to_ncnn(input_shape, onnx="out/model.onnx", ncnn_param="out/conv0.param", ncnn_bin = "out/conv0.bin"): + import os + # onnx2ncnn tool compiled from ncnn/tools/onnx, and in the buld dir + cmd = f"onnx2ncnn {onnx} {ncnn_param} {ncnn_bin}" + os.system(cmd) + with open(ncnn_param) as f: + content = f.read().split("\n") + if len(input_shape) == 1: + content[2] += " 0={}".format(input_shape[0]) + else: + content[2] += " 0={} 1={} 2={}".format(input_shape[2], input_shape[1], input_shape[0]) + content = "\n".join(content) + with open(ncnn_param, "w") as f: + f.write(content) + +onnx_to_ncnn(input_shape, onnx=onnx_out, ncnn_param=ncnn_out_param, ncnn_bin=ncnn_out_bin) +``` + +## 使用全志提供的`awnn`工具将`ncnn`模型进行量化到`int8`模型 + +在 [MaixHub](https://maixhub.com/) 点击`工具箱->模型转换->v831`进入模型转换页面, 将 ncnn 模型转换为 awnn 支持的 int8 模型 (网页在线转换很方便人为操作,另一个方面因为全志要求不开放 awnn 所以暂时只能这样做) + +在转换页面有更多的转换说明,可以获得更多详细的转换说明 + +这里有几组参数: +* 均值 和 归一化因子: 在 pytorch 中一般是 `(输入值 - mean ) / std`, `awnn`对输入的处理是 `(输入值 - mean ) * norm`, 总之,让你训练的时候的输入到第一层网络的值范围和给`awnn`量化工具经过` (输入值 - mean ) * norm` 计算后的值范围一致既可。 比如 这里打印了实际数据的输入范围是`[-2.1008, 2.6400]`, 是代码中`preprocess` 对象处理后得到的,即`x = (x - mean) / std` ==> `(0-0.485)/0.229 = -2.1179`, 到`awnn`就是`x = (x - mean_2*255) * (1 / std * 255)` 即 `mean2 = mean * 255`, `norm = 1/(std * 255)`, 更多可以看[这里](https://github.com/Tencent/ncnn/wiki/FAQ-ncnn-produce-wrong-result#pre-process)。 +所以我们这里可以设置 均值为 `0.485 * 255 = 123.675`, 设置 归一化因子为`1/ (0.229 * 255) = 0.017125`, 另外两个通道同理,但是目前 awnn 只能支持三个通道值一样。。。所以填`123.675, 123.675, 123.675`,`0.017125, 0.017125, 0.017125` 即可,因为这里用了`pytorch hub`的预训练的参数,就这样吧, 如果自己训练,可以好好设置一下 + +* 图片输入层尺寸(问不是图片怎么办?貌似 awnn 暂时只考虑到了图片。。) +* RGB 格式: 如果训练输入的图片是 RGB 就选 RGB +* 量化图片, 选择一些和输入尺寸相同的图片,可以从测试集中拿一些,不一定要图片非常多,但尽量覆盖全场景(摊手 + +自己写的其它模型转换如果失败,多半是啥算子不支持,需要在 使用说明里面看支持的 算子,比如之前的版本view、 flatten、reshape 都不支持所以写模型要相当小心, 现在的版本会支持 flatten reshape 等 CPU 算子 + +如果不出意外, 终于得到了量化好的 awnn 能使用的模型, `*.param` 和 `*.bin` + + + +## 使用模型,在v831上推理 + +可以使用 python 或者 C 写代码,以下两种方式 + +### MaixPy3 + +python 请看[MaixPy3](https://wiki.sipeed.com/soft/maixpy3/zh/) + +不想看文档的话,就是在系统开机使用的基础上, 更新 MaixPy3 就可以了: +``` +pip install --upgrade maixpy3 +``` + +然后在终端使用 python 运行脚本(可能需要根据你的文件名参数什么的改一下代码): + +https://github.com/sipeed/MaixPy3/blob/main/ext_modules/_maix_nn/example/load_forward_camera.py + +label 在这里: https://github.com/sipeed/MaixPy3/blob/main/ext_modules/_maix_nn/example/classes_label.py + +```python +from maix import nn +from PIL import Image, ImageDraw +from maix import camera, display + +test_jpg = "/root/test_input/input.jpg" +model = { + "param": "/root/models/resnet_awnn.param", + "bin": "/root/models/resnet_awnn.bin" +} + +camera.config(size=(224, 224)) + +options = { + "model_type": "awnn", + "inputs": { + "input0": (224, 224, 3) + }, + "outputs": { + "output0": (1, 1, 1000) + }, + "first_layer_conv_no_pad": False, + "mean": [127.5, 127.5, 127.5], + "norm": [0.00784313725490196, 0.00784313725490196, 0.00784313725490196], +} +print("-- load model:", model) +m = nn.load(model, opt=options) +print("-- load ok") + +print("-- read image") +img = Image.open(test_jpg) +print("-- read image ok") +print("-- forward model with image as input") +out = m.forward(img, quantize=True) +print("-- read image ok") +print("-- out:", out.shape) +out = nn.F.softmax(out) +print(out.max(), out.argmax()) + +from classes_label import labels +while 1: + img = camera.capture() + if not img: + time.sleep(0.02) + continue + out = m.forward(img, quantize=True) + out = nn.F.softmax(out) + msg = "{:.2f}: {}".format(out.max(), labels[out.argmax()]) + print(msg) + draw = ImageDraw.Draw(img) + draw.text((0, 0), msg, fill=(255, 0, 0)) + display.show(img) +``` + + +### C语言 SDK, libmaix + +访问这里,按照 https://github.com/sipeed/libmaix 的说明克隆仓库,并编译 https://github.com/sipeed/libmaix/tree/master/examples/nn_resnet + +上传编译成功后`dist`目录下的所有内容到 `v831`, 然后执行`./start_app.sh`即可 + + +## 参考 + +* [在V831上(awnn)跑 pytorch resnet18 模型](https://neucrack.com/p/358) + + + + + + + diff --git a/docs/soft/ai/en/maixhub/README.md b/docs/soft/ai/en/maixhub/README.md new file mode 100644 index 00000000..0c21662a --- /dev/null +++ b/docs/soft/ai/en/maixhub/README.md @@ -0,0 +1,11 @@ +--- +title: Introduction to MaixHub +--- + +[MaixHub](https://maixhub.com) is a platform released by Sipeed that integrates functions such as AI model service and community communication. It mainly provides the following functions: +* Model library, directly download the model to the device to run and use, and share your own model to the model library. +* Online training, you can easily train models without programming foundation and AI training experience, which is convenient for getting started with AI learning and accelerating AI application development. +* Project sharing, share your own projects and works, exchange learning or find inspiration in the community. + +For more functions and more content, please visit [MaixHub](https://maixhub.com). + diff --git a/docs/soft/ai/en/maixhub/train_best.md b/docs/soft/ai/en/maixhub/train_best.md new file mode 100644 index 00000000..f56660ea --- /dev/null +++ b/docs/soft/ai/en/maixhub/train_best.md @@ -0,0 +1,49 @@ +--- +title: Optimize MaixHub online train +--- + +>! this document need translation, help is welcome + +When using MaixHub to train a model, the recognition effect may not be very good or the actual running speed of the model may be slow. Here are some common tuning methods. + +Modifications and additions are welcome + + +## 识别效果优化 + +* 尽量多采集实际使用场景的图片,覆盖更多使用场景有利于提高最终识别率。 + +* 图片数量尽量不要太小,虽然平台限制最小数量为 20 张图才可以训练, 但要达到比较好的效果,显然一个分类 200 张都不算多,不要一直在 30 张训练图片上纠结为什么训练效果不好。。。 + +* 修改迭代次数,在发现`val_acc`仍然有上升趋势的情况下可以考虑适当增大迭代次数,但是迭代次数越大,训练时间越长,所以要根据实际情况权衡。 + +* 修改学习率和批数量大小,学习率不宜太大,否则会导致梯度爆炸出现`loss 为 0`或者`loss 为 inf`这样的错误,批数量大小不宜太小,否则会导致训练速度过慢,一般来说,学习率在 0.0001~0.001 之间,批数量大小在 16~64 之间都是比较合适的。另外需要注意批数量越大, 学习率就可以设置得稍微大一点。 + +* 每个标签的数据量都尽量多,而不是一个标签只有 20张,另一个500张图, 可以把训练参数处的“数据均衡“开关打开 + +* 默认分辨率但是 224x224, 是因为预训练模型是在 224x224 下训练的,当然也有其它分辨率的,比如 128x128,具体发现不支持的分辨率预训练模型,在训练日志中会打印警告信息。 + +* 为了让验证集的精确度的可信度更高(也就是在实际开发板上跑的精确度更接近训练时在验证集上的精确度),验证集的数据和实际应用的场景数据一致。比如训练集是在网上找了很多图片,那这些图片可能和实际开发板的摄像头拍出来的图有差距,可以往验证集上传一些实际设备拍的图来验证训练的模型效果。 + 这样我们就能在训练的时候根据验证集精确度(val_acc)来判断模型训练效果如何了,如果发现验证集精确度很低,那么就可以考虑增加训练集复杂度和数量,或者训练集用设备拍摄来训练。 + +* 对于检测训练项目,如果检测训练的物体很准,但是容易误识别到其它物体,可以在数据里面拍点其它的物体当背景;或者拍摄一些没有目标的图片,不添加任何标注也可以,然后在训练的时候勾选**允许负样本**来使能没有标注的图片。 + +* 检测任务可以同时检测到多个目标,如果你觉得识别类别不准,也有另外一种方式,先只检测模型检测到物体(一个类别),然后裁切出图片中的目标物体上传到分类任务,用分类任务来分辨类别。不过这样就要跑两个模型,需要写代码裁切图片(在板子跑就好了),以及需要考虑内存是否足够 + + +## 在开发板上运行速度慢 + +* 减小输入分辨率,比如在分类任务中可以使用`96x96`的小图来训练。 +* 裁切一部分图像进行识别,识别时不对整张图片进行识别,可以裁切出部分图像进行识别。 +* 选择更小的网络,比如分类选择`mobilenetV1 0.25`是`219KiB`, 而`mobilenetV1 0.75`则是`1.85MiB`,网络参数量减少了很多,不过相应地,模型识别精度也会降低。 + + +## 更快地标注数据 + +* 可以导入本地已经标注好了的数据到 MaixHub。 +* MaixHub 支持视频辅助自动标注,只需拍摄视频上传的时候使用辅助标注功能即可,对于画面中只有单个物体的场景标注十分有用。 +* 可以使用已经训练好的模型来辅助标注,虽然现在 MaixHub 不支持用训练好的模型来标注,但是你可以下载训练好模型到板子运行,写代码将识别到的物体坐标保存为`VOC`标注格式,就得到了新的标注数据,虽然可能会因为模型训练效果不够好标得不够准确,但是经过简单的手工筛选调整后,标注数据就可以新的训练了,如此反复,就会得到很多数据啦。 +* MaixHub 未来可能会上线更好用的辅助标注工具哦~ 有建议欢迎通过 MaixHub 的反馈功能告诉我们哦~ + + + diff --git a/docs/soft/ai/en/sidebar.yaml b/docs/soft/ai/en/sidebar.yaml new file mode 100644 index 00000000..d3eb4a0c --- /dev/null +++ b/docs/soft/ai/en/sidebar.yaml @@ -0,0 +1,19 @@ +items: +- label: Doc brief + file: README.md +- label: AI basic +- label: What is AI ? + file: basic/what_is_ai.md +- label: Deploy to edge devices +- label: Deploy to Maix-I K210 + file: deploy/k210.md +- label: Deploy to Maix-II V831 + file: deploy/v831.md +- label: TinyMaix Deployment + file: deploy/tinymaix.md +- label: MaixHub +- label: MaixHub brief + file: maixhub/README.md +- label: MaixHub online train optimization + file: maixhub/train_best.md + diff --git a/docs/soft/ai/zh/README.md b/docs/soft/ai/zh/README.md new file mode 100644 index 00000000..db6f82f9 --- /dev/null +++ b/docs/soft/ai/zh/README.md @@ -0,0 +1,33 @@ +--- +title: AI 指南 +date: 2022-09-15 +--- + + +## 关于本文档 + +`Sipeed` 推出了一系列 `AI` 开发板,包括: +* `Maix-I(M1)` 系列的 `MaixBit`,`Maixduino` `M1s-Dock`等带 `AI` 硬件加速的单片机开发板。 +* `Maix-II(M2)` 系列的 `M2-Dock` `MaixSense`等带 `AI`硬件加速的高性价比`SOC`开发板。 +* `Maix-III(M3)`系列的`M3 AX-Pi`等高性能`SOC`开发板。 + +为了普及 `AI` 在端侧设备(/边缘设备)上的应用,`Sipeed` 开发了易上手的 `MaixPy`和`MaixPy3` SDK, 以及上线了无需编程基础和 AI 训练经验也能轻松训练模型的 [MaixHub](https://maixhub.com/) 平台。 + +在此基础上,本文档致力于为开发者提供一份 `AI` 开发指南,目的是为了让新手快速上手 `AI` 应用, 或者已经掌握 `AI`相关知识的开发者将成果快速应用到设备或者产品上。 +内容包括不限于: +* AI 的基础知识 +* 模型训练指南 +* AI 教程推荐 +* AI 有趣项目推荐 +* 常用工具使用 +* 边缘设备部署指南 + + +## 参与贡献 + +文档内容会持续更新,欢迎大家参与内容编写,参与方式: + +* 可以直接点击文档右上角的 `编辑本页` 按钮跳转到`GitHub`上进行编辑并提交 `PR`(具体方法可以看[贡献文档](/share_docs/zh/)或者搜索引擎搜`GitHub 如何 提交 PR`)。 +* 也可以直接发送更改建议或者投稿到`support@sipeed.com`, 标题请以`[WiKi 投稿]`开头, 正文需要注明作者,以及需要修改的页面位置和内容,方便我们快速将您的内容更新到文档。 + + diff --git a/docs/soft/ai/zh/basic/what_is_ai.md b/docs/soft/ai/zh/basic/what_is_ai.md new file mode 100644 index 00000000..3fe6e275 --- /dev/null +++ b/docs/soft/ai/zh/basic/what_is_ai.md @@ -0,0 +1,57 @@ +--- +title: 什么是人工智能(AI)和机器学习 +date: 2022-09-15 +--- + +AI(Artificial Intelligence) 想必大家都听之极多了,有人觉得 AI 要毁灭人类了;有人认为 AI 只是擅长某些特殊场景不必惊慌(比如 alpha Go 打败了人类围棋大师);但大多数人接触到的 AI 可能更愿意称之为“人工智障”,比如手机里面的助手;对于很多工程师来说, AI 可能更多的是指机器学习,比如图像识别,语音识别,自然语言处理等等。 + +作为一个开发者, 首先我们需要了解大家经常听到的 `AI`,`机器学习`,`神经网络`等概念以及区别: +* AI: 人工智能,是指让机器具有人类智能的能力,比如人类可以看到一张照片,然后判断出这是一只猫,这是一个人,这是一只狗等等,而机器也可以做到这一点,这就是人工智能。 +* 机器学习: 一般是指让机器通过大量的数据,然后通过算法,让机器自己学习,比如通过大量的猫的图片,让机器自己学习,然后判断出一张图片是不是猫。 +* 神经网络: 一般是指在机器学习中用到的一种数据结构,因为其类似人大脑的神经网络,各个数据节点互相连接互相通信和影响,故称之为神经网络。 +* 模型: 指用来承载和表示机器学习过程中的相关参数的数据结构,一般可以保存为一个文件,可以将神经网络的结构和参数都存储在这个数据结构里面,方便用数学或者编程语言将其解析,比如取名叫`.model`格式的文件 + +所以可以理解为三者是包含关系: 神经网络 ∈ 机器学习 ∈ 人工智能, 模型文件则一般为机器学习中的产物, 另外你可能还听说过“深度神经网络”,其实也是属于神经网络,只不过是网络层数有深度不同一说。 + +而本文也大多阐述了如何利用各种神经网络模型和机器学习的方法来实现 AI 应用。 + +## 机器学习过程简介 + +这里首先对机器学习的过程做一个通俗的介绍,不涉及数学公式,只是简单的介绍一下机器学习的过程。 + +### 训练 + +以让机器区分猫咪和狗为例: +和教(训练)人类婴儿一样,可以把模型比作婴儿,为了让这个模型能认识猫和狗,我们需要一遍一遍地让它看各种猫猫狗狗,并让它去识别,错了我们就告诉它错了,对了我们就告诉它对了,这样一遍一遍地让它看,让它学习,最终它就能区分猫和狗了。 + +从这段话我们分析出训练的时候几个关键点: +* 模型: 一个工具或者黑盒,给它一个输入,它能给我们一个输出结果。 +* 输入: 这里是图像,猫或者狗的图像。 +* 输出: 猫或者狗 +* 判断错误的方法:也就是它的输出和真实的结果是否一样,这里是靠教学者判断正误的,也就是判断错误的方法是教学者。 +* 学习方法:就是当我们告诉模型输出结果是错的时候,它如何去改进。 + +得到这几个关键点后,就可以很好地理解这个机器学习的过程了: +* 定义模型的输入输出, 输入是图像,输出是猫或者狗。 +* 为了让这个模型能够有学习的本领,也就是和人一样有足够的脑容量, 我们定义一个属于模型的“脑子”,也就是一个看起来和人脑突触结构类似的神经网络结构: +![神经网络](../../assets/dnn.jpg) +可以看到输入和中间每个节点间都有线连起来,每条线都是一个计算公式,具体是什么样的公式以及具体如何设计一个这样的结构这里先不细究,先有个概念就行。 +* 然后就是判断错误和误差的方法,一般在代码中称之为损失函数,也就是模型的输出结果是否正确。 +* 然后就是学习方法,比如结果不正确,如何去微调模型内部的参数,让下一次的输出结果更接近正确的结果。 + +### 验证 + +经过很多数据的反复训练后,我们发现好像基本都能识别正确了,但是我们还是担心我们用的图片种类是否不够多,这个模型是否真的能够识别所有的猫和狗或者其它猫和狗,这个时候就需要验证了,验证的方法就是把模型拿出来,给它一些新的数据,即在训练的时候从来没用到过的数据,让它去识别,看看它的识别结果是否正确,如果正确,那我们就认为这个模型泛化效果不错,可以放心的使用这个模型去识别毛毛狗狗了。 + +一般我们会在训练是一段时间后拿出`验证集`(也就是用来验证的数据集,对应训练的数据叫`训练集`)来测试一下模型的效果,如果效果不错就可以停止训练了,如果效果不好,则需要继续训练或者考虑是不是训练集有问题,或者模型结构、损失函数、学习方法等有问题了。 + + +### 测试 + +验证效果的好坏决定了我们合适停止训练,也就是说模型效果如何和`验证集`紧密相关,相当于**验证集也变相地参与了模型的训练过程**, 所以在结束训练后,我们用一个新的数据集`测试集`来测试一下模型的效果,这个数据集是在训练和验证的时候从来没用过的,这样就可以更加客观地评估模型的效果了。 + +这里共提到了`训练集`,`验证集`,`测试集`,需要注意他们三个数据集的区别!前两者在训练过程参与,后者在训练过程不参与,只是用来评估模型的效果,并且三者互相不重合,防止训练过程中模型只对一小部分数据有效,到了新的场景就无法识别(也就是所谓的`过拟合`)。 + +### 总结 + +这里简单阐述了机器学习的过程的通俗解释,你也可以到 [MaixHub](https://maixhub.com) 注册登录后体验自己体验一遍在线训练过程加深理解,无需懂代码,懂得这里描述的机器学习的过程就可以了,然后再进行进一步学习。 diff --git a/docs/soft/ai/zh/config.json b/docs/soft/ai/zh/config.json new file mode 100644 index 00000000..6c9decb0 --- /dev/null +++ b/docs/soft/ai/zh/config.json @@ -0,0 +1,4 @@ +{ + "import": "config_zh", + "name": "AI 指南" +} \ No newline at end of file diff --git a/docs/soft/ai/zh/deploy/k210.md b/docs/soft/ai/zh/deploy/k210.md new file mode 100644 index 00000000..a533f025 --- /dev/null +++ b/docs/soft/ai/zh/deploy/k210.md @@ -0,0 +1,158 @@ +--- +title: 部署模型到 Maix-I(M1) K210 系列开发板 +date: 2022-09-15 +--- + +> 欢迎修改和补充 + +一般使用 `tensorflow` 训练出浮点模型, 再使用转换工具将其转换成 `K210` 所支持的 `Kmodel` 模型,然后将模型部署到 `K210` 开发板上。 + + +## K210 上的 KPU + +`K210` 上的 AI 硬件加速单元取名为`KPU`,`KPU` 实现了 卷积、批归一化、激活、池化 这 4 种基础操作的硬件加速, 但是它们不能分开单独使用,是一体的加速模块。 + +所以, 在 KPU 上面推理模型, 以下要求: + +### 内存限制 + + K210 有 6MB 通用 RAM 和 2MB KPU 专用 RAM。模型的输入和输出特征图存储在 2MB KPU RAM 中。权重和其他参数存储在 6MB 通用 RAM 中,在转换模型时,会打印模型使用的内存大小以及临时最大内存使用情况。 + +### 哪些算子可以被 KPU 完全加速? + +nncase 支持的算子: + * nncase v0.2.0 支持的算子: https://github.com/kendryte/nncase/blob/master/docs/tflite_ops.md + * nncase v0.1.0 支持的算子: https://github.com/kendryte/nncase/tree/v0.1.0-rc5 + +下面的约束需要全部满足。 + + * 特征图尺寸:输入特征图小于等于 320x240 (宽x高) 同时输出特征图大于等于 4x4 (宽x高),通道数在 1 到 1024。 + * Same 对称 paddings (TensorFlow 在 stride=2 同时尺寸为偶数时使用非对称 paddings)。 + * 普通 Conv2D 和 DepthwiseConv2D,卷积核为 1x1 或 3x3,stride 为 1 或 2。 + * 最大池化 MaxPool (2x2 或 4x4) 和 平均池化 AveragePool (2x2 或 4x4)。 + * 任意逐元素激活函数 (ReLU, ReLU6, LeakyRelu, Sigmoid...), KPU 不支持 PReLU。 + +### 哪些算子可以被 KPU 部分加速? + + * 非对称 paddings 或 valid paddings 卷积, nncase 会在其前后添加必要的 Pad 和 Crop(可理解为 边框 与 裁切)。 + * 普通 Conv2D 和 DepthwiseConv2D,卷积核为 1x1 或 3x3,但 stride 不是 1 或 2。 nncase 会把它分解为 KPUConv2D 和一个 StridedSlice (可能还需要 Pad)。 + * MatMul 算子, nncase 会把它替换为一个 Pad(到 4x4)+ KPUConv2D(1x1 卷积和) + Crop(到 1x1)。 + * TransposeConv2D 算子, nncase 会把它替换为一个 SpaceToBatch + KPUConv2D + BatchToSpace。 + +> 以上说明来自[这里](https://github.com/kendryte/nncase/blob/master/docs/FAQ_ZH.md) + + +## 训练出浮点模型 + +对于 K210, 建议使用 TensorFlow,因为它的转换工具对其支持最好。 + +tensorflow 举个例子, 两分类模型, 这里是随便叠的层结构 + +```python +import tensorflow as tf +from tensorflow import keras +from tensorflow.keras import layers + +input_shape = (240, 320, 3) + +model = tf.keras.models.Sequential() + +model.add(layers.ZeroPadding2D(input_shape = input_shape, padding=((1, 1), (1, 1)))) +model.add(layers.Conv2D(32, (3,3), padding = 'valid', strides = (2, 2)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); #model.add(MaxPool2D()); + + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (2, 2)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(32, (3,3), padding = 'valid',strides = (2, 2)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(32, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.ZeroPadding2D(padding=((1, 1), (1, 1)))); +model.add(layers.Conv2D(64, (3,3), padding = 'valid',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + +model.add(layers.Conv2D(64, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); +model.add(layers.Conv2D(64, (3,3), padding = 'same',strides = (1, 1)));model.add(layers.BatchNormalization());model.add(layers.Activation('relu')); + + +model.add(layers.Flatten()) +model.add(layers.Dropout(0.5)) +model.add(layers.Dense(2)) +model.add(layers.Activation('softmax')) + +model.summary() + +model.compile( + loss ='sparse_categorical_crossentropy', + optimizer = 'adam', + metrics =['accuracy']) + +mode.fit(...) +``` + +这里你可能注意到了, 在 `conv` 层中`stride != 1` 时, 都加了一个 `zeropadding` 层, 这是 K210 硬件支持的模式, 如果不这样做, 转换成 V3 模型时(使用 nncase v0.1.0 RC5) 则直接报错, 使用 V4 模型(nncase V0.2.0转换)可以通过,但是是使用软件运算的, 会消耗大量内存和时间, 会发现内存占用大了很多!!! 所以设计模型时也需要注意 + + +## 转换工具 + +使用 K210 芯片官方提供的 [nncase](https://github.com/kendryte/nncase) 工具来进行转换。 + +需要注意的是,工具版本更新迭代比较多, `K210`属于第一代芯片,算子支持有限,并且内存只有`6MiB(通用)+2MiB(AI专用)`内存,所以最新版本的工具可能并不是最好的选择,根据需求选择合适的版本。 + +由于代码更新, 在过程中**模型格式**产生了两个大版本, `V3` 和 `V4`, 其中 `V3` 模型是指用 [nncase v0.1.0 RC5](https://github.com/kendryte/nncase/releases/tag/v0.1.0-rc5) 转换出来的模型; `V4`模型指用 [nncase v0.2.0](https://github.com/kendryte/nncase/releases/tag/v0.2.0-beta4) 转换出来的模型,以及 V5 或更新版本等等。 + +两者有一定的不同,所以现在两者共存, `V3` 代码量更少,占用内存小,效率也高,但是支持的算子少; `V4` 支持的算子更多,但是都是软件实现的,没有硬件加速,内存使用更多,所以各有所长。 `MaixPy` 的固件也可以选择是否支持 `V4`, 如果你的模型 `V3` 能够满足算子支持,强烈建议用 `V3`,在遇到算子不支持而且一定要用那个算子时再用`V4`。 + + + +## 运行测试模型 + +使用 [MaixPy](/maixpy) 来运行模型,也可以用 [C SDK](https://github.com/sipeed/LicheeDan_K210_examples) 写。 + + +比如使用 `MaixPy`固件, 将模型放到 SD 卡, 然后使用代码加载 + + ```python + import KPU as kpu + import image + m = kpu.load("/sd/test.kmodel") + img = image.Image("/sd/test.jpg") + img = img.resize(224, 224) + img.pix_to_ai() + feature_map = kpu.forward(m, img) + p_list = feature_map[:] + ``` + +## 更多参考 + +* [K210 MaixPy 从入门到飞升--AI视觉篇--完全教程(以及一些小问题处理比如内存不足)](https://neucrack.com/p/325) +* [MaixPy AI 硬件加速基本知识](/soft/maixpy/zh/course/ai/basic/maixpy_hardware_ai_basic.html) + + +## 上传分享到 MaixHub + +可以上传分享你的模型到到 [MaixHub](https://maixhub.com/) 的模型库,可以让更多人发现并使用你的模型~ 一起做出更多有趣的项目吧!(K210 模型支持加密分享) + +另外你也可以使用 [MaixHub](https://maixhub.com/) 的模型库,下载别人分享的模型,或者使用在线训练出的模型,直接使用或者参考模型结构。 + + diff --git a/docs/soft/ai/zh/deploy/tinymaix.md b/docs/soft/ai/zh/deploy/tinymaix.md new file mode 100644 index 00000000..6356e29d --- /dev/null +++ b/docs/soft/ai/zh/deploy/tinymaix.md @@ -0,0 +1,13 @@ +--- +title: 使用 TinyMaix 将模型部署到单片机 +date: 2022-09-15 +--- + +[TinyMaix](https://github.com/sipeed/TinyMaix) 是针对小算力小内存的芯片设计的轻量级推理框架,甚至能在`2KB`内存的`Arduino ATmega328`单片机上运行`MNIST`,对各种架构的单片机都提供了支持和优化,包括 RISC-V、ARM Cortex-M 等。 + + +详细使用方法请看[TinyMaix 官方仓库](https://github.com/sipeed/TinyMaix) + + + + diff --git a/docs/soft/ai/zh/deploy/v831.md b/docs/soft/ai/zh/deploy/v831.md new file mode 100644 index 00000000..e6fa6cf9 --- /dev/null +++ b/docs/soft/ai/zh/deploy/v831.md @@ -0,0 +1,349 @@ +--- +title: 将模型部署到 V831 +date: 2022-09-15 +--- + + + +## 制作浮点模型 + +对于 V831, 强烈推荐使用`Pytorch`训练模型,因为模型转换工具对其支持较好。 + +这里直接使用 pytorch hub 的预训练模型为例。 + +这里省略了模型定义和训练过程, 直接使用 pytorch hub 的 resnet18 预训练模型进行简单介绍: +https://pytorch.org/hub/pytorch_vision_resnet/ + + +注意 V831 支持的算子有限,具体请在 [MaixHub](https://maixhub.com/) 点击`工具箱->模型转换->v831`中的文档查看。 + + +## 在 PC 端测试模型推理 + + +根据上面链接的使用说明, 使用如下代码可以运行模型 + +其中, label 下载: https://raw.githubusercontent.com/pytorch/hub/master/imagenet_classes.txt + +```python +import os +import torch +from torchsummary import summary + + +## model +model = torch.hub.load('pytorch/vision:v0.6.0', 'resnet18', pretrained=True) + +model.eval() + +input_shape = (3, 224, 224) +summary(model, input_shape, device="cpu") + +## test image +filename = "out/dog.jpg" +if not os.path.exists(filename): + if not os.path.exists("out"): + os.makedirs("out") + import urllib + url, filename = ("https://github.com/pytorch/hub/raw/master/images/dog.jpg", filename) + try: urllib.URLopener().retrieve(url, filename) + except: urllib.request.urlretrieve(url, filename) + +print("test image:", filename) + +## preparing input data +from PIL import Image +import numpy as np +from torchvision import transforms +input_image = Image.open(filename) +# input_image.show() +preprocess = transforms.Compose([ + transforms.Resize(max(input_shape[1:3])), + transforms.CenterCrop(input_shape[1:3]), + transforms.ToTensor(), + transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]), +]) +input_tensor = preprocess(input_image) + +print("input data max value: {}, min value: {}".format(torch.max(input_tensor), torch.min(input_tensor))) + +input_batch = input_tensor.unsqueeze(0) # create a mini-batch as expected by the model + +## forward model +# move the input and model to GPU for speed if available +if torch.cuda.is_available(): + input_batch = input_batch.to('cuda') + model.to('cuda') + +with torch.no_grad(): + output = model(input_batch) + +## result +# Tensor of shape 1000, with confidence scores over Imagenet's 1000 classes +# print(output[0]) +# The output has unnormalized scores. To get probabilities, you can run a softmax on it. +max_1000 = torch.nn.functional.softmax(output[0], dim=0) +max_idx = int(torch.argmax(max_1000)) +with open("imagenet_classes.txt") as f: + labels = f.read().split("\n") +print("result: idx:{}, name:{}".format(max_idx, labels[max_idx])) +``` + +运行后 结果: +``` +Using cache found in /home/neucrack/.cache/torch/hub/pytorch_vision_v0.6.0 +---------------------------------------------------------------- + Layer (type) Output Shape Param # +================================================================ + Conv2d-1 [-1, 64, 112, 112] 9,408 + BatchNorm2d-2 [-1, 64, 112, 112] 128 + ReLU-3 [-1, 64, 112, 112] 0 + MaxPool2d-4 [-1, 64, 56, 56] 0 + Conv2d-5 [-1, 64, 56, 56] 36,864 + BatchNorm2d-6 [-1, 64, 56, 56] 128 + ReLU-7 [-1, 64, 56, 56] 0 + Conv2d-8 [-1, 64, 56, 56] 36,864 + BatchNorm2d-9 [-1, 64, 56, 56] 128 + ReLU-10 [-1, 64, 56, 56] 0 + BasicBlock-11 [-1, 64, 56, 56] 0 + Conv2d-12 [-1, 64, 56, 56] 36,864 + BatchNorm2d-13 [-1, 64, 56, 56] 128 + ReLU-14 [-1, 64, 56, 56] 0 + Conv2d-15 [-1, 64, 56, 56] 36,864 + BatchNorm2d-16 [-1, 64, 56, 56] 128 + ReLU-17 [-1, 64, 56, 56] 0 + BasicBlock-18 [-1, 64, 56, 56] 0 + Conv2d-19 [-1, 128, 28, 28] 73,728 + BatchNorm2d-20 [-1, 128, 28, 28] 256 + ReLU-21 [-1, 128, 28, 28] 0 + Conv2d-22 [-1, 128, 28, 28] 147,456 + BatchNorm2d-23 [-1, 128, 28, 28] 256 + Conv2d-24 [-1, 128, 28, 28] 8,192 + BatchNorm2d-25 [-1, 128, 28, 28] 256 + ReLU-26 [-1, 128, 28, 28] 0 + BasicBlock-27 [-1, 128, 28, 28] 0 + Conv2d-28 [-1, 128, 28, 28] 147,456 + BatchNorm2d-29 [-1, 128, 28, 28] 256 + ReLU-30 [-1, 128, 28, 28] 0 + Conv2d-31 [-1, 128, 28, 28] 147,456 + BatchNorm2d-32 [-1, 128, 28, 28] 256 + ReLU-33 [-1, 128, 28, 28] 0 + BasicBlock-34 [-1, 128, 28, 28] 0 + Conv2d-35 [-1, 256, 14, 14] 294,912 + BatchNorm2d-36 [-1, 256, 14, 14] 512 + ReLU-37 [-1, 256, 14, 14] 0 + Conv2d-38 [-1, 256, 14, 14] 589,824 + BatchNorm2d-39 [-1, 256, 14, 14] 512 + Conv2d-40 [-1, 256, 14, 14] 32,768 + BatchNorm2d-41 [-1, 256, 14, 14] 512 + ReLU-42 [-1, 256, 14, 14] 0 + BasicBlock-43 [-1, 256, 14, 14] 0 + Conv2d-44 [-1, 256, 14, 14] 589,824 + BatchNorm2d-45 [-1, 256, 14, 14] 512 + ReLU-46 [-1, 256, 14, 14] 0 + Conv2d-47 [-1, 256, 14, 14] 589,824 + BatchNorm2d-48 [-1, 256, 14, 14] 512 + ReLU-49 [-1, 256, 14, 14] 0 + BasicBlock-50 [-1, 256, 14, 14] 0 + Conv2d-51 [-1, 512, 7, 7] 1,179,648 + BatchNorm2d-52 [-1, 512, 7, 7] 1,024 + ReLU-53 [-1, 512, 7, 7] 0 + Conv2d-54 [-1, 512, 7, 7] 2,359,296 + BatchNorm2d-55 [-1, 512, 7, 7] 1,024 + Conv2d-56 [-1, 512, 7, 7] 131,072 + BatchNorm2d-57 [-1, 512, 7, 7] 1,024 + ReLU-58 [-1, 512, 7, 7] 0 + BasicBlock-59 [-1, 512, 7, 7] 0 + Conv2d-60 [-1, 512, 7, 7] 2,359,296 + BatchNorm2d-61 [-1, 512, 7, 7] 1,024 + ReLU-62 [-1, 512, 7, 7] 0 + Conv2d-63 [-1, 512, 7, 7] 2,359,296 + BatchNorm2d-64 [-1, 512, 7, 7] 1,024 + ReLU-65 [-1, 512, 7, 7] 0 + BasicBlock-66 [-1, 512, 7, 7] 0 +AdaptiveAvgPool2d-67 [-1, 512, 1, 1] 0 + Linear-68 [-1, 1000] 513,000 +================================================================ +Total params: 11,689,512 +Trainable params: 11,689,512 +Non-trainable params: 0 +---------------------------------------------------------------- +Input size (MB): 0.57 +Forward/backward pass size (MB): 62.79 +Params size (MB): 44.59 +Estimated Total Size (MB): 107.96 +---------------------------------------------------------------- +out/dog.jpg +tensor(2.6400) tensor(-2.1008) +idx:258, name:Samoyed, Samoyede +``` + +可以看到模型有 `11,689,512`的参数, 即 `11MiB`左右, 这个大小也就几乎是实际在 831 上运行的模型的大小了 + +## 将模型转换为 V831 能使用的模型文件 + +转换过程如下: + +### 使用 Pytorch 将模型导出为 `onnx`模型, 得到`onnx`文件 + +```python +def torch_to_onnx(net, input_shape, out_name="out/model.onnx", input_names=["input0"], output_names=["output0"], device="cpu"): + batch_size = 1 + if len(input_shape) == 3: + x = torch.randn(batch_size, input_shape[0], input_shape[1], input_shape[2], dtype=torch.float32, requires_grad=True).to(device) + elif len(input_shape) == 1: + x = torch.randn(batch_size, input_shape[0], dtype=torch.float32, requires_grad=False).to(device) + else: + raise Exception("not support input shape") + print("input shape:", x.shape) + # torch.onnx._export(net, x, "out/conv0.onnx", export_params=True) + torch.onnx.export(net, x, out_name, export_params=True, input_names = input_names, output_names=output_names) + + +onnx_out="out/resnet_1000.onnx" +ncnn_out_param = "out/resnet_1000.param" +ncnn_out_bin = "out/resnet_1000.bin" +input_img = filename + +torch_to_onnx(model, input_shape, onnx_out, device="cuda:0") + +``` + +如果你不是使用 pytorch 转换的, 而是使用了现成的 ncnn 模型, 不知道输出层的名字, 可以在 https://netron.app/ 打开模型查看输出层的名字 + +## 使用 `onnx2ncnn` 工具将`onnx`转成`ncnn`模型,得到一个`.param`文件和一个`.bin`文件 + +>! 这一步可以跳过。 + +> 按照[ncnn项目](https://github.com/Tencent/ncnn)的编译说明编译,在`build/tools/onnx`目录下得到`onnx2ncnn`可执行文件 + +```python +def onnx_to_ncnn(input_shape, onnx="out/model.onnx", ncnn_param="out/conv0.param", ncnn_bin = "out/conv0.bin"): + import os + # onnx2ncnn tool compiled from ncnn/tools/onnx, and in the buld dir + cmd = f"onnx2ncnn {onnx} {ncnn_param} {ncnn_bin}" + os.system(cmd) + with open(ncnn_param) as f: + content = f.read().split("\n") + if len(input_shape) == 1: + content[2] += " 0={}".format(input_shape[0]) + else: + content[2] += " 0={} 1={} 2={}".format(input_shape[2], input_shape[1], input_shape[0]) + content = "\n".join(content) + with open(ncnn_param, "w") as f: + f.write(content) + +onnx_to_ncnn(input_shape, onnx=onnx_out, ncnn_param=ncnn_out_param, ncnn_bin=ncnn_out_bin) +``` + +## 使用全志提供的`awnn`工具将`ncnn`模型进行量化到`int8`模型 + +在 [MaixHub](https://maixhub.com/) 点击`工具箱->模型转换->v831`进入模型转换页面, 将 ncnn 模型转换为 awnn 支持的 int8 模型 (网页在线转换很方便人为操作,另一个方面因为全志要求不开放 awnn 所以暂时只能这样做) + +在转换页面有更多的转换说明,可以获得更多详细的转换说明 + +这里有几组参数: +* 均值 和 归一化因子: 在 pytorch 中一般是 `(输入值 - mean ) / std`, `awnn`对输入的处理是 `(输入值 - mean ) * norm`, 总之,让你训练的时候的输入到第一层网络的值范围和给`awnn`量化工具经过` (输入值 - mean ) * norm` 计算后的值范围一致既可。 比如 这里打印了实际数据的输入范围是`[-2.1008, 2.6400]`, 是代码中`preprocess` 对象处理后得到的,即`x = (x - mean) / std` ==> `(0-0.485)/0.229 = -2.1179`, 到`awnn`就是`x = (x - mean_2*255) * (1 / std * 255)` 即 `mean2 = mean * 255`, `norm = 1/(std * 255)`, 更多可以看[这里](https://github.com/Tencent/ncnn/wiki/FAQ-ncnn-produce-wrong-result#pre-process)。 +所以我们这里可以设置 均值为 `0.485 * 255 = 123.675`, 设置 归一化因子为`1/ (0.229 * 255) = 0.017125`, 另外两个通道同理,但是目前 awnn 只能支持三个通道值一样。。。所以填`123.675, 123.675, 123.675`,`0.017125, 0.017125, 0.017125` 即可,因为这里用了`pytorch hub`的预训练的参数,就这样吧, 如果自己训练,可以好好设置一下 + +* 图片输入层尺寸(问不是图片怎么办?貌似 awnn 暂时只考虑到了图片。。) +* RGB 格式: 如果训练输入的图片是 RGB 就选 RGB +* 量化图片, 选择一些和输入尺寸相同的图片,可以从测试集中拿一些,不一定要图片非常多,但尽量覆盖全场景(摊手 + +自己写的其它模型转换如果失败,多半是啥算子不支持,需要在 使用说明里面看支持的 算子,比如之前的版本view、 flatten、reshape 都不支持所以写模型要相当小心, 现在的版本会支持 flatten reshape 等 CPU 算子 + +如果不出意外, 终于得到了量化好的 awnn 能使用的模型, `*.param` 和 `*.bin` + + + +## 使用模型,在v831上推理 + +可以使用 python 或者 C 写代码,以下两种方式 + +### MaixPy3 + +python 请看[MaixPy3](https://wiki.sipeed.com/soft/maixpy3/zh/) + +不想看文档的话,就是在系统开机使用的基础上, 更新 MaixPy3 就可以了: +``` +pip install --upgrade maixpy3 +``` + +然后在终端使用 python 运行脚本(可能需要根据你的文件名参数什么的改一下代码): + +https://github.com/sipeed/MaixPy3/blob/main/ext_modules/_maix_nn/example/load_forward_camera.py + +label 在这里: https://github.com/sipeed/MaixPy3/blob/main/ext_modules/_maix_nn/example/classes_label.py + +```python +from maix import nn +from PIL import Image, ImageDraw +from maix import camera, display + +test_jpg = "/root/test_input/input.jpg" +model = { + "param": "/root/models/resnet_awnn.param", + "bin": "/root/models/resnet_awnn.bin" +} + +camera.config(size=(224, 224)) + +options = { + "model_type": "awnn", + "inputs": { + "input0": (224, 224, 3) + }, + "outputs": { + "output0": (1, 1, 1000) + }, + "first_layer_conv_no_pad": False, + "mean": [127.5, 127.5, 127.5], + "norm": [0.00784313725490196, 0.00784313725490196, 0.00784313725490196], +} +print("-- load model:", model) +m = nn.load(model, opt=options) +print("-- load ok") + +print("-- read image") +img = Image.open(test_jpg) +print("-- read image ok") +print("-- forward model with image as input") +out = m.forward(img, quantize=True) +print("-- read image ok") +print("-- out:", out.shape) +out = nn.F.softmax(out) +print(out.max(), out.argmax()) + +from classes_label import labels +while 1: + img = camera.capture() + if not img: + time.sleep(0.02) + continue + out = m.forward(img, quantize=True) + out = nn.F.softmax(out) + msg = "{:.2f}: {}".format(out.max(), labels[out.argmax()]) + print(msg) + draw = ImageDraw.Draw(img) + draw.text((0, 0), msg, fill=(255, 0, 0)) + display.show(img) +``` + + +### C语言 SDK, libmaix + +访问这里,按照 https://github.com/sipeed/libmaix 的说明克隆仓库,并编译 https://github.com/sipeed/libmaix/tree/master/examples/nn_resnet + +上传编译成功后`dist`目录下的所有内容到 `v831`, 然后执行`./start_app.sh`即可 + + +## 参考 + +* [在V831上(awnn)跑 pytorch resnet18 模型](https://neucrack.com/p/358) + + + + + + + diff --git a/docs/soft/ai/zh/maixhub/README.md b/docs/soft/ai/zh/maixhub/README.md new file mode 100644 index 00000000..db258aff --- /dev/null +++ b/docs/soft/ai/zh/maixhub/README.md @@ -0,0 +1,13 @@ +--- +title: MaixHub 简介 +--- + +[MaixHub](https://maixhub.com) 是 Sipeed 发布的集 AI 模型服务和社区沟通等功能的平台,主要提供了以下功能: +* 模型库,直接下载模型到设备即可运行使用,以及分享自己的模型到模型库。 +* 在线训练,无需编程基础和 AI 训练经验也能轻松训练模型,方便入门 AI 学习和加速 AI 应用开发。 +* 项目分享,分享自己制作的项目和作品,在社区中交流学习或者寻找灵感。 + +更多功能更多内容,请访问[MaixHub](https://maixhub.com)。 + + + diff --git a/docs/soft/ai/zh/maixhub/train_best.md b/docs/soft/ai/zh/maixhub/train_best.md new file mode 100644 index 00000000..ce65e5f0 --- /dev/null +++ b/docs/soft/ai/zh/maixhub/train_best.md @@ -0,0 +1,48 @@ +--- +title: MaixHub 训练调优方法 +--- + + +在使用 MaixHub 训练模型时,可能会遇到识别效果不太好或者模型实际运行速度慢等情况,此处提供一些常见调优方法。 + +欢迎修改和补充 + + +## 识别效果优化 + +* 尽量多采集实际使用场景的图片,覆盖更多使用场景有利于提高最终识别率。 + +* 图片数量尽量不要太小,虽然平台限制最小数量为 20 张图才可以训练, 但要达到比较好的效果,显然一个分类 200 张都不算多,不要一直在 30 张训练图片上纠结为什么训练效果不好。。。 + +* 修改迭代次数,在发现`val_acc`仍然有上升趋势的情况下可以考虑适当增大迭代次数,但是迭代次数越大,训练时间越长,所以要根据实际情况权衡。 + +* 修改学习率和批数量大小,学习率不宜太大,否则会导致梯度爆炸出现`loss 为 0`或者`loss 为 inf`这样的错误,批数量大小不宜太小,否则会导致训练速度过慢,一般来说,学习率在 0.0001~0.001 之间,批数量大小在 16~64 之间都是比较合适的。另外需要注意批数量越大, 学习率就可以设置得稍微大一点。 + +* 每个标签的数据量都尽量多,而不是一个标签只有 20张,另一个500张图, 可以把训练参数处的“数据均衡“开关打开 + +* 默认分辨率但是 224x224, 是因为预训练模型是在 224x224 下训练的,当然也有其它分辨率的,比如 128x128,具体发现不支持的分辨率预训练模型,在训练日志中会打印警告信息。 + +* 为了让验证集的精确度的可信度更高(也就是在实际开发板上跑的精确度更接近训练时在验证集上的精确度),验证集的数据和实际应用的场景数据一致。比如训练集是在网上找了很多图片,那这些图片可能和实际开发板的摄像头拍出来的图有差距,可以往验证集上传一些实际设备拍的图来验证训练的模型效果。 + 这样我们就能在训练的时候根据验证集精确度(val_acc)来判断模型训练效果如何了,如果发现验证集精确度很低,那么就可以考虑增加训练集复杂度和数量,或者训练集用设备拍摄来训练。 + +* 对于检测训练项目,如果检测训练的物体很准,但是容易误识别到其它物体,可以在数据里面拍点其它的物体当背景;或者拍摄一些没有目标的图片,不添加任何标注也可以,然后在训练的时候勾选**允许负样本**来使能没有标注的图片。 + +* 检测任务可以同时检测到多个目标,如果你觉得识别类别不准,也有另外一种方式,先只检测模型检测到物体(一个类别),然后裁切出图片中的目标物体上传到分类任务,用分类任务来分辨类别。不过这样就要跑两个模型,需要写代码裁切图片(在板子跑就好了),以及需要考虑内存是否足够 + + +## 在开发板上运行速度慢 + +* 减小输入分辨率,比如在分类任务中可以使用`96x96`的小图来训练。 +* 裁切一部分图像进行识别,识别时不对整张图片进行识别,可以裁切出部分图像进行识别。 +* 选择更小的网络,比如分类选择`mobilenetV1 0.25`是`219KiB`, 而`mobilenetV1 0.75`则是`1.85MiB`,网络参数量减少了很多,不过相应地,模型识别精度也会降低。 + + +## 更快地标注数据 + +* 可以导入本地已经标注好了的数据到 MaixHub。 +* MaixHub 支持视频辅助自动标注,只需拍摄视频上传的时候使用辅助标注功能即可,对于画面中只有单个物体的场景标注十分有用。 +* 可以使用已经训练好的模型来辅助标注,虽然现在 MaixHub 不支持用训练好的模型来标注,但是你可以下载训练好模型到板子运行,写代码将识别到的物体坐标保存为`VOC`标注格式,就得到了新的标注数据,虽然可能会因为模型训练效果不够好标得不够准确,但是经过简单的手工筛选调整后,标注数据就可以新的训练了,如此反复,就会得到很多数据啦。 +* MaixHub 未来可能会上线更好用的辅助标注工具哦~ 有建议欢迎通过 MaixHub 的反馈功能告诉我们哦~ + + + diff --git a/docs/soft/ai/zh/sidebar.yaml b/docs/soft/ai/zh/sidebar.yaml new file mode 100644 index 00000000..80568867 --- /dev/null +++ b/docs/soft/ai/zh/sidebar.yaml @@ -0,0 +1,19 @@ +items: +- label: 文档简介 + file: README.md +- label: AI 基础 +- label: 什么是 AI ? + file: basic/what_is_ai.md +- label: 部署到边缘设备 +- label: Maix-I 之 K210 模型部署 + file: deploy/k210.md +- label: Maix-II 之 V831 模型部署 + file: deploy/v831.md +- label: TinyMaix 模型部署 + file: deploy/tinymaix.md +- label: MaixHub +- label: MaixHub 简介 + file: maixhub/README.md +- label: MaixHub 在线训练调优 + file: maixhub/train_best.md + diff --git a/site_config.json b/site_config.json index b12ea4c7..f4420e99 100755 --- a/site_config.json +++ b/site_config.json @@ -16,7 +16,8 @@ "/soft/maixpy/zh/": "docs/soft/maixpy/zh", "/soft/maixpy3/zh/": "docs/soft/maixpy3/zh", "/soft/maixduino/zh/":"docs/soft/maixduino/zh", - "/share_docs/zh/": "share_docs/zh" + "/share_docs/zh/": "share_docs/zh", + "/ai/zh/": "docs/soft/ai/zh" }, "pages": { "/": "pages/index/zh" @@ -31,8 +32,8 @@ "/soft/maixpy/assets/": "docs/soft/maixpy/assets", "/soft/maixpy3/assets/": "docs/soft/maixpy3/assets", "/soft/maixduino/assets/":"docs/soft/maixduino/assets", - "/share_docs/assets":"share_docs/assets" - + "/share_docs/assets/":"share_docs/assets", + "/ai/assets/":"docs/soft/ai/assets" }, "blog": { "/news/": "news" @@ -70,6 +71,11 @@ "url": "/share_docs/en/", "src" : "share_docs/en" } + ], + "/ai/zh/": [{ + "url": "/ai/en/", + "src" : "docs/soft/ai/en" + } ] }, "pages": {