Toybrick

标题: 人工智能开发系列(4) 通过《人脸识别》学习从Keras到rknn [打印本页]

作者: jefferyzhang    时间: 2019-4-1 16:49
标题: 人工智能开发系列(4) 通过《人脸识别》学习从Keras到rknn
[attach]202[/attach]
本教程视频直播回看:
[attach]311[/attach]

前言:

本教学主要目的是学习如何一步一步用Keras自己编写一个模型,然后训练,一步一步最终部署到Toybrick嵌入式NPU上运行。之所以选则facenet,是因为他网络原理简单,loss函数需要手动编写(keras不提供,刚好可以学习如何训练),模型好坏可鉴别能力强,完全可以和原预训练模型进行对比,对于教学有非常好的帮助。学完后举一反三,可自行组建网络来玩。也建议有想学Keras或pyTorch的同学也可以找一个现成的论文模型,从头到尾按自己的理解写一遍代码,训练一次,部署一次。做完后你就可以毕业了。


下载地址:

Facenet源作者地址: https://github.com/davidsandberg/facenet.git
本教学源码:https://github.com/Jerzha/rknn_facenet.git
直播回看:http://www.yizhibo.com/l/GtN8pfmIJskQQ2kN.html


数据集:

CASIA-Webface:
  1. http://www.cbsr.ia.ac.cn/english/CASIA-WebFace-Database.html
复制代码

LFW :
  1. http://vis-www.cs.umass.edu/lfw/#download
复制代码

VGGFace:
  1. https://arxiv.org/abs/1710.08092v1
复制代码

应用中可以用一套数据集做训练,另外一套做评估。或者一套数据集分两部分,一部分训练,一部分评估。

训练数据的预处理:

* 下载的训练数据需要先用对齐算法(官方用mtcnn做align)清洗一遍,存成对齐后的数据集存放。
* 总的来说,最终部署时候如何使用该网络,你就应该如何送训练数据


编写模型(Keras篇):


1. 参看本教学源码的models目录,我们先抽象了一个BaseModel基类(继承自tf.keras.Model),用于扩展save_pb, save_rknn等等操作。建议大家也可以这么做,不要每次遇到一个新模型都从头写一遍冗余代码。这样我们最终这个模型的使用就变得异常简单,最终使用起来类似这样:

  1. model = InceptionResnetV1(...)  # 实例化模型
  2. model.compile(...)  # 初始化/编译模型
  3. model.fit(...)  # 训练模型
  4. model.evaluate(...) # 评估模型
  5. model.predict(...)  # 使用该模型推理
  6. model.load_weights(...) # 读取权重
  7. model.save_weights(...) # 存储权重
  8. model.load(...)  # 读取H5格式模型
  9. model.save(...) # 存储H5格式模型(配置+权重)
  10. model.save_pb(...) # 存储PB格式模型
  11. model.save_rknn(...) # 存储rknn格式模型
复制代码

2. 需要注意的是,本文写的keras.model是基于函数式的调用,而非子类调用,我们利用python的子父类结构抽象了这个函数式调用过程。如果用tf/keras官方教程写成子类式调用,很多的虚函数你需要去实现,会显得非常麻烦。

3. 使用tf.kears可以非常简洁快速的构建出想要的模型,参看本教学用代码models/inception_resnet_v1.py,可以看到我复写的整个模型可读性远远超过原作者tf写的一大坨乱七八糟的代码。(这个就是Keras/pyTorch的最大优势所在)


FaceNet介绍:

1. Facenet论文并没有提出新的模型结构,只是讲了一种通过128维embedding特征向量的欧式距离来比对人脸的方法,解决了有限的分类器来区分有限人脸的情况。并提出了一种新的triplet loss思想,利用三元组的比对,来让同一个人的特征距离 + 阈值(0.2) < 不同人的特征距离 。

2. 原作者的代码上用center loss(另外一篇论文提出的)替代了triplet loss,收敛效果更好。

3. 论文代码主要是用 Inception_Resnet_v1 和v2 来构建模型,这里的inception module是google提出的一种网络结构,而Resnet是微软研究院提出的一种网络结构,这两个都是当下非常流行的网络结构,大家看我的直播或者直接看论文来了解这两个优秀的基本网络结构。

4. 而其最基础的网络结构采用的是 conv2d - batch normalization - relu ,也是当下非常流行的结构,batch normalization很好的解决了网络层数过多时候的梯度爆炸或者消失的问题,也是很值得借鉴的。

5. 需要注意的是,Facenet的输入不需要归一化处理,但是需要align对齐,原作者采用的是mtcnn来对齐,实际部署上可以考虑其他的对齐方案,例如android自带的detect face接口,他是两点对齐;或者opencv的人脸检测方案(直播演示的就是这个)。 对齐后识别的准确度更加的精确。训练时候原作者把所有的数据集图像都用mtcnn对齐抠了人脸后送入网络训练的。而用于推理时候,还把图像进行了预白化(prewhiten)处理,让不同亮度下的人脸识别更加准确。


训练 (TripletLoss):

TripletLoss 由facenet论文提出,主要思想是每次训练输入三个参数和一个阈值:
1. auchor的照片(集)
2. 和auchor照片属于同一个人的positive照片(集)
3. 和auchor照片不属于一个人的negative照片(集)
4. 阈值alpha(论文里是0.2)

训练过程:
1. 计算 auchor、positive、negative 经过网络推理后得到的各个128维embedding向量
2. 计算 auchor 和 positive 的欧式距离 pos_dist
3. 计算 auchor 和 negative 的欧式距离 neg_dist
4. 得出基础损失(集) base_loss = pos_dist - neg_dist + alpha, 如果base_loss < 0,则base_loss = 0
5. 根据送入的batch_size得出损失(集)的均值loss

所以可以看到训练的目标是让不同人的照片的距离,大于同一个人的照片距离 + alpha

我们事例代码里每次取数据时候会取3倍于batch_size的数据,前1/3是auchor图集,中1/3是positive图集,最后1/3是negative图集,
计算损失时候就可以把数组分成三部分后分别计算dist,而label变量就不会用到了。


训练(softmax + center loss):
TBD
(工作原因,比较忙,未完待续)


作者: hjf515    时间: 2019-4-1 16:55
https://github.com/Jerzha/rknn_facenet.git  这个页面显示404,请问博主是不是有贴错了地址
作者: jefferyzhang    时间: 2019-4-1 17:05
hjf515 发表于 2019-4-1 16:55
https://github.com/Jerzha/rknn_facenet.git  这个页面显示404,请问博主是不是有贴错了地址 ...

还没完全更新完,我先放出来,你再刷新看看
作者: hjf515    时间: 2019-4-1 17:10
可以了,谢谢!
上周培训的mtcnn人脸检测,请问能尽快更新源码出来么?谢谢
作者: tof3d    时间: 2019-4-1 17:13
mtcnn 什么时候release,
作者: jefferyzhang    时间: 2019-4-1 17:14
hjf515 发表于 2019-4-1 17:10
可以了,谢谢!
上周培训的mtcnn人脸检测,请问能尽快更新源码出来么?谢谢 ...

那个哥么也在编帖子了,应该快了。
我这边主要注重教学,其他的一般都是注重转换现有模型
作者: vanilla    时间: 2019-4-2 10:36
直播回看的声音好小啊,是我耳机坏了嘛?
作者: jefferyzhang    时间: 2019-4-2 11:45
vanilla 发表于 2019-4-2 10:36
直播回看的声音好小啊,是我耳机坏了嘛?

一直播有bug,用他app看声音很大,用网页看声音很小
作者: 赵志阳    时间: 2019-6-13 17:09
facenet在3399pro上的例程源码什么时候能更新释放出来哟,等的好辛苦。。。
作者: jefferyzhang    时间: 2019-6-15 15:32
赵志阳 发表于 2019-6-13 17:09
facenet在3399pro上的例程源码什么时候能更新释放出来哟,等的好辛苦。。。

建议你别等,可以自己动手做起来。
因为我后面深入研究发现facenet的损失很难复现出论文的效果,真正使用上效果差强人意。
近几年有新的人脸识别网络ArcFace,那个是更好的做法。我打算有空转向研究ArcFace的损失函数,而非facenet的三元损失
作者: puyanan    时间: 2019-7-23 16:29
jefferyzhang 发表于 2019-6-15 15:32
建议你别等,可以自己动手做起来。
因为我后面深入研究发现facenet的损失很难复现出论文的效果,真正使用 ...

我想把facenet的模型pb模型转换成rknn模型,在加载tensorflow模型时出错。
测试代码如下:
-----------------------------------------------------------------------------
from rknn.api import RKNN
INPUT_SIZE = 160

if __name__ == '__main__':
    # Create RKNN object
    rknn = RKNN()

    # Config for Model Input PreProcess
    rknn.config(channel_mean_value='128 128 128 128', reorder_channel='0 1 2')
    print('config done')

    #load tensorflow model
    print('--> Loading model')
    rknn.load_tensorflow(tf_pb='./20180402-114759.pb',
                         inputs=['input'],
                         outputs=['embeddings'],
                         input_size_list=[[INPUT_SIZE, INPUT_SIZE, 3]])                  
    print('done')




出错信息是这样:
-----------------------------------------------------------------------------
pu@pu-vm:~/RKNN/demo/facenet$ python3 test1.py
config done
--> Loading model
W Not match node embeddings/Rsqrt Rsqrt
E Catch exception when loading tensorflow model: ./20180402-114759.pb!
T Traceback (most recent call last):
T   File "rknn/api/rknn_base.py", line 185, in rknn.api.rknn_base.RKNNBase.load_tensorflow
T   File "rknn/base/RKNNlib/converter/convert_tf.py", line 589, in rknn.base.RKNNlib.converter.convert_tf.convert_tf.match_paragraph_and_param
T   File "rknn/base/RKNNlib/converter/convert_tf.py", line 488, in rknn.base.RKNNlib.converter.convert_tf.convert_tf._tf_push_ready_node
T TypeError: 'NoneType' object is not iterable
done
pu@pu-vm:~/RKNN/demo/facenet$


inputs 和 outputs 节点名字,我用tensorboard打开看,就是“input”  和 “embeddings” 。
请问是哪里出了问题呢?
作者: jefferyzhang    时间: 2019-7-23 16:40
puyanan 发表于 2019-7-23 16:29
我想把facenet的模型pb模型转换成rknn模型,在加载tensorflow模型时出错。
测试代码如下:
------------- ...

rknn = RKNN(verbose=True)
打开verbose看下,不然log看不出什么,单独发一个贴问,不用回在这下头。
你回这帖子只有我看得到,真正懂的同事看不到问我也没办法。。。
作者: puyanan    时间: 2019-7-23 16:57
jefferyzhang 发表于 2019-7-23 16:40
rknn = RKNN(verbose=True)
打开verbose看下,不然log看不出什么,单独发一个贴问,不用回在这下头。
你 ...

好的。先谢过版主的回复
作者: puyanan    时间: 2019-7-23 17:56
jefferyzhang 发表于 2019-7-23 16:40
rknn = RKNN(verbose=True)
打开verbose看下,不然log看不出什么,单独发一个贴问,不用回在这下头。
你 ...

根据版主提示,看了详细的运行日志,猜测了出错原因("embeddings"既是输出node的名字,也是一个模块的名字,该模块内有多个中间节点,比如“embeddings/Rsqrt”、“embeddings/Sum”,    build过程中会一一比对这些节点,然后出错,似乎是因为“embeddings/Rsqrt” 与 “Rsqrt”不匹配)。理解的是否正确,还需要看后面能否顺利运行facenet。等我跑完了,就总结一下这个问题。(要我把facenet改成RK平台能跑的代码,还困难重重,水平有限 要是能够给出demo就多谢多谢啦




欢迎光临 Toybrick (http://t.rock-chips.com/) Powered by Discuz! X3.3