- 2024-12-27
-
发表了主题帖:
《深度学习的数学——使用Python语言》3. 统计学基础
这本书最大的亮点并不是简单地讲解数学知识,而是将数学知识与深度学习紧密结合。按照 概率论 -> 线性代数 -> 微积分 的顺序,每一部分内容都融入了深度学习的实际应用,让我们更清楚这些数学知识在深度学习中的作用和具体用法。
在构建深度学习模型之前,我们必须充分理解并构建数据集。优质的数据集是训练出优质模型的前提。基础统计学知识可以帮助我们更好地理解和分析数据,从而提升数据集的质量,为模型打下扎实的基础。
# 数据类型
数据类型可以分为四种,每种类型的特点各不相同,但它们之间又存在一定联系。在处理实际问题时,了解数据类型可以避免不必要的运算错误。具体分类如下:
1. 定类数据:又称类别数据,指的是取值不同且没有排序关系的数据。
2. 定序数据:不同取值之间存在排序关系,但他们之间的差距没有数学含义。
3. 定距数据:数据之间有实际的数学含义。
4. 定比数据:数据之间的差距不仅有实际的数学含义,而且还存在真实的零点。
可以看到不同数据类型之间具有不同的特点,但是又存在一定的联系,书中已经给我们绘图说明了它们之间的联系与区别。
## 定距数据与定比数据的区别
针对定距数据和定比数据,它们之间的区别在于是否存在真实的零点。书中给出的例子如下
定距数据:华氏温度、摄氏温度。(数据差距有实际数学含义)
定比数据:身高、年龄、开尔文温度。(具有绝对零点)
关于绝对零点这个概念书中并没有给出详细的解释,我根据书中给出的例子的理解是:绝对零点意味着该点表示完全没有所测量的属性或量。换句话说,在定比尺度中,0 表示“无”或“不存在”,而不是简单的基准点或起始点。
## 定类数据的使用
在深度学习特别是分类问题中,我们常用到的数据类型时定类数据,比如说颜色分类:红色、黄色、绿色。为了把这些数据作为特征传输给深度网络,我们需要先对其进行预处理。由于定类数据没有排序关系,因此不能简单地将其赋值为123。而是需要将其转换为具有意义的定距数据,即独热编码。
在Keras等组件中,类别数据标签默认会被预处理为独热码,这样在计算损失函数时,网络的输出和编码后的类别标签都是向量,且计算起来会更方便。
之前在训练图片分类网络时就有这个疑惑,好奇为什么一定要将类别转换为独热编码的形式,在这本书的数据类型部分找到了答案。
# 描述性统计量
此外,为了理解数据以及了解数据的形态和分布,需要使用统计量来描述数据。
-
回复了主题帖:
【测评入围名单】RDK X3机器人开发套件
个人信息无误,确认可以完成测评分享计划
- 2024-12-24
-
回复了主题帖:
【读书】《计算机视觉之PyTorch数字图像处理》7.模型部署
freebsder 发表于 2024-12-23 10:04
这个数据集有多少分类?有下载地址吗?看看丰不丰富
预训练模型用的是imagenet数据集,这个是它的官网imagenet
- 2024-12-23
-
回复了主题帖:
《深度学习的数学——使用Python语言》2. 概率分布
Jacktang 发表于 2024-12-23 07:47
伽玛分布比正态分布幅度高这么多啊
嗯嗯,它的形状跟参数的设置也有关
- 2024-12-22
-
发表了主题帖:
《深度学习的数学——使用Python语言》2. 概率分布
本帖最后由 waterman 于 2024-12-22 10:23 编辑
概率分布可以理解为生成所需数值的一个函数。数值的生成过程是随机的,但是任意数值出现的可能都遵循一般形式。
深度学习中主要用到对概率分布进行采样的地方,就是在训练之前对网络参数进行初始化。目前主流的神经网络都使用均匀分布或正态分布对参数中的权重项(或偏置项)进行初始化。
下面学习如何使用numpy对不同的概率分布进行采样。
# 离散型概率分布
## 二项分布
二项分布表示如果重复多次实验,每次都有某事件以相同概率发生,那么在所有实验中该事件有望发生的次数。在numpy中,调用random模块中的binomial来实现。
```python
>>> import numpy as np
>>> t = np.random.binomial(5, 0.3, size = 1000)
>>> s = np.bincount(t)
>>> s
array([155, 369, 313, 124, 38, 1], dtype=int64)
>>> s / s.sum()
array([0.155, 0.369, 0.313, 0.124, 0.038, 0.001])
```
上述代码实现了实验次数5次,事件概率0.3的1000个二项分布样本。并得出了所有可能取值的概率。
## 伯努利分布
伯努利分布是一种特殊的二项分布,对应二项分布中实验次数固定为1的情况。
当p=0.5时,实现代码如下:
```python
>>> t = np.random.binomial(1, 0.5, size = 1000)
>>> s = np.bincount(t)
>>> s
array([471, 529], dtype=int64)
>>> s / s.sum()
array([0.471, 0.529])
```
## 泊松分布
如果不知道一个时间在某次实验中发生的概率,但知道在某个区间上,该事件发生的平均次数为lamda,就能够计算在相同的区间上,时间发生k次的概率,这就是泊松分布。
实现代码如下:
```python
>>> import numpy as np
>>> lamda = 5
>>> t = np.random.poisson(5, size = 1000)
>>> s = np.bincount(t)
>>> s
array([ 6, 34, 97, 150, 166, 179, 147, 106, 67, 29, 13, 6],
dtype=int64)
>>> t.max()
11
>>> s / s.sum()
array([0.006, 0.034, 0.097, 0.15 , 0.166, 0.179, 0.147, 0.106, 0.067,
0.029, 0.013, 0.006])
```
在二项分布中,事件的发生次数不能大于n,但是在泊松分布中,事件的发生次数可以大于lamda。其中事件发生的最大次数为11,且最常出现的事件发生次数在均值lamda附近。
# 连续型概率分布
连续型概率分布与离散型概率分布一样,也有特定的形态,但是不同于离散分布对任意可取的整数都有一定的概率值,连续分布对于取值范围内每点的概率值都为0。讨论给定区间的取值概率才有意义。
下面分别给出均匀分布、正态分布、伽马分布和贝塔分布的概率密度函数采样代码
```python
import numpy as np
import matplotlib.pylab as plt
N = 10000000
B = 100
x = np.arange(B)/B
# uniform
t = np.random.random(N)
u = np.histogram(t, bins=B)[0]
u = u / u.sum()
# normal
t = np.random.normal(0, 1, size=N)
n = np.histogram(t, bins=B)[0]
n = n / n.sum()
# gamma
t = np.random.gamma(5.0, size=N)
g = np.histogram(t, bins=B)[0]
g = g / g.sum()
# beta
t = np.random.beta(5,2, size=N)
b = np.histogram(t, bins=B)[0]
b = b / b.sum()
```
绘制出来如下图所示:
```python
plt.plot(x,u,color='k',linestyle='solid')
plt.plot(x,n,color='k',linestyle='dotted')
plt.plot(x,g,color='k',linestyle='dashed')
plt.plot(x,b,color='k',linestyle='dashdot')
plt.ylabel("Probability")
plt.tight_layout(pad=0, w_pad=0, h_pad=0)
plt.show()
plt.close()
```
- 2024-12-20
-
回复了主题帖:
【读书】《计算机视觉之PyTorch数字图像处理》7.模型部署
freebsder 发表于 2024-12-20 17:06
最后输出的96是这个鸟的分类码?
那必须是,imagenet_classes里面的toucan,输出序号96
- 2024-12-19
-
回复了主题帖:
【读书】《计算机视觉之PyTorch数字图像处理》6.目标检测
freebsder 发表于 2024-12-18 20:03
这是自己训练的还是直接用的训练好的参数?
用预训练模型微调的
- 2024-12-17
-
发表了主题帖:
【读书】《计算机视觉之PyTorch数字图像处理》7.模型部署
【读书】《计算机视觉之PyTorch数字图像处理》7.模型部署
在深度学习任务中,无论是图像分类、分割还是目标检测,完成模型的训练和测试并不是终点。训练只是模型开发的第一步,真正的挑战在于如何让模型在实际场景中高效、稳定地运行。这就需要我们进行模型部署。
模型部署是指将训练好的深度学习模型集成到具体的项目或系统中,作为系统的一部分,提供实际的推理服务。与模型训练不同,部署的核心目标是让模型能够高效执行推理任务,提升系统的整体智能性和稳定性。
在模型部署中,ONNX(Open Neural Network Exchange)作为一款开源的标准化模型格式,逐渐成为了行业的热门选择。它能够将训练好的模型转换为高效的推理格式,支持跨平台、跨硬件部署,让模型真正从“实验室”走向“生产环境”。
ONNX简介
ONNX 是由微软和Facebook联合推出的开源模型交换格式,旨在实现不同深度学习框架之间的互操作性。它为我们提供了一个标准化的表示方式,可以轻松将模型从一个框架(如 PyTorch、TensorFlow)导出,然后在另一个框架或推理引擎(如 ONNX Runtime)中高效运行。
ONNX的核心优势
框架无关:支持主流的深度学习框架,如 PyTorch、TensorFlow 等。
高效推理:通过计算图优化,大幅提升推理速度和效率。
跨平台支持:无论是服务器、移动端还是嵌入式设备,ONNX 都能轻松部署。
ONNX的工作流程
使用ONNX进行模型部署大致分为以下几步:
模型导出
从训练框架(如 PyTorch、TensorFlow)将模型导出为 ONNX 格式。
示例:在PyTorch中,使用 torch.onnx.export() 函数轻松实现模型转换。
模型优化
使用 ONNX 工具链(如 onnxruntime-tools)对模型进行优化,包括计算图简化、量化等操作,以提升推理性能。
模型部署
利用 ONNX Runtime 等推理引擎加载和执行 ONNX 模型,实现高效推理。
接下来,让我们详细看看 ONNX 的导出与部署过程。
模型导出
在 PyTorch 中,ONNX 提供了一个简单易用的工具包,利用 torch.onnx.export() 函数即可完成模型的导出。这个函数的原理与 PyTorch 的 TorchScript 类似,都是通过追踪输入数据,导出模型的运行流程并记录到 ONNX 文件中。部分参数如下:
示例代码:导出ResNet18模型
Python
from torchvision import models
import torch as tc
#创建一个模型
res18=models.resnet18(weights=models.ResNet18_Weights.DEFAULT)
res18.eval()
#生成一个正确尺寸的伪造图像
img=tc.ones((1,3,224,224))
tc.onnx.export(res18, img, "res18.onnx", input_names=['img'], output_names=['out'])
以上代码会将训练好的 ResNet18 模型导出为一个名为 res18.onnx 的 ONNX 文件。导出后的模型可以被其他框架或推理引擎加载,进行高效部署。
导出的onnx模型如下:
模型部署
ONNX Runtime 是由微软推出的高性能推理引擎,可以加载 ONNX 格式的模型并执行推理任务。ONNX Runtime 支持 CPU 和 GPU 两种推理加速方式,安装方法如下:
Bash
pip install onnxruntime #安装支持CPU
pip install onnxruntime-gpu #安装支持GPU
在安装完onnxruntime库后,就可以使用InferenceSession()方法加载ONNX模型,得到用于模型推断的会话,调用会话的run()方法进行输入数据的计算,并返回模型的输出。
对于输入数据,不能直接使用numpy数组,需要使用OrtValue.ortvalue_from_numpy()函数进行格式转化后才能作为模型的输入。
此外,在调用run()方法时,指定输入和输出数据的名称,要与模型导出时设置的名称一致。
Python
from PIL import Image
import numpy as np
# 打开图像文件
image = Image.open('1.jpeg')
image = image.resize([224,224])
# 将图像转换为NumPy数组
image_array = np.array(image).astype(np.float32)
image_array = image_array/255
# 2. 改变通道顺序,从 (224, 224, 3) 转换为 (3, 224, 224)
image_array = np.transpose(image_array, (2, 0, 1))
# 3. 添加批次维度,从 (3, 224, 224) 转换为 (1, 3, 224, 224)
image_array = np.expand_dims(image_array, axis=0)
# 输出数组的形状和数据类型
print(image_array.shape) # 输出图像的尺寸(高度, 宽度, 通道数)
print(image_array.dtype) # 输出数组的数据类型,通常是uint8
import onnxruntime
import numpy as np
#导入onnx格式的res18模型
session=onnxruntime.InferenceSession('./res18.onnx',providers=['CPUExecutionProvider'])# providers=['CPUExecutionProvider','CUDAExecutionProvider']
#创建并转换数据
# img=np.ones((1,3,224,224)).astype(np.float32)
img = image_array
ortimg = onnxruntime.OrtValue.ortvalue_from_numpy(img)
#使模型执行输入数据,得到结果
results = session.run(["out"], {"img": ortimg})
#得到模型运行结果
results[0][0].argmax()
上述代码首先读取了名为1.jpeg的图片并处理成输入的大小,之后执行onnxruntime推理,得到输出结果。
输入图片如下:
输出结果如下:
- 2024-12-16
-
发表了主题帖:
【读书】《计算机视觉之PyTorch数字图像处理》6.目标检测
# 目标检测相关概念简介
在图像处理中,目标检测与分类和分割构成了图像处理的三大任务。与图像分类和分割不同,目标检测不仅要进行分类,而且还需要估计目标在图像中的位置。
1. 感受野(Receptive Field)
感受野指卷积神经网络中,特征图中某个元素在原始图像上对应的区域。特征图中每个元素的值是由它的感受野内的像素计算得到的。感受野的大小决定了该网络能够检测的目标尺寸范围。通过增加网络层数或联合特征图中的多个相邻元素,可以扩大感受野,以适应更大目标的检测。
2. 锚框(Anchor Box)
锚框是目标检测中用于表示目标位置的矩形框。锚框有两种来源:
- 真实锚框:由人工标注,作为训练模型的目标。
- 预测锚框:模型生成的矩形框,用于目标定位。
锚框的表示方法通常包括:
- 左上角和右下角坐标
- 左上角坐标与宽高
- 中心坐标与宽高
坐标表示可以采用绝对值(像素)或相对值(相对图像尺寸)。
3. 交并比(IoU,Intersection over Union)
交并比是两个矩形框重合程度的衡量指标,定义为相交面积与相并面积的比值:
- 当两个框完全重合时,IoU = 1。
- 当两个框完全分离时,IoU = 0。
- IoU 值介于 0 和 1 之间时,表示框有部分重叠,值越大表示重叠越多,值越小表示重叠越少。
IoU 常用于衡量目标检测中锚框的定位精度。
# YoLov5训练
由于YOLO系列具有检测速度快、精度也不低的优点,已经成为了最常用的目标检测模型。目前主流的YOLO模型是YOLOv5,整个模型以开源的形式发布在以下地址:https://github.com/ultralytics/yolov5。
## YOLOv5安装
1. 克隆YOLOv5项目到本地。
```bash
git clone https://github.com/ultralytics/yolov5
```
2. 下载权重文件。权重文件可以在readme页面中找到Pretrained Checkpoints,点击想要下载的模型即可进行下载。
3. 安装项目所需的第三方库。从终端进入到yolov5文件夹,输入下面的命令:
```bash
pip install -r .\requirements.txt
```
## 数据集构建
与torchvision中需要自定义数据集不同,YOLOv5将数据集的创建包含到项目本身,不需要使用者自定义数据集,只需要按照YOLOv5对数据集格式的要求整理好数据和标签,并使用配置文件记录数据集的位置和类别即可。
数据集的配置文件存放于yolov5文件夹下的data子文件夹中,并且以.yaml的格式进行存储。YAML文件是一种用于记录配置的标记语言,YOLOv5使用该格式存储数据集和模型的配置。我们以官方提供的coco和coco128数据集来学习。
coco:
```yaml
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: ../datasets/coco # dataset root dir
train: train2017.txt # train images (relative to 'path') 118287 images
val: val2017.txt # val images (relative to 'path') 5000 images
test: test-dev2017.txt # 20288 of 40670 images, submit to https://competitions.codalab.org/competitions/20794
# Classes
names:
0: person
1: bicycle
```
coco128:
```yaml
# Train/val/test sets as 1) dir: path/to/imgs, 2) file: path/to/imgs.txt, or 3) list: [path/to/imgs1, path/to/imgs2, ..]
path: datasets/coco128 # dataset root dir
train: images/train2017 # train images (relative to 'path') 128 images
val: images/train2017 # val images (relative to 'path') 128 images
test: # test images (optional)
# Classes
names:
0: person
1: bicycle
2: car
3: motorc
```
在上面的配置中,path参数指明了coco数据集的位置;train参数指向了包含所有图像训练样本路径的文本文件;val和test参数的含义与train参数含义相同,只是对验证集和测试集的区别;names指明了数据信中包含的所有类别的标签和对应的释义。具体目录结构如下:
在images目录下存放的是训练图片,而labels目录下存放的是与训练图片同名的txt文件,以其中一个为例:
该文件中存放了000000000659.jpg图片中的目标类别和位置。在该文件中图像中的每个目标使用一行记录,包含用空格分隔的5个数值,分别是目标的类别标签值、归一化的目标中心点x坐标、归一化的目标中心点y坐标、归一化的目标的宽度和归一化的目标的高度。
## 模型训练方式
1. 从预处理权重开始 建议用于中小型数据集。将模型名称传递给 --weights 。模型会自动下载。
```bash
python train.py --data custom.yaml --weights yolov5s.pt
yolov5m.pt
yolov5l.pt
yolov5x.pt
custom_pretrained.pt
```
2. 从零开始 建议用于大型数据集(即 COCO, 对象365, OIv6)。给出感兴趣或自定义的模型架构 YAML 以及空的 --weights '' :
```bash
python train.py --data custom.yaml --weights '' --cfg yolov5s.yaml
yolov5m.yaml
yolov5l.yaml
yolov5x.yaml
```
## 模型训练与预测
执行train.py进行模型训练,并使用coco128数据集和yolov5s预训练模型。
```bash
python train.py --data data/coco128.yaml --weights yolov5s.pt
```
模型训练完成后,执行detect.py进行预测,结果如下:
```bash
python detect.py --weights ./runs/train/exp5/weights/best.pt --source 000000000529.jpg
```
- 2024-12-12
-
发表了主题帖:
【读书】《计算机视觉之PyTorch数字图像处理》5.数据准备与预处理
本帖最后由 waterman 于 2024-12-12 12:07 编辑
在基于深度学习进行图像处理之前,我们首先要准备相应的数据集。而Torchvision库提供了对图像数据的支持,主要包括常用图像数据集的访问接口、经典的图像分类、分割和目标检测模型,图像数据预处理功能以及一些图像增强的快捷方法。
Torchvision库是由多个功能子包构成的,每个功能子包功能相对独立,其中与数据准备相关的功能包有dataset、transforms和utils。
- dataset:提供了20多个流行的图像数据集访问接口,部分数据集还提供数据下载功能,只需简单调用,即可完成图像数据集的准备。
- transforms:提供了图像数据的预处理功能,可在图像、数组和张量三者之间进行相互转换,能够进行图像数据的增强,扩充数据集。
- utils:提供数据可视化功能。包括:
- make_grid():将一组图像张亮合成一幅表示图像的张量
- save_image():与make_grid类似,但是将合成的图像保存为图像文件
- draw_bounding_boxes():将矩形框绘制在图像张量上
- fraw_segmentation_masks():将分割结果绘制在张量上
# 数据集准备
虽然torchvision的datasets包中提供了一些数据集的访问接口,但是不能用于自有数据集的创建。因此,为使用自建数据集,需要我们使用torch.utils.data.Dataset类将图像数据进行封装,为模型提供统一的数据访问接口。
最关键的是实现torch.utils.data.Dataset类中的\__len__()、\__getitem__()两个抽象方法。其中\__len__()方法返回数据集的样本数量,及数据集规模;\__getitem__()方法接受样本的索引号,返回数据集中特定索引号的样本。
数据集返回的样本数据,在训练集上一般是由图像张量和标签组成的元组,而样本数据在测试集上只返回图像张量。
```python
import torchvision as tvs
from torchvision import transforms as T
import torch as tc
import pickle
from pathlib import Path
import numpy as np
class CIFAR10(tc.utils.data.Dataset):
#CIFAR10自定义数据集
#CIFAR10数据下载:https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz
def __init__(self,path='./dataset/cifar-10-batches-py',istrain=True,isimg=True,transform=None, target_transform=None):
self.path=Path(path)
self.istrain=istrain
self.isimg=isimg
self.transform=transform
self.target_transform=target_transform
self.fnames=[self.path/f'data_batch_{i}' for i in range(1,6)] if self.istrain else [self.path/'test_batch']
self.labelfile=self.path/'batches.meta'
self.labeltext=self.__loadlabel(self.labelfile)
self.label2str=lambda lbidx: self.labeltext[lbidx]
self.labels=[]
self.data=[]
for f in self.fnames:
tmpdata,tmplabel=self.__loaddata(f)
self.labels+=tmplabel
self.data.append(tmpdata)
self.data=np.concatenate(self.data,axis=0)
def __loaddata(self,file):
f=open(file,'rb')
print(f'加载数据文件{file.name}... ....')
df=pickle.load(f,encoding='bytes')
imgs=df[b'data']
print(f'从数据文件{file.name}加载了{imgs.shape[0]}个样本... ...')
lbs=df[b'labels']
return imgs,lbs
def __loadlabel(self,file):
f=open(file,'rb')
lbf=pickle.load(f,encoding='bytes')
lb=[ lb.decode() for lb in lbf[b'label_names'] ]
return lb
def __len__(self):
#可通过len()方法得到数据集内的样本数
return self.data.shape[0]
def __getitem__(self,idx):
#得到第idx个样本,idx为样本索引
if idx>=self.__len__() or idx
- 2024-12-10
-
发表了主题帖:
《深度学习的数学——使用Python语言》1. 环境搭建
非常感谢电子工程世界提供了这样一个宝贵的学习机会!恰好近期在项目中需要借助深度学习解决实际问题。虽然已经接触过一些基础知识,但在原理层面的理解仍然不够透彻。因此,我希望通过这本书能够补全短板,为后续工作提供理论支撑和实践指导。
# 图书展示
以下是图书的内容概要:
这本书并不是一部深度学习的入门教程,而是一部为读者解决实际问题铺路的“数学武器库”。它系统地讲解了构建和训练神经网络所需的核心数学知识,包括 概率论、线性代数和微积分 等。同时,它还深入浅出地阐述了神经网络的工作原理和训练机制,帮助我们在了解基础数学的同时,更好地理解深度学习模型背后的逻辑。
需要注意的是,本书对读者的知识储备有一定要求——建议在学习本书之前对深度学习有初步的了解。
# 环境搭建
书中推荐的Python版本为 3.8.5,当然更高版本也可以兼容。需要安装的核心工具包包括:
- numpy:科学计算的基础库,处理多维数组和矩阵运算的利器;
- scipy:高级科学计算库;
- matplotlib:用于绘图和数据可视化;
- scikit-learn:提供经典机器学习模型和数据处理工具。
以上工具通过 pip 命令即可轻松安装:
```bash
pip install numpy scipy matplotlib scikit-learn
```
# 基础工具使用
在第一章中,详细介绍了numpy工具的使用,并简要介绍了scipy、matplotlib,在最后使用scikit-learn完成了一个简单的手写数字识别网络。我们主要来学习一下numpy的使用。
## numpy
numpy 是科学计算的核心工具。在使用之前,我们首先需要将numpy模块导入:
```python
import numpy as np
```
### 定义数组
Numpy以数组为运算对象,可提供了将 Python 列表快速转换为数组的方法,并可通过 shape 和 size 查看数组形状与大小,首先以一维数组为例:
```python
a = np.array([1,2,3,4])
a
a.shape
a.size
```
之后我们尝试更高维的数组
这里我们使用到了np.arrange函数,它的作用类似于python中的range,之后我们将它reshape成了形状为(3,2,2)的数组。
### 数据类型
由于numpy底层是用C实现的,因此其支持C中的所有数据类型,包括:float64、float32、int64、uint64、int32、uint32、uint8等。
在创建数组时,numpy默认情况下会根据数据自动推断类型。但是与在C中类似,我们需要注意数据溢出的情况,在高精度计算或防止溢出时,可以手动指定数据类型,从而避免运算错误。
下面是对uint8类型的数组赋了一个500的值,
```python
a= np.array([1,2,500],dtype="uint8")
```
可以看到系统只是给出了一个警告,但是并没有报错。但是打印出来的数据中500变成了244,这是由于numpy默认只保留这个数的最后8位。
### 数据索引
在numpy中,其还支持复杂的索引方式,常用的是使用单个索引查询整个子数组:
```
a=np.arange(12).reshape((3,2,2))
a[1]
a
```
此外,类似于python,还支持切片索引的方式
### 数据保存与读取
numpy支持使用np.load和np.save进行数据的读取和保存。其中数组文件必须保存为".npy"为后缀,如果没有指定,其会自动添加该后缀。
```python
np.save('a.npy',a)
np.load('a.npy')
```
# 阅读感悟
第一章不仅让我重新认识了 Numpy 的强大功能,也让我对深度学习中数据处理的重要性有了更深的理解。作为数学工具,它为后续模型的搭建和训练奠定了坚实的基础。
期待接下来的学习中,能够进一步掌握概率论与微积分在深度学习中的具体应用,同时提升自己解决实际问题的能力!
- 2024-12-05
-
回复了主题帖:
AI挑战营(进阶):3. onnx2rknn
241205ZHUCE 发表于 2024-12-5 21:58
这群人真酷
必须的
-
回复了主题帖:
AI挑战营(进阶):6. 多人人脸识别+rtsp
秦天qintian0303 发表于 2024-12-4 10:44
录入的是三个,为什么检测圈出来的基本上都是4个
检测出来4个是因为代码里设置的单张为图片中最多检测到4个人脸。人脸检测和人脸识别是不一样的,人脸检测会检测图片中的人脸并将它们圈出来,之后人脸识别会将圈出的这些人脸与提前录入的人脸进行比对,判断人脸的身份。在录的视频里每个圈出的人脸上方都有识别信息,判断当前人脸属于face1~3中哪一个,或者是unknow。
-
回复了主题帖:
读书活动入围名单: 《深度学习的数学——使用Python语言》
个人信息无误,确认可以完成阅读分享计划
- 2024-12-04
-
发表了主题帖:
AI挑战营(进阶):6. 多人人脸识别+rtsp
本帖最后由 waterman 于 2024-12-4 09:35 编辑
基于 https://github.com/LuckfoxTECH/luckfox_pico_rkmpi_example.git 以luckfox_pico_rtsp_retinaface为例,进行修改
# 新建工程
新复制一份改名为
```
luckfox_pico_rtsp_retinafacenet
```
并在build.sh中添加
重新编译生成luckfox_pico_rtsp_retinafacenet_demo,上传至板卡测试可以正常运行:
# 工程修改
经过对比,retinaface.h为retinaface_facenet.h的子集,因此直接对其进行替换。
经测试可以直接替换并正常使用。工程目录如下:
仿照luckfox_pico_retinaface_facenet对工程进行了修改,添加了facenet于luckfox_pico_rtsp_retinafacenet_one_shot,实现了单个人脸的录入与识别。并于luckfox_pico_rtsp_retinafacenet_three_face中实现了一个及三个人脸的录入与识别。
具体思路为在while(1)大循环中,每次获取一帧摄像头采集到的图像数据,输入到retinaface中,得到n个检测到的人脸,分别对每个人脸进行识别并标注信息,最后将其进行推流。
关键代码如下:
1. 模型加载
```python
// Rknn model
rknn_app_context_t app_retinaface_ctx;
rknn_app_context_t app_facenet_ctx;
object_detect_result_list od_results;
const char *retinaface_model_path = "./model/retinaface.rknn";
const char *facenet_model_path = "./model/mobilefacenet.rknn";
memset(&app_retinaface_ctx, 0, sizeof(rknn_app_context_t));
memset(&app_facenet_ctx, 0, sizeof(rknn_app_context_t));
//Init Model
if(init_retinaface_facenet_model(retinaface_model_path, facenet_model_path, &app_retinaface_ctx, &app_facenet_ctx) != RK_SUCCESS)
{
RK_LOGE("rknn model init fail!");
return -1;
}
```
2. 录入三个参考人脸
```python
//Init Opencv-mobile
int ret;
cv::Mat retinaface_input(retinaface_height, retinaface_width, CV_8UC3, app_retinaface_ctx.input_mems[0]->virt_addr);
cv::Mat facenet_input(facenet_height, facenet_width, CV_8UC3, app_facenet_ctx.input_mems[0]->virt_addr);
//Get referencve img feature
const char *image1_path = "./model/test1.png";
const char *image2_path = "./model/test2.png";
const char *image3_path = "./model/test3.png";
//get image 1 feature
cv::Mat image = cv::imread(image1_path);
letterbox(image,facenet_input);//resize image to 160x160
ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
uint8_t *output = (uint8_t *)(app_facenet_ctx.output_mems[0]->virt_addr);
float* reference1_out_fp32 = (float*)malloc(sizeof(float) * 128);
output_normalization(&app_facenet_ctx,output,reference1_out_fp32);
//get image 2 feature
image = cv::imread(image2_path);
letterbox(image,facenet_input);//resize image to 160x160
ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
output = (uint8_t *)(app_facenet_ctx.output_mems[0]->virt_addr);
float* reference2_out_fp32 = (float*)malloc(sizeof(float) * 128);
output_normalization(&app_facenet_ctx,output,reference2_out_fp32);
//get image 3 feature
image = cv::imread(image3_path);
letterbox(image,facenet_input);//resize image to 160x160
ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
output = (uint8_t *)(app_facenet_ctx.output_mems[0]->virt_addr);
float* reference3_out_fp32 = (float*)malloc(sizeof(float) * 128);
output_normalization(&app_facenet_ctx,output,reference3_out_fp32);
```
3. 人脸检测与识别
```
cv::resize(bgr, retinaface_input, cv::Size(retinaface_width ,retinaface_height), 0, 0, cv::INTER_LINEAR);
// memcpy(app_retinaface_ctx.input_mems[0]->virt_addr, model_bgr.data, retinaface_width * retinaface_height * 3);
inference_retinaface_model(&app_retinaface_ctx, &od_results);
for(int i = 0; i < od_results.count; i++)
{
//Get det
object_detect_result *det_result = &(od_results.results);
sX = (int)((float)det_result->box.left *scale_x);
sY = (int)((float)det_result->box.top *scale_y);
eX = (int)((float)det_result->box.right *scale_x);
eY = (int)((float)det_result->box.bottom *scale_y);
// printf("%d %d %d %d\n",sX,sY,eX,eY);
cv::rectangle(frame,cv::Point(sX,sY),cv::Point(eX,eY),cv::Scalar(0,255,0),3);
//Face capture
cv::Rect roi(sX,sY,
(eX - sX),
(eY- sY));
cv::Mat face_img = frame(roi);
letterbox(face_img,facenet_input);
//rknn run
ret = rknn_run(app_facenet_ctx.rknn_ctx, nullptr);
if (ret < 0) {
printf("rknn_run fail! ret=%d\n", ret);
return -1;
}
output = (uint8_t *)(app_facenet_ctx.output_mems[0]->virt_addr);
output_normalization(&app_facenet_ctx, output, out_fp32);
float norm1 = get_duclidean_distance(reference1_out_fp32,out_fp32);
float norm2 = get_duclidean_distance(reference2_out_fp32,out_fp32);
float norm3 = get_duclidean_distance(reference3_out_fp32,out_fp32);
float min_norm;
int min_index;
printf("@ (%d %d %d %d) %.3f %.3f %.3f\n",sX,sY,eX,eY,norm1,norm2,norm3);
if(norm1 < norm2 && norm1 < norm3)
{
min_norm = norm1;
min_index = 1;
}
else if(norm2 < norm1 && norm2 < norm3)
{
min_norm = norm2;
min_index = 2;
}
else
{
min_norm = norm3;
min_index = 3;
}
//Draw text
if(min_norm < 1.1)
{
sprintf(show_text, "face%d,norm=%.3f", min_index, min_norm);
}
else
{
sprintf(show_text, "unknow");
}
cv::putText(frame, show_text, cv::Point(sX, sY - 8),
cv::FONT_HERSHEY_SIMPLEX,0.5,
cv::Scalar(0,255,0),
1);
}
```
4. 内存及模型释放
```python
// Release rknn model
free(reference1_out_fp32);
free(reference2_out_fp32);
free(reference3_out_fp32);
free(out_fp32);
release_facenet_model(&app_facenet_ctx);
release_retinaface_model(&app_retinaface_ctx);
```
# 效果测试
网上随便找了一张待检测图片如下:
我们录入的三个人脸依次如下:
## 单张人脸识别
## 三张人脸识别
# 演示视频
## 单张人脸
[localvideo]dda1b4aefd9569fac011d0de3986f3b8[/localvideo]
## 多张人脸
[localvideo]c5cb6a993c0e33bc23e419b5abb762e4[/localvideo]
# 结果分析
目前虽然基本实现功能,但是效果还不是特别好。考虑可能会是以下几点原因的影响:
1. 人脸录入需自行截取人脸部分的图片,直接输入facenet,因此检测结果不是很理想。考虑可以在录入的时候加入retinaface,尝试能否提升检测的准确性。
2. 检测的准确性受阈值的影响,可适当调节检测的阈值来提高识别效果。
3. 还有可能是模型量化时量化数据太少的问题。
此外,也可以额外加上fps显示实时帧率。
-
发表了主题帖:
AI挑战营(进阶):5. 板端rtsp推流
基于 https://github.com/LuckfoxTECH/luckfox_pico_rkmpi_example.git 以luckfox_pico_rtsp_opencv例程为例,进行rtsp推流。
# 编译下载
设置环境变量
```
export LUCKFOX_SDK_PATH=/home/SDK/luckfox-pico
```
注意:使用绝对地址。
获取仓库源码并设置自动编译脚本执行权限
```
chmod a+x ./build.sh
./build.sh
```
执行 ./build.sh 后选择编译的例程
编译完成后会在 install 文件夹下生成对应的部署文件夹
使用adb push,将生成的部署文件夹完整上传到 Luckfox Pico 上。
# 运行程序
在 Luckfox Pico 板端进入luckfox_pico_rtsp_opencv_demo目录,运行luckfox_pico_rtsp_opencv可执行程序
```
chmod 777 luckfox_pico_rtsp_opencv
./luckfox_pico_rtsp_opencv
```
之后使用 VLC 打开网络串流 rtsp://172.32.0.93/live/0拉取图像
注意:
1. Ubuntu下使用apt安装的VLC无法拉取rtsp视频流,需要先安装flatpak,之后再通过flatpak安装VLC,具体步骤如下:
```
sudo apt-get remove vlc
sudo apt-get install flatpak
sudo apt install gnome-software-plugin-flatpak
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
sudo reboot
flatpak install vlc
```
# RKRTSP 库使用
RKRTSP 是 RockChip 提供的一套仅供测试使用的 rtsp 服务器软件接口,仅作为 RKMPI 库的功能验证使用,可以通过软件接口快速实现 rtsp 推流功能。
1. 创建 rtsp 实例
```
g_rtsplive = create_rtsp_demo(port);
```
2. 创建 rtsp 接口
```
g_rtsp_session = rtsp_new_session(g_rtsplive, "/live/0");
```
3. 设置 rtsp 传输属性
```
rtsp_set_video(g_rtsp_session, RTSP_CODEC_ID_VIDEO_H264, NULL, 0);
```
4. 同步 rtsp 时间戳
```
rtsp_sync_video_ts(g_rtsp_session, rtsp_get_reltime(), rtsp_get_ntptime());
```
5. 更新视频流设置并驱动事件
```
rtsp_tx_video(g_rtsp_session, (uint8_t *)pData, PackLen, PTS);
rtsp_do_event(g_rtsplive);
```
6. 删除 rtsp 实例
```
rtsp_del_demo(g_rtsplive);
```
-
发表了主题帖:
AI挑战营(进阶):4. 板端Retinaface+facenet测试
在上一节中我们得到了retinaface和facenet的两个rknn模型,在本节我们将以 https://github.com/LuckfoxTECH/luckfox_pico_rknn_example.git 库中的luckfox_pico_retinaface_facenet_demo为例,快速验证我们得到的模型能否正确推理。
# 程序编译
首先将库克隆下来,
```
git clone https://github.com/LuckfoxTECH/luckfox_pico_rknn_example.git
```
之后设置环境变量,把LUCKFOX_SDK_PATH设置为我们SDK的目录
```
export LUCKFOX_SDK_PATH=/home/sdk/luckfox-pico
```
获取仓库源码并设置自动编译脚本执行权限
```
chmod a+x ./build.sh
./build.sh
```
执行 ./build.sh 后选择编译的例程
```
1) luckfox_pico_retinaface_facenet
2) luckfox_pico_retinaface_facenet_spidev
3) luckfox_pico_yolov5
Enter your choice [1-3]:1
```
之后等待编译完成即可。
# 程序测试
编译完成后会在 install 文件夹下生成对应的**部署文件夹**,我们这里使用到的是luckfox_pico_retinaface_facenet_demo。
```
luckfox_pico_retinaface_facenet_demo
luckfox_pico_retinaface_facenet_spidev_pro_max_demo
luckfox_pico_retinaface_facenet_spidev_plus_demo
luckfox_pico_yolov5_demo
```
将luckfox_pico_retinaface_facenet_demo/model目录中的两个模型文件换成我们自己训练得到的rknn文件。
将生成的luckfox_pico_retinaface_facenet_demo目录使用adb完整上传到 Luckfox上
```
adb push ./luckfox_pico_retinaface_facenet_demo /
```
- 在Luckfox板端中赋予其中的可执行程序执行权限
```
cd /luckfox_pico_retinaface_facenet_demo
chmod 777 luckfox_pico_retinaface_facenet
```
执行luckfox_pico_retinaface_facenet
```
./luckfox_pico_retinaface_facenet ./model/retinaface.rknn ./model/facenet.rknn ./model/test.jpg
```
可以看到,当检测到人脸时,会将人脸的锚框位置和与输入人脸图片的距离输出至命令行。由于输入人脸图片为憨豆先生,测试时摄像头采集的人脸为我自己的脸,因此可以看到人脸距离还是挺大的。
以上说明我们的模型基本是没有问题的,能够正常执行推理。接下来就能够进一步实现rtsp推流+多人人脸识别了。
-
发表了主题帖:
AI挑战营(进阶):3. onnx2rknn
在上一节中,我们获得了retinaface和facenet的onnx模型,在这一节中我们将其转换为RKNPU能够识别并运行的rknn格式。
# 环境安装
参考我之前写的一篇博客:https://bbs.elecfans.com/jishu_2454118_1_1.html
# 模型转换
## retinaface
### 数据集准备
首先上传一张包含人脸的校准数据集,并将其路径存放至retinaface_dataset.txt文件中。
retinaface_dataset.txt内容如下:
```
../../img/detection/face.jpg
```
### 模型转换
编写模型转换脚本:
```python
import sys
from rknn.api import RKNN
def parse_arg():
if len(sys.argv) < 5:
print("Usage: python3 {} [onnx_model_path] [dataset_path] [output_rknn_path] [model_type]".format(sys.argv[0]));
exit(1)
model_path = sys.argv[1]
dataset_path= sys.argv[2]
output_path = sys.argv[3]
model_type = sys.argv[4]
return model_path, dataset_path, output_path,model_type
if __name__ == '__main__':
model_path, dataset_path, output_path, model_type= parse_arg()
# Create RKNN object
rknn = RKNN(verbose=False)
# Pre-process config
print('--> Config model')
if model_type == 'Retinaface':
rknn.config(mean_values=[[104, 117, 123]], std_values=[[1, 1, 1]], target_platform='rv1103',
quantized_algorithm="normal", quant_img_RGB2BGR=True,optimization_level=0)
print("Use retinaface mode")
else:
rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rv1103')
print('done')
# Load model
print('--> Loading model')
ret = rknn.load_onnx(model=model_path)
if ret != 0:
print('Load model failed!')
exit(ret)
print('done')
# Build model
print('--> Building model')
ret = rknn.build(do_quantization=True, dataset=dataset_path)
if ret != 0:
print('Build model failed!')
exit(ret)
print('done')
# Export rknn model
print('--> Export rknn model')
ret = rknn.export_rknn(output_path)
if ret != 0:
print('Export rknn model failed!')
exit(ret)
print('done')
# Release
rknn.release()
```
执行convert.py进行模型转换:
```bash
python convert.py ../model_zoo/export/retinaface.onnx ./dataset/retinaface_dataset.txt ../model_zoo/retinaface.rknn Retinaface
```
## facenet
### 数据集准备
首先上传一张包含人脸的校准数据集,并将其路径存放至facenet_dataset.txt文件中。
facenet_dataset.txt内容如下:
```
../../img/recongnition/calib.jpg
```
### 模型转换
执行convert.py进行模型转换:
```bash
python convert.py ../model_zoo/export/facenet.onnx ./dataset/facenet_dataset.txt ../model_zoo/facenet.rknn Facenet
```
转换完成后,就能够分别得到两个相应的rknn模型文件于model_zoo目录下:
# 模型评估
## 数据集准备
首先准备验证数据集:
目录结构如下:
```
eval
├── dataset
│ ├── facenet
│ │ ├── 1_001.jpg
│ │ ├── 1_002.jpg
│ │ └── 2_001.jpg
│ └── retinaface
│ └── j1.jpg
├── facenet.py
└── retinaface.py
```
## retinaface
```python
import os
import sys
import urllib
import urllib.request
import time
import numpy as np
import cv2
from math import ceil
from itertools import product as product
from rknn.api import RKNN
DATASET_PATH = '../dataset/retinaface_dataset.txt'
DEFAULT_QUANT = True
def letterbox_resize(image, size, bg_color):
"""
letterbox_resize the image according to the specified size
:param image: input image, which can be a NumPy array or file path
:param size: target size (width, height)
:param bg_color: background filling data
:return: processed image
"""
if isinstance(image, str):
image = cv2.imread(image)
target_width, target_height = size
image_height, image_width, _ = image.shape
# 计算调整后的图像尺寸
aspect_ratio = min(target_width / image_width, target_height / image_height)
new_width = int(image_width * aspect_ratio)
new_height = int(image_height * aspect_ratio)
# 使用 cv2.resize() 进行等比缩放
image = cv2.resize(image, (new_width, new_height), interpolation=cv2.INTER_AREA)
# 创建新的画布并进行填充
result_image = np.ones((target_height, target_width, 3), dtype=np.uint8) * bg_color
offset_x = (target_width - new_width) // 2
offset_y = (target_height - new_height) // 2
result_image[offset_y:offset_y + new_height, offset_x:offset_x + new_width] = image
return result_image, aspect_ratio, offset_x, offset_y
def PriorBox(image_size): #image_size Support (320,320) and (640,640)
anchors = []
min_sizes = [[16, 32], [64, 128], [256, 512]]
steps = [8, 16, 32]
feature_maps = [[ceil(image_size[0] / step), ceil(image_size[1] / step)] for step in steps]
for k, f in enumerate(feature_maps):
min_sizes_ = min_sizes[k]
for i, j in product(range(f[0]), range(f[1])):
for min_size in min_sizes_:
s_kx = min_size / image_size[1]
s_ky = min_size / image_size[0]
dense_cx = [x * steps[k] / image_size[1] for x in [j + 0.5]]
dense_cy = [y * steps[k] / image_size[0] for y in [i + 0.5]]
for cy, cx in product(dense_cy, dense_cx):
anchors += [cx, cy, s_kx, s_ky]
output = np.array(anchors).reshape(-1, 4)
print("image_size:",image_size," num_priors=",output.shape[0])
return output
def box_decode(loc, priors):
"""Decode locations from predictions using priors to undo
the encoding we did for offset regression at train time.
Args:
loc (tensor): location predictions for loc layers,
Shape: [num_priors,4]
priors (tensor): Prior boxes in center-offset form.
Shape: [num_priors,4].
variances: (list[float]) Variances of priorboxes
Return:
decoded bounding box predictions
"""
variances = [0.1, 0.2]
boxes = np.concatenate((
priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
priors[:, 2:] * np.exp(loc[:, 2:] * variances[1])), axis=1)
boxes[:, :2] -= boxes[:, 2:] / 2
boxes[:, 2:] += boxes[:, :2]
return boxes
def decode_landm(pre, priors):
"""Decode landm from predictions using priors to undo
the encoding we did for offset regression at train time.
Args:
pre (tensor): landm predictions for loc layers,
Shape: [num_priors,10]
priors (tensor): Prior boxes in center-offset form.
Shape: [num_priors,4].
variances: (list[float]) Variances of priorboxes
Return:
decoded landm predictions
"""
variances = [0.1, 0.2]
landmarks = np.concatenate((
priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:],
priors[:, :2] + pre[:, 2:4] * variances[0] * priors[:, 2:],
priors[:, :2] + pre[:, 4:6] * variances[0] * priors[:, 2:],
priors[:, :2] + pre[:, 6:8] * variances[0] * priors[:, 2:],
priors[:, :2] + pre[:, 8:10] * variances[0] * priors[:, 2:]
), axis=1)
return landmarks
def nms(dets, thresh):
"""Pure Python NMS baseline."""
x1 = dets[:, 0]
y1 = dets[:, 1]
x2 = dets[:, 2]
y2 = dets[:, 3]
scores = dets[:, 4]
areas = (x2 - x1 + 1) * (y2 - y1 + 1)
order = scores.argsort()[::-1]
keep = []
while order.size > 0:
i = order[0]
keep.append(i)
xx1 = np.maximum(x1, x1[order[1:]])
yy1 = np.maximum(y1, y1[order[1:]])
xx2 = np.minimum(x2, x2[order[1:]])
yy2 = np.minimum(y2, y2[order[1:]])
w = np.maximum(0.0, xx2 - xx1 + 1)
h = np.maximum(0.0, yy2 - yy1 + 1)
inter = w * h
ovr = inter / (areas + areas[order[1:]] - inter)
inds = np.where(ovr 0.5)[0]
boxes = boxes[inds]
landmarks = landmarks[inds]
scores = scores[inds]
order = scores.argsort()[::-1]
boxes = boxes[order]
landmarks = landmarks[order]
scores = scores[order]
# 非极大值抑制
dets = np.hstack((boxes, scores[:, np.newaxis])).astype(
np.float32, copy=False)
keep = nms(dets, 0.2)
dets = dets[keep, :]
landmarks = landmarks[keep]
dets = np.concatenate((dets, landmarks), axis=1)
# 画框标记
for data in dets:
if data[4] < 0.5:
continue
print("face @ (%d %d %d %d) %f"%(data[0], data[1], data[2], data[3], data[4]))
text = "{:.4f}".format(data[4])
data = list(map(int, data))
cv2.rectangle(img, (data[0], data[1]),
(data[2], data[3]), (0, 0, 255), 2)
cx = data[0]
cy = data[1] + 12
cv2.putText(img, text, (cx, cy),
cv2.FONT_HERSHEY_DUPLEX, 0.5, (255, 255, 255))
# landmarks
cv2.circle(img, (data[5], data[6]), 1, (0, 0, 255), 5)
cv2.circle(img, (data[7], data[8]), 1, (0, 255, 255), 5)
cv2.circle(img, (data[9], data[10]), 1, (255, 0, 255), 5)
cv2.circle(img, (data[11], data[12]), 1, (0, 255, 0), 5)
cv2.circle(img, (data[13], data[14]), 1, (255, 0, 0), 5)
img_path = './result.jpg'
cv2.imwrite(img_path, img)
print("save image in", img_path)
# 释放
rknn.release()
```
执行结果如下:
## facenet
```
import numpy as np
import cv2
import os
from rknn.api import RKNN
from PIL import Image
from sklearn import preprocessing
from scipy.spatial.distance import pdist
os.environ['RKNN_DRAW_DATA_DISTRIBUTE']="1"
if __name__ == '__main__':
BUILD_QUANT = True
# Create RKNN object
rknn = RKNN()
print('--> config model')
rknn.config(mean_values=[[0, 0, 0]], std_values=[[255, 255, 255]], target_platform='rv1103')
print('done')
# Load model
print('--> Lccoading model')
ret = rknn.load_onnx(model='../../model_zoo/export/facenet.onnx')
if ret != 0:
print('Load facenet failed!')
exit(ret)
print('done')
# Build model
print('--> Building model')
ret = rknn.build(do_quantization=True, dataset='../dataset/facenet_dataset.txt')
if ret != 0:
print('Build model failed!')
exit(ret)
print('done')
# Init runtime
print('--> Init runtime environment')
ret = rknn.init_runtime()
if ret != 0:
print('Init runtime environment failed')
exit(ret)
print('done')
# Set inputs and inference
image_1 = Image.open("./dataset/facenet/1_001.jpg")
image_1 = image_1.resize((160,160), Image.BICUBIC)
img1 = np.asarray(image_1, np.uint8)
outputs1 = np.array(rknn.inference(data_format='nhwc', inputs=[img1])[0])
outputs1 = preprocessing.normalize(outputs1, norm='l2')
image_2 = Image.open("./dataset/facenet/1_002.jpg")
image_2 = image_2.resize((160,160), Image.BICUBIC)
img2 = np.asarray(image_2, np.uint8)
outputs2 = np.array(rknn.inference(data_format='nhwc', inputs=[img2])[0])
outputs2 = preprocessing.normalize(outputs2, norm='l2')
# Get distance
distance = np.linalg.norm(outputs2 - outputs1, axis=1)
print("distance:", distance)
rknn.release()
```
执行结果如下:
工程文件在附录中。
-
发表了主题帖:
AI挑战营(进阶):2. retinaface+facenet测试及导出onnx
在上一节中我们完成了使用insigtface进行多人人脸识别,其本质是分别使用人脸检测模型进行人脸检测,之后使用人脸识别模型对检测出的人脸进行识别。在本节,我们将分别使用retinaface模型和facenet模型在PC端分别完成人脸检测和人脸识别,并导出相应的onnx模型。
# retinaface
下载源码
```bash
git clone https://github.com/bubbliiiing/retinaface-pytorch.git
```
下载完库后解压,运行predict.py,输入需要预测的图片,在img文件夹下提供了两张测试图片可以使用。
```bash
img/timg.jpg
```
若在服务器端测试,无法显示图片的话,可以注释掉cv2.imshow和cv2.waitKey,否则会报错。
若想要将识别到的结果输出至图片,则可以对predict.py进行如下修改:
首先在文件开头添加
```python
import os
```
之后添加如下代码
```python
if not os.path.exists(dir_save_path):
os.makedirs(dir_save_path)
```
第一部分的代码用于创建不存在的dir_save_path文件夹
```python
cv2.imwrite(os.path.join(dir_save_path, 'predict.jpg'), r_image)
```
第二部分的代码用于将识别后的结果保存至dir_save_path文件夹下的predict.jpg文件中。
最终结果如下:
# facenet
下载源码
```bash
git clone https://github.com/bubbliiiing/facenet-pytorch.git
```
在model_data目录中已包含facenet_mobilenet.pth权重文件,可直接运行根目录的predict.py进行人脸识别
```python
from PIL import Image
from facenet import Facenet
if __name__ == "__main__":
model = Facenet()
while True:
image_1 = input('Input image_1 filename:')
try:
image_1 = Image.open(image_1)
except:
print('Image_1 Open Error! Try again!')
continue
image_2 = input('Input image_2 filename:')
try:
image_2 = Image.open(image_2)
except:
print('Image_2 Open Error! Try again!')
continue
probability = model.detect_image(image_1,image_2)
print(probability)
```
其中在根目录的img目录下已存在三张图片,1_001和1_002为一类,2_001为单独一类。可直接进行测试
测试结果如下:
从结果中可以看到,1_001和1_002的距离很近,为0.699;1_001和2_001的距离较远,为1.336。说明模型能够正确对人脸进行比对识别。
# 导出onnx
完成pytorch模型的测试之后,我们需要将它们转换为onnx格式的模型备用。
以下分别为retinaface和facenet导出onnx模型的代码。
- retinaface_export_onnx.py
```python
from nets.retinaface import RetinaFace
from utils.config import cfg_mnet
import torch
model_path='model_data/Retinaface_mobilenet0.25.pth' #模型路径
model=RetinaFace(cfg=cfg_mnet,pretrained = False) #模型初始化
device = torch.device('cpu')
model.load_state_dict(torch.load(model_path,map_location=device),strict=False) #模型加载
net=model.eval()
example=torch.rand(1,3,640,640) #给定输入
torch.onnx.export(model,(example),'model_data/retinaface.onnx',verbose=True,opset_version=9) #导出
```
- facenet_export_onnx.py
```python
from nets.facenet import Facenet
from torch import onnx
import torch
model_path='model_data/facenet_mobilenet.pth' #模型路径
model = Facenet(backbone="mobilenet",mode="predict",pretrained=True) #模型初始化
device = torch.device('cpu')
model.load_state_dict(torch.load(model_path, map_location=device), strict=False)
example=torch.rand(1,3,160,160) #给定一个输入
torch.onnx.export(model,example,'model_data/facenet.onnx',verbose=True,opset_version=9) #导出
```
执行以上命令后,就能够在model_data目录下生成相应的onnx模型如下图:
使用 Netron 工具查看 ONNX 模型的结构,确定是否存在 RV1103/RV1106 暂时无法支持的操作符(如Layer Normalization),参考手册 RKNN支持操作待列表(https://github.com/rockchip-linux/rknn-toolkit2/blob/master/doc/05_RKNN_Compiler_Support_Operator_List_v1.6.0.pdf) 。如果不支持的操作符位于模型的最后几层,可以考虑使用 CPU 进行实现。使用 Netron 工具观察 facenet 源码的模型,可以发现模型在输出前使用了 RKNPU 不支持的 ReduceSum 算子,我们可以对模型进行裁减,自行编写最后输出部分的运算。
ONNX模型的最后输出部分如下图所示:
下面我们进行模型的裁剪,参考源码路径 facenet-pytorch/nets/facenet.py 中的 forward 函数源码可以获知在模型推理阶段输出 128 维特征向量前进行了一个标准化的过程。
我们将最后一层的实现分配给 CPU 来运行,将最后一层的代码注释后,重新导出 ONNX 模型,得到的模型结构如下:
- 2024-12-03
-
回复了主题帖:
【读书】《计算机视觉之PyTorch数字图像处理》4.特征点提取
hellokitty_bean 发表于 2024-12-2 22:34
突然想看Computer Vision的基础东西。。。。。。。否则貌似无以为继。。。。。。
谢谢支持,一起学习