- 2025-01-18
-
回复了主题帖:
《深度学习的数学——使用Python语言》7. 线代进阶之”向量范数和距离度量“
Jacktang 发表于 2025-1-18 10:14
结果中竟然出现了inf,虽然这并不影响我们的判断
这个inf出现的原因是什么
下面是打印出来的p q和w的值 根据离散情况下的K-L散度计算公式,因为q中出现了0,所以在计算KL(p||q)时,由于分母为零导致出现无穷大inf的情况。
-
回复了主题帖:
RDK X3测评:2.摄像头使用
eew_Eu6WaC 发表于 2025-1-17 16:27
写的真详细,期待这块板子后续更多功能,比如一些AI相关的应用
-
回复了主题帖:
RDK X3测评:2.摄像头使用
SeasonMay 发表于 2025-1-17 15:47
opencv-python的部署效果好不好呢?
暂时还没来得及测试,但官方文档里给出了基于python的它自己的hobot_cv和opencv的性能对比,里面有提到opencv的性能,可以参考一下5.2.4 图像处理加速 | RDK DOC
- 2025-01-16
-
发表了主题帖:
《深度学习的数学——使用Python语言》7. 线代进阶之”向量范数和距离度量“
在深度学习中,范数和向量距离是两个不同的概念。向量范数是一种函数,用于将一个实数或复数向量映射为一个值。虽然范数通常用于度量向量之间的距离,但是同样也有其它的一些表示距离的方式。
# 范数距离
范数是具有“长度”概念的函数。在向量空间内,为所有的向量的赋予非零的增长度或者大小。不同的范数,所求的向量的长度或者大小是不同的。
L1范数是指向量中各个元素绝对值之和,L2范数定义为向量所有元素的平方和的开平方。
L1范数的定义:
L2范数的定义:
在深度学习中,二范数可用于正则化的权值衰减法,从而避免模型的权重变得过大。
此外,在facenet人脸识别模型中,就是将将人脸图片表征为一个128维向量,通过计算两个人脸向量之间的二范数距离,来判断两张人脸图片是否属于同一个人。
下面的代码读取了两张人脸图片,分别获得了128维的输出向量并进行正则化,之后使用L2范数计算它们之间的距离。
```
# 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)
```
# 马氏距离
当需要度量某个特征向量与多个特征向量之间的距离时,如果直接采用欧式距离,衡量的是两个特征向量之间的直接距离,而没有考虑数据的分布特性。而采用马氏距离,在计算中对协方差进行归一化,则可以规避欧式距离对于数据特征方差不同的风险,从而使所谓的“距离”更加符合数据分布特征以及实际意义。
其中Σ是多维随机变量的协方差矩阵,μ为样本均值向量。马氏距离刻画了x与以μ为均值的某个分布之间的距离。如果协方差矩阵是单位向量,即各维度独立同分布,此时马氏距离就变成了欧氏距离。
直观的解释如上图,虽然Point1和Point2距离样本中心点的距离相同,但是在右图中,可以明显看出来,Point2不属于该分布。
因此,我们可以使用马氏距离来构建一个简单的分类器。比如,给定数据集,判断输入样本与所有类别质心之间的马氏距离,并选择最小距离作为该输入的类别。
下面以sklearn中的乳腺癌数据集来构建基于马氏距离的最近质心分类器,从而进一步加深理解与认识。
代码实现如下:
导入需要的库
```
import numpy as np
from sklearn import datasets
from scipy.spatial.distance import mahalanobis
读取数据集并打乱,取前400个样本作为训练数据并将剩下的样本作为测试数据
bc = datasets.load_breast_cancer()
d = bc.data
l = bc.target
i = np.argsort(np.random.random(len(d)))
d = d
l = l
xtrn, ytrn = d[:400], l[:400]
xtst, ytst = d[400:], l[400:]
```
计算每个类别数据的均值以及训练数据集的协方差矩阵和协方差矩阵的逆
```
i = np.where(ytrn == 0)
m0 = xtrn.mean(axis=0)
i = np.where(ytrn == 1)
m1 = xtrn.mean(axis=0)
S = np.cov(xtrn, rowvar=False)
SI= np.linalg.inv(S)
```
定义函数,对测试数据集进行分类
```
def score(xtst, ytst, m, SI):
nc = 0
for i in range(len(ytst)):
d = np.array([mahalanobis(xtst,m[0],SI),
mahalanobis(xtst,m[1],SI)])
c = np.argmin(d)
if (c == ytst):
nc += 1
return nc / len(ytst)
```
分别计算马氏距离和欧氏距离的结果得分
```
mscore = score(xtst, ytst, [m0,m1], SI)
escore = score(xtst, ytst, [m0,m1], np.identity(30))
print("Mahalanobis score = %0.4f" % mscore)
print("Euclidean score = %0.4f" % escore)
```
运行结果如下:
可以看到,马氏距离分类的效果比欧氏距离的效果有所提升。
# K-L散度
K-L散度又称相对熵,用于衡量两个概率分布的相似程度。若K-L散度越小,则说明两个概率分布越相似。
我们设定两个概率分布分别为P和Q,则连续和离散情况下的K-L散度计算公式分别为
其中log为以2为底的对数,在scipy.special中使用rel_entr函数来实现K-L散度的计算,但是它使用的是自然对数而不是以2为底的对数。此外,K-L散度不满足对称性,并非数学意义上的距离度量。
下面我们通过一个实验来加深对于K-L散度的理解。
代码实现如下:
首先导入使用到的库
```
import numpy as np
from scipy.special import rel_entr
import matplotlib.pylab as plt
```
分别生成服从均匀分布和两组二项分布B(12,0.4)以及B(12,0.9)。
```
N = 1000000
p = np.random.randint(0,13,size=N)
p = np.bincount(p)
p = p / p.sum()
q = np.random.binomial(12,0.9,size=N)
q = np.bincount(q)
q = q / q.sum()
w = np.random.binomial(12,0.4,size=N)
w = np.bincount(w)
w = w / w.sum()
```
使用rel_entr函数计算两组二项分布与均匀分布的K-L散度,判断哪个二项分布更接近于均匀分布
```
print(rel_entr(q,p).sum())
print(rel_entr(w,p).sum())
```
输出结果如下:
可以看到,w与p的K-L散度更小,说明w与p的概率分布更为相似。事实是否如此呢?我们来看一下三个不同的离散概率分布的图像。
```
plt.bar(np.arange(13),p,0.333,hatch="///",edgecolor='k')
plt.bar(np.arange(13)+0.333,q,0.333,hatch="---",edgecolor='k')
plt.bar(np.arange(13)+0.666,w,0.333,hatch="\\\\",edgecolor='k')
plt.xlabel("Value")
plt.ylabel("Proportion")
plt.tight_layout(pad=0,h_pad=0,w_pad=0)
plt.savefig("kl_divergence.png", dpi=300)
plt.show()
```
其中蓝色为p,橙色为q,绿色为w。可以看到与q相比,p确实更像w,说明我们通过K-L散度的判断是正确的。
此外,既然前面提到了K-L散度是非对称的,那么我们在计算K-L散度时将输入变量换一下位置会产生什么样的后果呢?我们将计算w与p的散度时的输入换一下顺序
```
print(rel_entr(q,p).sum())
print(rel_entr(p,w).sum())
```
得到结果如下:
可以看到第二项反而比第一项更大了,这显然不是我们期望的结果。那么在实际使用中,应该如何确定输入变量的顺序呢?
为了解决这个问题,我们首先需要理解K-L散度的意义,KL(P||Q)衡量的是分布P相对于Q的信息损失,即当Q被用作P的近似时,描述P所需的额外信息量。如果将P设为目标分布、Q设为近似分布,则KL(P||Q)越小说明用Q描述P所需的额外信息量越小,即P和Q越接近。相反KL(Q||P)越小,则说明用P描述Q所需的额外信息量越小。因此,在上面比较q和w与p之间的距离时,我们需要固定输入中p的位置,这样得到的数值才具有可比性。
然而,若我们将p都作为第一个输入变量,下面又出现了新的问题
```
print(rel_entr(p,q).sum())
print(rel_entr(p,w).sum())
```
输出结果如下:
可以看到,结果中竟然出现了inf,虽然这并不影响我们的判断,但是inf在计算中会给我们带来极大的不便,如何避免这个问题呢?下面给出了解答。
- 2025-01-14
-
回复了主题帖:
RDK X3测评:2.摄像头使用
dirty 发表于 2025-1-14 21:14
摄像头驱动功能验证了,后面可以做模型部署 目标检测人脸识别这些AI了哈,期待后续
-
回复了主题帖:
【Wio Lite AI STM32H725AE视觉开发板】--1.开箱与准备
dirty 发表于 2025-1-14 20:24
看芯片手册是没有专门的NPU,结合ST的工具链,跑的边缘AI,不太适合跑大模型
期待后续的测评分享
-
回复了主题帖:
RDK X3测评:2.摄像头使用
摄像头接线方式如图
-
回复了主题帖:
【Wio Lite AI STM32H725AE视觉开发板】--1.开箱与准备
板卡的AI算力怎么样,适合跑深度学习模型吗
- 2025-01-13
-
回复了主题帖:
EEWORLD陪你过大年,新年积分兑换专场来啦~好物多多还有幸运盲盒!
感谢论坛,已兑换一个盲盒,期待!!!!
- 2025-01-12
-
加入了学习《基于Arduino玩转pico RP2040》,观看 基于Arduino玩转pico RP2040
- 2025-01-11
-
发表了主题帖:
RDK X3测评:2.摄像头使用
# wifi连接
1. 使用sudo nmcli device wifi rescan命令扫描热点
1. 使用sudo nmcli device wifi list命令列出扫描到的热点
2. 使用 sudo wifi_connect "SSID" "PASSWD"命令连接热点
# 系统更新
官方文档中推荐我们安装完系统后,通过apt命令对系统进行更新。
首先打开终端命令行,输入如下命令:
```
sudo apt update
```
其次,升级所有已安装的软件包到最新版本,命令如下:
```
sudo apt full-upgrade
```
执行```apt full-upgrade```命令后,需要手动重启设备使更新生效,命令如下:
```
sudo reboot
```
# 图像处理
RDK X3 提供基于 MIPI Camera 的图像采集功能,并通过 python的libsrcampy 库完成相关操作。以下内容涵盖摄像头初始化、图像获取、编码和显示的详细步骤。
## 初始化配置并使用MIPI Camera
RDK X3 提供了完善的 MIPI Camera 图像采集和处理功能,能够满足不同场景下的图像采集需求。这里我们将介绍 RDK X3 中 MIPI Camera 的初始化配置并实现常规功能。
### Camera对象功能概述
Camera对象主要用于管理和控制MIPI摄像头,提供以下几种常用方法:
- open_cam:打开指定通道的摄像头,并设置帧率与分辨率。
- open_vps:使能图像处理功能,包括缩放、旋转和裁剪。
- get_img:从摄像头获取图像数据。
- set_img:向图像处理模块输入图像数据。
- close_cam:关闭摄像头。
Camera初始化与配置
1. 打开摄像头
open_cam方法用于打开指定通道的摄像头,设置摄像头输出的帧率和分辨率。
```
Camera.open_cam(pipe_id, video_index, fps, width, height, raw_height, raw_width)
```
2. 配置图像处理功能
open_vps方法使能图像处理功能,包括缩放、裁剪和旋转。
```
Camera.open_vps(pipe_id, proc_mode, src_width, src_height, dst_width, dst_height, crop_rect, rotate, src_size, dst_size)
```
3. 获取图像
在完成open_cam和open_vps的配置后,可以通过get_img获取图像。
```
# 等待图像采集
time.sleep(1)
# 获取图像
img = camera.get_img(2)
if img is not None:
with open("output.img", "wb") as f:
f.write(img)
print("图像保存成功")
else:
print("获取图像失败")
```
4. 关闭摄像头
在使用完摄像头后,调用close_cam关闭。
```
camera.close_cam()
print("摄像头已关闭")
```
### 完整代码示例
以下是一个简单示例,展示如何从初始化到保存img图像:
```python
import sys, os, time
from hobot_vio import libsrcampy
def test_camera():
cam = libsrcampy.Camera()
vps = libsrcampy.Camera()
#open MIPI camera, fps: 30, solution: 1080p
ret = cam.open_cam(0, -1, 30, 2560, 1440)
print("Camera open_cam return:%d" % ret)
ret = vps.open_vps(1, 1, 2560, 1440, 1920, 1080)
# wait for 1s
time.sleep(1)
#get one image from camera
img = cam.get_img(2)
ret = vps.set_img(img)
img = vps.get_img()
if img is not None:
#save file
fo = open("output.img", "wb")
fo.write(img)
fo.close()
print("camera save img file success")
else:
print("camera save img file failed")
#close MIPI camera
cam.close_cam()
print("test_camera done!!!")
test_camera()
```
上述代码中我们使用到了VPS进行图像变换,将其从2560x1440大小缩放为1920x1080大小,注意到,我们需要分别创建vps和cam,并将其绑定到不同的通道上,之后将cam读取到的图像输入到vps中,再将vps的输出保存。除此之外,如果仅仅是简单的缩放,我们可以不需要使用vps,直接指定cam的输出为1920x1080即可,示例代码如下:
```python
import sys, os, time
from hobot_vio import libsrcampy
def test_camera():
cam = libsrcampy.Camera()
#open MIPI camera, fps: 30, solution: 1080p
ret = cam.open_cam(0, -1, 30, 1920, 1080)
print("Camera open_cam return:%d" % ret)
# wait for 1s
time.sleep(1)
#get one image from camera
img = cam.get_img(2)
if img is not None:
#save file
fo = open("output.img", "wb")
fo.write(img)
fo.close()
print("camera save img file success")
else:
print("camera save img file failed")
#close MIPI camera
cam.close_cam()
print("test_camera done!!!")
test_camera()
```
在读取到摄像头采集到的图像数据后,我们得到的是一个如下图的img文件,并不能直接进行查看,需要将其编码为jpeg格式并保存至文件或者输出到显示屏上进行查看,下面我们分别进行图像编码和图像显示的实验。
## 图像编码
Encoder对象实现了对视频数据的编码压缩功能,我们可以方便地将视频数据编码为H264、H265或MJPEG格式。其中包含了encode、encode_file、get_img、close等几种方法,使用方法如下:
### 配置编码模块
```
Encoder.encode(video_chn, encode_type , width, height, bits)
```
使用方法
```
#create encode object
encode = libsrcampy.Encoder()
#enable encode channel 0, solution: 1080p, format: H264
ret = encode.encode(0, 1, 1920, 1080)
```
### 输入图像文件
```
Encoder.encode_file(img)
```
使用方法
```
fin = open("output.img", "rb")
input_img = fin.read()
fin.close()
#input image data to encode
ret = encode.encode_file(input_img)
```
### 获取编码后的数据
```
Encoder.get_img()
```
### 关闭编码通道
```
Encoder.close()
```
### 完整代码示例
```
import sys, os, time
from hobot_vio import libsrcampy
def test_camera():
encode = libsrcampy.Encoder()
ret = encode.encode(0, 3, 1920, 1080)
fin = open("output.img", "rb")
input_img = fin.read()
fin.close()
ret = encode.encode_file(input_img)
fo = open("output.jpeg",'wb')
img = encode.get_img()
fo.write(img)
encode.close()
test_camera()
```
得到的output.jpeg文件如下:
可以看到我们已经正确地编码了图像文件并能够正常显示。此外我们还可以将cam得到的图像数据直接送入encoder中,编码并直接输出jpeg图像,具体代码如下所示:
```
import sys, os, time
from hobot_vio import libsrcampy
def test_camera():
cam = libsrcampy.Camera()
encode = libsrcampy.Encoder()
ret = cam.open_cam(0, -1, 30, 2560, 1440)
ret = encode.encode(0, 3, 2560, 1440)
time.sleep(1)
input_img = cam.get_img(2)
ret = encode.encode_file(input_img)
fo = open("output2.jpeg",'wb')
img = encode.get_img()
fo.write(img)
fo.close()
encode.close()
test_camera()
```
## 图像显示
使用Display对象,我们可以快速地将cam采集到的图像显示到hdmi显示器上。Display对象实现了视频显示功能,可以将图像数据通过HDMI接口输出到显示器,该对象包含display、set_img、set_graph_rect、set_graph_word、close等方法。我们这里主要使用到的是display和bind接口。
### display
显示模块初始化,并配置显示参数
```
Display.display(chn, width, height, vot_intf, vot_out_mode)
```
### bind接口
该接口可以把Camera、Encoder、Decoder、Display模块的输出与输入数据流进行绑定,绑定后无需用户操作,数据可在绑定模块之间自动流转。例如,绑定 Camera 和 Display 后,摄像头数据会自动通过显示模块输出到显示屏上,无需调用额外接口。
```
libsrcampy.bind(src, dst)
```
### 完整代码示例
```
import sys, os, time
from hobot_vio import libsrcampy
def test_camera():
cam = libsrcampy.Camera()
disp = libsrcampy.Display()
ret = cam.open_cam(0, -1, 30, 1920, 1080)
disp.display(0, 1920, 1080)
time.sleep(1)
libsrcampy.bind(cam, disp)
while(1):
print(1)
test_camera()
```
# 总结
通过本文的学习,可以看到使用 RDK X3 进行图像与视频开发非常高效。其内置的 libsrcampy 库提供了丰富的功能,包括摄像头图像采集、图像编码、视频显示等,且支持 Python 编程语言,降低了开发门槛,非常适合快速上手和项目实践。
下一篇文章中,我将进一步学习并探索 RDK X3 在 神经网络推理 方面的能力!
-
发表了主题帖:
AI挑战营(进阶):7. 模型修改及应用场景测试
本帖最后由 waterman 于 2025-1-11 13:34 编辑
在之前的实验中,我们基于 RetinaFace 和 FaceNet 完成了图片级别的人脸检测与人脸识别测试,取得了良好的效果。接下来,我们将把 FaceNet 替换为 ArcFace,并将测试对象从图片扩展到视频场景中,实现在监控场景中,对大规模人脸视频中的期望目标人脸检测与识别。
# arcface
通过观察 ONNX 模型文件,可以得出两者的主要区别:
1. 输入大小:
- FaceNet 的输入为 160×160。
- ArcFace 的输入为 112×112。
2. 输出特征维度:
- FaceNet 输出 128 维特征向量。
- ArcFace 输出 512 维特征向量。
由于两者在输入输出上的差异,我们需要对原有的测试代码和推理逻辑进行相应的调整。
## 模型转换
首先,将 ArcFace 的 ONNX 模型转换为 RKNN 格式,并进行测试。以下是完整的 Python 脚本:
```python
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')
rknn.config(mean_values=[[127.5, 127.5, 127.5]], std_values=[[128, 128, 128]], target_platform='rv1103')
print('done')
# Load model
print('--> Loading model')
ret = rknn.load_onnx(model='../../model_zoo/w600k_mbf_modefied.onnx')
# 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/3_001.png")
image_1 = image_1.resize((112,112), Image.BICUBIC)
img1 = np.asarray(image_1, np.uint8)
img1 = img1[:,:,:3]
img1 = np.expand_dims(img1,axis=0)
outputs1 = np.array(rknn.inference(data_format='nhwc', inputs=[img1])[0])
outputs1 = preprocessing.normalize(outputs1, norm='l2')
image_2 = Image.open("./dataset/facenet/3_002.png")
image_2 = image_2.resize((112,112), Image.BICUBIC)
img2 = np.asarray(image_2, np.uint8)
img2 = img2[:,:,:3]
img2 = np.expand_dims(img2,axis=0)
outputs2 = np.array(rknn.inference(data_format='nhwc', inputs=[img2])[0])
outputs2 = preprocessing.normalize(outputs2, norm='l2')
# Get distance
distance1 = np.linalg.norm(outputs2 - outputs1, axis=1)
distance2 = np.dot(outputs2,outputs1.reshape((512,1)))
print("distance:", distance1, distance2)
# Export rknn model
print('--> Export rknn model')
ret = rknn.export_rknn("arcface.rknn")
if ret != 0:
print('Export rknn model failed!')
exit(ret)
print('done')
rknn.release()
```
运行脚本后,我们可以在控制台中看到推理测试结果,同时生成了 ArcFace 的 RKNN 模型文件 arcface.rknn。
## 嵌入式端代码修改
将 ArcFace 模型部署到嵌入式平台上,需对原有代码进行以下改动:
1. 加载新模型
修改模型路径:
```
const char *facenet_model_path = "./model/arcface.rknn";
```
2. 调整输入尺寸
修改输入图像大小:因为arcface的输入为112*112,而facenet的输入为160*160
```
int facenet_width = 112;
int facenet_height = 112;
```
3. 更改输出特征维度
此外,与facenet输出的128维的向量不同,arcface的输出为512维的向量,因此也需要进行相应的修改
main.cc
```
float* reference1_out_fp32 = (float*)malloc(sizeof(float) * 512);
float* reference2_out_fp32 = (float*)malloc(sizeof(float) * 512);
float* out_fp32 = (float*)malloc(sizeof(float) * 512);
```
facenet.cc
```
void output_normalization(rknn_app_context_t* app_ctx,uint8_t *output, float *out_fp32)
{
int32_t zp = app_ctx->output_attrs->zp;
float scale = app_ctx->output_attrs->scale;
for(int i = 0; i < 512; i ++)
out_fp32 = deqnt_affine_to_f32(output,zp,scale);
float sum = 0;
for(int i = 0; i < 512; i++)
sum += out_fp32 * out_fp32;
float norm = sqrt(sum);
for(int i = 0; i < 512; i++)
out_fp32 /= norm;
}
```
4. 距离计算函数
ArcFace 使用 512 维特征向量,并采用余弦距离衡量相似性,其取值范围是 [-1, 1],其中1表示方向完全相同,-1表示方向完全相反,0表示两向量正交。因此我们还需要对facenet.cc中计算人脸向量的函数进行修改
```
float get_duclidean_distance(float *output1,float *output2)
{
float norm = 0;
for(int i = 0;i < 512;i++)
{
norm += output1 * output2;
}
return norm;
}
```
5. 修改识别阈值
在余弦相似度中,数值越高说明两个人脸越相近,因此我们将阈值从 1.0 调整为 0.3,以适配余弦距离的范围([-1, 1])。
# 测试
此外,对程序进行适当的修改,将检测到的人脸框为红色,并添加了帧率显示。
[localvideo]103d2de55b775e35f835d221cf8afea3[/localvideo]
测试中,帧率可以达到10fps左右,但是检测的目标框还会存在一定抖动,会对人脸识别结果产生一定的影响。经测试,当视频中仅有一个人脸时,可以简单的加入一个均值滤波,对识别框进行平滑,能够降低视频中识别框的抖动,且可以一定程度上提高人脸检测精度。但是当视频中有多个人脸时,处理起来较为复杂,查到的资料可以采用追踪算法或者卡尔曼滤波来进行平滑。目前可能来不及了,之后有时间了学习学习,看看能不能加进来。
在实际测试中,我们的目标人脸检测与识别系统在嵌入式平台上实现了约 10 FPS 的处理速度,基本满足实时性的需求。然而,在测试过程中也发现了一些问题,主要表现在目标检测框的抖动上。这种抖动不仅影响了视频的视觉效果,也对人脸识别结果的稳定性产生了一定的干扰。
# 优化尝试
## 单人场景中的均值滤波
在简单场景中(如视频中仅包含一个人脸时),我们尝试加入 均值滤波 对检测框进行平滑处理。其核心思想是对检测框的坐标进行短时间内的平均,从而降低抖动。以下为均值滤波的实现思路:
均值滤波实现步骤:
1. 定义一个固定大小的滑动窗口(如最近 5 帧)。
2. 对检测框的四个点坐标进行平滑计算:
```
sX= sum(frames_Sx) / frames_sX_len
sY= sum(frames_Sy) / frames_sY_len
eX= sum(frames_eX) / frames_eX_len
eY= sum(frames_eY) / frames_eY_len
```
3. 更新检测框为平滑后的结果,并用于后续帧的检测与识别。
测试效果:
经测试,当视频中仅有一个人脸时,可以简单的加入一个均值滤波,对识别框进行平滑,能够降低视频中识别框的抖动,且可以一定程度上提高人脸检测精度。同时,这种方法计算简单,对系统性能几乎没有额外开销,是一种有效的短期优化方案。
## 复杂场景中的问题与改进方向
然而,当视频中出现多个目标人脸时,简单的均值滤波会出现问题,主要表现为:
1. 目标混淆:当多个人脸相邻或交错时,均值滤波可能会导致不同目标的检测框相互影响。
2. 无法跟踪:无法区分视频中多个目标的身份,也无法对每个目标进行独立平滑。
改进方向:
针对多目标的场景,现有文献中推荐了以下方法:
1. 目标追踪算法
2. 卡尔曼滤波
目前,由于时间有限,我暂时未能实现复杂场景下的优化方法,之后有时间了学习学习,看看能不能加进来。
- 2025-01-07
-
回复了主题帖:
《深度学习的数学——使用Python语言》6. 线性代数
wangerxian 发表于 2025-1-7 09:07
机器学习的数据处理基本上都是处理的代数
所以补充一些线性代数的知识还是很有必要的
- 2025-01-06
-
发表了主题帖:
《深度学习的数学——使用Python语言》6. 线性代数
线性代数在深度学习中是解决多维数学对象计算问题的核心工具。这些数学对象包括标量、向量、矩阵和张量,借助它们可以高效地对数据进行操作和建模。以下将详细介绍这些数学对象及其在深度学习中的典型用途。
# 数学对象概述
## 标量
标量是最简单的数学对象,通常表示单个数值变量,是构成高阶数据结构的基础。例如:
```python
import numpy as np
x = 42 # 标量
print(x)
```
## 向量
向量由标量组成,表示为一维数组。根据表示方式不同,可以分为行向量和列向量。在深度学习中,向量常用于描述样本的多个特征。例如:
```python
import numpy as np
x = np.array([1,2,3])
print(x)
print(x.shape)
print(x.reshape((3,1)))
```
在深度学习和机器学习中,向量的各个成员之间通常用于描述样本不同的特征。模型可以通过输入的这些特征量得到有用的输出,如分类标签或者是回归值。
## 矩阵
矩阵是由数字构成的二维数组。在矩阵中,各个元素所处的行数和列数为元素的下标。在python语言中,数组的下标是从0开始的,而在matlab语言中,数组的下标从1开始,不同语言的特点不同,需要注意。此外,在矩阵中,元素的位置由行和列索引确定。
```python
import numpy as np
A = np.arange(12).reshape((3,4))
print(A)
print(A[1,2])
print(A[0,0])
```
此外,我们可以看到除开头可结尾的[]外,每一行的数据都由一组[]包括着,这说明numpy将二维数组当作行向量来对待,其中每一个元素也为一个行向量。
## 张量
张量是更高维的数组,超越矩阵的二维结构。例如,在计算机视觉中,RGB图像可以表示为形状为c*h*w的三维张量,其中c表示通道数,h和w分别表示图像的高度和宽度。加上批量(batch size)维度后,形成四维张量。然而,不同框架可能对张量的维度顺序有不同约定,例如 ONNX 通常使用h*w*c。示例代码如下:
```python
import numpy as np
t = np.arange(36).reshape(3,3,4)
print(t)
```
在计算机视觉模型推理阶段,尽管我们通常输入的是一张三维图片,但模型的输入通常还需要一个最高维度的批量大小(通常默认为1)。那么,如何对输入进行转换,将其扩展为四维数据呢?以下介绍两种方法,通过增加一个大小为1的维度来实现这一转换。
```python
t = np.arange(36).reshape(3,3,4)
w = t[np.newaxis,:,:,:]
w2 = np.expand_dims(t,axis=0)
print(w.shape)
print(w2.shape)
print(w)
print(w2)
```
# 代数运算
本节主要设计向量和矩阵的计算,
## 数组运算
标量运算中的加减乘除,以及指数等初等运算都适用于数组运算。当两个运算数组形状相同时,可以简单理解为对应位置上的元素进行运算。
```python
import numpy as np
a = np.array([[1,2,3],[4,5,6]])
b = np.array([[7,8,9],[10,11,12]])
print(a+b)
print(a-b)
print(a*b)
print(a/b)
```
当两个数组形状不匹配时,就会涉及到NumPy的广播机制。举个例子,假设有三个人,第一个人分别拥有1个梨、2个苹果、3个香蕉和4个橘子;第二个人各类水果的数量是第一个人的两倍,第三个人则是第一个人的三倍。我们可以利用NumPy的广播机制,轻松地表示出每个人每种水果的拥有量。
```python
import numpy as np
a = np.array([1,2,3,4])
b = np.array([[1],[2],[3]])
print(a*b)
```
其中行为4种水果,列为3个人。
## 向量运算
### 单位向量
将一个向量中的各个元素除以向量的模长,我们就能得到一个方向不变且模值为1的单位向量。
```python
import numpy as np
v = np.array([2,-4,3])
print(v / np.sqrt((v*v).sum()))
print(v / np.sqrt(np.dot(v,v)))
```
我们既可以使用各元素平方求和开根号的方式来求得向量的模长,也可以使用内积的方式来得到。
### 内积
向量内积是最基础的向量运算,其计算方法如下
向量内积的结果是一个标量。向量内积满足交换律和分配律,但是不满足结合律。且内积为0的两个向量相互正交,它们之间的夹角为90°。
### 外积
与向量内积不同,两个向量的外积得到的是一个矩阵。个人理解,可以用前面的广播机制来理解它。向量的外积不要求两个向量具有相同数量的元素
```python
a = np.array([1,2,3,4])
b = np.array([5,6,7])
print(np.outer(a,b))
```
### 叉积
叉积是定义在三维空间中的,两个向量叉积的结果是一个新的向量,这个向量垂直于这两个向量构成的平面。新向量的方向服从右手法则。
```python
import numpy as np
a = np.array([1,0,0])
b = np.array([0,1,0])
c = np.array([1,1,0])
print(np.cross(a,b))
print(np.cross(a,c))
```
# 总结
线性代数是深度学习的基础,其数学对象和运算在数据表示和模型计算中无处不在。掌握这些基本概念和操作,将为理解和优化深度学习模型提供有力支持。
- 2025-01-04
-
发表了主题帖:
《深度学习的数学——使用Python语言》5. 数据的相关性
不同的数据集之间可能会存在关联,如一方变大,另一方也随之变大或者变小。例如,大多数图像的像素点是高度相关的。图像上的一个像素与其周围的像素点在很大概率上会类似。而”相关性“十分适合用于描述这种关系,描述相关性的统计量能够清晰地体现出数据特征之间有何关联。
在传统的机器学习领域,我们并不希望不同特征之间有很强的关联性,因为这样不但难以提供新的信息,还会对模型产生干扰。因此,在传统的机器学习领域,通常需要对获得的数据进行特征工程,其中一项目的就是去吃相关性的影响。然而在深度学习领域,由于深度网络本身会为输入学习新的特征表示,因此并不严格要求对输入去除相关性。
无论是传统的机器学习还是深度神经网络,作为描述和探索数据集的一环,对特征间相关性的分析都值得研究和理解。下面主要介绍两种相关性。
# 皮尔森相关性
皮尔森相关系数是位于-1到1的实数值r。它描述的是两个特征之间线性相关的强度。注意这里是线性相关性,如果两个特征之间存在非线性相关关系,则皮尔森相关系数不能够很好地体现出来。
r越接近1,则两个特征越正线性相关;r越接近-1,则两个特征越负线性相关;如果r接近0,则两个特征之间没有明显的线性关系。
皮尔森相关系数的计算公式如下:
其中X和Y是两个样本,E(.)为期望。对应的python计算代码如下:
```
import numpy as np
def pearson(x,y):
exy = (x*y).mean()
ex = x.mean()
ey = y.mean()
exx = (x*x).mean()
ex2 = x.mean()**2
eyy = (y*y).mean()
ey2 = y.mean()**2
return (exy - ex*ey) / ( np.sqrt(exx - ex2) * np.sqrt(eyy - ey2) )
```
下面我们用python实验来进一步学习,首先生成三组数据
```
import numpy as np
import matplotlib.pylab as plt
np.random.seed(8675309)
N = 100
x = np.linspace(0,1,N) + (np.random.random(N)-0.5)
y = np.random.random(N)*x
z = -0.1*np.random.random(N)*x
```
其中x为在0-1线段上叠加了噪声的结果,y和z分别为跟踪x的数据,其中y为在x的基础上叠加了在区间[0,1)的噪声,z为在y的基础上乘了一个负的系数。绘制图像如下:
之后我们调用前面定义的pearson函数和numpy中自带的计算相关性的函数来计算皮尔森相关系数:
```
print("pearson(x,y):", pearson(x,y))
print("pearson(x,z):", pearson(x,z))
print("pearson(y,z):", pearson(y,z))
print()
d = np.vstack((x,y,z))
print(np.corrcoef(d))
print()
```
结果如下:
从中可以看到上面定义的函数计算基本上是准确的,且x与y之间存在正相关性;x与z之间则存在较强的负相关性;相应地,y与z之间也存在一定的负相关性。
之后我们对比一下一张图片中不同行之间的相关性
```
from sklearn.datasets import load_sample_image
china = load_sample_image('china.jpg')
a = china[230,:,1].astype("float64")
b = china[231,:,1].astype("float64")
c = china[400,:,1].astype("float64")
d = np.random.random(640)
print("china(a,b): ", pearson(a,b))
print("china(a,c): ", pearson(a,c))
print("china(a,d): ", pearson(a,d))
print()
```
输出结果如下:
对比结果可以发现,图像的第230行与231行具有较强的正相关性,但是230行与400行之间的相关性较弱。这也证明了我们前面的判断。
# 斯皮尔曼相关性
皮尔森相关性衡量的数据之间的线性关系,而斯皮尔曼相关性则是衡量的输入之间是否存在单调的关联性。斯皮尔曼相关系数是及v特征取值的次序而非特征取值本身进行计算的,范围为[-1,1]。当两个特征中都不包含重复值时,其定义如下:
其中,n为样本容量,d表示随机变量X和Y中对应元素的次序差值。对应的计算代码如下:
```
# spearman
def spearman(x,y):
n = len(x)
t = x[np.argsort(x)]
rx = []
for i in range(n):
rx.append(np.where(x == t)[0][0])
rx = np.array(rx, dtype="float64")
t = y[np.argsort(y)]
ry = []
for i in range(n):
ry.append(np.where(y == t)[0][0])
ry = np.array(ry, dtype="float64")
d = rx - ry
return 1.0 - (6.0/(n*(n*n-1)))*(d**2).sum()
```
我们首先调用np.argsort(x)对x排序为t,之后根据每个x判断其在t中的排序位置并将其存入rx。同理,对y做类似的处理,最后就是根据公式进行计算得到斯皮尔曼相关系数。
下面我们针对一条斜线和sigmoid函数分别计算他们之间的皮尔森和斯皮尔曼相关性来看看两者有什么区别。
```
a = np.linspace(-20,20,1000)
b = 1.0 / (1.0 + np.exp(-a))
print(pearson(a,b))
print(spearman(a,b))
```
输出结果如下:
结果表明皮尔森相关系数约为0.9,斯皮尔曼相关系数为1.0。这是因为随着a的增加,b也会增加,但是增加的幅度未必相同。而斯皮尔曼相关系数则能很好地捕捉这种非线性的相关关系,从而能够更好地帮助我们判断哪些特征应该丢弃,哪些特征应该保留。
- 2025-01-03
-
发表了主题帖:
《深度学习的数学——使用Python语言》4. 缺失数据处理
# 缺失值检测
缺失数据就是我们没有的数据。如果数据集是由向量表示的特征组成,那么缺失值可能表现为某些样本的一个或多个特征因为某些原因而没有测量的值。通常情况下,缺失值由特殊的编码方式。如果正常值都是正数,那么缺失值可能被标记为-1;如果正常值是字符串,那么缺失值可能是空的字符串;如果正常值是浮点数,那么缺失值可能是NaN。
在python中,我们能够通过使用np.isnan来判断数据是否为NaN:
```
>>> import numpy as np
>>> a = np.arange(10, dtype="float64")
>>> a[3] = np.nan
>>> np.isnan(a[3])
True
>>> a[3] == np.nan
False
>>> a[3] is np.nan
False
>>> np.isnan(a)
array([False, False, False, True, False, False, False, False, False,
False])
```
可以看到,只能用np.isnan来判断,“==”和“is”进行比较是无效的。
# 缺失值处理
假定我们已经知道数据中存在缺失值,那么我们就需要寻找一些手段来对其进行处理,从而避免对模型训练产生负面影响。
首先我们先生成一组数据,其中包含1000个样本,且每个样本都具有四维特征:
```
>>> import numpy as np
>>> N=1000
>>> np.random.seed(100)
>>> x = np.zeros((N,4))
>>> x[:,0] = 5*np.random.random(N)
>>> x[:,1] = np.random.normal(10,1,size=N)
>>> x[:,2] = 3*np.random.beta(5,2,N)
>>> x[:,3] = 0.3*np.random.lognormal(size=N)
```
其中我们固定随机种子,从而确保结果的可复现性,并且1-4维的数据分别来自均匀分布、正态分布、贝塔分布和对数正态分布。之后我们向其中随机添加5%的缺失值:
```
>>> i = np.random.randint(0,N,size=int(0.05*N))
>>> x[i,0] = np.nan
>>> i = np.random.randint(0,N,size=int(0.05*N))
>>> x[i,1] = np.nan
>>> i = np.random.randint(0,N,size=int(0.05*N))
>>> x[i,2] = np.nan
>>> i = np.random.randint(0,N,size=int(0.05*N))
>>> x[i,3] = np.nan
```
那么我们应该如何来处理这些缺失值呢?下面给出两种方法。
### 1. 直接丢弃
如果我们拥有的数据集较为庞大,而缺失值的占比很小,且分布较为均匀,那么直接丢弃这些缺失值也是可以的。但是如果缺失值与某个类别存在相关性,直接丢掉这些样本可能在某种程度上会导致数据集有偏,进而影响模型的准确性。
### 2. 均值/中位数填充
我们还可以参考剔除缺失值后数据的分布形态,选择使用数据均值或者中位数来对缺失值进行填充。我们首先剔除数据中的缺失值,并使用matplotlib.pyplot中的boxplot函数来查看剔除缺失值后数据的箱形图:
```
import matplotlib.pyplot as plt
# 剔除缺失值
def remove_nan_columnwise(data):
"""从每一列中剔除NaN值"""
return [data[~np.isnan(data[:, col]), col] for col in range(data.shape[1])]
# 应用函数到数据
cleaned_data = remove_nan_columnwise(x)
# 绘制箱形图
plt.figure(figsize=(10, 6))
plt.boxplot(cleaned_data, labels=['Column 1', 'Column 2', 'Column 3', 'Column 4'])
plt.title('Box Plot of Data After Removing Missing Values')
plt.ylabel('Value')
plt.show()
```
从图中可以看出,第1、2维的数据偏差较小,且箱形框较为对称;第3维的数据偏上,第4维的数据偏下。因此我们使用均值来替代第1、2维的缺失数据,使用中位数来替换第3、4维的数据。
```
i = np.where(np.isnan(x[:,0]) == False)
m = np.mean(x[i,0])
i = np.where(np.isnan(x[:,0]) == True)
x[i,0] = m
i = np.where(np.isnan(x[:,1]) == False)
m = np.mean(x[i,1])
i = np.where(np.isnan(x[:,1]) == True)
x[i,1] = m
i = np.where(np.isnan(x[:,2]) == False)
m = np.median(x[i,2])
i = np.where(np.isnan(x[:,2]) == True)
x[i,2] = m
i = np.where(np.isnan(x[:,3]) == False)
m = np.median(x[i,3])
i = np.where(np.isnan(x[:,3]) == True)
x[i,3] = m
```
关于何时用中位数替代缺失值,何时用均值替代缺失值,书中给出的指导方案为:接近正态分布用均值进行替换,不接近正态分布则用中位数进行替换。
参考GPT给出的详细参考如下:
#### 使用中位数填充的情况
1. 当数据是偏斜分布或存在异常值时:如果数据不是正态分布,而是偏向一侧(即有长尾),或者存在极端值(outliers),那么使用中位数可能是更好的选择。因为中位数不受极值影响,可以更好地代表中心趋势。
2. 对于非正态分布的数据:如果变量不符合正态分布,中位数可能是一个更稳健的选择,因为它更能反映大多数观测值的集中位置。
3. 对于有序分类数据:当处理有序分类变量(例如,教育水平、满意度评分等)时,中位数也可能是更合适的选择。
4. 当需要保持数据的原始分布特征时:在某些情况下,使用中位数可以帮助保持数据集的原有分布特性,尤其是在存在明显偏斜的情况下。
#### 使用平均值填充的情况
1. 当数据接近正态分布且没有显著异常值时:如果数据大致呈正态分布,并且不存在明显的异常值,那么使用平均值填充可能是合适的,因为它会考虑所有观测值。
2. 对于数值型数据:平均值通常用于数值型数据,尤其是那些期望值具有实际意义的变量。
3. 当想要最小化总平方误差时:平均值是使得预测误差平方和最小化的估计量,因此在某些统计分析中,它可能是首选。
4. 对于回归分析中的连续变量:在进行回归分析时,如果变量是连续的,并且你认为缺失值应该反映总体的“平均水平”,那么平均值可能是一个合理的选择。
- 2024-12-31
-
回复了主题帖:
RDK X3测评:1.环境搭建
wangerxian 发表于 2024-12-31 09:44
这个开发板适合跑AI项目吗?
非常适合,官方文档说板卡具有5Tops 端侧推理算力,并且提供了很多AI算法例程,常用的图像分类、目标检测和人手关键点检测等都有
- 2024-12-30
-
发表了主题帖:
RDK X3测评:1.环境搭建
硬件简介
收到快递后共有三个小包装,其中分别是主板、摄像头模块和设备的外壳。
开箱后所有的设备如下,其中外壳是铝合金的,应该是为了散热方便,但是摄像头上并没有标注型号,只标注了像素是400W
官方放出的文档中给出的目前开发板支持的mipi摄像头如下:
因为只有第一个摄像头是400W,因此猜测摄像头是GC4663,之后我们也可以通过命令行来查看摄像头的参数信息。
主板特写照如下,RDK X3提供了网口、USB、摄像头、LCD、HDMI、40PIN等功能接口,可以说接口部分已经非常完善了。
其中的USB Type C 接口仅可作为供电接口,需要使用支持5V/3A的电源适配器为开发板供电。将电源适配器接入开发板后,开发板红色电源指示灯亮起,说明开发板供电正常。
开发板在下方还提供了串口接口,方便进行串口调试。下方右侧的micro-USB接口能够提供数据传输功能,可以使用adb登录设备,由于买的数据线还没到,所以暂时没法测试。此外,还提供一路千兆以太网接口,支持1000BASE-T、100BASE-T标准,默认采用静态IP模式, 3.0.0及以后系统的默认IP调整为192.168.127.10 。如需确认开发板IP地址,可通过串口登录设备,并用ifconfig命令进行查看 eth0网口的配置。
系统烧录
了解完基本的硬件后,为了能够将板卡使用起来,我们还需要烧录相应的镜像。RDK X3开发板采用Micro SD存储卡作为系统启动介质,推荐至少8GB容量的存储卡,以便满足Ubuntu系统、应用功能软件对存储空间的需求。
下载镜像
下载地址:https://archive.d-robotics.cc/downloads/os_images/rdk_x3/rdk_os_3.0.0-2024-08-31/release/
可以看到网站上提供了两个版本的img文件,分别为desktop和server版本,它们之间的具体区别如下:
desktop:带有桌面的Ubuntu系统,可以外接屏幕、鼠标操作
server:无桌面的Ubuntu系统,可以通过串口、网络远程连接操作
我们这里下载的是desktop版本,下载完成后得到的是xz压缩文件,需要将其解压为img文件。
值得注意的是,虽然官方推荐的SD卡容量至少为8GB,但是镜像解压后的大小就达到了7GB,因此建议采用更大容量的SD卡来安装镜像。
镜像烧录
文档中给出的是基于balenaEtcher的烧录方法,但是由于在使用balenaEtcher时总是会提示错误,因此我这里按照视频中使用Rufus进行镜像烧录。
安装工具:Rufus https://rufus.ie/downloads/#google_vignette
选择SD卡设备和相应的镜像文件
点击开始进行烧录,结束后将SD卡插入板卡卡槽即可。
值得注意的是
开发板不支持热插拔Micro SD存储卡,如发生误操作请重启开发板
禁止带电时拔插除USB、HDMI、网线之外的任何设备
需要选用正规品牌的USB Type C 口供电线,否则会出现供电异常,导致系统异常断电的问题
串口调试
在完成镜像烧录后,我们就能够使用串口连接设备并调试啦。给设备上电后,红色指示灯点亮代表硬件上电正常;绿色指示灯点亮代表系统启动中,熄灭或闪烁代表系统启动完成。
开发板提供一路调试串口,以实现串口登录、调试功能。电脑串口工具的参数配置如下:
波特率(Baud rate):921600
数据位(Data bits):8
奇偶校验(Parity):None
停止位(Stop bits):1
流控(Flow Control):无
设置如下图:
登录时使用到的用户名和密码均为root。
查询摄像头设备
由于提供的摄像头上并没有写明其型号,我们使用命令行来查看i2c上是否有相应的地址,从而判断摄像头属于什么型号
在命令行输入
i2cdetect -y -r 1
打印信息如下:
可以看到存在一个地址为0x29的设备,从文档中提供的支持摄像头的表格中可以看到,当前的摄像头是分辨率为400W的GC4663。
HDMI测试
由于我们烧录的是desktop版本的镜像,系统启动完成后,可以通过HDMI接口连接显示器,查看桌面系统界面。
至此,开发板的基本配置已完成,就可以开始使用开发板进行进一步的开发和测试啦。
- 2024-12-27
-
发表了主题帖:
《深度学习的数学——使用Python语言》3. 统计学基础
这本书最大的亮点并不是简单地讲解数学知识,而是将数学知识与深度学习紧密结合。按照 概率论 -> 线性代数 -> 微积分 的顺序,每一部分内容都融入了深度学习的实际应用,让我们更清楚这些数学知识在深度学习中的作用和具体用法。
在构建深度学习模型之前,我们必须充分理解并构建数据集。优质的数据集是训练出优质模型的前提。基础统计学知识可以帮助我们更好地理解和分析数据,从而提升数据集的质量,为模型打下扎实的基础。
# 数据类型
数据类型可以分为四种,每种类型的特点各不相同,但它们之间又存在一定联系。在处理实际问题时,了解数据类型可以避免不必要的运算错误。具体分类如下:
1. 定类数据:又称类别数据,指的是取值不同且没有排序关系的数据。
2. 定序数据:不同取值之间存在排序关系,但他们之间的差距没有数学含义。
3. 定距数据:数据之间有实际的数学含义。
4. 定比数据:数据之间的差距不仅有实际的数学含义,而且还存在真实的零点。
可以看到不同数据类型之间具有不同的特点,但是又存在一定的联系,书中已经给我们绘图说明了它们之间的联系与区别。
## 定距数据与定比数据的区别
针对定距数据和定比数据,它们之间的区别在于是否存在真实的零点。书中给出的例子如下
定距数据:华氏温度、摄氏温度。(数据差距有实际数学含义)
定比数据:身高、年龄、开尔文温度。(具有绝对零点)
关于绝对零点这个概念书中并没有给出详细的解释,我根据书中给出的例子的理解是:绝对零点意味着该点表示完全没有所测量的属性或量。换句话说,在定比尺度中,0 表示“无”或“不存在”,而不是简单的基准点或起始点。
## 定类数据的使用
在深度学习特别是分类问题中,我们常用到的数据类型时定类数据,比如说颜色分类:红色、黄色、绿色。为了把这些数据作为特征传输给深度网络,我们需要先对其进行预处理。由于定类数据没有排序关系,因此不能简单地将其赋值为123。而是需要将其转换为具有意义的定距数据,即独热编码。
在Keras等组件中,类别数据标签默认会被预处理为独热码,这样在计算损失函数时,网络的输出和编码后的类别标签都是向量,且计算起来会更方便。
之前在训练图片分类网络时就有这个疑惑,好奇为什么一定要将类别转换为独热编码的形式,在这本书的数据类型部分找到了答案。
# 描述性统计量
此外,为了理解数据以及了解数据的形态和分布,需要使用统计量来描述数据。
-
回复了主题帖:
【测评入围名单】RDK X3机器人开发套件
个人信息无误,确认可以完成测评分享计划