人脸识别技术总结

专业术语

FPS

FPS: 每秒传输帧数(Frames Per Second)

FPS也可以理解为我们常说的“刷新率(单位为Hz)”,例如我们常在CS游戏里说的“FPS值”。我们在装机选购显卡和显示器的时候,都会注意到“刷新率”。一般我们设置缺省刷新率都在75Hz(即75帧/秒)以上。例如:75Hz的刷新率刷也就是指屏幕一秒内只扫描75次,即75帧/秒。而当刷新率太低时我们肉眼都能感觉到屏幕的闪烁,不连贯,对图像显示效果和视觉感观产生不好的影响。

电影以每秒24张画面的速度播放,也就是一秒钟内在屏幕上连续投射出24张静止画面。有关动画播放速度的单位是fps,其中的f就是英文单词Frame(画面、帧),p就是Per(每),s就是Second(秒)。用中文表达就是多少帧每秒,或每秒多少帧。电影是24fps,通常简称为24帧。

常见参数

媒体 FPS帧率
电影 24 fps
电视 (PAL) 25 fps
电视 (NTSC) 30 fps
CRT显示器 > 75 fps
液晶显示器 60 fps

影响FPS的因素

既然刷新率越快越好,为什么还要强调没必要追求太高的刷新率呢?其中原因是在显示“分辨率”不变的情况下,FPS越高,则对显卡的处理能力要求越高。电脑中所显示的画面,都是由显卡来进行输出的,因此屏幕上每个像素的填充都得由显卡来进行计算、输出。
当画面的分辨率是1024×768时,画面的刷新率要达到24帧/秒,那么显卡在一秒钟内需要处理的像素量就达到了“1024×768×24=18874368”。如果要求画面的刷新率达到50帧/秒,则数据量一下子提升到了“1024×768×50=39321600”。

FPS与分辨率、显卡处理能力的关系如下:
$$\text{处理能力} = \text{分辨率}*\text{刷新率}$$
这也就是为什么在玩游戏时,分辨率设置得越大,画面就越不流畅的原因了。分辨率建议设置为显示器分辨率,过高或过低可能造成画面变形。

显示器的刷新率

刷新频率:即屏幕刷新的速度。
刷新频率越低,图像闪烁和抖动的就越厉害,眼睛疲劳得就越快。采用70Hz以上的刷新频率时才能基本消除闪烁,显示器最好稳定工作在允许的最高频率下,一般是85Hz。
普通显示器的刷新频率在15.75kHz-95kHz间。15.75kHz是人体对显示器最低要求的刷新频率。在游戏过程中一般人能接受的最低FPS约为30Hz,基本流畅等级则需要>60Hz。

人脸识别专业术语

FPS

一些对人脸识别算法的描述如下:

人脸检测速度最快可达1500+ FPS!
人脸识别涉及到的运算量很大,我的电脑最快的识别速度也就是0.2S识别一张照片,算下来帧数就是5帧左右,最后的显示效果就给人一种卡卡的感觉,很不好。
人脸检测领域每年都会有大量算法被提出,拼精度当然很重要,但真正要做到实际的应用里,算法还必须要快。

视频其实是有很多张图片(帧)组成的,那么只要把图片中的人脸检测出来,那么视频就迎刃而解了。
帧数就是在1秒钟时间里传输的图片的量,也可以理解为图形处理器每秒钟能够刷新几次,通常用fps(Frames Per Second)表示。每一帧都是静止的图象,快速连续地显示帧便形成了运动的假象。高的帧率可以得到更流畅、更逼真的动画。帧数 (fps) 越高,所显示的动作就会越流畅。

检测、识别、验证

  1. 人脸检测
    检测图片里面有没有人脸
  2. 人脸识别
    首先检测出图片中的人脸,再与已知的人脸做对比,看这个人脸是张三的,还是李四的

涉及技术

多进程来处理大数据

人脸识别帧数太低的解决方法
python使用pyqt多线程来显示视频

算法设计

人脸检测领域每年都会有大量算法被提出,拼精度当然很重要,但真正要做到实际的应用里,算法还必须要快。算法设计时,追求复杂度低和适合硬件加速(比如适合GPU运算等)是算法加速的两大方向。

亚毫秒级人脸检测算法

视频其实是有很多张图片(帧)组成的,那么只要把图片中的人脸检测出来,那么视频就迎刃而解了。

Python 人脸检测方法总结

目标跟踪

利用目标跟踪来提高实时人脸识别处理速度

One/zero-shot learning

如此小的训练集不足以去训练一个稳健的神经网络。

迁移学习的两种极端形式是一次学习(one-shot learning)和零次学习(zero-shot learning),有时也被称为零数据学习(zero-data learning)。只有一个标注样本的迁移任务被称为一次学习;没有标注样本的迁移任务被称为零次学习。
只有在训练时使用了额外信息,零数据学习(Larochelle et al., 2008)和零次学习(Palatucci et al., 2009; Socher et al., 2013b)才是有可能的。

零次学习是迁移学习的一种特殊形式。同样的原理可以解释如何能执行多模态学习(multimodal learning),学习两种模态的表示,和一种模态中的观察结果$x$与另一种模态中的观察结果$y$组成的对($x,y$)之间的关系(通常是一个联合分布)(Srivastava and Salakhutdinov, 2012)。通过学习所有的三组参数(从$x$到它的表示、从$y$到它的表示,以及两个表示之间的关系),一个表中的概念被锚定在另一个表示中,反之亦然,从而可以有效地推广到新的对组。

deeplearningbook-chinese

打个广告,我们刚刚release了我们在cvpr 2018的用gcn做zero-shot learning的代码。比起之前所有zero-shot learning的结果都有巨大的提升,在ImageNet上面的部分指标接近accuracy x 2。
相比起之前的方法,我们做出的主要改变是不单单利用word embeddings,还把knowledge graph的信息通过graph convolution network利用起来。我们相信这是zero shot learning之后发展的方向。
paper
github

在Deepmind用LSTM作为controller的Neural Turing Machine解one-shot learning的paper中, 用到的一个dataset是Omniglot, 这个dataset有1600个class每个class只有少量数据。
这篇Paper一个好的insight是meta-learning分为一种慢的可以用于许多task并基于gradient descent的学习方式和一种快的学一个specific task学三四遍就学会基于augment memory的学习方式。而此Paper test自己model有多好的方法也很有趣,只测试未在训练集中出现过的class,并且看这个class在慢的学习过多少个episode之后快的学习新知识有多快。如下图所示。
如果要一句话总结的话应该是:“他山之石可以攻玉”吧。
paper

zero-shot learning是一种学习范式,这种方式学出来的模型能够预测没在训练集中出现过的类怎么做?考虑图片分类任务,训练类是“马”,“老虎”,“熊猫”,测试类是“马”,“老虎”,“熊猫”和“斑马”步骤取训练类和测试类的并集,对其中每一个类加一个描述,描述是<key,value>对的集合,举个例子,“马”这个类可以是{‘horse_like’:yes,’stripes’:no,’black&white’:no},“老虎”是{‘horse_like’:no,’stripes’:yes,’black&white’:no},“熊猫”是{‘horse_like’:no,’stripes’:no,’black&white’:yes},“斑马”则是{‘horse_like’:yes,’stripes’:yes,’black&white’:yes}根据“马”,“老虎”,“熊猫”的训练样本训练一个mapping,这个mapping能将图片feature映射到对应的描述(这个mapping理应关注到图片的动物有没有像马,有没有条纹,是不是黑白的,只有这样才能得到正确的描述,从而在训练集上得分)将测试图片通过2中的mapping,得到预测的描述,根据这个描述和类的一一对应关系得到预测的类(如果一张图是斑马,根据2的分析mapping应该会得到{‘horse_like’:yes,’stripes’:yes,’black&white’:yes},从而成功预测出斑马)zero-shot learning的好处?极大节省标注量,在传统分类任务中,假设每个类别需要500个样本,增加10个“斑马”就要标注5000个样本,而zero-shot只需要增加10个描述。

就像人的“触类旁通”。举个简单的例子。假设1,小明知道斑马是一个黑白条纹,外形像马动物。假设2,小明未见过斑马。小明见到斑马大概率能认出这个是斑马。
零样本学习就是这种:可见类学习模型+辅助性知识、边缘描述+判断识别的过程。

transfer learning

domain adaption

重新训练

人脸识别虽然看起来是一个分类任务,但是在很多实际应用中很难用普通的分类器来处理,原因之一是每个类(人)的训练样本很少。比如我们做一个门禁系统,每个员工可能只能提供几张照片,不可能让每个人提供几百几千张照片,而且员工总数可能也很少,小型企业一般只有几十或者几百人。当然我们可以把非公司的其他人的照片也拿过来作为训练数据和分类,如果分类为其他人,那么就不能通过门禁。

这样似乎可以解决数据不够的问题,但是还有一个更难处理的问题——重新训练。通常公司的人员是经常变动的,每次人员变动都要重新训练模型,这样的门禁系统估计没人会买。

那怎么办呢?有没有不需要训练的分类器?KNN这是排上用场了。KNN是不需要训练的分类器,或者说训练的过程这是把训练数据存储下来,这显然很适合人脸识别的这类应用。之前KNN被诟病的一个问题就是需要存储所有的训练数据,因此速度很慢而且需要大量空间。而在这个场景下都不是问题,门禁系统的训练数据是非常少的,因此两两计算距离(相似度)不是问题。

比如1-NN算法:来了一个人的照片,我们计算这张照片和库里的所有照片一一计算距离,选择最相似的照片作为它的分类。但是我们还要寻找一个距离阈值,如果小于这个阈值,我们就判断它们是同一个人,否则就认为是其他人。

因此问题的关键是计算两张人脸照片的距离(相似度)。一种方法是学习一个神经网络,输入是两张照片,输出是它们的距离或者相似度。这就是所谓Siamese Network——它的输入是两个物体,而不是分类器的一个物体;输出是它们的距离(区别),而不是分类概率。

Siamese是连体人的意思,那为什么叫Siamese网络呢?比如下图是一种Siamese网络。第一张图片经过一个深度神经网络最后变成一个特征向量,而第二张照片也经过一个完全一样的网络变成另外一个特征向量,最后把它们组合起来用另外一些网络层来判断它们是否同一个物体。因为问题的对称性,dist(x, y)==dist(y, x),所以我们通常要求这两个提取特征的网络是完全一样的,包括参数。因此这种网络结构是对称的,很像一个连体人(Siamese),故名为Siamese网络。

使用Siamese实现的学习算法是一种One-Shot Learning(当然还有其它的One-Shot Learning算法)。增加一个新的类别只需要提供一个训练数据就行了(当然多几个没有坏处,不过要改个名字叫Few-Shot Learning,当然不能太多,否则就是普通的Learning了)。一个训练样本,似乎不能再少了,但是还有Zero-Shot Learning。这似乎有点不可能,我没见过一个物体,怎么能认识它呢?

不见过不代表不认识,比如下图中的动物,大家都没有见过。但是我们是“认识”的,我们知道它不是马,也不是犀牛,这是一个新的我们每见过的动物。也许有人会反驳,我都不知道它的名字,怎么算”认识“它了?其实认识不认识和知不知道它的名字是两回事,名字只是一个符号而已,中国人叫它”独角兽”,美国人叫它”Unicorn”,泰国人叫它”ตัวยูนิคอน”。符号本身没有什么意义,只是作为语言用于交流才有意义。猴子不能用语言给物体起名字,不代表它们不能识别物体。

我们还是回到人脸识别的问题上来。前面介绍的Siamese网络是一种解决增加新类的方法。但是它有一个缺点——不能提前计算(或者说索引)。对于门禁系统,上面的方法问题不大。但是比如我们的摄像头拍摄到了小偷的人脸照片,我们需要从几百万甚至几千万张照片中搜索可能的嫌疑人,用神经网络两两计算是不可能的。如果有十张小偷的照片,我们的计算就需要十倍的时间。

另外一种我们即将介绍的方法——Face Embedding就能解决这个问题。Face Embedding指的是输入一张人脸照片,我们用一个深度神经网络提取特征,把它表示成一个d维空间的向量。而计算两张照片的距离就变成这两个向量的运算,比如最简单的计算欧式距离。

对于前面的嫌疑人搜索,我们利用提前把人类库中的所有照片都变成一个d维向量,然后搜索的时候就只需要计算欧式距离就行了,这样会快得多。而且欧式空间的点我们可以通过一些空间索引或者近似索引的方法来找近似的(approximate)近邻,从而实现Approximate KNN算法。

那怎么把一张人脸照片表示成一个向量呢?最常见的做法是用大量的人脸数据库(不只是你们公司员工的那一点点照片)训练一个人脸分类器,这通常是一个深度的神经网络。然后我们把softmax之前的某一个全连接隐层作为人脸的特征向量。这个向量可以认为是这张人脸照片最本质的特征。给定两张照片,我们可以分别计算它们的特征向量,然后简单的计算欧式距离(或者训练一个相似度模型,当然不能太复杂)。这里其实也是一种Transfer Learning,我们用其它人脸的数据学到了区分人脸的有效特征,然后把它用于区分公司员工的人脸。当然Transfer Learning要求两个数据集的Domain是匹配的,用欧洲的人脸数据库来Transfer到亚洲可能并不好用。

但是前面提到的方法有一个问题——全连接层的隐单元通常比较多(通常上千,太少了效果不好)。因此这个特征向量的维度太大,后续的计算很慢(当然和Siamese比是快的)。本文使用了Triple-based损失函数直接学习出一个128维向量,这里的神经网络的输出就是这个128维向量。和前面的方法不同,前面的方法的神经网络的输出是一个分类概率,表示这张照片属于某个人的可能性。而本文的输出就是一个128维的向量,并且这个向量使得同一个人的两张照片得到的向量的欧式距离很近;而不同人的两种照片的欧式距离很远。

人脸识别简介

识别方法

方法比较

方法 效果
opencv 最简单、速度最快的方法,但效果一般
基于dlib pip有发布的库,不用自己训练模型,可以直接拿来使用,比较方便,效果较好
mtcnn 需要训练自己的模型,github有开源的项目,效果较好
基于pytorch 需要训练自己的模型,github有开源的项目,效果最好

以上这些都是亲自试验过的人脸检测方法,RetinaFace(基于pytorch)方法效果最好,最终采用这种方法,进行了一些优化。

Package FPS (1080x1920) FPS (720x1280) FPS (540x960)
facenet-pytorch 12.97 20.32 25.50
facenet-pytorch (non-batched) 9.75 14.81 19.68
dlib 3.80 8.39 14.53
mtcnn 3.04 5.70 8.23

Comparison of face detection packages

黑科技

Teachable Machine

Teachable Machine


windows下使用vscode远程连接Linux服务器进行开发

远程连接Linux服务器

通过vscode中的Remote-SSH插件完成远程连接,这个插件是微软帝国亲自操刀编写的,让你通过SSH连接远程服务器作为本地的开发环境。

安装OpenSSH

该服务的作用是让你可以在终端使用ssh指令,Windows10通常自带OpenSSH不需要安装。

  • Windows10下检查是否已经安装OpenSSH
    # Win + X -> 选择Windows PoweShell(管理员)
    $Get-WindowsCapability -Online | ? Name -like 'OpenSSH*'
    # 如果电脑未安装OpenSSH,则State会显示NotPresent | 安装了则是Installed
    $Add-WindowsCapability -Online -Name OpenSSH.Client~~~~0.0.1.0
    
    # Win+R输入cmd进入终端
    $ssh

安装Remote-SSH

  1. 打开VSCode左侧的插件搜索: SSH
  2. 找到Remote-SSH直接安装即可

配置Remote-SSH

  1. 安装完成后,在VSCode左侧会出现一个远程资源管理器图标,点击
  2. 左侧右边的SSH Targets右上方点击管理图标会出现一个输入框,进入config配置文件: C:\Users\ACS1.ssh\config
  3. 在配置文件中设置服务器信息,输入HostName和User,保存以后左侧会出现对应机器名称
    Host Linux # 随意机器名
        Hostname 10.6.36.231 # 服务器IP地址
        User kh # 用户名
    Host Windows # 随意机器名
        Hostname 10.6.36.230 # 服务器IP地址
        User AstriACS # 用户名
  4. 更改设置: File -> Preferences -> Settings -> Extension -> Remote-SSH
    • 找到Show Login Terminal并勾选

连接服务器

  1. 在左侧出现的对应机器名称的右侧有一个按钮,点击
  2. 选择服务器的平台,并输入密码
  3. 成功连上服务器,点击有右侧的+号创建服务器的终端窗口,可以正常使用了!

进入服务器的文件夹

  1. 左侧点击就可以看到 Open Folder,打开以后可以看到服务器文件目录
  2. 直接在文件目录中选择文件进行编辑,实时同步到服务器上,这时候已经可以开始愉快的进行开发了,开发体验媲美本地开发!

Linux开发

查看系统信息

$cat /proc/version              # 查看Linux内核版本命令
$uname -a                       # 查看Linux内核版本命令

$lsb_release -a                 # 查看Linux系统版本的命令: Ubuntu 20.04.1 LTS
$cat /etc/issue

$echo $PATH                     # 查看PATH环境变量
$vim /etc/profile               # 添加PATH环境变量
# 在文档最后,添加: export PATH="/opt/xxx/bin:$PATH"
# 保存,退出: Esc : wq
$source /etc/profile

$cd ~                           # 用户的主目录: /home/name

查看GPU信息

$lspci | grep -i nvidia         # 查看NVIDIA GPU显卡信息
3b:00.0 3D controller: NVIDIA Corporation GV100GL [Tesla V100 PCIe 32GB] (rev a1)
af:00.0 3D controller: NVIDIA Corporation GV100GL [Tesla V100 PCIe 32GB] (rev a1)
d8:00.0 3D controller: NVIDIA Corporation GV100GL [Tesla V100 PCIe 32GB] (rev a1)
序号 3b:00.0, af:00.0, d8:00.0 是显卡的代号

$lspci -v -s 3b:00.0            # 查看指定显卡的详细信息
$nvidia-smi                     # 查看NVIDIA GPU显卡使用情况
$watch -n 10 nvidia-smi         # 周期性的输出显卡的使用情况, -n后边跟的是执行命令的周期,以s为单位
$sudo lshw -c video             # 查看显卡驱动
configuration: driver=nvidia latency=0

$apt search nvidia-cuda-toolkit # 查询目前可安装的CUDA Toolkit版本

Ubuntu20.04安装CUDA Toolkit

  1. 安装NIVIDIA GPU的驱动
    $sudo apt update            # 更新软件源
    $sudo apt upgrade           # 升级软件包
    $nvidia-smi                 # 查看支持的cuda版本, 如果无法查看,则说明尚未安装nvidia驱动
    • 查看CUDA不同的版本在linux平台上需要的驱动版本: here
  2. 下载对应平台架构的CUDA Toolkit x.0
    $uname -a                   # 查到Linux的架构是x86_64
    $nvcc -V                    # 查看系统当前安装的CUDA的版本: Build cuda_11.0_bu.TC445_37.28540450_0
    $conda activate facenet
  3. 不用修改facenet代码,测试GPU是否能够使用 (CUDA 11.0 + tensorflow-gpu 1.14.0)
    $pip install tensorflow-gpu==1.14.0 # CUDA 11.0 + tensorflow-gpu 1.14.0
  4. 修改facenet代码,测试GPU是否能够使用 (CUDA 11.0 + tensorflow-gpu 2.4.0)
    $pip install tensorflow-gpu==2.4.0 # CUDA 11.0 + tensorflow-gpu 2.4.0
    - 修改文件:
        1. facenet/src/align/align_dataset_mtcnn.py
        2. anaconda3/envs/facenet/lib/python3.6/site-packages/align/detect_face.py
    - 修改内容: 把所有的 tf. 替换为 tf.compat.v1.  (兼容性处理: tf.compat允许您编写在TensorFlow 1.x和2.x中均可使用的代码)
    
    - 修改文件: anaconda3/envs/facenet/lib/python3.6/site-packages/align/detect_face.py
    - 修改内容: 把第194行的 feed_in, dim = (inp, input_shape[-1].value) 替换为 feed_in, dim = (inp, input_shape[-1])
    $conda activate facenet
    $pip install numpy==1.16.2
    $pip install scipy==1.2.1
    $python src/align/align_dataset_mtcnn.py datasets/lfw datasets/haha --image_size 160 --margin 32 --gpu_memory_fraction 0.5
    Successfully opened dynamic library libcudart.so.11.0
    Creating networks and loading parameters
    tensorflow/core/platform/cpu_feature_guard.cc:142] This TensorFlow binary is optimized with oneAPI Deep Neural Network Library (oneDNN) to use the following CPU instructions in performance-critical operations:  AVX512F
    To enable them in other operations, rebuild TensorFlow with the appropriate compiler flags.
    tensorflow/compiler/jit/xla_gpu_device.cc:99] Not creating XLA devices, tf_xla_enable_xla_devices not set
    Successfully opened dynamic library libcuda.so.1
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 0 with properties: 
        pciBusID: 0000:3b:00.0 name: Tesla V100-PCIE-32GB computeCapability: 7.0
        coreClock: 1.38GHz coreCount: 80 deviceMemorySize: 31.75GiB deviceMemoryBandwidth: 836.37GiB/s
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 1 with properties: 
        pciBusID: 0000:af:00.0 name: Tesla V100-PCIE-32GB computeCapability: 7.0
        coreClock: 1.38GHz coreCount: 80 deviceMemorySize: 31.75GiB deviceMemoryBandwidth: 836.37GiB/s
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1720] Found device 2 with properties: 
        pciBusID: 0000:d8:00.0 name: Tesla V100-PCIE-32GB computeCapability: 7.0
        coreClock: 1.38GHz coreCount: 80 deviceMemorySize: 31.75GiB deviceMemoryBandwidth: 836.37GiB/s
    Successfully opened dynamic library libcudart.so.11.0
    Successfully opened dynamic library libcublas.so.11
    Successfully opened dynamic library libcublasLt.so.11
    Successfully opened dynamic library libcufft.so.10
    Successfully opened dynamic library libcurand.so.10
    Successfully opened dynamic library libcusolver.so.10
    Successfully opened dynamic library libcusparse.so.11
    tensorflow/stream_executor/platform/default/dso_loader.cc:60] Could not load dynamic library 'libcudnn.so.8'; dlerror: libcudnn.so.8: cannot open shared object file: No such file or directory; LD_LIBRARY_PATH: :/usr/local/cuda-11.0/lib64/:/usr/local/cuda-11.0/lib64/
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1757] Cannot dlopen some GPU libraries. Please make sure the missing libraries mentioned above are installed properly if you would like to use GPU. Follow the guide at https://www.tensorflow.org/install/gpu for how to download and setup the required libraries for your platform.
    Skipping registering GPU devices...
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1261] Device interconnect StreamExecutor with strength 1 edge matrix:
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1267]      0 1 2 
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1280] 0:   N Y Y 
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1280] 1:   Y N Y 
    tensorflow/core/common_runtime/gpu/gpu_device.cc:1280] 2:   Y Y N 
    WARNING:tensorflow:From /home/kh/anaconda3/envs/facenet/lib/python3.6/site-packages/align/detect_face.py:213: div (from tensorflow.python.ops.math_ops) is deprecated and will be removed in a future version.
    Instructions for updating:
    Deprecated in favor of operator or tf.math.divide.
    tensorflow/compiler/mlir/mlir_graph_optimization_pass.cc:196] None of the MLIR optimization passes are enabled (registered 0 passes)
    tensorflow/core/platform/profile_utils/cpu_utils.cc:112] CPU Frequency: 2200000000 Hz

远程连接Windows服务器

配置远程Windows服务器

环境配置

用ssh连接时,本地称为client,远程主机称为host。

Windows服务器安装 OpenSSH:
- 设置 -> 应用 -> 应用和功能 -> 管理可选功能
- 查看OpenSSH客户端是否已安装
- 如果没有,则在页面顶部选择“添加功能”: OpenSSH客户端 or OpenSSH 服务器

SSH服务器的初始配置

在搜索框中搜索powershell,单击“以管理员身份运行”,然后运行以下命令来启动 SSHD 服务:

$Start-Service sshd                                 # OPTIONAL but recommended:
$Set-Service -Name sshd -StartupType 'Automatic'    # Confirm the Firewall rule is configured. It should be created automatically by setup. 
$Get-NetFirewallRule -Name *ssh*                    # There should be a firewall rule named "OpenSSH-Server-In-TCP", which should be enabled
# If the firewall does not exist, create one
$New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22         

找到Windows服务器的的username: C:\Users\username的username(AstriACS)

配置本地电脑

启动PowerShell测试ssh连接

# $Ssh username@servername    # <username> 是服务器 <server> 上的用户账号
$Ssh AstriACS@10.6.36.230
# 到任何服务器的第一个连接都将生成类似以下内容的消息
The authenticity of host servername (10.00.00.001) cant be established.
ECDSA key fingerprint is SHA256:(<a large string>).
Are you sure you want to continue connecting (yes/no)?
# 回答 Yes 会将该服务器添加到本地系统的已知 ssh 主机列表中
# 系统此时会提示你输入密码(也就是远程主机的开机密码)。 作为安全预防措施,密码在键入的过程中不会显示
# 在连接后,你将看到类似于以下内容的命令shell提示符: 
astriacs@DESKTOP-U1MHT5M C:\Users\AstriACS> # domain\username@SERVERNAME C:\Users\username>

在vscode中连接

To connect to a remote host for the first time, follow these steps:
Verify you can connect to the SSH host by running the following command from a terminal / PowerShell window replacing user@hostname as appropriate.

$ssh user@hostname          # for Linux Server
$ssh user@domain@hostname   # Or for Windows when using a domain / AAD account 

打开vscode,打开Command Palette (F1/ctrl+shift+P) ,输入”Remote-SSH: Connect to Host…” 并选中,选择”+ Add New SSH Host…”,and use the same user@hostname as above,选择服务器的平台,并输入密码,可以正常使用了!


Detect Face PS Effect

Reference

Configure Environment

  1. Install PyTorch
    1. Install Anaconda.
    2. Install CUDA, if your machine has a CUDA-enabled GPU. (GPU的TyTorch做实验的时候有问题,所以我跳过,只用CPU的PyTorch做实验了)
    3. Install PyTorch
      # select: Stable(1.8.1), Windows, Conda, Python, CUDA None
      # open an Anaconda prompt via Start | Anaconda3 | Anaconda Prompt
      $conda update -n base -c defaults conda # update conda
      $conda create -n pytorch python=3.6
      $conda activate pytorch
      # 因为GPU的PyTorch出问题,所以跳过 $conda install pytorch==1.1.0 torchvision==0.3.0 cudatoolkit=9.0 -c pytorch # 下载很慢 # CUDA 9.0
      $conda install pytorch torchvision torchaudio cpuonly -c pytorch
    4. To ensure that PyTorch was installed correctly
      $python
      # import torch
      # x = torch.rand(5, 3)
      # print(x)
    5. To check if your GPU driver and CUDA is enabled and accessible by PyTorch
      $python
      # import torch
      # torch.cuda.is_available()
  2. Install packages
    1. 一键安装
      $conda activate pytorch
      $pip install -r requirements.txt
    2. 安装dlib
  3. Download model weights
    1. Download 64-bit ZIP of Wget for Windows (wget-1.21.1-1-win64.zip).
      1. 解压缩,并存放至硬盘合适的目录 (C:/Path/wget/)
      2. 配置环境变量: 系统属性 -> 高级系统设置 -> 高级 -> 环境变量 -> Path -> 编辑 -> 浏览 -> 选择存放 wget 程序文件的位置 -> 一路点击确定
      3. 查看是否应用成功: $wget -V
    2. Download model weights
      # 在根目录下打开Git命令行
      $bash weights/download_weights.sh # 大概2分钟
  4. Download validation dataset for evaluation which consisting of 500 original and 500 modified images.
    1. original folder: original faces
    2. modified folder: modified faces
    3. reference folder: the same faces in the modified folder, but those are before modification (original)

Run Program

Run Example

  1. Global classifer

    $python global_classifier.py --input_path examples/modified.jpg --model_path weights/global.pth
    # Probibility being modified by Photoshop FAL: 98.00%
  2. Local classifer

    $python local_detector.py --input_path examples/modified.jpg --model_path weights/local.pth --dest_folder out/

evaluate the dataset

# Run evaluation script. Model weights need to be downloaded.
$python eval.py --dataroot data --global_pth weights/global.pth --local_pth weights/local.pth --gpu_id 0
Accuracy AP PSNR Increase
93.9% 98.9% +2.66

Project Analysis

advantage

  1. 里面吸引我注意的是“自动生成脸上各种部位被拉伸过的假图”,这个东西可用用来做数据增强(data arguementation)。对于带有语义标签的图片,用这个能多出很多数据来,可能会像random crop一样好用。
    • 里面的“瘦脸”操作,类似于PS的“液化”可以用来做图片增强。以前的crop操作,是全图进行放大/缩小,现在用“瘦脸/液化”操作,就能放大/缩小图中的某几个目标。既能得到更多的图片,又不需要丢弃/填充边缘的某些像素。缺点是容易造成目标的扭曲。
  2. 利用光流场?还真是个好主意!思路新颖。
  3. 让我想到了国产动画《超级肥皂》,制毒剂和解毒剂都来自同一个商家,营销学的典范hhh(滑稽),以子之矛陷子之盾,何如?左右手互搏玩得真溜。技术发展,向可回退可延伸方向发展。

weakness

  1. 项目需要原始图片,文章中也提到了需要原图,都有原图了,谈何还原?如果有原图,就不需要机器说P了哪里吧?人眼就可以看出。如果人眼无法看出的,那PS在美颜的场景下的作用也就不存在了。猜测这个项目要有未经过ps的图训练。
  2. 这个训练方法只能针对特定的优化算法去还原,在此项目背景下特定为PS中的“图片液化方法”,所以它只能检测图片的拉伸和扭曲。不同人使用的美颜软件、P图软件以及方式和效果都不同,不能够泛化,需要所有这些软件、P图方式和效果的先验知识才行。
    • 例如,对于如果把图片P成黑白的P图方式和效果,无法还原为原来的颜色,现在AI的技术也只是利用GAN去猜测并合成的颜色,并不知道真实的颜色是什么样的。同样,对于图片全涂白,也无法还原,因为整个光流场改变或者说是“消失”,模型失去了推理条件。
    • 所以这个技术就针对液化有效,毕竟液化是改变了原有光照的位置,光照射在同一材质的连续曲面上,衰减或增强也应该是连续的,分析光在曲面上的强弱差异可能会有一些效果。
      破坏这个“光场”的方法估计不少。国内的美颜效果比如磨皮之类的,应该还没法还原,因为改变了整个光流场。
    • “然后,在真图和假图之间,计算出一个光流场 (Optical Flow Field) 。”—-引用自原文。光流法是cv常用的算法,用于计算两帧图像间的差异,并不是(也不能)用来检测图像是否被PS过(也不是用来检测图像是否是真实图像)。文章说了光流场变化了,明显就和真实图片的分布有了差距,在这上面训的,怎么泛化到真实数据集。
    • “训练完成之后,给网络输入一张假图,它便能自己预测光流场” —-引用自原文。用光流法进行数据标记,训练得到的网络可以预测“光流场”发生的变化。用这种方法进行训练,是有问题的。当你输入一张真实图片,那么这个网络也会用输出一个光流场。Adobe之前也出过卸妆的网络,输入化妆图片,输出素颜。但是输入素颜图片,它也能输出素颜的“卸妆”图来。请注意,这一次的研究没有和以前一样把代码开源到github上。
  3. 项目不能在Windows平台上运行,只能在Unix平台上运行。
  4. 最后,项目只开源了测试的部分,并没有开源训练数据集的部分,所以也不知道它训练使用的数据集处理方法和训练的方法,只能验证此项目,没办法扩展。

knowledge

  1. PS的真正原理:
    • 默认导入图片就自动上传服务器,并形成链接关系,任你如何PS都逃不过原图,简单粗暴。
    • 基于图像分析不可能“一秒还原”,况且真实的人的相貌就是各种各样的,怎么判断哪里是假的哪里是正常的?所以我认为ps还是有后门的,所谓的快速还原只是标记了曾经修改过的特征,然后“还原”的时候把以前隐藏起来的原始特征重现。
  2. 一秒你知道是什么概念么:
    • FLOPS,即每秒浮点运算次数(亦称每秒峰值速度 Floating-point operations per second)被用来评估电脑效能,尤其是在使用到大量浮点运算的科学计算领域中。每秒浮点运算次数所量测的实际上就是浮点运算器的执行速度。
      一个MFLOPS(megaFLOPS)= 每秒一百万(=10^6)次的浮点运算
      一个GFLOPS(gigaFLOPS)= 每秒十亿(=10^9)次的浮点运算
      一个TFLOPS(teraFLOPS)= 每秒一万亿(=10^12)次的浮点运算
      一个PFLOPS(petaFLOPS)= 每秒一千万亿(=10^15)次的浮点运算
      一个EFLOPS(exaFLOPS) = 每秒一百亿亿(=10^18)次的浮点运算
    • 一块显卡的计算能力是以TFlops为单位的,也就是10^12次单精度运算。网络结构的参数量在百万级别应该完全可以在主流显卡上一秒出结果。
    • 但是仍然和算法的时间复杂度还有效果的实现方法息息相关。
  3. 图片也换:
    • 液化滤镜可用来推、拉、旋转、反射、缩拢及膨胀影像的任何区域。您建立的扭曲可细微、可夸张,这使得「液化」指令成为一项功能强大的工具,可用来润饰影像以及建立艺术效果。「液化」滤镜适用于8 位元/色版或16 位元/色版的影像。
  4. 类似去马赛克理论上不可能,至少现在还是没法百分百还原的,主要是靠“猜”。只要片看的够多,那么有码也是无的,AI去码的原理类似。

Create a HTTP Server by Nodejs

Quick Start

配置环境和依赖

  1. 下载安装Nodejs
    • $node -v 查看是否已经安装
    • nodejs安装完自带npm(Nodejs平台的默认包管理工具)
  2. 安装 Yarn
    # 通过 npm 可以全局或本地安装依赖包,当本地安装时 npm 会根据 package.json 安装最新的依赖包,同时自动生成并更新 package-lock.json 文件,这就引发了一个问题:如果一个依赖包在 package.json 标记版本范围内发布了有问题的新版本,那么我们自己的项目也会跟着出问题。
    # 相比 npm,yarn 会严格按照自动生成的 yarn.lock 本地安装依赖包,只有增删依赖或者 package.json 标记版本发生不可兼容的变化时才会更新 yarn.lock,这就彻底杜绝了上述 npm 的问题。考虑到企业级 Web 服务器对稳定性的要求,yarn 是必要的。
    $npm i -g yarn
  3. 下载第三方模块
    # 新建一个文件夹,并在当前文件夹内创建一个js文件,比如app.js
    $npm init # 安装package.json文件
    $npm install express --save # 安装express库
  4. 安装Docker
    做一个 Web 服务器会有两种部署选择,要么是传统的包部署,要么是容器镜像部署
    后者较前者在编排上更方便,结合 Kubernetes 可以做到很好的伸缩,是当前发展的趋势

创建一个静态资源服务器

初始化工程

准备好了环境就可以开始编码了,先新建工程根目录,然后进入并初始化package.json与目录结构

$ mkdir node        # 新建工程根目录
$ cd node           # 进入工程根目录

$ yarn init -y      # 初始化 package.json
yarn init v1.22.4
success Saved package.json

$ mkdir src         # 新建 src 目录存放核心逻辑
$ mkdir public      # 新建 public 目录存放静态资源

$ tree -L 1         # 展示当前目录内容结构
.
├── package.json
├── public
└── src

Express

Express是Node.js服务端基础框架。发布于2010年,凭借出色的中间件机制在开源社区积累了大量的成熟模块,现在是OpenJS基金会At-Large级别项目。Express模块的贡献者热心于维护与更新,在全面的考量下Express是更稳健的选择。
几个常用模块:

模块名称 功能简介 Star Contributers Used by 最近提交时间
passport 认证登录 17.7k 33 385k 2020-06-10
connect-redis 会话存储 2.3k 51 26.3k 2020-07-10
helmet 网络安全 7.2k 25 136.4k 2020-07-11

在工程根目录执行以下命令安装Express

$ yarn add express  # 本地安装 Express
# ...
info Direct dependencies
└─ express@4.17.1
# ...

$ tree -L 1         # 展示当前目录内容结构
.
├── node_modules
├── package.json
├── public
├── src
└── yarn.lock

静态服务

现在可以开始写应用逻辑了,先做一个静态资源服务器,以public目录为静态资源目录:

// src/server.js
const express = require('express');
const { resolve } = require('path');
const { promisify } = require('util');

const server = express();
const port = parseInt(process.env.PORT || '9000');
const publicDir = resolve('public');

async function bootstrap() {
  server.use(express.static(publicDir));
  await promisify(server.listen.bind(server, port))();
  console.log(`> Started on port ${port}`);
}

bootstrap();
<!-- public/index.html -->
<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <h1>It works!</h1>
  </body>
</html>
$ tree -L 2 -I node_modules   # 展示除了node_modules之外的目录内容结构
.
├── package.json
├── public
│   └── index.html
├── src
│   └── server.js
└── yarn.lock

逻辑写好之后在package.json中设置启动脚本:

{
  "name": "00-static",
  "version": "1.0.0",
-  "main": "index.js",
+  "scripts": {
+    "start": "node src/server.js"
+  },
  "license": "MIT",
  "dependencies": {
    "express": "^4.17.1"
  }
}

refer

//1. 加载http模块
var http = require('http');

//2. 创建http服务器
// 参数: 请求的回调, 当有人访问服务器的时候,就会自动调用回调函数
var server = http.createServer(function (request, response) {
    console.log('有人访问了服务器')

    //回调数据
    response.write('Hello, My Love')
    response.end()
})

//3. 绑定端口
server.listen(3000, '127.0.0.1')

//4. 执行
console.log('执行了3000')

// http://localhost:5000
$node app.js # 运行服务器

Create Server by Express

Use Express Framework

1. 引入express模块
2. 创建express服务器
3. get, post请求中:
    - 参数一: 请求根路径
        - 若传'/', 则url为: http://192.168.0.0:3030
        - 若传'/home', 则url为: http://192.168.0.0:3030/home
    - 参数二: 请求数据的回调函数
4. 监听端口: 默认url为当前电脑的IP地址
/* express的服务器 */

//1. 导入express
var express = require('express')

//2. 创建express服务器
var server = express()

//3. 访问服务器(get或者post)
//参数一: 请求根路径
//3.1 get请求
server.get('/', function (request, response) {
    // console.log(request)
    response.send('get请求成功')
})

//3.2 post请求
server.post('/', function (request, response) {
    response.send('post请求成功')
})

//4. 绑定端口
server.listen(5000)
console.log('启动5000')

// http://localhost:5000

路由

1. 路由: 针对不同的URL有不同的处理方式
2. 添加url路径: 根据不同路径,显示不同内容
3. 路由句柄(索引): 执行完一个函数,再执行下一个,因为有时候处理一个请求,需要做很多其他事情,写在一起业务逻辑不好分开
4. 函数一定要添加next参数,一定要调用next(),才会进行下面操作,代码使一行一行执行
//3.1 get请求
server.get('/', function (request, response, next) {
    // console.log(request)
    console.log('从据库获取数据') // 显示在terminal里
    next()
}, function (request, response) {
    response.send('get请求成功') // 显示在网页上
})

// http://localhost:5000

中间件

- 优化代码,使代码清晰可读
- 原理: 发送一个请求给服务器的时候,会被中间件拦截,先由中间件处理,每个中间件都有一个回调函数作为参数,拦截到参数,就会自动执行回调函数。
- 注意:有中间件use,会先执行中间件的回调函数,然后才会调用get或者post的回调函数,也就是当监听到请求,先执行中间件,才会到get、post请求。
- use是express注册中间件的方法
//3. 创建中间件:use
//截取请求, 拦截回调
server.use('/', function (request, response, next) {
    console.log('执行中间件')
    // console.log('获取数据库数据')
    console.log(request.query.page)
    next()
})

//4. 访问服务器(get或者post)
//参数一: 请求根路径
//4.1 get请求
server.get('/home', function (request, response) {
    // console.log(request)
    response.send('get参数请求成功')
})

// http://localhost:5000/home

get请求参数

- request.query会把请求参数包装成字典对象,直接通过点就能获取参数
//4. 访问服务器(get或者post)
//参数一: 请求根路径
//4.1 get请求
server.get('/home', function (request, response) {
    // console.log(request)
    console.log(request.query.page) // 12
    response.send('get参数请求成功')
})

// http://localhost:5000/home?page=12

post请求参数

先看一下request的部分参数

headers: 
   { 
   //请求头
     host: 'http://localhost:5000/home',
     //保持长连接
     connection: 'keep-alive',
     'cache-control': 'max-age=0',
     'user-agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36',
     'upgrade-insecure-requests': '1',
     //可接受的数据解析方式
     accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8',
     'accept-encoding': 'gzip, deflate',
     'accept-language': 'zh-CN,zh;q=0.9',
     'if-none-match': 'W/"15-H7HlVCzzVfmRL56LAnLfNUaMM+8"' 
   }
- 使用http发送请求,需要设置content-type字段
- content-type字段
    - application/x-www-form-urlencoded(普通请求,默认一般使用这种)
    - application/json(带有json格式的参数,需要使用这个,比如参数是字典或者数组)
    - multipart/form-data(传输文件,文件上传使用这个)
- Node.js需要使用body-parser模块,解析post请求参数
- 可以采用中间件的方式解析post请求参数
    - 注意bodyParser.urlencoded参数是一个字典,需要添加{}包装
    - extends必传参数,是否展开

Face Adversary Attack Defense System

Reference

This project aims to develop a software platform for analyzing adversary attacks on facial recognition. The objectives of the software platform include (1) quantitative evaluation of emerging adversary attack algorithms; (2) analyzing and identifying effective detection algorithms for adversary attacks; (3) collection and analysis of digital evidence for criminal activities due to adversary attacks based on forensic methodologies. The platform will be beneficial for a wide variety of users that include law enforcement bodies, AI technology providers, research communities and the public.

Paper Reading

Reference

Abstract

对抗性样本攻击扰乱良性输入,导致DNN行为不当。 现有的防御技术要么假设预先知道特定的攻击,要么由于其潜在的假设而不能很好地在复杂的模型上工作。这篇paper认为,对抗性样本攻击与DNN模型的可解释性有密切的关系:良性输入的分类结果可以基于人类可感知(perceptible)的特征/属性进行推理,而对抗性样本上的结果却很难解释。因此,这篇paper基于可解释性(interpretability)提出了一种新的对抗样本检测技术的人脸识别模型。
1. 它具有一种新的属性与内部神经元之间的双向对应推理,以识别对单个属性至关重要的神经元
2. 关键神经元的激活值被增强以放大计算推理部分,其他神经元的激活值被削弱以抑制不可解释部分。
将变换后的分类结果与原模型的分类结果进行比较以发现adversary。结果表明:
1. 该技术对7种攻击的检测准确率达到94%,良性输入的误报率为9.91%。
2. 相比之下,最先进的特征压缩技术只能达到55%的准确率和23.3%的假阳性。

Introduction

深度神经网络(DNNs)在自然语言处理、场景识别、目标检测、异常检测等领域取得了巨大的成功。尽管它们取得了成功,但DNN的安全问题阻碍了DNN在现实世界任务中的广泛应用。 在Intriguing Properties of Neural Networks中显示,轻微的难以察觉的扰动能够导致DNN的不正确行为。从那时起,各种针对DNN的攻击被证实。关于这些攻击的详细讨论见§2。其后果可能是毁灭性的,例如,对手可以使用一个精心制作的路标,使一辆自动驾驶的汽车偏离轨道; 一张人类无法解读的图片可能会骗过面部识别系统,从而解锁智能设备。

现有的防御技术要么强化DNN模型,使其不那么容易受到敌方样本的攻击,要么在操作过程中检测这些样本。然而许多这些技术通过额外训练对抗样本而起作用,因此需要对可能的攻击有先验知识。 最先进的检测技术,特征压缩Feature Squeezing: Detecting Adversarial Examples in Deep Neural Networks,减少特征空间(例如,通过减少颜色深度和平滑图像),使对手依赖的特征被破坏。然而根据我们在§4.2中的实验,由于该技术可能挤压DNN正常功能所依赖的特征,它会导致检测敌对样本和分类良性输入的准确性下降。

我们的假设是,对抗性样本攻击与DNN分类输出的可解释性是密切关联的。 具体来说,在一个完全连接的网络中,所有神经元都直接/传递地影响最终输出。许多神经元表示人类难以感知的抽象特征/属性。 因此,敌对样本通过利用这些神经元来实现其目标。这样,虽然一个良性的分类结果输入可能是基于一组人类可感知的属性(例如给定的脸图片因为颜色的眼睛和鼻子的形状被公认为人物A),对抗性输入的结果很难归因于人类可感知的特征。也就是说,对于对抗样本,DNN没有使用可解释的特征,或猜测。

图1:DNN与人类面部识别的比较。
像素级的差异被放大了50倍,以获得更好的视觉显示效果。左边的两张女演员艾拉·费舍尔的图像分别是DNN模型的输入和待识别的人脸。最右边的两个图像是对应身份的代表图像。蓝色箭头表示良性输入的路径。红色箭头表示使用C&W L2攻击生成的对抗样本的路径

图1展示了一个攻击示例。对抗样本采用Carlini和Wagner (C&W)提出的方法生成。观察它几乎与原始良性输入相同。 图1中所示的DNN模型是VGG-Face,这是目前最知名、最精确的人脸识别系统之一。 在图1中,女演员艾拉·费舍尔(Isla Fisher)的照片被错误地识别为演员A.J.巴克利(A.J. Buckley)。

显然,对抗性样本的分类不能依赖人类可感知的属性,如眼睛、鼻子和嘴巴,因为这些属性(几乎)与原始图像中的属性相同。(相对于机器可以被这种攻击骗过)事实上,人类几乎不会对被干扰的图像进行错误分类(因为人类根据眼睛、鼻子和嘴巴等可感知的属性来识别脸的身份)。因此,我们的技术的总体思想是检查DNN是否主要基于人类可感知的属性来产生其分类结果。否则,就不能信任结果,输入被认为是对抗的。

针对人脸识别系统(FRSes),提出了一种基于可解释性的对抗样本检测技术AmI (Attacks meet Interpretability)。 FRSes是一种具有代表性的DNN,应用非常广泛。具体来说,AmI首先利用一小组训练图像提取一组神经元(称为属性目击者),这些神经元对人脸的个体属性(如眼睛和鼻子)至关重要。虽然现有的DNN模型解释技术可以生成输入区域,使给定神经元的激活值最大化,或识别出受物体(在图像中)影响的神经元,但这些技术对于我们的目的来说是不够的,因为它们识别出的是弱相关性。

AmI的特点是一种新的推理技术,揭示了人脸属性和一组内部神经元之间的强相关性(类似于一对一对应)。它需要两者之间的双向关系:属性的变化导致神经元的变化,神经元的变化意味着属性的变化,而大多数现有的技术只关注单向关系。在确定属性目击者后,通过对原模型进行转换,即强化属性目击者神经元的值弱化非属性目击者神经元的值,构造了一个新的模型。直观上,前者是为了增强FRS决策程序的推理方面,后者是为了抑制无法解释的方面

给定一个测试输入,两个模型的预测结果不一致表明输入是对抗性的。综上所述,我们做出了以下贡献:
1. 我们提出利用这些样本上的结果来检测对抗样本,很难用人类可感知的属性来解释。
2. 我们提出了一种新的技术AmI,利用FRSes的可解释性来检测对抗样本。其特点是:
(1) 人脸属性与神经元之间的对应关系的双向推理;
(2) 对应推理时,属性级突变代替像素级突变(如每次替换整个鼻子);
(3) 神经元的增强和减弱。
3. 我们用7种不同的攻击类型将AmI应用于VGG-Face,这是一种广泛使用的FRS。AmI可以成功检测出对抗样本, true positive rate平均为94%,而最先进的特征压缩技术只能达到55%的准确率。AmI有9.91%的false positives rate(即将良性输入0误分类为恶意输入1),而特征压缩有23.32%。
4. AmI在GitHub上可用。

图2:对FRS的不同攻击的对抗样本。
图2(a)为actor阿隆索的原始图像。图2(b)-(h)是使用不同攻击产生的7个对抗性样本(见标题)。左边的图像是生成的对抗样本,右边的图像是与原始图像相比的像素级差异。为了更好地显示视觉效果,这种差异被放大了50倍。

Model Interpretation:

解释DNN是一个长期的挑战。现有的研究从多个角度研究可解释性,特征可视化是其中之一。尽管解释DNN主要是理解单个神经元和层的作用,特征可视化通过生成神经元的接受域来解决这个问题,接受域是使神经元的激活值最大化的感兴趣的输入区域。

Network Dissection: Quantifying Interpretability of Deep Visual Representations中,作者提出了一种将神经元与输入图像中的物体联系起来的技术。特别是当一组神经元的接受域与某些人类可理解的物体(如狗和房子)重叠时,这组神经元就被标记上相应的物体,并被视为该物体的检测器。这种方法将焦点从一个神经元(在特征可视化中)扩展到一组神经元。

然而,虽然这些现有技术与我们的技术中的属性目击者提取相关,但它们还不足以满足我们的目的。特征可视化和中的推理是单向的,即像素扰动导致神经元值的变化。在§3.1和§4.2中,我们讨论/证明了单向推理经常导致过度近似(即,神经元是相关的但不是必要的某些属性),从而导致次优检测结果。在我们的上下文中需要更强的相关性概念。此外,我们在训练图像中利用属性替换来进行突变(例如,用其他不同的鼻子替换一个鼻子),而不是像现有工作中那样使用随机像素扰动

Adversarial Samples

对抗性样本是对手为了欺骗DNN而生成的模型输入。存在两种类型的对抗性样本攻击:定向攻击和非定向攻击。定向攻击操纵DNN输出特定的分类结果,因此更具灾难性。另一方面,没有目标的攻击只会导致错误分类。

定向攻击进一步分为两类:补丁和普遍干扰。前者生成补丁,可以应用于图像来欺骗DNN。图2(b)的左侧图像显示了一个补丁攻击的例子。相比之下,普遍的扰动使任意位置的良性输入发生突变,从而构建敌对的样本。Carlini和Wagner提出了三种这样的攻击。图2(d)显示了L0攻击,它限制可以改变的像素的数量,但不限制其大小; 图2(e)显示L2攻击,使对抗样本和原始图像之间的欧氏距离最小化。图2(f)展示了L∞攻击,它限制了单个像素的变化幅度,但不限制像素的变化数量。

非定向攻击最早由Szegedy等人提出。快速梯度符号法(FGSM)通过沿梯度方向一步变异整个图像来生成对抗样本。Kurakin等人随后通过采取多个小步骤对FGSM进行了扩展。这种攻击称为基本迭代法(Basic Iterative Method, BIM),如图2(h)所示。

Defense Techniques

研究人员已经做出了过多的努力来保护DNN免受对抗性攻击。现有的防御技术大致可以分为两类:模型强化和对抗检测。我们提议的AmI系统属于第二种类型。

模型强化是为了提高DNN的鲁棒性,防止对抗样本导致DNN的错误行为。以前的工作将对抗样本作为一个额外的输出类,用新的类训练DNN。然而由于需要对抗性样本的先验知识,这些方法可能不实用。防御蒸馏Distillation as a Defense to Adversarial Perturbations against Deep Neural Networks旨在向敌人隐藏梯度信息。该策略通过将最后一层替换为修改后的softmax函数来实现。

对抗检测在执行过程中识别对抗样本。Grosse等人使用统计检验来测量敌对样本和良性输入之间的距离。这种技术需要预先生成对抗性样本,这在实践中可能不能满足要求。徐等人则提出了一种不需要对抗样本的特征压缩策略。它通过减少8位尺度的颜色深度和图像平滑来缩小特征空间。经过特征压缩后,相对于未压缩样本,敌对样本很可能会产生不同的分类结果,而良性样本则不会。它被认为是检测对抗样本的最先进的方法。

我们的AmI系统也依赖于分类不一致性,但AmI利用了FRSes的可解释性,并且在人脸识别环境中比特征压缩具有明显更好的性能,如§4.2所示。

Approach

我们的目的是探索使用DNN的可解释性来检测对抗样本。我们关注FRSes,其目标是识别给定人脸图像的身份。在这种特定情况下,我们的技术的首要思想是确定FRS DNN的预测结果是否基于人类可识别的特征/属性(例如,眼睛、鼻子和嘴巴)。否则,我们认为输入图像是对抗性的。

我们的技术包括两个关键步骤:(1)通过分析FRS在一小组训练输入上的行为,识别与人类可感知属性相对应的神经元,称为属性目击者; (2)给定一幅测试图像,观察属性目击者的激活值,并与其他神经元的激活值进行比较,以预测该图像是否为对抗性。在(2)中,也采用活化值的变换来增强对比。

图3: AmI的架构。
它首先识别人脸属性的形状(步骤1-Landmark generation),然后要求人类对识别出的形状进行注释(步骤2-Attribute annotation)(Left eye, Right eye, Nose, Mouth …)。对于每个属性,AmI自动提取原始模型中的属性目击者(步骤3-Attribute witness extraction)。随后利用提取的属性目击者来构建属性导向模型(步骤4-Attribute steered model)。对于一个测试输入,新模型和原始模型并排执行,以检测输出不一致,表明为对抗样本(步骤5-Consistency observer)。

AmI的工作流如图3所示。
1. 首先,我们分析了FRS如何使用人类可感知的属性来识别输入图像,以提取属性目击者。图3中的步骤1-3说明了提取过程。对于训练输入,我们在步骤1中利用自动技术dlib检测人脸属性的形状。
2. 注意,在这一步之后,重要的形状是用虚线标识的。在第2步中,利用人工工作来用属性注释形状。虽然可以使用人脸属性识别DNN模型实现自动化,但这些模型中的固有错误可能会阻碍证明我们技术的概念,因此我们决定使用人类的努力来完成这一步。注意,这些都是一次性的工作,带注释的图像可以在多个模型中重用。
3. 对于每个人类识别的属性,我们在FRS中提取相应的神经元(步骤3),这些神经元就是属性目击者。这一步涉及一种新的双向推理属性变化和神经元变化之间的相关性。具体内容见§3.1。对于每个属性,我们将这些步骤应用于一组10个训练输入(经验上足够),以提取统计上有意义的证据。其次,利用所提取的证据构造了一种新的对抗样本检测模型。图3中的步骤4-5显示了这个过程。
4. 在第四步中,我们通过增强属性目击者神经元和削弱其他神经元来获得一个新的模型。我们将结果模型称为属性导向模型。直观地说,新模型旨在抑制模型执行中不可解释的部分,鼓励可解释的部分(即推理部分)。这类似于人类决策的回顾和强化过程。详细内容见§3.2。
5. 为了检测对抗样本,对于每个测试输入,我们都使用原始模型和属性导向模型来预测其身份。如果观察到不一致的预测(第5步),我们将其标记为对抗性。

Attribute Witness Extraction

图4: 属性目击者提取。
步骤A-Attribute substitution: 将属性替换应用于基础图像,将属性替换为其他图像中的对应属性,然后观察具有不同值的神经元(步骤C)。观察最上面一排的图片有不同的鼻子,但脸是一样的。(数值发生改变的区域A就包含属性1的区域)
步骤B-Attribute preservation: 通过将其他图像中的属性替换为基础图像的属性,然后观察没有改变的神经元来保存一个属性(步骤D)。(数值保持不管的区域B就包含属性1的区域)
注意,下面一行不同的脸有相同的鼻子(从基础图像复制)。两个集合相交产生属性目击者(步骤E)。(区域A和区域B求交集)

  1. AmI的第一步是确定属性目击者,即单个属性对应的神经元。提取过程如图4所示,由两个相反方向的推理组成:属性替换(步骤A)和属性保持(步骤B)。更具体地说,在步骤A中,给定一个称为基本图像的输入图像和一个感兴趣的属性,我们的技术自动用其他图像中相应的属性替换图像中的属性。例如,在图4的第一行图像中,原始图像中的鼻子被替换为其他不同的鼻子。
  2. 然后,FRS执行基础图像和每个替换图像,提取激活值较大差异的内部神经元作为替换属性的超集(步骤C)。然而,并不是所有这些神经元都对这个属性至关重要。因此,我们通过属性保持来进一步细化神经元集。
  3. 特别是在步骤B中,我们使用基础图像中的属性来替换其他图像中的属性。我们将得到的图像称为属性保留图像,然后利用这些图像提取与基础图像在步骤上没有或相差很小的神经元激活值(步骤D)。从直观上看,由于所有的图像都保留了相同的属性,所以属性目击者神经元不会有激活值的变化。但是,没有变化的神经元并不一定是属性的本质,因为一些抽象的特征也可能不会发生变化。
  4. 因此,我们将属性替换和属性保持提取的神经元相交作为属性目击者(步骤E)。

双向推理的设计选择至关重要。理想情况下,我们希望提取与个体属性有很强相关性的神经元。换句话说,给定一个属性,它的目击者将包括与该属性有对应关系的神经元(或某种意义上“等价于”该属性)。属性替换表示下面的正向推理。
$$\text{Attribute changes} → \text{Neuron activation changes (1)} $$

然而,这种单向关系比等价关系弱。理想情况下,我们希望见证神经元也能满足以下逆向推理。
$$\text{Neuron activation changes} → \text{Attribute changes (2)} $$

然而,这种逆向推理在实践中很难实现。因此,我们诉诸推理下列性质,它在逻辑上等同于等式2,但可向前计算。
$$\text{No attribute changes} → \text{No neuron activation changes (3)} $$
式3所示的推理本质上对应于属性保持。在§4.2中,我们证明了单向推理产生的检测结果不如双向推理。

详细设计。假设$F: X \rightarrow Y$表示一个$FRS$,其中$X$是输入图像的集合而$Y$是输出身份的集合。令$f^l$表示$l \verb|-| th$层,$x \in X$表示一些输入。一个有$n+1$层的$FRS$可以按下述定义:
$$F(x)=\text{softmax} \circ f^n \circ \cdots \circ f^1(x) \text{ } \text{(4)}$$

对于每一层$f^l$,它从上一层获取一个激活向量,并生成一个新的激活向量。每个新的激活值(表示该层中的一个神经元)由一个神经元函数计算,该神经元函数取前一层的激活向量并生成该神经元的激活值。换句话说,
$$f^l(p)=\langle f_1^l(p), \cdots, f_m^l(p) \rangle \text{ } \text{(5)}$$
$m$代表第$l$层的神将元个数,$p$代表第$l-1$层的激活向量,$f_j^l(p)$代表神经元$j$的函数。

属性替换推理属性变化导致神经元值的变化。具体来说,它将一个基础图像中的一个属性(例如,鼻子)替换为其他图像中的对应属性,然后观察神经元的变化。在第$l$层的神经元$j$的激活差异定义如下:
$$\triangle f_{j,as}^l=\lvert f_j^l(p) - f_j^l(p_{as}) \rvert \text{ } \text{(6)}$$

其中下标$as$表示替换后的图像。变化的幅度表明了属性与相应神经元之间正向关系的强度。变异越大,关系越强。具体来说,如果一个神经元$f_j^l(p)$的变化大于该层所有观测变化的中位数,则我们考虑其为(属性目击者的)一个候选人。令$g_{as}^{i,l}(p)$为第$l$层属性$i$的目击者候选集。它可以用下面的方法计算:
$$g_{as}^{i,l}(p)=\{ f_j^l(p) \vert f_{j,as}^l > median_{j \in [1,m]}(\triangle f_{j,as}^l) \} \text{ } \text{(7)}$$
我们对10幅训练图像计算$g_{as}^{i,l}(p)$,并采取多数票。注意,属性目击者表示(训练过的)$FRS$的属性,可以通过分析少量的训练运行来提取。

值得指出的是,一个关键的设计选择是用对应的属性替换属性(来自其他人)。一个简单的图像转换,消除了一个属性(例如,用一个黑/白矩形取代鼻子)不能很好地工作。直观地说,我们正在推理一个属性的不同实例化(例如,不同形状和不同肤色的鼻子)如何影响内部神经元。然而,消除属性反而能推导出属性的存在(或不存在)如何影响神经元,这并不是身份识别的基础。

属性保存可以推导出,一个属性不改变就不会导致其目击者神经元的改变。换句话说,它通过将基础图像复制到其他图像来保留基础图像的属性。令$g_{ap}^{i,l}(p)$表示在属性$i$变换下,第$l$层中变化不大的神经元函数集合,下标$ap$表示属性保持。可以用下面的方法计算:
$$g_{ap}^{i,l}(p)=\{ f_j^l(p) \vert f_{j,ap}^l \leq median_{j \in [1,m]}(\triangle f_{j,ap}^l) \} \text{ } \text{(8)}$$

它本质上是捕捉那些有很强反向关系的神经元(如公式2和公式3)。然后通过交集计算属性$i$的见证$g^i$:
$$g^i=\bigcap\limits_{l=1}^n g^{i,l}(p)=\bigcap\limits_{l=1}^n g_{as}^{i,l}(p) \cap g_{ap}^{i,l}(p) \text{ } \text{(9)}$$

最后,我们想指出的是,不同属性的见证集可能会重叠,特别是对于模型属性间关联的神经元(见表1)。对于$FRS$能够基于属性进行推理的良性输入,新模型能够产生与原模型一致的预测结果。相对于对抗性输入,新模型会产生不一致的结果,因为这种输入很难推理。

Attribute-steered Model

在提取见证后,通过对原模型的转换(不需要额外的训练)构造一个新的模型。我们称之为属性导向模型。特别是,我们加强了目击神经元而削弱了其他神经元。直觉上,我们强调推理和压抑直觉。

神经元弱化使每一层非目击神经元的激活值小于该层目击神经元激活值的平均值。对所有满意的(非目击的)神经元进行以下缩减:
$$v^{'}=e^{- \frac{\upsilon - \mu}{\alpha \cdot \sigma}} \cdot \upsilon \text{ } \text{(10)}$$
$\upsilon$代表非目击神经元的值;$\mu$和$\sigma$分别是同一层中目击神经元的平均值和标准差;$\alpha$定义减弱的幅度,在本文中设置为100。值得注意的是,非目击神经元的值越大,它们的减少幅度就越大。这里使用的函数$e^{-x}$是受广泛用于图像处理去噪核的高斯函数$e^{-x^2}$的启发。

神经元增强使所有目击神经元的值增加如下:
$$v^{'}= \epsilon \cdot \upsilon + \left( 1 - e^{- \frac{\upsilon - \text{min}}{\beta \cdot \sigma}}\right) \cdot \upsilon \text{ } \text{(11)}$$

其中是$\epsilon$强化因子,第二项的范围在$[0,\upsilon)$(因为$\upsilon \verb|-| \text{min}$必须大于0)。第二项的存在允许更大的激活值有更大的扩展(不仅仅是$\epsilon$)。 在本文中,$\epsilon$和$/beta$分别设为1.15和60。它们是通过由100张良性图像组成的调优集来选择的,这与测试集没有重叠。在生产运行过程中对各试验投入逐层进行弱化强化。

除了弱化和增强之外,我们还应用属性保持变换进一步破坏对抗性样本所利用的不可解释特征。卷积/池化层的激活以矩阵的形式存在。在这个变换中,我们先去掉矩阵的边缘,然后通过插值的方法将其重新塑形到原来的尺寸。请注意,属性导向性模型对于良性输入(其预测结果是基于属性的)的属性保持转换具有弹性。特别是,对于池化层中的非目击神经元,我们首先删除边缘,然后扩大剩余的以适应层来调整它们的大小。例如,在VGG-Face pool3层的激活形状是28x28。我们首先去掉大小为2的边距得到一个新的24x24的激活形状。然后我们使用双三次插值将激活的大小调整回28×28。由于该变换仅适用于非目击神经元,因此将其视为神经元弱化的一部分,其结果见表3。

Experiments

我们使用最广泛使用的FRSes之一VGG-Face来证明AmI的有效性。三个数据集,VGG Face dataset (VF), labelled Faces in the Wild (LFW)和CelebFaces
使用属性数据集(CelebA)。我们使用VF数据集的一个小子集(10幅图像)来提取属性见证,它与其他用于测试的数据集没有交集,以评估提取的属性见证和对抗样本检测的质量。由于见证表示训练过的FRS的特性,它们的提取只需要对一小组良性输入进行分析。将实验结果与目前最先进的特征压缩检测技术对7种敌对攻击进行了比较。AmI在GitHub上公开可用。

Evaluation of Extracted Attribute Witnesses

提取的见证在表1中,表1显示了层数(第1行)、每层神经元数(第2行)和见证数(剩余行)。值得注意的是,虽然每一层有64-4096个神经元,但提取的证人数(见证数)小于20个。有些层由于专注于融合特征而不是提取特征而没有提取见证人。为了评价所提取的见证人的质量,即他们描述相应属性的程度,我们设计了类似于Attribute and Simile Classifiers for Face Verification的实验。对于每个属性,我们创建一组可能包含也可能不包含该属性的图像,然后我们训练一个两类模型,基于目击者的激活值来预测属性的存在预测的准确性反映了证人的素质
该模型有两层:
1. 第一层获取属性目击神经元的激活值(跨越所有层)。
2. 第二层是带有两个输出类的输出层。

我们使用来自VF集的2000幅训练图像(1000幅有属性,1000幅没有属性)来训练模型。然后我们在一个不相交的集合上进行测试来自VF和LFW的400个测试图像(200个带属性,200个不带属性)。为了比较,我们还使用了VGG-Face人脸描述符层(即fc7层)的激活值来预测属性的存在。其他研究人员认为这一层代表了Deep Face Recognition中的抽象人脸特征。结果如表2所示。 对于VF数据集,对于3个属性,证人始终达到高于93%的准确率,而人脸描述符低于86%。因为LFW是一个与训练集中的人员不同的数据集(来自VF),我们观察到准确度下降。然而,属性目击者的准确率仍然高于面部描述符。结果表明,AmI提取的见证人质量较高。

Detection of Adversarial Samples

我们为§2中讨论的每种攻击生成100个对抗样本。对于补丁攻击(表3中的Patch),使用Liu等人提供的代码生成对抗样本。因为Sharif等人没有提供攻击代码,我们根据论文实现了他们的攻击(眼镜)。使用Carlini和Wagner的原始实现生成了三种扰动攻击($C \text{&W}_0$、$C \text{&W}_2$和$C \text{&W}_\infty$)。我们使用CleverHans库生成非目标攻击FGSM和BIM。对于每个目标攻击,我们测试两个类似特征压缩Feature Squeezing: Detecting Adversarial Examples in Deep Neural Networks的设置:目标作为第一个输出标签(First),目标是正确的作为下一个标签(Next)。注意,成功的攻击样本包括具有重叠属性(如眼镜和胡须)的图像。虽然这些重叠属性没有被明确提取,但AmI仍然可以成功地检测到存在的对抗性样本。从表3可以看出对于大多数攻击,AmI可以有效地检测对抗性样本,准确率超过90%。对特征压缩(FS)进行了相同的测试。FS的最佳效果为77%。FS尤其在FGSM和BIM上表现不佳,这与Xu等的观察一致。结果表明,尽管FS在数字识别(即MNIST)和目标分类(即CIFAR-10)方面取得了成功,但它对FRSes的效果并不好。我们怀疑特征压缩只对颜色深度等原始特征进行压缩,因此在人脸识别等依赖更抽象特征的复杂任务中效率较低。然而,目前还不清楚如何压缩人眼等抽象特征。

我们还评估了检测技术对良性输入的影响。假阳性率(表3中的FP列)在VF数据集上,AmI为9.91%,FS为23.32%。同样的评估也在外部数据集CelebA上进行,它不与原始数据集相交。我们从CelebA中随机选择1000张图片,并对FS和AmI进行测试。AmI的假阳性率为8.97%,FS为28.43%。我们怀疑FS较高的假阳性率是由于与良性输入严重依赖的压缩原始特征相同的原因。

为了支持我们使用双向推理提取属性见证和使用神经元增强和弱化的设计选择,我们进行了几个额外的实验:(1)仅使用属性替换提取证人,建立属性导向模型进行检测,结果如表3所示(AS);(2)只使用属性保存(AP);(3)只削弱了非证人(WKN);(4)只加强证人(STN)。我们的结果显示,虽然AS或AP单独可以检测对抗样本具有良好的准确性,但它们的假阳性率(见FP一栏)非常高(即20.41%和30.61%)。这是因为他们提取了太多的神经元作为见证人,而在新模型中,这些神经元得到了强化,导致良性输入的分类结果是错误的。此外,使用弱化有不错的结果,但加强进一步改善了他们与假阳性的权衡。

我们通过在对抗性检测中排除某些属性,进一步研究了使用不同见证集的AmI的鲁棒性。具体来说,我们对使用双向推理提取的属性见证的四个子集进行评估,如表3所示:(1)排除左眼证人(w/o left eye);(2)排除有右眼的证人((w/o Right Eye);(3)排除鼻子证人(w/o Nose);(四)排除嘴巴证人((w/o Mouth)。我们观察到检测精度下降了一点(在大多数情况下小于5%)。这表明AmI对于不同的属性见证是鲁棒的。

Conclusion

我们提出了一种对抗样本检测技术AmI在人脸识别的背景下,利用DNN的可解释性。我们的技术具有一些关键的设计选择:新的面部属性和内部神经元之间的双向对应推理;用属性级突变代替像素级突变;神经元的增强和减弱。结果表明,AmI是高效的,在目标环境中取代了最先进的技术。

Acknowledgment

我们感谢匿名审稿人提出的建设性意见。该研究部分由美国国防部高级研究计划局根据FA8650-15-C-7562合同支持,美国国家科学基金会根据1748764和1409668合同支持,
ONR在N000141410468和N000141712947合同下,Sandia国家实验室在合同下 1701331. 本文中的任何观点、发现和结论都只是作者的观点,并不一定反映我们的发起人的观点。

Demo Pre

Reference

Prepare

对抗攻击: 生成器和判别器一个freeze一个unfrezee,生成对抗性例子和学习对抗性例子,不断迭代,两者的能力都变强。

项目的机制: 强化识别五官的神将元(我们称之为关键神经元)的功能(推理),弱化识别非五官的神经元的功能(直觉)。来抵抗对抗性攻击,做到和人类一样的感官效果来识别人脸。从像素级提高到人脸五官的属性级

优势:
1. 现有的防御技术大致可以分为两类:模型强化和对抗检测。
1. 模型强化:提高DNN的鲁棒性需要对抗性样本的先验知识,通过额外训练对抗样本而起作用,这些方法可能不实用。
2. 对抗检测:在执行过程中识别对抗样本,这种技术需要预先生成对抗性样本,这在实践中可能不能满足要求。
2. 相比之下,最先进的特征压缩技术只能达到55%的准确率和23.3%的假阳性;该技术对7种攻击的检测准确率达到94%,良性输入的误报率为9.91%。所以项目的技术在人脸识别环境中比特征压缩具有明显更好的性能。
3. 无论对定向攻击还是非定向攻击都一样有效,无须再针对攻击类型分别训练,节约成本,提高了效率
1. 定向攻击使用了patching(including a patch in the shape of glasses)和pervasive perturbations(任意位置突变)做实验
2. 非定向攻击使用了FGSM(Fast Gradient Sign Method)和BIM(extended FGSM by taking multiple small steps)做实验
3. 定向攻击:C&W0攻击,限制可以改变的像素的数量,但不限制其大小; C&W2攻击,使对抗样本和原始图像之间的欧氏距离最小化。C&W∞攻击,它限制了单个像素的变化幅度,但不限制像素的变化数量。

Video:
为了验证防御系统的有效性,我们需要先生成一个对抗性例子,使它可以成功攻击人脸识别系统,然后我们的防御系统再进行检测。
1. 首先演示的是人脸识别系统的正常的工作过程,第一步可以观察到,系统会将attacker和victim识别为不同的两个人,所以人脸验证不通过,结果为Fail
2. 第二步,我们随机选取一个最著名的对抗性攻击的算法FGSM来结合attacker和victim的脸生成一个对抗性的例子,并用它来攻击人脸识别系统。观察到对抗攻击成功了,人脸验证被attacker通过了,结果为Pass
3. 第三步,用我们的防御系统来检测对抗性例子,如果是对抗性例子,结果应该为’Adversary’,如果是没有被对抗攻击过的真实图片,结果应该是’Real’。这里我们观察到防御系统给出的结果是’Adversary’,说明对于这张对抗攻击的人脸图片它检测成功了。

Cmpile Caffe

(fgsm detect时需要使用caffe model) cmake-3.9.2 + vs2015 + Anaconda3-2.5.0 (python3.5) + CUDA 8.0 + cuDNN 5

  1. 安装conda
    • Anaconda3-2.5.0-Windows-x86_64.exe
    • 得到系统default python (3.5)
  2. 安装CUDA Toolkit 8.0
    • Windows下多版本的CUDA可以共存
      • 不需要切换,只要环境变量PATH中有相应的CUDA路径即可,无需手动切换了
      • 可以使用任何一个版本,只要在环境变量中有对应的CUDA路径即可
  3. 安装cuDNN 5
    • cuDNN v5 Library for Windows 10
    • 把cuDNN下的lib,include,lib文件夹拷贝到CUDA的安装路径: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0
  4. 添加系统Path
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\bin
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\libnvvp
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\lib\x64
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\include
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0\extras\CUPTI\lib64
    C:\Program Files\NVIDIA Corporation\NVSMI
    C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR
    C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common
  5. 添加系统环境变量
    CUDA_PATH_V8_0 = C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0
  6. 安装Cmake
    • 版本要>=3.4 | 所有选项保持默认即可,一直点击下一步,直到安装完成
    • 添加系统Path: C:\Program Files\CMake\bin
  7. 安装VS2015 community
    - 安装时
        - vs2013=Microsoft Visual Studio 12.0 
        - vs2015=Microsoft Visual Studio 14.0
        - 两者可以安装在一台电脑上并不冲突,可以同时安装,但必须先安装低版本(vs2013)再安装高版本(vs2015),可以兼容
    - 安装时勾选
        - Technically only the VS C/C++ compiler is required (cl.exe)
        - 通用Windows平台开发(包括其子选项C++通用Windows平台工具)
        - 使用C++的桌面开发
    - 安装地址要添加到系统环境
        - vs2013: C:\Program Files (x86)\Microsoft Visual Studio 12.0
        - vs2015: C:\Program Files (x86)\Microsoft Visual Studio 14.0
    • 若需要安装VS2013 community,点击此处,则Python只能用2.7版本的
  8. 编译caffe
    • 介绍
      caffe是用C++语言编写的深度学习框架,作者是伯克利大学的博士贾扬清,caffe目前主要用于深度学习下的图像处理方面,也就是支持卷积神经网络CNN
    • 下载caffe-windows源码
      $git clone https://github.com/BVLC/caffe.git
      $cd caffe
      $git checkout windows
    • 下载Ninjia (用Nijia编译才做这一步,用VS编译不需要这一步)
      $cd xx\caffe
      $git clone git://github.com/ninja-build/ninja.git
      $cd ninja
      # 打开VS2015 x64 Native Tools Command Prompt
      # 将python.exe(3.5版本的)添加到Path
      $python ./configure.py --bootstrap
      # 编译完成(出现一些东西),将xx\caffe\ninja添加到Path
    • 修改caffe\scripts\build_win.cmd文件
      • 根据自己的编译器(VS or Nijia)、python版本选择修改
        - WITH_NINJA=1      | 表示用Ninja编译(或者直接利用vs2015 直接编译也可以,不用安装Ninja)
        - CPU_ONLY=1        | 沒有GPU,或者就是想编译CPU版本
        - CPU_ONLY=0        | 有GPU,想编译GPU版本
            - 在cmake -G"!CMAKE_GENERATOR!"里添加CUDA的路径: 
                - -DCUDNN_ROOT="C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v8.0" ^
        - CUDA_ARCH_NAME=Pascal | Fermi  Kepler  Maxwell  Pascal  All (这些都是NVIDIA GPU不同的架构)
            - 对于我的 DELL Desktop: NIVIDA GeForce GTX 1050 就是采用的Pascal架构
            - 对于Azure VM 和 Windows Server: NVIDIA Tesla V100 采用的是Volta架构
        - PYTHON_VERSION=3  | 表示python3.x版本接口
            - 相应地: set CONDA_ROOT=C:\Anaconda3 (C:\Anaconda3\python.exe是Python 3.5.1 :: Anaconda 2.5.0 (64-bit))
        - CONDA_ROOT=上面python3.5的路径
        - BUILD_PYTHON=1
        - RUN_INSTALL=1
    • C:\Users\PC.caffe\dependencies\libraries_v140_x64_py35_1.1.0\libraries\include\boost-1_61\boost\config\compiler\nvcc.hpp 删除最后三行
    • build之前的准备
      - python.exe的路径一定只能有python3.5的,如果有conda的python.exe(非3.5版本的)先删除,build完再恢复
      - 否则会报错: Found PythonInterp: C:/ProgramData/Anaconda3/python.exe (found suitable version "3.8.5", minimum required is "2.7")
    • 编译caffe
      • 在caffe\scripts下新建build文件夹,并清空里面的所有文件
      • 以“管理员权限”打开VS2015 x64 Native Tools Command Prompt
        $cd scripts
        $build_win.cmd
        # 每次运行build_win.cmd之前都要先清空build文件夹
        # VS2015 build结果: 9037 个警告 | 0 个错误 | 已用时间 00:22:02.25  √
  9. 添加Python接口到detect.py运行的虚拟环境
    • 将caffe\python\caffe拷贝到虚拟环境的\Lib\site-packages
      $python
      $import caffe

Compile Error

  1. Unsupported gpu architecture ‘compute_90’
    CustomBuild:
    Building NVCC (Device) object src/caffe/CMakeFiles/cuda_compile_1.dir/layers/Release/cuda_compile_1_generated_absval_
    layer.cu.obj
    nvcc fatal   : Unsupported gpu architecture 'compute_90'
    CMake Error at cuda_compile_1_generated_absval_layer.cu.obj.Release.cmake:222 (message):
        Error generating
        C:/Path/caffe/scripts/build/src/caffe/CMakeFiles/cuda_compile_1.dir/layers/Release/cuda_compile_1_generated_absval_
    layer.cu.obj
    
    
    C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V140\Microsoft.CppCommon.targets(171,5): error MSB6006: “cmd.exe”已 退出,
    代码为 1。 [C:\Path\caffe\scripts\build\src\caffe\caffe.vcxproj]
    已完成生成项目“C:\Path\caffe\scripts\build\src\caffe\caffe.vcxproj”(默认目标)的操作 - 失败。
    
    已完成生成项目“C:\Path\caffe\scripts\build\ALL_BUILD.vcxproj”(默认目标)的操作 - 失败。
    - 解决方法: C:\Users\PC\.caffe\dependencies\libraries_v140_x64_py35_1.1.0\libraries\include\boost-1_61\boost\config\compiler\nvcc.hpp 删除最后三行

Configure Virtual Environment

  1. 配置虚拟环境
    • Visual Studio 2015 + Anaconda env(python 3.5) + CUDA 8.0 + cuDNN 5 |用于编译detect需要使用的caffe model的caffe module
    • Visual Studio 2015 + Anaconda env(python 3.6) + tensorflow-gpu 1.7.0 + CUDA 9.0 + cuDNN 7.0.5 |用于运行facenet recognition和fgsm attack的环境
    • Visual Studio 2015 + Anaconda env(python 3.5) + tensorflow-gpu 1.7.0 + CUDA 9.0 + cuDNN 7.0.5 |用于运行detect的环境
  2. 添加系统环境
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\libnvvp
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\lib\x64
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\include
    C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\extras\CUPTI\lib64
    C:\Program Files\NVIDIA Corporation\NVSMI
    C:\Program Files\NVIDIA Corporation\NVIDIA NvDLISR
    C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common
    测试GPU是否安装成功: nvcc -V
    显示当前GPU使用情况: nvidia-smi.exe -l 1 (一秒钟更新一次信息)
  3. 创建conda虚拟环境
    1. facenet recognition和fgsm attack的环境
      $conda create -n attack python=3.6
      $conda activate attack
      $pip install cleverhans tensorflow-gpu==1.7.0 scikit-learn opencv-python numpy==1.16.2 scipy==1.2.1 matplotlib==3.1.1 Pillow
      $python origin\origin.py Elon_Musk Jeff_Bezos
      $python adv/fgsm.py
    2. detect的环境
      $conda create -n detect python=3.5
      $conda activate detect
      $pip install scikit-image scipy==1.2.1 opencv-python scikit-learn matplotlib Pillow tensorflow-gpu==1.7.0
      $conda install protobuf
      # 将caffe\python\caffe拷贝到python所在\Lib\site-packages(即虚拟环境的site-packages)
      $python detect/detect.py

Run Program

  1. 下载模型20180402-114759
    • LFW accuracy: 0.9965 VGGFace2
    • 放入 Face-Adversary-Attack-Defense\model下
  2. 下载模型VGG-Face caffe model
    • 放入 Face-Adversary-Attack-Defense\model下
  3. 运行程序
    $python C:\ASTRI-Adv-Detector\origin\origin.py Elon_Musk Jeff_Bezos
    # 结果: demo/origin/下生成3张图片
    #   1. 160x160的两张人脸: victim原始剪裁人脸 & attacker原始剪裁人脸
    #   2. origin.png: victim | attacker with face verification probability (fail)
    $python C:\ASTRI-Adv-Detector\adv\fgsm.py
    # 结果: demo/adv/下生成3张图片
    #   1. 160x160的两张人脸: victim原始剪裁人脸 & attacker原始剪裁人脸+fgsm noise
    #   2. adv.png:
    #       - 上排: victim | attacker with face verification probability (fail)
    #       - 下排: noise  | attacker(fgsm face) with face verification probability (pass)
    $python C:\ASTRI-Adv-Detector\detect\detect.py
    # 结果: demo/detect/下生成1张图片detect.png
    #       - 上排: victim | attacker with face verification probability (fail)
    #       - 下排: noise  | attacker(fgsm face) with detect result (Real or Adversary)

Run Server

  1. terminal {node server.js}:
    - 应用程序会启动服务器,并在端口3000上侦听连接。此应用程序以不同的响应针对不同的URL或路由的请求。
    1. 对'/facenet'路由:
        - 接收pathname(Elon_Musk&Jeff_Bezos),分解成attacker(Elon_Musk)和victim(Jeff_Bezos)
        - 写入origin.bat:python ASTRI-Adv-Detector/origin/origin.py attacker victim
        - 执行origin.bat
        - 生成demo/origin/origin.png
    2. 对'/adv'路由:
        - 写入adv.bat: python ASTRI-Adv-Detector/adv/fgsm.py
        - 执行adv.bat
        - 生成demo/adv/adv.png
    3. 对'detect'路由
        - 写入detect.bat: python ASTRI-Adv-Detector/detect/detect.py
        - 执行detect.bat
        - 生成demo/detect/detect.png
  2. Web Browser
    1. http://127.0.0.1:3000/facenet?Elon_Musk&Jeff_Bezos
        - 对服务器发起访问,得到'/facenet'路由下的响应{页面上显示图片demo/origin/origin.png}
    2. http://127.0.0.1:3000/adv
        - 对服务器发起访问,得到'/adv'路由下的响应{页面上显示图片demo/adv/adv.png}
    3. http://127.0.0.1:3000/detect
        - 对服务器发起访问,得到'/detect'路由下的响应{页面上显示图片demo/detect/detect.png}

KOL Platform Design

KOL Score

微播易发明

SNBT指数

微播易发明了一个SNBT指数,来计算社交媒体账号的影响力指数,SNBT指的是社交媒体账号的影响力指数,是微播易的发明专利(专利名称:广告精准投放方法和系统、专利号:ZL2015 1 0958772.0)的简称,数值范围:0~100,数值越高表示账号质量越好。它由账号影响力(Rank_Score)账号判假系数(FD_Ratio)计算得出,其计算公式如下

$$SNBT = (Rank_{Score} \times FD_{Ratio}) \times 100$$

为了更好让您理解SNBT,下面将对SNBT进行详细介绍。

Rank_Score - 账号影响力

Rank_Score指的是账号影响力,数值范围:0.0 - 1.0,它由账号总阅读数头条平均阅读数等数据计算得出,下面是Rank_Score的计算方式。

  1. 计算公式
    $$Rank_{Score} =\sum_{i=1}^8 f_i \times w_i$$

  2. 公式释义
    $$\begin{array}{c|lcr}
    特征值(F) & \text{特征标准化计算方法} & \text{权重(W)} \\
    \hline
    总阅读数指标 F1 & \ln(R_{total} \quad + 1) \over \ln(30 \times 800001) & 0.48 \\
    头条平均阅读指标 F2 & \ln(R_{head} \quad + 1) \over \ln(30 \times 100001) & 0.04 \\
    单篇最高阅读指标 F3 & \ln(R_{max} \quad + 1) \over \ln(100001) & 0.04 \\
    平均阅读指标 F4 & \ln(R_{average} \quad + 1) \over \ln(100001) & 0.24 \\
    总点赞数指标 F5 & \ln(L_{total} \quad + 1) \over \ln(30 \times 800001) & 0.12 \\
    头条平均点赞数指标 F6 & \ln(L_{head} \quad + 1) \over \ln(30 \times 100001) & 0.01 \\
    单篇点赞数指标 F7 & \ln(L_{max} \quad + 1) \over \ln(100001) & 0.01 \\
    平均阅读指标 F8 & \ln(L_{average} \quad + 1) \over \ln(100001) & 0.06 \\
    \end{array}$$

    $$\begin{array}{c|lcr}
    指标 & \text{说明} \\
    \hline
    R_{total} & = 最近30天所有位置文章总阅读数 \\
    R_{head} & = 最近30天所有头条文章总阅读数 \\
    R_{max} & = 最近30天所有位置文章最高阅读数 \\
    R_{average} & = 最近30天所有位置文章平均阅读数 \\
    L_{total} & = 最近30天所有位置文章总点赞数 \\
    L_{head} & = 最近30天所有头条文章总点赞数 \\
    L_{max} & = 最近30天所有位置文章最高点赞数 \\
    L_{average} & = 最近30天所有位置文章平均点赞数 \\
    \end{array}$$

  3. 相关数据说明

    1. 阅读数选取的时间周期是最近3个月的文章
      • 如果最近三个月推送次数不够4次,Rank Score统一记为0.10
      • 如果最近三个月推送次数超过4次,按照公式计算Rank Score
    2. 选取文章列表时,需要过滤阅读数Read Number或点赞数Like Number异常的文章
      • 过滤方法是:超过3月同一位置平均阅读数3倍方差的文章
      • 也就是说,我们鼓励文章阅读或者点赞正常有序增长,突变文章会被当做阅读异常点过滤掉
    3. 单篇文章的阅读数或者点赞数至少需要48+小时方可取

FD_Ratio - 账号判假系数

FD_Ratio指的是账号的判假系数,数值范围:0.0 – 1.0,数值越小,代表账号越真实,数值越大,代表账号刷阅读点赞的行为越严重。下面是对FD_Ratio计算方法的详细介绍。

  1. 计算方法
    1. FD_Ratio计算规则比较复杂,是一套规则系统
    2. 一个账号违反规则越多,FD_Ratio越低,表示账号刷阅读点赞的情况越严重
      • 阅读和点赞的比例不合理
      • 同一个位置文章的阅读数随时间发生突变,且此类异常文章比例较多
      • 不同位置文章的阅读数比例不合理
      • 文章中嵌入的链接追踪到文章PV数与文章实际显示的阅读数差距较大
      • 单篇文章阅读数随时间变化不符合一般规律,且此类异常文章较多
    3. FD取值范围是0到1的一个连续数

智能推荐算法说明

推荐指数是微播易根据账号传播能力行业匹配度进行综合评定的推荐指标,数值范围为 0~1000,数值越高表示账号越适合您。其计算公式为:$推荐指数 = 账号传播能力 * 行业匹配度$
为了让您更好的理解“推荐指数”特做以下详细说明

  1. 账号传播能力
    账号传播能力由账号时效性、账号传播广度与账号传播深度三个维度构成,账号传播能力计算公式为:$账号传播能力 = α 账号时效性 + β 账号传播广度 + γ 账号传播深度1$。 $$\begin{array}{c|lcr}
    维度 & \text{计算依据和基础} & \text{数值范围} & \text{说明} \\
    \hline
    账号时效性 & 账号最后一次发文时间 & [0, 100] & 最后一次发文时间越近,分值越高,账号活跃度越好 \\
    账号传播广度 & 账号近90天发文次数与阅读数 & [0, 100] & 发文次数越多,平均阅读数越大,分值越高,账号传播触达能力强 \\
    账号传播深度 & 账号近90天互动数据(转发、评论、点赞) & [0, 100] & 互动数越高,分值越高,账号传播互动能力强 \\
    \end{array}$$
  2. 行业匹配性
    行业匹配性受自有发布内容的行业匹配性其他行业标签映射值两个方面影响,其公式为:$行业匹配性 = MAX(自有发布内容的行业匹配值,其他行业标签的映射值)^1 \over 21$。 $$\begin{array}{c|lcr}
    维度 & \text{统计} & \text{计算} \\
    \hline
    自有发布内容的行业匹配性 & 账号近3个月发布文章中具有各行业属性的文章数 & 各账号在各个行业的行业贡献度 \\
    其他行业标签映射值 & 活跃用户的行为数据对各行业的浏览和互动情况 & 用户一段时间内在两个行业账号的浏览或互动行为 \\
    \end{array}$$

注意:
$$自有发布内容的行业匹配值=发布的相关行业文章数 \over 总文章数^2$$
其他行业标签映射值对于未曾发表相应行业文章的账号其行业匹配度并非为0,若用户在一段时间内在两个行业账号产生多次浏览或互动行为,则代表此两个行业关联性大。

专利内容

本发明公开了一种广告精准投放方法和系统,所述方法包括:S10、根据待投放广告要求的投放类别获取对应的归一化需求强度值;S20、分别获取预设所有投放账号的影响领域;S30、分别获取每个投放账号在各自影响领域下的影响力;S40、根据所述待投放广告要求的投放类别对应的归一化需求强度值和每个投放账号在各自影响领域下的影响力,获取所述待投放广告与每个投放账号的匹配度;S50、由匹配度高到低依次从所有投放账号中选取预设第一个数个账号作为目标投放账号,并向所述目标投放账号发送所述待投放广告;所述系统包括预处理服务器、数据处理集群、数据库服务器和供电电源;所述方法和系统可解决现有技术中的诸多缺陷,从而具有良好的应用潜力。

  1. 一种广告精准投放方法,其特征在于,包括:
    S10、根据待投放广告要求的投放类别获取对应的归一化需求强度值
    S20、分别获取预设所有投放账号的影响领域
    S30、分别获取每个投放账号在各自影响领域下的影响力
    S40、根据所述待投放广告要求的投放类别对应的归一化需求强度值和每个投放账号在各自影响领域下的影响力,获取所述待投放广告与每个投放账号的匹配度
    S50、由匹配度高到低依次从所有投放账号中选取预设第一个数个账号作为目标投放账号,并向所述目标投放账号发送所述待投放广告
    所述S10,包括:
    S101、判断所述待投放广告要求的投放类别的个数N是否为1;如果是,执行S102;否则,执行S103;所述N≥1;
    S102、获取值为1的归一化需求强度值;
    S103、获取每个投放类别对应的优先级,根据所述优先级分别获取每个投放类别对应的归一化需求强度值。
  2. 根据权利要求1所述的广告精准投放方法,其特征在于,所述S103,包括:
    S1031、根据所述优先级将所述待投放广告的所有投放类别进行排序,得到每个投放类别的排列顺序;
    S1032、对于任一投放类别x,获取该投放类别x的排列顺序$rank_x$的倒数所述,N≥x≥1;
    S1033、将所述 $1 \over rank_x$加1,得到${1 \over rank_x} + 1$;
    S1034、获取所述${1 \over rank_x} + 1$的对数,得到$ \log({1 \over rank_x} + 1) $;
    S1035、获取$ \sum_{x=0}^N \log({1 \over rank_x} + 1) $;
    S1036、将所述$ \log({1 \over rank_x} + 1) $除以$ \sum_{x=0}^N \log({1 \over rank_x} + 1) $,得到所述投放类别$x$的归一化需求强度值。
  3. 根据专利要求1所述的广告精准投放方法,其特征在于,所述S20包括:
    S201、对于任一投放账号,获取该投放账号所发表的所有文章
    S202、分别获取该投放账号所发表的每篇文章的内容分类
    S203、分别获取该投放账号所发表的每篇文章的权重
    S204、根据该投放账号所发表的所有文章的内容分类和权重获取该投放账号的影响领域
  4. 根据权利要求3所述的广告精准投放方法,其特征在于,所述S202,包括:
    S2021、对于该投放账号所发表的任意一篇文章,对该篇文章进行分词,得到该篇文章的至少一个词语
    S2022、对于该篇文章的至少一个词语中任一词语,根据该词语在该篇文章中出现的次数获取该词语的出现频率,并根据所有文章中包含该词语的文章个数获取该词语的逆文档频率
    S2023、根据每个词语的出现频率和逆文档频率,以及预先训练的所有类别的类别分类器,分别获取该篇文章属于每个类别的权重
    S2024、根据该篇文章属于每个类别的权重,获取权重最大的类别作为该篇文章的内容分类
  5. 根据权利要求4所述的广告精准投放方法,其特征在于,在所述S2023之前,所述S202还包括:
    S2025、训练任一类别的类别分类器时,获取P篇文章样本和每篇文章样本的类别值,属于该类别的文章样本占所述P篇文章样本的一半;所述P>1;
    S2026、从所述P篇文章样本中随机选取第二个数篇文章,并分别获取第二个数篇文章中每篇文章的至少一个词语、每个词语的出现频率和每个词语的逆文档频率;
    S2027、将所述第二个数篇文章中每篇文章的至少一个词语、每个词语的出现频率和每个词语的逆文档频率,以及每篇文章样本的类别值,输入至预设第一核函数和第一模型参数的第一SVM分类器进行训练,得到该类别的类别分类器
    S2028、将所述P篇文章样本中除所述第二个数篇文章之外的测试文章分别输入到该类别的类别分类器,得到测试文章的分类结果
    S2029、根据测试文章的分类结果和类别值,判断分类差异是否大于预设第一分类阈值;如果小于,训练结束;否则,修改所述第一核函数和/或第一模型参数,重新执行所述S2027至S2029。
  6. 根据权利要求3所述的广告精准投放方法,其特征在于,所述S203包括:
    S2031、对于该投放账号所发表的任意一篇文章,获取该篇文章的转发数和所有文章的最高转发数;
    S2032、将该篇文章的转发数除以最高转发数,得到转发数加权
    S2033、获取该篇文章的评论数和所有文章的最高评论数;
    S2034、将该篇文章的评论数除以最高评论数,得到评论数加权
    S2035、根据该篇文章的发表时间获取该篇文章的时间加权
    S2036、获取转发数加权、评论数加权和时间加权之积,得到该篇文章的权重
  7. 根据权利要求3所述的广告精准投放方法,其特征在于,所述S204,包括:
    S2041、将该投放账号所发表的所有文章中相同内容分类的文章的权重求和,得到每个内容分类的汇总权值
    S2042、将该投放账号所发表的所有文章的权重求和,得到所有类别的汇总权值
    S2043、分别将每个内容分类的汇总权值除以所有类别的汇总权值,得到每个内容分类的归一化权重值
    S2044、从每个内容分类的归一化权重值中,获取预设第三个数个较大的归一化权重值;
    S2045、根据所述第三个数个较大的归一化权重值对应的内容分类确定该投放账号的影响领域
  8. 根据权利要求3所述的广告精准投放方法,其特征在于,所述S30包括:
    S301、对于任一投放账号的任一影响领域,获取属于该影响领域的分类文章组;
    S302、根据所述分类文章组中每篇文章的转发数,获取转发中位数
    S303、获取该投放账号的账号真粉率
    S304、根据所述账号真粉率、该投放账号的粉丝数和关注数、所有投放账号的个数和预设阻尼系数,获取该投放账号的PR值;(表现网页等级的一个标准,级别分别是0到10,是Google用于评测一个网页“重要性”的一种方法)
    S305、根据该投放账号的PR值、粉丝数、转发中位数和提及数,获取该投放账号在该影响领域下的影响力
  9. 根据权利要求8所述的广告精准投放方法,其特征在于,所述S303包括:
    S3031、对于该投放账号的所有粉丝中任一粉丝,获取该粉丝的粉丝特征信息,所述粉丝特征信息包括粉丝昵称中包含的英文字母的个数、粉丝昵称中包含的数字的个数、个人描述的字数、是否有头像、收藏数、关注数、粉丝数、微博数、关注与粉丝之比中的一种或多种;
    S3032、将该粉丝的粉丝特征信息预设基础规则进行匹配,判断该粉丝是否属于僵尸粉;当该粉丝不属于僵尸粉时,执行S3033;否则,执行S3034;
    S3033、将该粉丝的粉丝特征信息输入到预先训练的僵尸粉分类器,获取该粉丝的真粉率;
    S3034、获取值为0的该粉丝的真粉率;
    S3035、获取该投放账号所有粉丝的真粉率之和,得到总真粉率
    S3036、将所述总真粉率除以该投放账号的粉丝个数,得到该投放账号的账号真粉率
  10. 根据权利要求9所述的广告精准投放方法,其特征在于,在所述S3033之前,所述S303还包括:
    S3037、获取Q个粉丝样本、每个粉丝样本的粉丝特征信息和真粉值;
    S3038、从所述Q个粉丝样本中随机选取第四个数个粉丝样本,将第四个数个粉丝样本中每个粉丝样本的粉丝特征信息和真粉值输入预设第二核函数和第二模型参数的第二SVM分类器进行训练,得到僵尸粉分类器
    S3039、将所述Q个粉丝样本中除上述第四个数个粉丝样本之外的粉丝样本分别输入到所述僵尸粉分类器,得到僵尸粉分类结果
    S3030、根据所述僵尸粉分类结果和真粉值,判断分类差异是否大于预设第二分类阈值;如果小于,训练结束;否则,修改所述第二核函数和/或第二模型参数,重新执行所述S3038至S3030。
  11. 根据权利要求8所述的广告精准投放方法,其特征在于,所述S304包括:
    S3041、获取该投放账号$t$的初始PR值$PR_t^{(0)}$, 所述$PR_t^{(0)}=\log(10+followersCount_t*TFR_t)$, 所述$followersCount_t$为该投放账号$t$的粉丝数,$TFR_t$为该投放账号$t$的账号真粉率;预设所有投放账号的个数为$y$,所述$y≥1,y≥t≥1$;
    S3042、根据所述$PR_t^{(0)}$和$y$获取该投放账号$t$第$i$轮迭代的PR值$PR_t^{(i)}$, 所述$PR_t^{(i)}=d*\sum_{j=1}^{followersCcount_t} {PR_j^{(i-1)} \over friendsCount_t} + {(1-d) \over y}$; 所述$d$为预设阻尼系数,所述$d>0$;所述$PR_j^{(i-1)}$为投放账号$j$第$i-1$轮迭代的PR值;所述$friendsCount_t$为投放账号$j$的关注数;所述$u≥i≥1$,所述$u$为预设迭代阈值;
    S3043、根据该投放账号$t$第$i$轮迭代的PR值$PR_t^i$, 获取整体PR差异$\Delta PR_i$, 所述$\Delta PR_i=\sum_t^y abs(PR_t^{(i)} - PR_t^{(i-1)})$;
    S3044、判断所述$\Delta PR_i$是否小于预设差异阈值;如果小于,根据迭代结果获取该投放账号的PR值;否则,判断迭代次数是否超过预设迭代阈值,如果超过预设迭代阈值,计算结束;如果未超过预设迭代阈值,将所述$i$置为$i+1$后重复所述S3042至S3044。
  12. 根据权利要求8所述的广告精准投放方法,其特征在于,所述S305包括:
    S3051、获取该投放账号$t$的粉丝数的标准化值$f1$,所述$f1=\log(followersCount_t+e)$;所述$followersCount_t$为该投放账号$t$的粉丝数;
    S3052、获取该投放账号t的提及数的标准化值$f2$,$f2=\log(reference_Count_t+e)$;所述$reference_Count_t$为该投放账号$t$的提及数;
    S3053、获取该投放账号$t$的转发中位数的标准化值$f3$,$f3=\log(medPostCount_t+e)$;所述$medPostCount_t$为该投放账号$t$的转发中位数;
    S3054、获取该投放账号$t$的PR值的标准化值$f4$,所述$f4=\log(PR+e)$;所述PR为该投放账号$t$的PR值;
    S3055、获取$f_2$的归一化值$f_2^{'}={1 \over {1 + e^{-1 * (a_1 * f_2 + b_1)}}}$, 所述$a1>0,b1<0$;
    S3056、获取$f_3$的归一化值$f_3^{'}={1 \over {1 + e^{-1 * (a_2 * f_3 + b_2)}}}$, 所述$a2>0,b2<0$;
    S3057、对所述$f1$、$f_2^{'}$、$f_3^{'}$和$f_4$进行线性组合,得到初步得分score,所述$score=a_3*f1 + b_3*f_2^{'} + c_3*f_3^{'} + d_3*f_4$;所述$a_3$、$b_3$、$c_3$、$d_3$均大于0;
    S3058、对所述初步得分score进行对数化处理,得到logisticScore,所述$logisticScore=1 \over {1 + e^{-1 * (a_4)*score+b_4}}$;所述$a_4>0,b_4<0$;
    S3059、对所述logisticScore进行分值变化,得到该投放账号在该影响领域下的影响力inft,所述$inft=e*logisticScore*10+2+100.5$;所述$e$为自然常数。
  13. 根据权利要求1所述的广告精准投放方法,其特征在于,所述S40,包括:
    S401、将预设M个类别中除所述至少一个投放类别外其他类别的归一化需求强度值设置为0;
    S402、将预设M个类别中除每个投放账号的各自影响领域外的其他类别的影响力设置为0;
    S403、对于任意账号,获取$\sum_{j=1}^M ads_j*acs_j$, 所述$ads_j$为类别$j$的归一化需求强度值,所述$acs_j$该投放账号在类别$j$下的影响力;所述$M≥1,M≥j≥1$;
    S404、获取$\sqrt{\sum_{j=1}^M ads_j^2}$和$\sqrt{\sum_{j=1}^M acs_j^2}$之积,得到向量积;
    S405、将所述$\sum_{j=1}^M ads_j*acs_j$除以所述向量积,得到该投放账号的匹配度
  14. 一种广告精准投放系统,其特征在于,包括:
    预处理服务器、数据处理集群、数据库服务器和供电电源;所述供电电源用于为所示预处理服务器、数据处理集群和数据库服务器供电;所述预处理服务器和所述数据库服务器分别与所述数据处理集群相连;
    所述预处理服务器,包括:用于接收广告投放请求的请求接收单元;用于获取初始账号信息的信息抓取单元;与所述信息抓取单元相连,用于对所述初始账号信息进行预处理,得到处理后的账号信息的预处理单元;分别与所述请求接收单元和所述预处理单元相连,用于将处理后的账号信息和所述广告投放请求要求的投放类别发送至所述数据处理集群的信息发送单元;
    所述数据处理集群,包括:与所述信息发送单元相连,用于接收所述处理后的账号信息和广告投放请求要求的投放类别的信息接收单元;与所述信息接收单元相连,用于根据所述广告投放请求要求的投放类别获取对应的归一化需求强度值的归一化处理单元;与所述信息接收单元相连,用于根据处理后的账号信息获取所有账号的影响领域的领域处理单元;与所述领域处理单元相连,用于根据领域处理单元获取的所有账号的影响领域,分别获取每个投放账号在各自影响领域下的影响力的影响力处理单元;分别与所述归一化处理单元和所述影响力处理单元相连,用于根据所述待投放广告要求的投放类别对应的归一化需求强度值和每个投放账号在各自影响领域下的影响力,获取所述待投放广告与每个投放账号的匹配度的匹配度处理单元;与所述匹配度处理单元,用于由匹配度高到低依次从所有投放账号中选取预设第一个数个账号作为目标投放账号,并向所述数据库服务器发送所述目标投放账号的账号发送单元;
    所述数据库服务器,用于接收所述数据处理集群发送的目标投放账号并存储;
    所述归一化处理单元用于根据所述广告投放请求要求的投放类别获取对应的归一化需求强度值,所述归一化需求强度值的获取包括:
    S101、判断所述待投放广告要求的投放类别的个数N是否为1;如果是,执行S102;否则,执行S103;所述N≥1;
    S102、获取值为1的归一化需求强度值;
    S103、获取每个投放类别对应的优先级,根据所述优先级分别获取每个投放类别对应的归一化需求强度值。
  15. 根据权利要求14所述的广告精准投放系统,其特征在于,还包括:
    存储服务器,所述存储服务器与所述预处理服务器相连,用于对所述预处理服务器获取的初始账号信息和/或待投放广告要求的投放类别进行备份。
  16. 根据权利要求14所述的广告精准投放系统,其特征在于,还包括:
    请求队列服务器,与所述预处理服务器相连,用于对输入到预处理服务器的广告投放请求进行排队,并根据排队结果依次向所述预处理服务器发送广告投放请求。
  17. 根据权利要求14所述的广告精准投放系统,其特征在于,还包括:
    数据抓取集群,与所述预处理服务器相连,用于抓取初始账号信息,并向所述预处理服务器发送所述初始账号信息。
  18. 根据权利要求14所述的广告精准投放系统,其特征在于,还包括:
    不间断电源,所述不间断电源用于为所述数据处理集群和所述数据库服务器供电。
  19. 根据权利要求14至18中任意一项所述的广告精准投放系统,其特征在于,所述数据处理集群,包括:
    名称节点和至少一个数据节点,所述名称节点与所述至少一个数据节点相连;
    所述名称节点,用于接收所述预处理服务器发送的处理后的账号信息和投放类别,并从所述至少一个数据节点中选取目标节点后,向所述目标节点发送所述处理后的账号信息和投放类别;
    所述目标数据节点,包括:所述信息接收单元、所述归一化处理单元、所述领域处理单元、所述影响力处理单元、所述匹配度处理单元和所述账号发送单元。

说明书

广告精准投放方法和系统 | 技术领域 | 本发明涉及广告投放领域,尤其涉及一种广告精准投放方法和系统。

背景技术
社交网络经过近几年的发展,已经逐渐成为人们相互沟通、传播信息的重要途径之一;由于社交网络具有传播速度快、覆盖范围广等特点,因此越来越多的广告商选择通过社交网络推送广告。现有技术中,广告推送一般使用广播的方式,即向所有社交网络的用户无差别的推送广告。
然而,由于每个广告的受众群体不同,因此无差别的广告推送可能引起用户的反感甚至导致用户屏蔽广告,进而导致广告的传播效果较差。
因此,对于新型的广告精准投放方法和系统,仍存在着继续研究的必要和需求,这也正是本发明得以完成的动力所在和基础所倚。

发明内容
为了解决上述缺陷以及提供新型的广告精准投放方法和系统,本发明人进行了大量的深入研究,在付出了创造性劳动后,从而完成了本发明。
具体而言,本发明提供一种广告精准投放方法和系统,所述方法和系统能够有针对性地推送广告,进而提升广告的传播效果。
更具体而言,第一个方面,本发明提供一种广告精准投放方法,所述方法包括:
S10、根据待投放广告要求的投放类别获取对应的归一化需求强度值;
S20、分别获取预设所有投放账号的影响领域
S30、分别获取每个投放账号在各自影响领域下的影响力
S40、根据所述待投放广告要求的投放类别对应的归一化需求强度值和每个投放账号在各自影响领域下的影响力,获取所述待投放广告与每个投放账号的匹配度
S50、由匹配度高到低依次从所有投放账号中选取预设第一个数个账号作为目标投放账号,并向所述目标投放账号发送所述待投放广告。
在本发明的所述广告精准投放方法中,所述S10,包括:
S101、判断所述待投放广告要求的投放类别的个数N是否为1;如果是,执行S102;否则,执行S103;所述N≥1;
S102、获取值为1的归一化需求强度值;
S103、获取每个投放类别对应的优先级,根据所述优先级分别获取每个投放类别对应的归一化需求强度值。
在本发明的所述广告精准投放方法中,所述S103,包括:
S1031、根据所述优先级将所述待投放广告的所有投放类别进行排序,得到每个投放类别的排列顺序;
S1032、对于任一投放类别$x$,获取该投放类别$x$的排列顺序$rank_x$的倒数$1 \over rank_x$;所述,N≥x≥1;
S1033、将所述$1 \over rank_x$加1,得到${1 \over rank_x} + 1$;
S1034、获取所述${1 \over rank_x} + 1$的对数,得到$\log_({1 \over rank_x} + 1)$;
S1035、获取$\sum_{x=1}^N \log_({1 \over rank_x} + 1)$;
S1036、将所述$\log_({1 \over rank_x} + 1)$除以$\sum_{x=1}^N \log_({1 \over rank_x} + 1)$,得到所述投放类别x的归一化需求强度值。
在本发明的所述广告精准投放方法中,所述S20包括:
在本发明的所述广告精准投放方法中,所述S202,包括:
在本发明的所述广告精准投放方法中,在所述S2023之前,所述S202还包括:
在本发明的所述广告精准投放方法中,所述S203包括:
在本发明的所述广告精准投放方法中,所述S30包括:
在本发明的所述广告精准投放方法中,所述S303包括:
在本发明的所述广告精准投放方法中,所述S304包括:
在本发明的所述广告精准投放方法中,所述S305包括:
在本发明的所述广告精准投放方法中,所述S40,包括:

第二个方面,本发明还提供了一种使用上述广告精准投放方法的广告精准投放系统,所述系统包括:

如上所述,本发明提供了一种广告精准投放方法和系统,通过该方法和系统的使用,可根据待投放广告的归一化需求强度值每个投放账号的影响力,确定待投放广告与每个投放账号的匹配度,从所有投放账号中选取匹配度较高的进行广告投放,从而实现广告精准投放。
本发明所提供的所述方法和系统,解决了现有技术中由于每个广告的受众群体不同,因此无差别的广告推送可能引起用户的反感甚至导致用户屏蔽广告,进而导致广告的传播效果较差的问题,具有良好的应用潜力。

附图说明
图1为本发明实施例1提供的广告精准投放方法的流程图;
图2为本发明实施例2提供的广告精准投放系统的结构示意图一;
图3为本发明实施例2提供的广告精准投放系统的结构示意图二;
图4为本发明实施例2提供的广告精准投放系统的结构示意图三;
图5为本发明实施例2提供的广告精准投放系统的结构示意图四;
图6为本发明实施例2提供的广告精准投放系统的结构示意图五。

具体实施方式

下面结合附图和实施例对本发明进一步说明。但这些例举性实施方式的用途和目的仅用来例举本发明,并非对本发明的实际保护范围构成任何形式的任何限定,更非将本发明的保护范围局限于此。

实施例1
如图1所示,本发明实施例提供一种广告精准投放方法,包括:

  • 步骤101,根据待投放广告要求的投放类别获取对应的归一化需求强度值
    在本实施例中,系统可以预设内容分类体系,当广告提供商需要投放广告时,根据该内容分类体系选取该待投放广告的投放类别;该选取的投放类别可以为一个类别或多个类别,当为多个类别时,还需要设置每个投放类别对应的优先级,使系统能够根据要求的投放类别和设置的优先级实现精准投放。
    在本实施例中,通过步骤101获取归一化需求强度值的过程包括:首先判断待投放广告要求的投放类别的个数N是否为1,该N≥1;如果待投放广告要求的投放类别的个数N为1,获取值为1的归一化需求强度值;如果待投放广告要求的投放类别的个数N不为1,获取每个投放类别对应的优先级,根据该优先级分别获取每个投放类别对应的归一化需求强度值。其中,根据该优先级分别获取每个投放类别对应的归一化需求强度值,包括:根据优先级将待投放广告的所有投放类别进行排序,得到每个投放类别的排列顺序;对于任一投放类别$x$,获取该投放类别$x$的排列顺序$rank_x$的倒数N≥x≥1;将加1,得到获取的对数,得到获取将除以得到该投放类别$x$的归一化需求强度值。通过上述过程,依次将$x$取值1~N,即可得到每个投放类别的归一化需求强度值。
    在本实施例中,当待投放广告的投放类别不能完全覆盖内容分类体系的所有预设类别时,可以将未覆盖的类别的归一化需求强度值设为0。
  • 步骤102,分别获取预设所有投放账号的影响领域
    在本实施例中,当广告商需要投放广告时,可以选择该待投放广告的投放平台,从而确定预设的所有投放账号。步骤102可以根据每个投放账号的信息统计用户的行为,进而确定投放账号的影响领域;具体的,通过步骤102获取预设所有投放账号的影响领域的过程包括:对于任一投放账号,获取该投放账号所发表的所有文章;分别获取该投放账号所发表的每篇文章的内容分类;分别获取该投放账号所发表的每篇文章的权重;根据该投放账号所发表的所有文章的内容分类和权重获取该投放账号的影响领域
    其中,可以通过网络爬虫分别获取该投放账号所发表的所有文章,也可以通过其他方式获取该投放账号所发表的所有文章,在此不做限制;通过上述过程,既可以获取该投放账号所发表的每篇文章的内容,也可以获取每篇文章的评论数、转发数和发送时间等信息。
    分别获取该投放账号的每篇文章的内容分类的过程包括:对于该投放账号所发表的任意一篇文章,对该篇文章进行分词,得到该篇文章的至少一个词语;对于该篇文章的至少一个词语中任一词语,根据该词语在该篇文章中出现的次数获取该词语的出现频率,并根据所有文章中包含该词语的文章个数获取该词语的逆文档频率;根据每个词语的出现频率和逆文档频率,以及预先训练的所有类别的类别分类器,获取该篇文章属于每个类别的权重;根据该篇文章属于每个类别的权重,获取权重最大的类别作为该篇文章的内容分类。其中,可以使用普通分词词典对该篇文章进行分词;进一步的,为提高分词的准确率,还可以使用普通分词词典与自定义分词词典结合的方式对该篇文章进行分词,该自定义分词词典可以包含昵称、自定义标签等词语,此时还可以结合预先设置的普通分词词典和自定义分词词典的优先级进行分词,在此不再一一赘述。词语的出现频率等于该词语在该篇文章中出现的次数,词语的逆文档频率可以等于所有文章中包含该词语的文章个数的倒数。通过对该篇文章进行分词,能够清洗该篇文章中标点符号以及不具有含义的文字等,从而得到至少一个有含义的词语。
    可以基于通用SVM算法,通过参数定制的方式,分别训练生成每个类别的类别分类器;训练类别分类器的过程包括:训练任一类别的类别分类器时,获取P篇文章样本和每篇文章样本的类别值,属于该类别的文章样本占P篇文章样本的一半;P>1;从P篇文章样本中随机选取第二个数篇文章,并分别获取第二个数篇文章中每篇文章的至少一个词语、每个词语的出现频率和每个词语的逆文档频率;将第二个数篇文章中每篇文章的至少一个词语、每个词语的出现频率和每个词语的逆文档频率,以及每篇文章样本的类别值,输入至预设第一核函数和第一模型参数的第一SVM分类器进行训练,得到该类别的类别分类器;将P篇文章样本中除第二个数篇文章之外的测试文章分别输入到该类别的类别分类器,得到测试文章的分类结果;根据测试文章的分类结果和类别值,判断分类差异是否大于预设第一分类阈值;如果小于,训练结束;否则,修改第一核函数和/或第一模型参数,重新执行SVM分类器训练、测试文章分类和分类差异判断过程。其中,为了提高训练的准确率,P一般大于500;第二个数一般占P的80%以上;当文章样本属于该类别时,该文章样本的类别值为1,否则为0;初始模型参数中惩罚系数C=1,新特征空间的分布控制系数Gamma=8,核函数=径向基函数;分别获取第二个数篇文章中每篇文章的至少一个词语、每个词语的出现频率和每个词语的逆文档频率与之前分别获取该投放账号的每篇文章的内容分类的过程中分词、获取出现频率和逆文档频率的过程类似,在此不再一一赘述。
    分别获取该投放账号所发表的每篇文章的权重的过程包括:对于该投放账号所发表的任意一篇文章,获取该篇文章的转发数和所有文章的最高转发数;将该篇文章的转发数除以最高转发数,得到转发数加权;获取该篇文章的评论数和所有文章的最高评论数;将该篇文章的评论数除以最高评论数,得到评论数加权;根据该篇文章的发表时间获取该篇文章的时间加权;获取转发数加权、评论数加权和时间加权之积,得到该篇文章的权重。其中,时间加权的计算方式可以预先设置,如设定发表时间在一个月以内的时间加权为1,设定发表时间在一个月至半年之间的时间加权为0.7,设定发表时间在半年以外的时间加权为0.3等,也可以设定时间加权的其他计算方式,在此不做限制。
    分别获取每个投放账号的影响领域的过程包括:将该投放账号所发表的所有文章中相同内容分类的文章的权重求和,得到每个内容分类的汇总权值;将该投放账号所发表的所有文章的权重求和,得到所有类别的汇总权值;分别将每个内容分类的汇总权值除以所有类别的汇总权值,得到每个内容分类的归一化权重值;从每个内容分类的归一化权重值中,获取预设第三个数个较大的归一化权重值;根据第三个数个较大的归一化权重值对应的内容分类确定该投放账号的影响领域。其中,第三个数可以根据需要设定,如设置为3等,在此不做限制。
  • 步骤103,分别获取每个投放账号在各自影响领域下的影响力
    在本实施例中,通过步骤103获取影响力的过程包括:对于任一账号的任一影响领域,获取属于该影响领域的分类文章组;根据分类文章组中每篇文章的转发数,获取转发中位数;获取该投放账号的账号真粉率;根据账号真粉率、该投放账号的粉丝数、所有投放账号的个数、和预设阻尼系数,获取该投放账号的PR值;根据该投放账号的PR值、粉丝数、转发中位数和提及数,获取该投放账号在该影响领域下的影响力
    具体的,通过步骤102确定所有投放账号的影响领域以及每个投放账号所发表的每篇文章的内容分类后,可以将同一投放账号的内容分类与影响领域进行匹配,确定每篇文章的影响领域。获取某影响领域的影响力时,从所有文章中选取属于该影响领域的文章即可。
    获取该投放账号的账号真粉率的过程,包括:对于该投放账号的所有粉丝中任一粉丝,获取该粉丝的粉丝特征信息,粉丝特征信息包括粉丝昵称中包含的英文字母的个数、粉丝昵称中包含的数字的个数、个人描述的字数、是否有头像、收藏数、关注数、粉丝数、微博数、关注与粉丝之比中的一种或多种;分别将该粉丝的粉丝特征信息与预设基础规则进行匹配,判断该粉丝是否属于僵尸粉;当该粉丝不属于僵尸粉时,将该粉丝的粉丝特征信息输入到预设僵尸粉分类器,获取该粉丝的真粉率;当该粉丝属于僵尸粉时,获取值为0的该粉丝的真粉率;获取该投放账号所有粉丝的真粉率之和,得到总真粉率;将总真粉率除以该投放账号的粉丝个数,得到该投放账号的账号真粉率。其中,预设基础规则可以设定文章数、粉丝数、关注与粉丝之比、微博名称与真粉率的关系。
    可以基于通用SVM算法,通过参数定制的方式,分别训练生成僵尸粉分类器;具体的,僵尸粉分类器的训练过程包括:获取Q个粉丝样本、每个粉丝样本的粉丝特征信息和真粉值;从Q个粉丝样本中随机选取第四个数个粉丝样本,将第四个数个粉丝样本中每个粉丝样本的粉丝特征信息和真粉值输入预设第二核函数和第二模型参数的第二SVM分类器进行训练,得到僵尸粉分类器;将Q个粉丝样本中除上述第四个数个粉丝样本之外的粉丝样本分别输入到僵尸粉分类器,得到僵尸粉分类结果;根据僵尸粉分类结果和真粉值,判断分类差异是否大于预设第二分类阈值;如果小于,训练结束;否则,修改第二核函数和/或第二模型参数,重新执行SVM分类器训练、测试文章分类和分类差异判断过程。为了提高训练的准确率,Q一般大于500;第四个数一般占Q的80%以上;当粉丝为真粉时,该粉丝的真粉值为1,否则为0;初始模型参数中惩罚系数C=1,新特征空间的分布控制系数Gamma=8,核函数=径向基函数。
    获取投放账号的PR值的过程包括:获取该投放账号$t$的初始PR值$followersCount_t$为该投放账号$t$的粉丝数,$TFR_t$为该投放账号$t$的账号真粉率;预设所有投放账号的个数为$y$,$y≥1,y≥t≥1$;根据和$y$获取该投放账号$t$第$i$轮迭代的PR值$d$为预设阻尼系数,$d>0$;为投放账号$j$第$i-1$轮迭代的PR值;$friendsCount_j$为投放账号$j$的关注数;$u≥i≥1$,$u$为预设迭代阈值;根据该投放账号$t$第$i$轮迭代的PR值获取整体PR差异ΔPRi,判断ΔPRi是否小于预设差异阈值;如果小于,根据迭代结果获取该投放账号的PR值;否则,判断迭代次数是否超过预设迭代阈值,如果超过预设迭代阈值,计算结束;如果未超过预设迭代阈值,将$i$置为$i+1$后重复第i轮迭代的PR值的计算、整体差异的计算和判断过程。其中,如果某个账号没有落在$friendsCount_j$内,该账号的为0;根据迭代结果获取投放账号的PR值时,该投放账号的PR值为最后一次迭代的PR值。
    获取投放账号在该影响领域下的影响力的过程包括:获取该投放账号t的粉丝数的标准化值$f_1$,$f_1=\log(followersCount_t+e)$;$followersCount_t$为该投放账号$t$的粉丝数;获取该投放账号t的提及数的标准化值$f_2$,$f_2=\log(referenceCount_t+e)$;$referenceCount_t$为该投放账号$t$的提及数;获取该投放账号t的转发中位数的标准化值$f_3$,$f_3=\log(medPostCount_t+e)$;获取该投放账号$t$的PR值的标准化值$f_4$,$f_4=\log(PR+e)$;PR为该投放账号$t$的PR值;获取$f_2$的归一化值$f_2^{'}$,$a1>0,b1<0$;获取$f_3$的归一化值$f_3^{'}$,$a2>0,b2<0$;对$f_1$、$f_2^{'}$、$f_3^{'}$和$f_4$进行线性组合,得到初步得分score,$score=a_3*f_1+b_3*f_2^{'}+c_3*f_3^{'}+d_3*f_4$;$a_3$、$b_3$、$c_3$、$d_3$均大于0;对初步得分score进行对数化处理,得到logisticScore,$a_4>0,b4<0$;对logistic_score进行分值变化,得到该投放账号在该影响领域下的影响力inft,$inft=e*logisticScore*10+2+100.5$;$e$为自然常数。在本实施例中,上述参数可以根据影响力数值的账号排序结果,与人工选定的账号优劣排序结果进行比对;根据试验得到最匹配的参数。特别的,为了提高影响力评估的准确性,可以对各参数进行以下设定:a1=0.355,b1=-1.732,a2=0.281,b2=-0.54,a3=0.4122,b3=0.3199,c3=0.2545,d3=0.0024,a4=0.231,b4=-0.393。
  • 步骤104,根据待投放广告要求的投放类别对应的归一化需求强度值和每个投放账号在各自影响领域下的影响力,获取待投放广告与每个投放账号的匹配度
    在本实施例中,通过步骤104获取匹配度的过程包括:将预设M个类别中除至少一个投放类别外其他类别的归一化需求强度值设置为0;将预设M个类别中除每个投放账号的各自影响领域外的其他类别的影响力设置为0;对于任意账号,获取$ads_j$为类别j的归一化需求强度值,$acs_j$该投放账号在类别$j$下的影响力;$M≥1,M≥j≥1$;获取和之积,得到向量积;将除以向量积,得到该投放账号的匹配度。
  • 步骤105,由匹配度高到低依次从所有投放账号中选取预设第一个数个账号作为目标投放账号,并向目标投放账号发送待投放广告
    在本实施例中,通过步骤105可以首先将待投放广告与每个投放账号的匹配度进行排序,然后由匹配度高到低依次从所有投放账号中选取第一个数个账号作为投放账号。
    本发明实施例提供的广告精准投放方法,由于根据待投放广告的归一化需求强度值与每个投放账号的影响力,确定待投放广告与每个投放账号的匹配度,从所有投放账号中选取匹配度较高的进行广告投放,从而实现广告精准投放。本发明实施例提供的技术方案,解决了现有技术中由于每个广告的受众群体不同,因此无差别的广告推送可能引起用户的反感甚至导致用户屏蔽广告,进而导致广告的传播效果较差的问题。

实施例2

如图2所示,本发明实施例提供的广告精准投放系统,包括:
预处理服务器201、数据处理集群202、数据库服务器203供电电源;供电电源用于为所示预处理服务器、数据处理集群和数据库服务器供电;预处理服务器和数据库服务器分别与数据处理集群相连。

  • 其中,所述预处理服务器201,包括:用于接收广告投放请求的请求接收单元2011;用于获取初始账号信息的信息抓取单元2012;与所述信息抓取单元相连,用于对所述初始账号信息进行预处理,得到处理后的账号信息的预处理单元2013;分别与所述请求接收单元和所述预处理单元相连,用于将处理后的账号信息和所述广告投放请求要求的投放类别发送至所述数据处理集群的信息发送单元2014;
  • 所述数据处理集群202,包括:与所述信息发送单元相连,用于接收所述处理后的账号信息和广告投放请求要求的投放类别的信息接收单元2021;与所述信息接收单元相连,用于根据所述广告投放请求要求的投放类别获取对应的归一化需求强度值的归一化处理单元2022;与所述信息接收单元相连,用于根据处理后的账号信息获取所有账号的影响领域的领域处理单元2023;与所述领域处理单元相连,用于根据领域处理单元获取的所有账号的影响领域,分别获取每个投放账号在各自影响领域下的影响力的影响力处理单元2024;分别与所述归一化处理单元和所述影响力处理单元相连,用于根据所述待投放广告要求的投放类别对应的归一化需求强度值和每个投放账号在各自影响领域下的影响力,获取所述待投放广告与每个投放账号的匹配度的匹配度处理单元2025;与所述匹配度处理单元,用于由匹配度高到低依次从所有投放账号中选取预设第一个数个账号作为目标投放账号,并向所述数据库服务器发送所述目标投放账号的账号发送单元2026;
  • 所述数据库服务器,用于接收所述数据处理集群发送的目标投放账号并存储。
    在本实施例中,通过预处理服务器、数据处理集群和数据库服务器确定目标投放账号,进而实现广告精准投放的过程,与本发明实施例1提供的相似,在此不再一一赘述。
    进一步的,如图3所示,本发明实施例提供的广告精准投放系统,还包括:
    存储服务器204,存储服务器与预处理服务器相连,用于对预处理服务器获取的初始账号信息和/或待投放广告要求的投放类别进行备份。在本实施例中,为防止预处理服务器出现故障导致数据丢失,设置存储服务器,以对预处理服务器的数据进行备份。
    进一步的,如图4所示,本发明实施例提供的广告精准投放系统,还包括:
    请求队列服务器205,与预处理服务器相连,用于对输入到预处理服务器的广告投放请求进行排队,并根据排队结果依次向预处理服务器发送广告投放请求。在本实施例中,为防止广告投放请求丢失,在向预处理服务器发送广告投放请求时,可以先通过请求队列服务器对广告投放请求进行排队。
    进一步的,如图5所示,本实施例提供的广告精准投放系统,还包括:
    数据抓取集群206,与预处理服务器相连,用于抓取初始账号信息,并向预处理服务器发送初始账号信息。在本实施例中,可以通过数据库实时存储初始账号信息;为获得最新的初始账号信息,预处理服务器在获取时,可以直接通过数据抓取集群进行抓取。
    进一步的,本实施例提供的广告精准投放系统还包括:不间断电源,不间断电源用于为数据处理集群和数据库服务器供电。在本实施例中,为防止供电电源突然断电,对数据处理集群和数据库服务器造成影响,可以单独通过不间断电源为数据处理集群和数据库服务器进行供电。
    进一步的,如图6所示,为了提高数据处理速度,本实施例提供的广告精准投放系统中数据处理集群,包括:
    名称节点和至少一个数据节点,所述名称节点与所述至少一个数据节点相连;
    所述名称节点2027,用于接收所述预处理服务器发送的处理后的账号信息和投放类别,并从所述至少一个数据节点中选取目标节点后,向所述目标节点发送所述处理后的账号信息和投放类别;
    所述目标数据节点2028,包括:所述信息接收单元、所述归一化处理单元、所述领域处理单元、所述影响力处理单元、所述匹配度处理单元和所述账号发送单元。
    在本实施例中,名称节点可以为目标数据节点处理信息进行分配,实现负载均衡,从而提高处理速度。
    本发明实施例提供的广告精准投放系统,由于根据待投放广告的归一化需求强度值与每个投放账号的影响力,确定待投放广告与每个投放账号的匹配度,从所有投放账号中选取匹配度较高的进行广告投放,从而实现广告精准投放。本发明实施例提供的技术方案,解决了现有技术中由于每个广告的受众群体不同,因此无差别的广告推送可能引起用户的反感甚至导致用户屏蔽广告,进而导致广告的传播效果较差的问题。
    以上实施例的先后顺序仅为便于描述,不代表实施例的优劣。
    最后应说明的是:以上实施例仅用以说明本发明的技术方案,而非对其限制;尽管参照前述实施例对本发明进行了详细的说明,本领域的普通技术人员应当理解:其依然可以对前述各实施例所记载的技术方案进行修改,或者对其中部分技术特征进行等同替换;而这些修改或者替换,并不使相应技术方案的本质脱离本发明各实施例技术方案的精神和范围。

Technical Report Writing Skills

撰写流程

  1. 首页:
    • 页眉: 公司徽标或logo
    • 标题
    • Version: 1.0 | Jan 2020
    • Prepared by: Person 1 | Reviewed by: Person 2
    • Revision History
      Version Author(s) Description of Version Date
      1.0 Person 1 Initial document drafting 13 Feb 2019
      1.1 Person 2 Chapter 3 (3.1, 3.2, 3.3) 16 Feb 2019
  2. 第二页
    • 目录: ​Table of Contents
  3. 之后
    • 正文

目录模板

模板1

1. Introduction
    1. Project Purpose
    2. Project Deliverables
    3. Technical Challenges and Proposed Solutions
    3.  
    4. State-of-the-art Technigues Integration   
    5. Novel Features 

2. Technical Preliminaries 
    1. Facial Recognition
    2. Adversarial Attacks and Defenses 
    3. Face Swapping and Detection 

3. Platform System Architecture
    1. Facial Recognition System
        1. FaceNet Model
        2. VGG-Face Model
    2. Adversarial Attack System
    3. Adversary Detection System
    4. Face Swapping System
    5. Face Swapping Detection System

4. Software Architecure Design 
    1. Overall Architecture 
    2. Adversary Attack Tesing Module 
    3. Adversary Attacks Detection and Forensics Module
    4. Face Swapping Detection and Forensics Module 
    5. Intermediate Transmission Modules
    6. GUI Analysis and Visualization module
    7. The other module 

5. Conclusion 
    1. Project Achievements
    2. Future Research 

- References

- Appendix I 
3. ASTRI Adversarial Face Detection System 
    1. System Architecture 
    2. Facenet Recognition System 
        1. Face Detection
        2. Face Alignment
        3. Face Comparison
        4. Face Recognition
    3. Adversarial Attack System 
        1. Threat Model
        2. Adversarial Attack Theory
        3. Implement Adversarial Attack
        4. Adversarial Attack Result
    4. Adversarial Face Detection System
        1. Face 68 landmarks Location
        2. VGG-Face Model Architecture
        3. Attribute Neurons Extraction
        4. Augmented Model Implementation
        5. Adversarial Face Detection

4. Face swapping and detection system
    1. System Architecture 
    2. Face Swapping System 
        1. FFmpeg 
        2. Face Location 
        3. Face swapping 
    3. XceptionNet Detecting System
        1. Xception Model Architecture 
        2. Automatic Xception Detection Method 
    4. FWA(Face Warping Artifacts) Detection System
        1. Face Warping Artifacts Method 
        2. Method 

- Testing and Forensics Results  
    1. Test cases
        1. Case I: Adversarial attack testing 
        2. Case 2: Adversarial face detection testing 
    2. Results Analysis

模板2

1. Introduction

2. System Architecture
    1. System Architecture Design
    2. Web Frontend Portal Server
    3. Web Admin Portal Server
    4. OCR Engine Server
    5. Data Management and Analytics Server
    6. Credit Assessment Engine Server
    7. Loan Management Server
    8. User Management Server
    9. Identity and Access Management Server
    10. Secure Data Server
    11. API Partner Servers
    12. Database Servers

- APPENDIX I
    1.	System Process Flow
    2.	Application Process Flow
    3.	API Specifications
    4.	UML Sequence Diagrams
    5.	Define Rule for OCR Engine

- Data Analytics Engine for Credit Assessment and Monitoring
    1. Framework of Data Analytics Engine 
    2. Datasets of Data Analytics Engine
    3. List of Machine Learning Algorithms
    4. Model Output Visualization

- White Paper for Proposing a New Alternative Credit Assessment Framework

模板3

- Acknowledgements
    | No. | Member of the Advisory Panel |       Representatives       |
    |-----|------------------------------|-----------------------------|
    |  1  |    Bank of China Hong Kong   |Dr xx, Ms xx, Ms xx and Mr xx|
- Foreword
- Executive Summary

- Part One: The creditworthiness of micro-, small and medium-sized enterprises: a 360-degree view
    1. Challenges and opportunities relating to credit assessment for MSMEs
        1. Micro-, small, and medium-sized enterprises in Hong Kong
        2. The conventional credit assessment approach
        3. Key challenges in assessing MSME credit risk
        4. Adoption of artificial intelligence and machine learning for alternative credit assessment
    2. Data for alternative credit assessment
        1. Classification of data for evaluation of creditworthiness
        2. Transactional data
        3. Non-transactional data
        - Featured section: An example of a psychometry assessment from CRIF
        4. Benefits and challenges for lenders of using alternative data for credit assessment
    3. Industry examples of alternative credit assessment
        1. The global landscape of MSME loan lenders in practicing alternative credit assessment
        2. Case studies in the implementation of alternative credit assessment

- Part Two: Fintech infrastructural components for alternative credit assessment
    1. Development of machine learning models for default prediction
        1. Model Selection
        2. Data Exploration
        3. Model Training
        4. Model Assessment
        5. Feature Importance
        6. Model Interpretability
    2. Automation of credit underwriting for alternative credit assessment
        1. Online lending platform for the automation of credit underwriting
        2. Workflow for Alternative Credit Assessment
        3. Combining conventional and alternative credit assessment models
        - Featured section: High-level machine learning-based credit scoring framework for MSME lending
    3. Privacy-enhancing technologies in sharing alternative credit data
        1. Differential privacy
        2. Zero-knowledge proof
        3. Secure multi-party computing and homomorphic encryption
        4. Federated Learning
        5. Evaluating MSME credit ratings with federated analysis, homomorphic encryption, and differential privacy
    
- Part Three: Deploying alternative credit assessment
    1. Evaluating the performance of machine learning algorithms on MSME data
        1. Setup of the experiments
        2. Results of the comparison of machine learning algorithms
        3. Insights gained from applying machine learning algorithms to MSMEs’ financial data
        4. Case Study: Credit assessment of Japanese MSMEs
    2. An industry-specific alternative credit assessment framework
        1. Proposed framework for alternative credit assessment
        2. The demonstration of the Retail Alternative Credit Assessment (RACA)
    3. Roadmap ahead
        1. Facilitation of data availability
        2. Continuous technical advances in modelling
        3. Centralised data-sharing platform for alternative credit assessment

- Appendix A - Operational considerations in the deployment of the alternative credit assessment
    1. Section 1: Background	
    2. Section 2: Regulatory construct	
    3. Section 3: Government leadership	
    4. Section 4: Machine learning and AI	
    5. Section 5: Data subject consent	
    6. Section 6: Building an ecology	

- Appendix B - Machine learning algorithms for model training and default prediction
    1. Section 1: Ensemble learning techniques
    2. Section 2: Common machine learning algorithms
        1. Logistic Regression
        2. K-Nearest Neighbours (KNN)
        3. Random Forest
        4. Extra-Trees
        5. CatBoost
        6. LightGBM
        7. XGBoost
        8. Convolutional neural network (CNN)
        9. Stacking

模板4

1. Introduction

2. Architecture
    1. System Architecture
    2. Android mobile app
    3. Dispatcher Server
    4. Token server
    5. Verification server
    6. Risk management server
    7. User server
    8. Database table

3. Framworks for Biometric Authentication
    1. Facial recognition
        1. Face Detection
        2. Face feature extraction and landmark detection
        3. Threshold configuration
    2. Speaker recognition
    3. Handwritten signature recognition

4. Conclusion

- Appendix I
    - System Process Flow
        - System Process Flow for Onboarding
        - System Process Flow for Login
        - System Process Flow for Biometric Verification
        - System Process Flow for Banking Service 
    - APP Flow Diagram
        - Onboarding Cross-Functional Process Flow
        - Login Cross-Functional Process Flow
    - API Specification
        - API Overview
        - Function Logic – User Onboard
        - API Sequence Diagram – User Onboarding
        - Function Logic – User Login
        - API Sequence Diagram – Login
        - Function Logic – Banking Service
        - API Sequence Diagram - Banking Service

Github README Writing Skills

ReadMe模板

实用工具

撰写流程

  1. 写好标题: # README题目
  2. 添加项目logo:
    • 方法1: 缩放logo大小
      <div align="center"><img width="90%" src="pictures/logo.png"/></div>
    • 方法2: 自定义logo大小
      <div align="center"><img width="450" height="300" src="pictures/logo.png"/></div>
  3. 添加徽标: https://img.shields.io
    ![](https://img.shields.io/static/v1?label=license&message=MIT&color=brightgreen)  ![](https://img.shields.io/static/v1?label=build&message=success&color=green) ![](https://img.shields.io/static/v1?label=language&message=python&color=blue) ![](https://img.shields.io/static/v1?label=version&message=v1.0.0&color=green) [![](https://img.shields.io/static/v1?label=author&message=ElaineZhong&color=blueviolet)](https://elainexhzhong.github.io/) ![](https://img.shields.io/badge/topic-digital%20payment-brightgreen) ![](https://img.shields.io/badge/topic-DLT-ff69b4) ![](https://img.shields.io/badge/topic-crypto-red)
  4. 添加目录
    1. 方法1: Markdown
      - <a href="#Logistic-Regression">Logistic Regression</a>
      
      <span id="Logistic-Regression">Logistic Regression</span>
    2. 方法2: HTML
      <p align="center">
      <a href="./docs/Digital-Payment/DCEP.md">DCEP</a>&nbsp;&nbsp;&nbsp;
      <a href="./docs/Digital-Payment/CBDC.md">CBDC</a>&nbsp;&nbsp;&nbsp;
      </p>
  5. 添加维护项目的人
    ## Maintainers
    
    [@ElaineZhong](https://github.com/ElaineXHZhong)
  6. 添加读者问问题的渠道
    ## Contribute
    
    Contributions are always welcome! Feel free to dive in! 
    
    Please read the <a href="./docs/contributing.md">contribution guideline</a> first, then [open an issue](https://github.com/ElaineXHZhong/Content-Sentiment-Analysis/issues/new) open an issue</a> or submit PRs.
    
    This repository follows the [Contributor Covenant](http://contributor-covenant.org/version/1/3/0/) Code of Conduct.
  7. 添加对项目有贡献的人(点击头像即可去往对方的github主页)
    This project exists thanks to all the people who contribute. 
    
    <img src="./pictures/portrait.png" width="10%" height="10%" href="https://elainexhzhong.github.io/">
  8. 添加项目的License
    ## License
    [MIT](LICENSE) © Elaine Zhong

Github优秀项目

  • repo-badges
    解释代码存储库中各个“徽章”的正确含义
  • markdown-here
    一个用来在电子邮件中编写和渲染Markdown的工具
  • freeCodeCamp
    开放源码代码库和课程。与数百万人一起免费学习编程。网站:https://www.freeCodeCamp.org
  • 996.ICU
    96.ICU 是指 “工作 996, 生病 ICU” 。这是中国程序员之间的一种自嘲说法,意思是如果按照 996 的模式工作,那以后就得进 ICU 了。这个项目最早是某个中国程序员发起的,然后就火遍全网,甚至火到了全世界很多其他国家,其网站被翻译成了多种语言。网站地址:https://996.icu
  • Vue
    国人用的最多(容易上手,文档比较丰富),所以 Star 数量比较多还是有道理的。Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合。另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动
  • React
    Facebook开源的,大公司有保障。用于构建用户界面的声明式、基于组件开发,高效且灵活的JavaScript框架。我司大部分项目的前端都是React,感觉还不错,但是也有一些小坑
  • tensorflow
    适用于所有人的开源机器学习框架。TensorFlow 是用于机器学习的端到端开源平台。TensorFlow 最初是由 Google 机器智能研究组织内 Google Brain 团队的研究人员和工程师开发的,用于进行机器学习和深度神经网络研究。该系统具有足够的通用性,也可以适用于多种其他领域。TensorFlow 提供了稳定的 Python 和 C ++ API,以及其他语言的非保证的向后兼容 API
  • bootstrap
    初学前端的时候,大家一定或多或少地接触过这个框架。官网说它是最受欢迎的HTML,CSS和JavaScript框架,用于在网络上开发响应式,移动优先项目
  • free-programming-books
    免费提供的编程书籍
  • Awesome
    awesome系列是Github伤一个非常有名的系列。awesome系列专门用来收集各种插件、第三方库、教程工具等
  • You-Dont-Know-JS
    您还不认识JS(书籍系列)- 第二版
  • AdminLTE
    非常流行的基于Bootstrap3.x的免费的后台UI框架
  • vue-Element-Admin
    一个基于vue2.0和Eelement的控制面板UI框架
  • tabler
    构建在BootStrap 4之上的免费的HTML控制面板框架
  • Gentelella
    一个基于Bootstarp的免费的后台控制面板
  • ng2-admin
    基于Angular 2, Bootstrap 4和Webpack的后台管理面板框架
  • ant-design-pro
    开箱即用的中台前端/设计解决方案
  • blur-admin
    基于Angular和Bootstrap的后台管理面板框架

通过社区学习

  • 程序员客栈
    中国非常领先的自由工作平台,为中高端程序员、产品经理和设计师等等互联网相关人员提供稳定的线上工作机会,包括自由工作、远程工作和兼职工作,还支持按需雇佣,工作模式非常多,除了程序员,像产品经理,设计师等等互联网相关人员,都能在上面找到适合自己的项目,感兴趣的可以体验一下
  • 码市
    码市是Coding推出的互联网软件外包服务平台,意在连接需求方与广大开发者,让项目的需求方快速的找到合适的开发者,完成项目开发工作
  • 猪八戒网
    猪八戒网创建于2006年,是服务中小微企业的人才共享平台。开创式地为人才与雇主搭建起双边市场,通过线上线下资源整合与大数据服务,实现人才与雇主精准无缝对接。找兼职的地方,主要是入门级项目,不适合专业程序员,只适合新手
  • 开源众包
    专业的软件众包平台,超过350万开发者为雇主提供网站、APP、微信/小程序、企业应用等软件开发服务,有效降低企业IT软件开发成本、解决技术资源不足等问题
  • 智城外包网
    智城外包网,聚合全国软件团队资源,官方认证,1小时响应,零交易佣金,托管安全保障。十年口碑运营,万家靠谱团队。免费比价,免费一站式外包项目管理工具。平台汇集软件咨询专家,软件技术专家,软件开发专家,软件开发公司,软件外包公司,软件外派公司。在线竞标模式,让IT外包项目和短期IT招聘、人力派遣需求可以获得高性价比的候选
  • 实现网
    北京实现与爱科技有限公司 是一个互联网工程师兼职平台。解决创业公司招人难、成本高的问题。创业公司通过实现网可以快速预约知名互联网企业的工程师、设计师到自己的团队工作。上午预约工程师,最快晚上即可到班兼职。互联网工程师可以在实现网注册成为技术顾问,利用业余时间助力创业公司,并且获得以时薪为单位的报酬。
  • 猿急送
    一个高级技术共享平台,汇聚知名互联网公司的技术、设计、产品大牛,通过实际坐班、远程等方式,一对一为创业公司解决问题,提高创业效率。提供兼职程序员,兼职工程师信息,是一个高级技术共享平台,是优质的程序员兼职网站,这里汇聚BAT等知名互联网公司的技术开发、产品、设计大牛,通过实际坐班等方式,一对一为创业公司解决程序员、工程师等开发、产品设计人力问题
  • 人人开发
    人人开发基于可视化快速开发平台 - 捷得(Joget)/捷得云(Joget Cloud)(PaaS),集众多开发者资源,为企业提供企业管理软件服务。应用市场提供应用产品、插件的在线试用和销售,服务市场以威客众包模式提供管理软件定制开发服务,各类企业级应用开发服务
  • 开发邦
    公司位于北京中关村科技园区核心区海淀园,成立于2010年,专注于为客户提供互联网软件技术开发与咨询服务,致力于利用互联网软件技术为客户提高效率、降低成本、提升效能、优化管理。开发邦致力于成为企业业务互联网软件服务与咨询的定制方案提供商
  • 电鸭社区
    电鸭社区旨在帮助更多人走上「只工作,不上班」的自由工作之路,我们是一个「分布式组织」,通过分享及行动带来积极的影响,相信点滴的力量能改变潮水的方向
  • 快码
    深圳快码科技 成立于2014年11月,是一家创新型的互联网公司,致力于通过创新的开发方式,为软件技术开发行业带来改变,提供更快速、更高性价比的软件定制服务。“快码”的意思是“快速编写代码”。公司采用“专属项目经理 + 自有开发团队 + 平台程序员”的创新开发方式,严格按照互联网公司的标准来管理开发团队,确保每个项目都有充足的人员投入,确保项目的进度和开发质量。2015年,我们和全球最大的手游、APP云测试平台Testin达成战略合作协议,并获得Testin数百万的战略投资。是一个创新的软件开发平台。项目方可以更省钱、高效地完成项目的开发;开发者可以充分利用闲置时间,实现更高的商业价值!
  • 英选
    可信赖的软件外包服务。用优秀的人,做漂亮的产品,写干净的代码。平台以定制开发外包服务为主,也是外包项目平台
  • Upwork
    全球最大的、最优秀的、最规范的综合类人力外包服务平台,由著名的Elance和oDesk合并。这里聚集900万来自全球各地的自由工作者,你肯定可以在找到适合你的职位
  • Freelancer
    reelancer 的工作类型覆盖了很多不同的领域,由程序开发到市场营销、广告、会计、法务等一系列的可以远程的工作
  • Dribbble
    Dribbble不只是全球最受欢迎的设计师社区,同样是设计师寻找远程工作的好出处。自从被Tiny收购后,Dribbble的招聘属性正在慢慢增强,试着持续PO出自己的好作品,等待你的伯乐,同样你可以关注Jobs页面,给心仪的Team提交简历
  • Remoteok
    Remoteok不仅提供最初的兼职类远程工作,还有全职类,签署合同类和实习类的工作。网站创始人Pieter Levels本身就是一名数字游民,他同样是Nomadlist的创始人
  • Toptal
    一个高端一些的自由职业者平台,适合比较有经验和工作尽力的远程工作者。它将企业与全球的软件工程师,设计师和业务顾问联系起来
  • AngelList
    AngelList主要是服务于初创公司和天使投资人的平台,这里还有初创公司提供的远程工作的机会,如果对远程加入初创公司感兴趣的,可以尝试一下
  • Topcoder
    Topcoder通过算法比赛吸引世界顶级的程序员,他会将一下大型项目分割成很多小模块,通过竞赛的模式交给用户来做,优胜者可以拿到制定模块的奖金

Markdown语法

Reference: here

目录

插件vim-markdown-toc

<html>
    <p align="center">
    <a href="./docs/Cryptography/Token.md">Token</a>&nbsp;&nbsp;&nbsp;
    <a href="./docs/Cryptography/OAuth2.md">OAuth2.0</a>&nbsp;&nbsp;&nbsp;
    </p>
</html>

Github标签

# ![](放入徽标url)
![](https://img.shields.io/static/v1?label=PRs&message=Welcome&color=brightgreen) 
![](https://img.shields.io/static/v1?label=license&message=MIT&color=brightgreen) 
![](https://img.shields.io/static/v1?label=build&message=passing&color=brightgreen) 
![](https://img.shields.io/badge/language-python-blue) 
[![](https://img.shields.io/static/v1?label=author&message=ElaineZhong&color=blueviolet)](https://elainexhzhong.github.io/)

锚点

# 方法1
[点击跳转](#jump)                   # []中是显示的文字,jump是锚点文字
<span id="jump">目标位置</span>     # id填锚点文字

# 方法2
<a href="#jump">要显示文本</a>
<span id="jump">跳转到的地方</span> # id填锚点文字

标题

# 一级标题
## 二级标题
### 三级标题
#### 四级标题
##### 五级标题
###### 六级标题

段落

# 使用两个以上空格加上回车 | 也可以在段落后面使用一个空行来表示重新开始一个段落

字体

*斜体文本*
_斜体文本_
**粗体文本**
__粗体文本__
***粗斜体文本***
___粗斜体文本___
~~删除线~~
>引用
<u>下划线</u>

文字颜色

<html>
    <font color="#FF0000">我是红色字体</font> 
    <font face="黑体">我是黑体字</font>
    <font face="微软雅黑">我是微软雅黑</font>
    <font face="STCAIYUN">我是华文彩云</font>
    <font color=#0099ff size=7 face="黑体">color=#0099ff size=72 face="黑体"</font>
    <font color=#00ffff size=72>color=#00ffff</font>
    <font color=gray size=72>color=gray</font>
</html>

文字背景色

  • 方法1
    <html>
        <table><tr><td bgcolor=orange>背景色是:orange</td></tr></table>
    </html>
  • 方法2
    <html>
        <span style="background-color: #ff6600;">你的文字</span>
    </html>

缩进和换行

直接在Markdown里用空格和Tab键缩进在渲染后会被忽略掉,需要借助HTML转义字符在行首添加空格来实现:&ensp; 代表半角空格,&emsp; 代表全角空格

  1. 缩进
    <html>
        &emsp;&emsp;Sentences
    </html>
  2. 换行
    • 连续两个以上空格+回车
    • 使用html语言换行标签<br>

分隔线

# 可以在一行中用三个以上的星号、减号、底线来建立一个分隔线,行内不能有其他东西 | 也可以在星号或是减号中间插入空格
***
* * *
*****
- - -
----------

删除线

# 在文字的两端加上两个波浪线 ~~ 
~~BAIDU.COM~~

下划线

<!--通过 HTML 的 <u> 标签来实现--> 
<u>带下划线文本</u>

脚注

# 脚注是对文本的补充说明
脚注1 [^RUNOOB]。
[^RUNOOB]: ElaineXHZhong -- 世界是趋于混沌的,我们做的是减少熵。

无序列表

# 使用星号(*)、加号(+)或是减号(-)作为列表标记,这些标记后面要添加一个空格,然后再填写内容
* 第一项
* 第二项
* 第三项

+ 第一项
+ 第二项
+ 第三项

- 第一项
- 第二项
- 第三项

有序列表

# 使用数字并加上 . 号来表示
1. 第一项
2. 第二项
3. 第三项

列表嵌套

# 在子列表中的选项前面添加四个空格
1. 第一项:
    - 第一项嵌套的第一个元素
    - 第一项嵌套的第二个元素
2. 第二项:
    - 第二项嵌套的第一个元素
    - 第二项嵌套的第二个元素

任务列表

**清单**
- [ ] 非填充空格
- [x] 填充打勾格

表格

  1. 使用|来分隔不同的单元格,使用 - 来分隔表头和其他行
    |  表头   | 表头  |
    |  ----  | ----  |
    | 单元格  | 单元格 |
    | 单元格  | 单元格 |
  2. 设置表格的对齐方式
    # -: 内容和标题栏居右对齐 | :- 内容和标题栏居左对齐 | :-: 内容和标题栏居中对齐
    | 左对齐 | 右对齐 | 居中对齐 |
    | :-----| ----: | :----: |
    | 单元格 | 单元格 | 单元格 |
    | 单元格 | 单元格 | 单元格 |
  3. 在表格单元格里换行: Markdown兼容部分HTML标签的特性,借助于HTML里的<br/>实现
    | Header1 | Header2                          |
    |---------|----------------------------------|
    | item 1  | 1. one<br />2. two<br />3. three |
  4. 格式化表格: vim-table-mode 插件
  5. 利用HTML语法调列宽
    • 预留宽度,用于打印后填写(粗暴方法&nbsp; | 优雅方法<img>标签)
      | a | b | c |
      |---|---|---|
      | 1 |  <img width=200/> | 3 | 
    • 指定列宽,控制换行(div width
      | a | b | d |
      |---|---|---|
      | 1 | <div style="width:[长度]">[单元格文本]</div> | 3 |

引用

# 在段落开头使用 > 符号 ,然后后面紧跟一个空格符号
> ElaineXHZhong
> 世界是趋于混沌的,我们做的是减少熵。

# 区块是可以嵌套的,一个 > 符号是最外层,两个 > 符号是第一层嵌套,以此类推
> 最外层
> > 第一层嵌套
> > > 第二层嵌套

# 区块中使用列表实例
> 区块中使用列表
> 1. 第一项
> 2. 第二项
> + 第一项
> + 第二项
> + 第三项

# 列表中使用区块需要在 > 前添加四个空格的缩进
* 第一项
    > 菜鸟教程
    > 学的不仅是技术更是梦想
* 第二项

代码区块

  1. 多行代码用3个反引号`来标记
    # 用 ``` 包裹一段代码,并指定一种语言(也可以不指定):
    ```javascript```
    ```
  2. 行内代码用一个反引号`引起来就好
  3. 代码和markdown冲突时
    1. 把最外层的```markdown```换成~~~markdown~~~
    2. 或者把内层的```html```换成<html></html>

链接

# 使用方法: [链接名称](链接地址) | <链接地址>
这是一个Elaine的[博客地址](https://elainexhzhong.github.io)

# 高级链接: 通过变量来设置一个链接,变量赋值在文档末尾进行
这个链接用 1 作为网址变量 [Google][1]
这个链接用 runoob 作为网址变量 [Runoob][2]
然后在文档的结尾为变量赋值(网址)

  [1]: http://www.google.com/
  [2]: http://www.runoob.com/

超链接

[GcsSloop](http://www.gcssloop.com)
[XGBoost](files/XGBoost.py)

图片

  1. Markdown引用图片
    # 语法: ![alt 属性文本](图片地址) | ![alt 属性文本](图片地址 "可选标题")
    # 方括号里面放图片描述,普通括号里放图片的网址,引号用来包住并加上选择性的 'title' 属性的文字
    ![这里写图片描述](http://img3.douban.com/mpic/s1108264.jpg)
  2. Markdown使用变量引用图片
    # 也可以像网址那样对图片网址使用变量: 在文档的结尾为变量赋值(网址)
    这个链接用 1 作为网址变量 [RUNOOB][1].
    [1]: http://static.runoob.com/images/runoob-logo.png
  3. HTML实现:图文混排 + 图片显示在N段文字的右边
    Markdown 还没有办法指定图片的高度与宽度,如果你需要的话,你可以使用普通的<img>标签。
    <html>
        <img align="right" src="http://static.runoob.com/images/runoob-logo.png" width="50%">
    </html>
  4. HTML实现:使用div标签加align属性来控制位置,图片宽高则用width和height来控制
    <html>
        <div align="center"><img width="65" height="75" src="http://static.runoob.com/images/runoob-logo.png"/></div>
        <div align="center"><img width="70%" src="../../pictures/DCEP.jpg"/></div> 
    </html>

HTML嵌入

# 不在 Markdown 涵盖范围之内的标签,都可以直接在文档里面用HTML撰写
使用 <kbd>Ctrl</kbd>+<kbd>Alt</kbd>+<kbd>Del</kbd> 重启电脑

转义字符

# 需要显示特定的符号则需要使用转义字符
**文本加粗** 
\*\* 正常显示星号 \*\*

# Markdown 支持以下这些符号前面加上反斜杠来帮助插入普通的符号:

\   反斜线
`   反引号
*   星号
_   下划线
{}  花括号
[]  方括号
()  小括号
#   井字号
+   加号
-   减号
.   英文句点
!   感叹号

公式

  1. Markdown方式
    使用两个”$”符号引用行内公式$公式$,使用两对“$$”符号引用行间公式$$公式$$
    # 使用两个美元符 $$ 包裹TeX或LaTeX格式的数学公式来实现。文章页会根据需要加载Mathjax对数学公式进行渲染
    $$
    \mathbf{V}_1 \times \mathbf{V}_2 =  \begin{vmatrix} 
    \mathbf{i} & \mathbf{j} & \mathbf{k} \\
    \frac{\partial X}{\partial u} &  \frac{\partial Y}{\partial u} & 0 \\
    \frac{\partial X}{\partial v} &  \frac{\partial Y}{\partial v} & 0 \\
    \end{vmatrix}
    ${$tep1}{\style{visibility:hidden}{(x+1)(x+1)}}
    $$
  2. 贴图方式
    • 网页输入框中输入LaTeX公式
    • 在网页下部拷贝URL Encoded内容
    • 在文档中贴上step2中的URL贴图

流程图

Reference: here

  1. 横向流程图源码格式
    graph LR
    A[方形] -->B(圆角)
        B --> C{条件a}
        C -->|a=1| D[结果1]
        C -->|a=2| E[结果2]
        F[横向流程图]
  2. 竖向流程图源码格式
    graph TD
    A[方形] --> B(圆角)
        B --> C{条件a}
        C --> |a=1| D[结果1]
        C --> |a=2| E[结果2]
        F[竖向流程图]
  3. 标准流程图源码格式
    st=>start: 开始框
    op=>operation: 处理框
    cond=>condition: 判断框(是或否?)
    sub1=>subroutine: 子流程
    io=>inputoutput: 输入输出框
    e=>end: 结束框
    st->op->cond
    cond(yes)->io->e
    cond(no)->sub1(right)->op
  4. 标准流程图源码格式(横向)
    st=>start: 开始框
    op=>operation: 处理框
    cond=>condition: 判断框(是或否?)
    sub1=>subroutine: 子流程
    io=>inputoutput: 输入输出框
    e=>end: 结束框
    st(right)->op(right)->cond
    cond(yes)->io(bottom)->e
    cond(no)->sub1(right)->op

时序图(顺序图)

Reference: here

  1. UML时序图源码样例
    对象A->对象B: 对象B你好吗?(请求)
    Note right of 对象B: 对象B的描述
    Note left of 对象A: 对象A的描述(提示)
    对象B-->对象A: 我很好(响应)
    对象A->对象B: 你真的好吗?
  2. UML时序图源码复杂样例
    Title: 标题:复杂使用
    对象A->对象B: 对象B你好吗?(请求)
    Note right of 对象B: 对象B的描述
    Note left of 对象A: 对象A的描述(提示)
    对象B-->对象A: 我很好(响应)
    对象B->小三: 你好吗
    小三-->>对象A: 对象B找我了
    对象A->对象B: 你真的好吗?
    Note over 小三,对象B: 我们是朋友
    participant C
    Note right of C: 没人陪我玩
  3. UML标准时序图样例
    %% 时序图例子,-> 直线,-->虚线,->>实线箭头
    sequenceDiagram
        participant 张三
        participant 李四
        张三->王五: 王五你好吗?
        loop 健康检查
            王五->王五: 与疾病战斗
        end
        Note right of 王五: 合理 食物 <br/>看医生...
        李四-->>张三: 很好!
        王五->李四: 你怎么样?
        李四-->王五: 很好!

甘特图

Reference: here

%% 语法示例
        gantt
        dateFormat  YYYY-MM-DD
        title 软件开发甘特图
        section 设计
        需求                      :done,    des1, 2014-01-06,2014-01-08
        原型                      :active,  des2, 2014-01-09, 3d
        UI设计                     :         des3, after des2, 5d
    未来任务                     :         des4, after des3, 5d
        section 开发
        学习准备理解需求                      :crit, done, 2014-01-06,24h
        设计框架                             :crit, done, after des2, 2d
        开发                                 :crit, active, 3d
        未来任务                              :crit, 5d
        耍                                   :2d
        section 测试
        功能测试                              :active, a1, after des3, 3d
        压力测试                               :after a1  , 20h
        测试报告                               : 48h

色彩条

  • 色码表
  • 扁平化设计颜色码
    ![#f03c15](https://placehold.it/15/f03c15/000000?text=+) `#f03c15`
    ![#c5f015](https://placehold.it/15/c5f015/000000?text=+) `#c5f015`
    ![#1589F0](https://placehold.it/15/1589F0/000000?text=+) `#1589F0`
    ![#921AFF](https://placehold.it/15/921AFF/000000?text=+) `#921AFF`
    ![#F00078](https://placehold.it/15/F00078/000000?text=+) `#F00078`
    ![#02C874](https://placehold.it/15/02C874/000000?text=+) `#02C874`
    ![#9393FF](https://placehold.it/15/9393FF/000000?text=+) `#9393FF`
    ![#FF95CA](https://placehold.it/15/FF95CA/000000?text=+) `#FF95CA`
    ![#FF77FF](https://placehold.it/15/FF77FF/000000?text=+) `#FF77FF`
    ![#C39BD3](https://placehold.it/15/C39BD3/000000?text=+) `#C39BD3`
    ![#884EA0](https://placehold.it/15/884EA0/000000?text=+) `#884EA0`
    ![#1ABC9C](https://placehold.it/15/1ABC9C/000000?text=+) `#1ABC9C`
    ![#52BE80](https://placehold.it/15/52BE80/000000?text=+) `#52BE80`
    ![#F1C40F](https://placehold.it/15/F1C40F/000000?text=+) `#F1C40F`
    <span id="Kaggle-Competition">![#52BE80](https://placehold.it/15/52BE80/000000?text=+) `Kaggle Competition`</span>
    
    ```diff
    - red
    + green
    ! orange
    # gray

Emoji

更多可用Emoji代码参见此处

Good Expression

FALdetector

  • Was just wondering if this code will be published? The project page linked here for the code, but the repository is empty.
    • Yes, the testing code is now released, and we will release our training code as well in the future. Thank you!
    • Sorry about the plan change, but we cancelled the plan for training code release due to licensing issues with the training dataset.
  • Hi, thanks for sharing the model! I wonder if you will also release the PhotoShop script for generating the fake images? Thanks!
    • Yes, we are planning to release the PhotoShop script to automatically generate FAL-warped faces
    • Can’t wait! Very interested to see what y’all did.
    • Been almost a year since the last post, is there any plan to still release the script?
    • Due to licensing issues, the released validation set is different from the set we evaluate in the paper, and the training set will not be released.
    • Note that the guide above not necessarily covers all operations that can be automated; for example, filters like face-aware liquify.
    • As a quick start, you can open up an image with face in photoshop, click on File->Scripts->Browse, and then select the .jsx file to run it. Hope this helps!
  • Do you have plan to build Pypi and conda-forge package so that people could install it conveniently? I could help on that.
    • Thanks for bringing this up. However, currently we do not have a plan to build Pypi and conda-forge packages.
  • İs there any best resolution for best accuracy?
    • For any given images, … , so 400 should be a good resolution in this case.
  • Hi, I’m not the repo’s maintainer, but I think you should know this is not intended to run in Photoshop.
  • Have the weight files changed location or been corrupted?

Deploy Face Recognition server in Azure GPU VM

Azure GPU VM Configuration

  • Address
  • GPU Info
    configuration: 24 vCPUs, 448G Memory and 4 NVIDIA Tesla V100 card
    price: 85442.18 HKD per month, or 117.04 HKD per hour.
  • Windows System
    Processor:              Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60 GHz 2.59 GHz (2 processors)
    Installed memory(RAM):  448 GB
    System type:            64-bit Operationg System, x64-based processor
    Pen and Touch:          No Pen or Touch Input is available for this Display
    Windows Produc ID:      00430-00000-00000-AA040
    Windows edition:        Windows Server 2019 Datacenter
  • Azure Page
    Location:               East US
    Subscription ID:        712cbc67-a904-48cf-83e6-05c0629ce9f3
    Operating system:       Windows (Windows Server 2019 Datacenter)
    Size:                   Standard NC24rs_v3 (24 vcpus, 448 GiB memory)
    Public IP address:      13.92.214.100
    Virtual network/subnet: GPU-vnet/default

Install Azure VM GPU Drivers

  1. Install the Drivers
  2. 让Windows Server 2019 Datacenter上的IE浏览器可以下载chrome
    右上角工具按钮 -> Internet options -> Security -> Internet -> Customer level -> Downloads: (1) File download -> Enable (2) Fond download -> Enable
  3. 下载安装NVIDIA Tesla (CUDA) drivers: Windows Server 2019的Driver(.exe文件)
    - 安装在: C:\NVIDIA\DisplayDriver\451.82\Win10_64\International
    - NVIDIA Graphics Driver (Version 451.82): 一路NEXT

Configure FaceNet Environment

下载安装必要软件

  • 下载安装Git
    - 安装在: C:\Program Files\Git
  • 下载安装Anaconda
    - 安装在:   C:\ProgramData\Anaconda3
    # 安装了Anaconda,也就有了python和pip
    - conda:    C:\ProgramData\Anaconda3\Scripts\conda.exe  # where conda
    - python:   C:\ProgramData\Anaconda3\python.exe         # where python
    - pip:      C:\ProgramData\Anaconda3\Scripts\pip.exe    # where pip
  • 下载解压软件7-Zip
    - 安装在: C:\Program Files\7-Zip\
  • 下载代码编辑软件VSCode
    - 安装在: C:\Users\gputest\AppData\Local\Programs\Microsoft VS Code\Code.exe

    下载项目

  • 下载facenet代码
    $cd /c/Users/gputest/Desktop
    $git clone https://github.com/davidsandberg/facenet
    $cd facenet
    $conda create -n facenet python=3.6 && conda activate facenet
    $pip install -r requirements.txt
  • 下载模型20180402-114759 LFW accuracy: 0.9965 VGGFace2
    在根目录下新建models文件夹,将模型放入models下: models\20180402-114759\20180402-114759
  • 下载数据集LFW
    在根目录下新建datasets文件夹,将lfw解压后放入datasets下: datasets\lfw

    配置包

  • Add envs\facenet\Lib\site-packages\facenet
    1. envs\facenet\Lib\site-packages下新建facenet文件夹
    2. 复制facenet-master\src下所有文件到上述facenet文件夹下
    3. 在conda环境中python, 然后import facenet, 不会报错即可
  • Edit Program
    Desktop\facenet\src\align\align_dataset_mtcnn.py
    import facenet.facenet as facenet # import facenet
    Desktop\facenet\contributed\predict.py
    import facenet.facenet as facenet # import facenet
  • Add envs\facenet\Lib\site-packages\align
    1. 复制facenet-master\src\align文件夹到envs\facenet\Lib\site-packages下
    2. 在conda环境中python, 然后import align, 不会报错即可
  • Change module version
    $pip install numpy==1.16.2
    $pip install scipy==1.2.1
  • 为了观察程序效率,最好为每个运行程序配置上时间消耗的显示:
    import time
    if __name__ == '__main__':
        start_time = time.time()
        print('Program.py start at: ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
        main(parse_arguments(sys.argv[1:]))
        print('Program.py end at: ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
        print('Program.py all time: {0} seconds = {1} minutes = {2} hrs'.format((time.time() - start_time), (time.time() - start_time) / 60, (time.time() - start_time) / 3600))

    测试cpu模式下能否正常运行

    # (1) Align
    $python src/align/align_dataset_mtcnn.py datasets/lfw datasets/lfw_160 --image_size 160 --margin 32 
    # (2) Train
    $python src/classifier.py TRAIN datasets/lfw_160 models/20180402-114759/20180402-114759.pb models/lfw.pkl
    # (3) Classify
    $python src/classifier.py CLASSIFY datasets/lfw_160 models/20180402-114759/20180402-114759.pb models/lfw.pkl
    # (4) Predict
    $python contributed/predict.py datasets/lfw/Aaron_Eckhart/Aaron_Eckhart_0001.jpg models/20180402-114759 models/lfw.pkl

测试gpu模式下能否正常运行

测试命令

# (1) Align
$python src/align/align_dataset_mtcnn.py datasets/lfw datasets/lfw_160 --image_size 160 --margin 32 # 如果GPU够强劲
$python src/align/align_dataset_mtcnn.py datasets/lfw datasets/lfw_160 --image_size 160 --margin 32 --gpu_memory_fraction 0.5 # 如果GPU不够强劲
# (2) Train
$python src/classifier.py TRAIN datasets/lfw_160 models/20180402-114759/20180402-114759.pb models/lfw.pkl
# (3) Classify
$python src/classifier.py CLASSIFY datasets/lfw_160 models/20180402-114759/20180402-114759.pb models/lfw.pkl
# (4) Predict
$python contributed/predict.py datasets/lfw/Aaron_Eckhart/Aaron_Eckhart_0001.jpg models/20180402-114759 models/lfw.pkl # 如果GPU够强劲
$python contributed/predict.py datasets/lfw/Aaron_Eckhart/Aaron_Eckhart_0001.jpg models/20180402-114759 models/lfw.pkl --gpu_memory_fraction 0.5 # 如果GPU不够强劲

安装环境依赖

配置facenet环境: Visual Studio 2017 + python 3.6.12 + tensorflow-gpu 1.7.0 + CUDA 9.0 + cuDNN 7.0.5 + facenet site-packages

  1. Visual Studio 2017
    # 安装选项
    - Technically only the VS C/C++ compiler is required (cl.exe)
    - 通用Windows平台开发(包括其子选项C++通用Windows平台工具)
    - 使用C++的桌面开发
    # 安装地址
    - 安装在: C:\Program Files (x86)\Microsoft Visual Studio\2017\Community
  2. CUDA Toolkit 9.0
    - Base installer | Patch1 | Patch2 | Patch3 | Patch4
    - 安装在: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0
    - 右键点击桌面 -> 如果在弹出窗口中看到“NVIDIA控制面板”或“NVIDIA显示”,则说明您具有NVIDIA GPU
  3. cuDNN 7.0.5
    # 把cuDNN下的bin,include,lib文件夹拷贝到CUDA的安装路径: C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0
    # tensoflow版本不同,所对应的CUDA 和cuDNN版本也不同 (一定要对应上,否则会报错)
    $nvcc -V   # 检查CUDA是否安装成功
    # nvcc: NVIDIA (R) Cuda compiler driver
    # Copyright (c) 2005-2017 NVIDIA Corporation
    # Built on Fri_Sep__1_21:08:32_Central_Daylight_Time_2017
    # Cuda compilation tools, release 9.0, V9.0.176

添加环境变量

C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\bin
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\libnvvp
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\lib\x64
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\include
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v9.0\extras\CUPTI\libx64
C:\Program Files\NVIDIA Corporation\NVSMI   (nvidia-smi.exe的Path)
C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common
# 测试并查看GPU使用率: Task Manager (dedicated GPU) 
$nvidia-smi.exe -h
$nvidia-smi.exe -l 1   # 一秒钟更新一次信息

添加环境包

  • install tensorflow-gpu 1.7.0
    $conda activate facenet
    $pip list # tensorflow 1.7.0 
    $pip uninstall -y tensorflow
    $pip install tensorflow-gpu==1.7.0 # tensorflow-gpu 1.7.0 

修改程序(用GPU加速)

  1. src/align/align_dataset_mtcnn.py

    # 修改sess配置 -> 使用GPU进行数据预处理 (使用GPU加速计算) 不要忘了在虚拟环境中也更替此文件: C:\ProgramData\Anaconda3\envs\facenet\Lib\site-packages\facenet\src\align\align_dataset_mtcnn.py
    
    # sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))修改为
    config                          = tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False, allow_soft_placement=True)
    config.gpu_options.allow_growth = True
    sess                            = tf.Session(config=config)
  2. src/train_tripletloss.py

    # 修改sess配置 -> 使用GPU加速计算(供GPU玩家修改,使用CPU的话就不用修改了,跳过这一点直接训练就好了)
    
    # 191行: sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
    config                          = tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False, allow_soft_placement=True)
    config.gpu_options.allow_growth = True
    sess                            = tf.Session(config=config)
  3. src/classifier.py

    # 修改sess配置 -> 使用GPU加速计算(供GPU玩家修改,使用CPU的话就不用修改了,跳过这一点直接训练就好了)
    
    # with tf.Graph().as_default():
    #     with tf.Session() as sess:
    
    with tf.Graph().as_default():
        config                                              = tf.ConfigProto()
        config.gpu_options.allow_growth                     = True      # 不全部占满显存, 按需分配
        config.gpu_options.per_process_gpu_memory_fraction  = 1.0       # 限制GPU内存占用率
        with tf.Session(config=config) as sess:
  4. contributed/predict.py

    # 修改sess配置 -> 使用GPU加速计算
    
    # sess = tf.Session(config=tf.ConfigProto(gpu_options=gpu_options, log_device_placement=False))
    config                                              = tf.ConfigProto()
    config.gpu_options.allow_growth                     = True                      # 不全部占满显存, 按需分配
    config.gpu_options.per_process_gpu_memory_fraction  = 1.0                       # 限制GPU内存占用率
    sess                                                = tf.Session(config=config)

打包及导入环境

  1. pip打包
    $pip freeze > facenet.txt
  2. conda打包
    $conda env export > facenet.yaml 
  3. 导入环境
    $conda create -n facenet python=3.6
    $conda activate facenet
    $pip install -r facenet.txt
    $conda env create -f facenet.yaml
    # 或者替换site-packages文件夹就行了

conda操作

# 创建新环境
$conda create -n facenet python=3.6
# 删除环境
$conda remove -n facenet --all
# 查看所有虚拟环境
$conda info --env
# 查看env所有地址
$where conda

Experiment Results

  • 测试数据集: lfw
  • 测试命令如下:
    # 1. Align
    $python src/align/align_dataset_mtcnn.py datasets/lfw datasets/lfw_160 --image_size 160 --margin 32 # GPU内存足够
    $python src/align/align_dataset_mtcnn.py datasets/lfw datasets/lfw_160 --image_size 160 --margin 32 --gpu_memory_fraction 0.5 # GPU内存不够
    # 2. Train
    $python src/classifier.py TRAIN datasets/lfw_160 models/20180402-114759/20180402-114759.pb models/lfw.pkl
    # 3. Classify
    $python src/classifier.py CLASSIFY datasets/lfw_160 models/20180402-114759/20180402-114759.pb models/lfw.pkl
    # 4. Predict
    $python contributed/predict.py datasets/lfw/Aaron_Eckhart/Aaron_Eckhart_0001.jpg models/20180402-114759 models/lfw.pkl # GPU内存足够
    $python contributed/predict.py datasets/lfw/Aaron_Eckhart/Aaron_Eckhart_0001.jpg models/20180402-114759 models/lfw.pkl --gpu_memory_fraction 0.5 # GPU内存不够

Azure GPU VM (4 GPU)

  • GPU Info
    configuration:  24 vCPUs, 448G Memory and 4 NVIDIA Tesla V100 card
    Processor:      Intel(R) Xeon(R) CPU E5-2690 v4 @ 2.60 GHz 2.59 GHz (2 processors)
    Installed memory(RAM):  448 GB
    System type:    64-bit Operationg System, x64-based processor
    Windows edition:Windows Server 2019 Datacenter
  • GPU Performance
    # Align
    - GPU耗时
        Total number of images: 13233 | Number of successfully aligned images: 13233
        Program.py all time: 636.5618450641632 seconds = 10.60936408440272 minutes = 0.17682273474004534 hrs
        So processing efficiency is: 0.0481 second/image
    - GPU使用情况: $nvidia-smi.exe -l 1
        CPU Memory Usage: 952MiB | 480MiB | 480MiB | 480MiB
        GPU Memory Usage: 1860MiB|1388MiB | 1388MiB| 1388MiB  <16280MiB>
        GPU Utilization:   7%    |   0%   |   0%   |   0%
    - CPU耗时
        Program.py all time: 1133.1881606578827 seconds = 18.886469344298046 minutes = 0.3147744890716341 hrs
        So processing efficiency is: 0.0856 second/image
    - CPU使用情况
        Utilization 31%, Speed 2.59 GHz, Processes 138, Threads 1620
        Memory: 2% (10/448), In use/Available (9.9 GB/438 GB), Committed 10/448 GB, Cached 11.7 GB, Paged pool 1.2 GB
    
    # Train
    - GPU耗时
        Number of classes: 5749 | Number of images: 13233
        Loading feature extraction model 20180402-114759 -> Calculating features for images -> Training classifier -> Saved classifier model
        Program.py all time: 1013.1615738868713 seconds = 16.886026231447854 minutes = 0.28143377052413093 hrs
        So processing efficiency is: 0.0765 second/image
    - GPU使用情况: $nvidia-smi.exe -l 1
        CPU Memory Usage: 2248MiB| 480MiB | 480MiB | 480MiB
        GPU Memory Usage: 3156MiB|1388MiB | 1388MiB| 1388MiB  <16280MiB>
        GPU Utilization:   0%    |   0%   |   0%   |   0%
    - CPU耗时
        Program.py all time: 1531.372586965561 seconds = 25.522876449426015 minutes = 0.42538127415710025 hrs
        So processing efficiency is: 0.1157 second/image
    - CPU使用情况
        Utilization 87%, Speed 2.59 GHz, Processes 139, Threads 1685
        Memory: 2% (11/448), In use/Available (10.7 GB/437 GB), Committed 11/448 GB, Cached 12.0 GB, Paged pool 1.3 GB
    
    # Classify
    - GPU使用情况: $nvidia-smi.exe -l 1
        CPU Memory Usage: 2248MiB| 480MiB | 480MiB | 480MiB
        GPU Memory Usage: 3156MiB|1388MiB | 1388MiB| 1388MiB  <16280MiB>
        GPU Utilization:   0%    |   0%   |   0%   |   0%
    - CPU使用情况
        Utilization 85%, Speed 2.59 GHz, Processes 114, Threads 1396
        Memory: 2% (8/448), In use/Available (7.9 GB/440 GB), Committed 9/448 GB, Cached 1.2 GB, Paged pool 112 MB
    
    # Predict
    - GPU耗时
        Program.py all time: 35.54524612426758 seconds = 0.5924207687377929 minutes = 0.009873679478963216 hrs
        So processing efficiency is: 35.5 second/image
    - CPU耗时
        Program.py all time: 29.006401300430298 seconds = 0.4834400216738383 minutes
        So processing efficiency is: 29.0 second/image

Windows Server

  • GPU Info
    configuration:  2 NIVIDA GeForce RTX 2080 Ti
  • GPU Performance
    # Align
    - 耗时
        Total number of images: 13233 | Number of successfully aligned images: 13233
        ProgramName All Time: 938.7497174739838 seconds = 15.645828624566397 minutes = 0.2607638104094399 hrs
        So processing efficiency is: 0.0709 second/image
    - GPU使用情况: $nvidia-smi.exe -l 1
        GPU Memory Usage: 1183MiB|456MiB  | <11264MiB>
        GPU Utilization:   8%    |   0%
    
    # Train
    - GPU耗时
        Number of classes: 5749 | Number of images: 13233
        Loading feature extraction model 20180402-114759 -> Calculating features for images -> Training classifier -> Saved classifier model
        Program All Time: 1790.332600593567 seconds = 29.838876676559448 minutes = 0.497314611275908 hrs
        GPU processing efficiency is: 0.1353 second/image
    - GPU使用情况: $nvidia-smi.exe -l 1
        GPU Memory Usage: 2582MiB|463MiB  | <11264MiB>
        GPU Utilization:   13%   |   0%   
    - CPU耗时
        Program All Time: 1733.3322100639343 seconds = 28.88887016773224 minutes = 0.481481169462204 hrs
        CPU processing efficiency is: 0.130 second/image
    - CPU使用情况
        Utilization 87%, Speed 2.59 GHz, Processes 139, Threads 1685
        Memory: 2% (11/448), In use/Available (10.7 GB/437 GB), Committed 11/448 GB, Cached 12.0 GB, Paged pool 1.3 GB
    
    # Classify
    - GPU耗时
        Number of classes: 5749 | Number of images: 13233
        Program All Time: 36935 seconds = 615 minutes = 10 hrs
        GPU processing efficiency is: 2.791 second/image
    
    # Predict
    - GPU耗时
        Program.py all time: 34.344 seconds
        So processing efficiency is: 34.3 second/image

CPU和GPU的性能比较

# Azure VM CPU (4 GPU) CPU Performance:
    image processing efficiency (before training) is:   0.0856  second/image
    face image training efficiency is:                  0.1157  second/image
    face image prediction efficiency is:                29.0    second/image
# Azure VM GPU (4 GPU) GPU Performance:
    image processing efficiency (before training) is:   0.0481  second/image
    face image training efficiency is:                  0.0765  second/image
    face image prediction efficiency is:                35.5    second/image
# Windows Server GPU (2 GPU) GPU Performance:
    image processing efficiency (before training) is:   0.0709  second/image
    face image training efficiency is:                  0.1353  second/image
    face image prediction efficiency is:                34.3    second/image

GPU Log

Azure GPU VM 使用GPU的命令行提示

cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
gpu_device.cc:1344] Found device 0 with properties:
    name: Tesla V100-PCIE-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.38
    pciBusID: 0001:00:00.0
    totalMemory: 15.90GiB freeMemory: 14.60GiB
gpu_device.cc:1344] Found device 1 with properties:
    name: Tesla V100-PCIE-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.38
    pciBusID: 0002:00:00.0
    totalMemory: 15.90GiB freeMemory: 14.60GiB
gpu_device.cc:1344] Found device 2 with properties:
    name: Tesla V100-PCIE-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.38
    pciBusID: 0003:00:00.0
    totalMemory: 15.90GiB freeMemory: 14.60GiB
gpu_device.cc:1344] Found device 3 with properties:
    name: Tesla V100-PCIE-16GB major: 7 minor: 0 memoryClockRate(GHz): 1.38
    pciBusID: 0004:00:00.0
    totalMemory: 15.90GiB freeMemory: 14.60GiB
gpu_device.cc:1423] Adding visible gpu devices: 0, 1, 2, 3
gpu_device.cc:911] Device interconnect StreamExecutor with strength 1 edge matrix:
gpu_device.cc:917]      0 1 2 3
gpu_device.cc:930] 0:   N N N N
gpu_device.cc:930] 1:   N N N N
gpu_device.cc:930] 2:   N N N N
gpu_device.cc:930] 3:   N N N N
gpu_device.cc:1041] Created TensorFlow device (task:0/device:GPU:0 with 16280 MB memory) -> physical GPU (device: 0, name: Tesla V100-PCIE-16GB, pci bus id: 0001:00:00.0, compute capability: 7.0)
gpu_device.cc:1041] Created TensorFlow device (task:0/device:GPU:1 with 16280 MB memory) -> physical GPU (device: 1, name: Tesla V100-PCIE-16GB, pci bus id: 0002:00:00.0, compute capability: 7.0)
gpu_device.cc:1041] Created TensorFlow device (task:0/device:GPU:2 with 16280 MB memory) -> physical GPU (device: 2, name: Tesla V100-PCIE-16GB, pci bus id: 0003:00:00.0, compute capability: 7.0)
gpu_device.cc:1041] Created TensorFlow device (task:0/device:GPU:3 with 16280 MB memory) -> physical GPU (device: 3, name: Tesla V100-PCIE-16GB, pci bus id: 0004:00:00.0, compute capability: 7.0)

Azure GPU VM GPU闲置时的使用情况

# $nvidia-smi.exe -l 1
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+
Fri Apr 16 16:22:42 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 385.54                 Driver Version: 385.54                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name            TCC/WDDM | Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla V100-PCIE...  TCC  | 00000001:00:00.0 Off |                    0 |
| N/A   32C    P0    24W / 250W |      1MiB / 16280MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   1  Tesla V100-PCIE...  TCC  | 00000002:00:00.0 Off |                    0 |
| N/A   29C    P0    22W / 250W |      1MiB / 16280MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   2  Tesla V100-PCIE...  TCC  | 00000003:00:00.0 Off |                    0 |
| N/A   28C    P0    23W / 250W |      1MiB / 16280MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
|   3  Tesla V100-PCIE...  TCC  | 00000004:00:00.0 Off |                    0 |
| N/A   29C    P0    24W / 250W |      1MiB / 16280MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

**Azure GPU VM 使用GPU的命令行提示**

configuration: 28 vCPUs (Intel(R) Xeon(R) CPU E5-2660 v4 @ 2.00 GHz), 128G Memory and 2 NVIDIA GeForce RTX 2080 Ti
```markdown
cpu_feature_guard.cc:140] Your CPU supports instructions that this TensorFlow binary was not compiled to use: AVX2
gpu_device.cc:1344] Found device 0 with properties:
    name: GeForce RTX 2080 Ti major: 7 minor: 5 memoryClockRate(GHz): 1.545
    pciBusID: 0000:02:00.0
    totalMemory: 11.00GiB freeMemory: 9.90GiB
gpu_device.cc:1344] Found device 1 with properties:
    name: GeForce RTX 2080 Ti major: 7 minor: 5 memoryClockRate(GHz): 1.545
    pciBusID: 0000:03:00.0
    totalMemory: 11.00GiB freeMemory: 9.90GiB
gpu_device.cc:1423] Adding visible gpu devices: 0, 1
gpu_device.cc:911] Device interconnect StreamExecutor with strength 1 edge matrix:
gpu_device.cc:917]      0 1
gpu_device.cc:930] 0:   N N
gpu_device.cc:930] 1:   N N
gpu_device.cc:1041] Created TensorFlow device (task:0/device:GPU:0 with 11264 MB memory) -> physical GPU (device: 0, name: GeForce RTX 2080 Ti, pci bus id: 0000:02:00.0, compute capability: 7.5)
gpu_device.cc:1041] Created TensorFlow device (task:0/device:GPU:1 with 11264 MB memory) -> physical GPU (device: 1, name: GeForce RTX 2080 Ti, pci bus id: 0000:03:00.0, compute capability: 7.5)

Face Recognition System based on FaceNet

Reference

Project Backgroun

  1. Project Foundation
  2. Experiment Condition: computational resource (NVIDIA GPU 显卡)
    • DELL Laptop: GeForce GTX 1050 (4G) x1
    • Windows Server: GeForce RTX 2080 Ti (11G) x2

Face Recognition Function Process

Install ffmpeg

点击
然后点击https://www.gyan.dev/ffmpeg/builds/ffmpeg-git-essentials.7z下载ffmpeg

Crop from video to images

src/crop.py

1. 准备一个文件夹kol_video,子文件夹为KOL人名,子文件夹下放该KOL的video
2. 准备另一个文件夹kol_crop,剪切后的图片会依次存入不同KOL人名的子文件夹
$python src/crop.py C:/Users/PC/Desktop/kol_video C:/Users/PC/Desktop/kol_crop
import os
import sys
import time
import argparse
import subprocess

allowed_set = set(['avi', 'mp4', 'mkv', 'flv', 'wmv', 'mov']) 

def allowed_file(filename, allowed_set):
    check = '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_set
    return check

def main(args):

    ffmpegCmd   = "C:/Path/ffmpeg/bin/ffmpeg.exe"   # ffmpeg路径
    videoDir    = args.videoDir
    cropDir     = args.cropDir

    if not os.path.exists(cropDir):
            os.makedirs(cropDir)

    kol_name = []
    for i in os.listdir(videoDir):
        kol_name.append(i)

    for i in range(len(kol_name)):
        # count = 0
        dir_name = kol_name[i]
        kol_dir = os.path.join(videoDir, kol_name[i])
        if os.path.isdir(kol_dir):
            kol_crop_dir = os.path.join(cropDir, kol_name[i])
            if not os.path.exists(kol_crop_dir):
                    os.makedirs(kol_crop_dir)
            for kol_video_file in os.listdir(kol_dir):
                if allowed_file(filename=kol_video_file, allowed_set=allowed_set):   # | if (kol_video_file.endswith(".mp4")):
                    kol_video_path  = os.path.join(kol_dir, kol_video_file)
                    kol_crop_path   = os.path.join(kol_crop_dir, kol_video_file.rsplit('.')[0] + "_%04d.png")
                    video2framesCmd = ffmpegCmd + " -i " + kol_video_path + " -f image2 -vf fps=fps=3 -qscale:v 2 " + kol_crop_path
                    try:
                        subprocess.call(video2framesCmd, shell=True)
                        print('crop from %s to %s' % (kol_video_path, kol_crop_path))
                    except:
                        continue

def parse_arguments(argv):
    parser = argparse.ArgumentParser()
    parser.add_argument('videoDir', type=str, help='KOL video root dirctory which contains different KOL identity directories.')
    parser.add_argument('cropDir', type=str, help='KOL crops root dirctory which contains different KOL identity directories which containing labeled and aligned face thumbnails.')
    return parser.parse_args(argv)

if __name__ == '__main__':
    start_time = time.time()
    print('Program.py start at: ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
    main(parse_arguments(sys.argv[1:]))
    print('Program.py end at: ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
    print('Program.py all time: {0} seconds = {1} minutes = {2} hrs'.format((time.time() - start_time),
                                                                     (time.time() - start_time) / 60,
                                                                     (time.time() - start_time) / 3600))

以FFmpeg剪切一个抖音视频为例

- Tik Tok Video: 13.2MB, 59 seconds, 720x1280, 1751kbps(1751 kb/s 数据速率、码率、取样率), 30帧/秒(30 fps 帧速率)
- crop fps: 1(生成59张图片)、2(生成118张图片) | 生成格式类似 视频名_0001.png 的图片
- 要调整生成图片的多少,就调整 ffmpeg -i /data/video_1.mp4 -f image2  -vf fps=fps=1/60 -qscale:v 2 /data/mp4-%05d.jpg 中 fps的数值

- 取样率:单位时间内取样率越大,精度就越高,处理出来的文件就越接近原始文件,但是文件体积与取样率是成正比的,所以几乎所有的编码格式重视的都是如何用最低的码率达到最少的失真,围绕这个核心衍生出来的cbr(固定码率)与vbr(可变码率),都是在这方面做的文章,不过事情总不是绝对的,从音频方面来说,码率越高,被压缩的比例越小,音质损失越小,与音源的音质越接近
- 帧速率:指每秒钟刷新的图片的帧数,也可以理解为图形处理器每秒钟能够刷新几次。对影片内容而言,帧速率指每秒所显示的静止帧格数。要生成平滑连贯的动画效果,帧速率一般不小于8;而电影的帧速率为24fps。捕捉动态视频内容时,此数字愈高愈好。

FFmpeg剪切视频的几种用法

$ffmpeg -i /data/video_1.mp4 -f image2  -vf fps=fps=1/60 -qscale:v 2 /data/mp4-%05d.jpg # 该函数实现将视频集视频进行分解成图像序列,并放在一个文件夹里面
$ffmpeg -i /data/video_1.mp4 -ss 00:00:10 -t 00:00:50 -f image2 -vf fps=fps=2 -qscale:v 2 /data/mp4-%05d.jpg # -ss规定从什么时候开始剪切,-t规定从什么时候结束剪切

Detect, extract and align face images

src/align/align_dataset_mtcnn.py (基于原facenet代码进行改写)

# 此时大量消耗CPU和内存
$python src/align/align_dataset_mtcnn.py C:/Users/PC/Desktop/kol_crop C:/Users/PC/Desktop/kol_160 --image_size 160 --margin 32 # 如果GPU够强劲
$python src/align/align_dataset_mtcnn.py C:/Users/PC/Desktop/kol_crop C:/Users/PC/Desktop/kol_160 --image_size 160 --margin 32 --gpu_memory_fraction 0.5 # 如果GPU不够强劲
# *********** 第一处
all_people = 0 # 加入all_people计算
with open(bounding_boxes_filename, "w") as text_file:

# *********** 第二处
for cls in dataset:
    count = 0 # 加入count计算

# *********** 第三处
for image_path in cls.image_paths:
    count += 1 # count增加

# *********** 第四处
# filename_base, file_extension = os.path.splitext(output_filename)
# if args.detect_multiple_faces:
#     output_filename_n = "{}_{}{}".format(filename_base, i, file_extension)
# else:
#     output_filename_n = "{}{}".format(filename_base, file_extension)
# misc.imsave(output_filename_n, scaled)
# text_file.write('%s %d %d %d %d\n' % (output_filename_n, bb[0], bb[1], bb[2], bb[3]))

filename_base, file_extension   = os.path.splitext(output_filename)
filename                        = cls.name + '_' + str(count).zfill(4)
if args.detect_multiple_faces:
    all_people += 1
    output_filename_n = "{}_{}{}".format(filename, i, file_extension)
    print("{0} people: crop {1} to {2}".format(all_people, image_path, output_filename_n))
else:
    all_people += 1
    output_filename_n = "{}{}".format(filename, file_extension)
    print("{0} people: crop {1} to {2}".format(all_people, image_path, output_filename_n))
save_filename = os.path.join(output_class_dir, output_filename_n)
misc.imsave(save_filename, scaled)
text_file.write('%s %d %d %d %d\n' % (output_filename_n, bb[0], bb[1], bb[2], bb[3]))

# *********** 第五处
import time
if __name__ == '__main__':
    start_time = time.time()
    print('label_align_dataset.py start at: ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
    main(parse_arguments(sys.argv[1:]))
    print('label_align_dataset.py end at: ' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()))
    print('label_align_dataset.py all time: {0} seconds = {1} minutes = {2} hrs'.format((time.time() - start_time),
                                                                     (time.time() - start_time) / 60,
                                                                     (time.time() - start_time) / 3600))

Manually clean the data set

手动清洗数据集 kol_160: 每个子文件夹下不是此KOL的图片(手动删除) 、有脸部遮挡的图片(比如手部遮挡、物品遮挡、文字动图遮挡等,手动删除)、非人脸图(手动删除)

Train model with face thumbnails

src/classifier.py

$python src/classifier.py TRAIN C:/Users/PC/Desktop/kol_160 models/20180402-114759/20180402-114759.pb models/kol.pkl

Validate model with face thumbnails

src/classifier.py

$python src/classifier.py CLASSIFY C:/Users/PC/Desktop/kol_160 models/20180402-114759/20180402-114759.pb models/kol.pkl

Predict KOL identity

contributed/predict.py

$python contributed/predict.py C:/Users/PC/Desktop/kol_160/01/01_0001.png models/20180402-114759 models/kol.pkl # 如果GPU够强劲
$python contributed/predict.py C:/Users/PC/Desktop/kol_160/01/01_0001.png models/20180402-114759 models/kol.pkl --gpu_memory_fraction 0.5 # 如果GPU不够强劲

Configura Project Virtual Environment

  1. 添加缺失的包
    $conda activate facenet # FaceNet-Configuration-and-Deployment中配置的facenet环境
    $pip install waitress imutils flask pypinyin
  2. 修改源码C:\Users\PC\.conda\envs\haha\Lib\site-packages\werkzeug\utils.py
    # 使用时: from werkzeug.utils import secure_filename
    
    # *****************Action1:修改代码
    # filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip("._")
    
    _filename_ascii_add_strip_re = re.compile(r'[^A-Za-z0-9_\u4E00-\u9FBF.-]')
    filename = str(_filename_ascii_add_strip_re.sub('', '_'.join( 
                    filename.split()))).strip('._')
  3. 使用cmd开启服务器时,要“以管理员身份运行”
    $cd 根目录下(server.py所在目录)
    $python server.py
  4. 保持一致
    • 测试时的包版本要与训练模型时的包版本一致才可以预测(所以不要随便升级包版本)
      1. AttributeError: 'SVC' object has no attribute '_probA' (or something like that)
      It turned out that I had to stay with the same version of scikit that was used to train the models I currently have. Later versions of scikit don't work with the trained face models. If you want to upgrade scikit, you have to retrain you models with the new version of scikit.
    • 训练和测试的基准模型保持一致
      - 若训练时使用的基准模型为models/20180402-114759/,predict的时候也请使用此模型
      - 若训练时使用的基准模型为models/20180408-102900/,predict的时候也请使用此模型

Run C/S Server to Load Model

Folder Structure

  • Framework: Python Flask + HTML
  • 目录
    - server.py
    - utils.py
    - templates
        - index.html
        - warning.html
        - predict_single.html
        - predict_single_result.html
        - predict_batch.html
        - predict_batch_result.html
        - find_similar_kol.html
        - find_similar_kol_result.html
    - models
        - 20180402-114759
            - 20180402-114759.pb
            - model-20180402-114759.meta
            - model-20180402-114759.ckpt-275.index
            - model-20180402-114759.ckpt-275.data-00000-of-00001
        - kol.pkl (训练好的classifier放在这里| 训练和测试时的包版本必须保持一致,不要随便更新包)

Program Details

Please refer my github project: Face-Recognition-Server-based-on-FaceNet

server.py

server.py

import os
import cv2
import time
import pickle
import shutil
import numpy as np
import tensorflow as tf
from waitress import serve
from scipy.misc import imread
import facenet.facenet as facenet
import align.detect_face as detect_face
from pypinyin import lazy_pinyin
from werkzeug.utils import secure_filename
from imutils.video import WebcamVideoStream
from flask import Flask, request, render_template

from utils import (
    allowed_set,
    allowed_file,
    load_and_align_data
)

tf.reset_default_graph()

app             = Flask(__name__)
app.secret_key  = os.urandom(24)
APP_ROOT        = os.path.dirname(os.path.abspath(__file__))
uploads_path    = os.path.join(APP_ROOT, 'static')

@app.route("/")
def index_page():   # select prediction mode: single | batch | top k similar
    return render_template(template_name_or_list="index.html")

@app.route("/predictSinglePage")
def predict_single_page(): # manually upload single image file for identity prediction
    return render_template(template_name_or_list="predict_single.html")

@app.route('/predictSingleImage', methods=['POST', 'GET'])
def predict_single_image(): # get single image prediction result | upload image files via POST request and feeds them to the FaceNet model to get prediction result
    images_savedir = "static/"
    if  os.path.exists(images_savedir):
        shutil.rmtree(images_savedir)
    if not os.path.exists(images_savedir):
        os.makedirs(images_savedir)
    if request.method == 'POST':
        if 'file' not in request.files:
            return render_template(
                template_name_or_list="warning.html",
                status="No 'file' field in POST request!"
            )
        file        = request.files['file']                                    # <FileStorage: 'download.jpg' ('image/jpeg')>
        filename    = secure_filename(''.join(lazy_pinyin(file.filename))) # download.jpg
        if filename == "":
            return render_template(
                template_name_or_list="warning.html",
                status="No selected file!"
            )
        upload_path = os.path.join(uploads_path, filename)
        file.save(upload_path)
        static_path             = "static/" + filename
        image_paths = []
        image_paths.append(static_path)
        path_nofperson_identity_similarity_timeused = []
        display = path_nofperson_identity_similarity_timeused
        if file and allowed_file(filename=filename, allowed_set=allowed_set):
            tf.reset_default_graph()
            start_time  = time.time()
            images, count_per_image = load_and_align_data(image_paths) # count_per_image = [x] | x = 0时代表没有一个人脸被检测到            
            if count_per_image[0] != 0:
                if count_per_image[0] == "0":
                    return render_template(
                        template_name_or_list="warning.html",
                        status="The uploaded file is illegal. Please upload safe image file!"
                    )
                feed_dict               = {images_placeholder: images , phase_train_placeholder:False}
                emb                     = sess.run(embeddings, feed_dict=feed_dict)
                classifier_filename_exp = os.path.expanduser(classifier_filename)
                if images is not None: 
                    with open(classifier_filename_exp, 'rb') as infile:
                        (model, class_names) = pickle.load(infile)
                    if model:
                        print('Loaded classifier model from file "%s"\n' % classifier_filename_exp)
                        predictions                 = model.predict_proba(emb)
                        best_class_indices          = np.argmax(predictions, axis=1) # <class 'numpy.ndarray'> [0]
                        best_class_probabilities    = predictions[np.arange(len(best_class_indices)), best_class_indices] # [0.99692782]
                        k = 0
                        for j in range(count_per_image[0]):
                            sentence        = str(static_path) + ","
                            sentence        = sentence + str(count_per_image[0]) + " people detected!,"
                            print("\npeople in image %s :" %(filename), '%s: %.3f' % (class_names[best_class_indices[k]], best_class_probabilities[k]))
                            identity        = class_names[best_class_indices[k]]
                            probabilities   = best_class_probabilities[k]
                            k+=1
                            probabilities   = "Similarity: " + str(probabilities).split('.')[0] + '.' + str(probabilities).split('.')[1][:3]
                            spent           = str(time.time() - start_time)
                            spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                            sentence        = sentence + "Person " + str(k) + ": " + str(identity) + "," + str(probabilities) + "," + str(spent)
                            display.append(sentence)
                    else:
                        sentence        = str(static_path) + ","
                        sentence        = sentence + "No embedding classifier was detected!,"
                        identity        = ""
                        probabilities   = ""
                        spent           = str(time.time() - start_time)
                        spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                        sentence        = sentence + str(identity) + "," + str(probabilities) + "," + str(spent)
                        display.append(sentence)
                else:
                    sentence        = str(static_path) + ","
                    sentence        = sentence + "No human face was detected!,"
                    identity        = ""
                    probabilities   = ""
                    spent           = str(time.time() - start_time)
                    spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                    sentence        = sentence + str(identity) + "," + str(probabilities) + "," + str(spent)
                    display.append(sentence)
            else:
                sentence        = str(static_path) + ","
                sentence        = sentence + "No human face was detected!,"
                identity        = ""
                probabilities   = ""
                spent           = str(time.time() - start_time)
                spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                sentence        = sentence + str(identity) + "," + str(probabilities) + "," + str(spent)
                display.append(sentence)
        return render_template(
            template_name_or_list='predict_single_result.html',
            display=display
        )      
    else:
        return render_template(
            template_name_or_list="warning.html",
            status="POST HTTP method required!"
        )

@app.route("/predictBatchPage")
def predict_batch_page(): # manually upload multiple image files for identity prediction
    return render_template(template_name_or_list="predict_batch.html")

@app.route('/predictBatchImage', methods=['POST', 'GET'])
def predict_batch_image(): # get multiple image prediction results | upload image files via POST request and feeds them to the FaceNet model to get prediction results
    images_savedir = "static/"
    if  os.path.exists(images_savedir):
        shutil.rmtree(images_savedir)
    if not os.path.exists(images_savedir):
        os.makedirs(images_savedir)
    if request.method == 'POST':
        if 'file' not in request.files:
            return render_template(
                template_name_or_list="warning.html",
                status="No 'file' field in POST request!"
            )
        files   = request.files.getlist('file')
        path_nofperson_identity_similarity_timeused = []
        display = path_nofperson_identity_similarity_timeused
        for file in files: # file: <FileStorage: '中文.jpg' ('image/jpeg')>  
            one_image = []  
            filename = secure_filename(''.join(lazy_pinyin(file.filename))) # 中文.jpg -> zhongwen.jpg
            if allowed_file(filename=filename, allowed_set=allowed_set):
                if filename == "":
                    return render_template(
                        template_name_or_list="warning.html",
                        status="No selected file!"
                    )
                upload_path             = os.path.join(uploads_path, filename)
                file.save(upload_path)
                static_path             = "static/" + filename
                image_paths = []
                image_paths.append(static_path)
                tf.reset_default_graph()
                start_time  = time.time()
                images, count_per_image = load_and_align_data(image_paths)
                if count_per_image[0] != 0:
                    if count_per_image[0] == "0":
                        return render_template(
                            template_name_or_list="warning.html",
                            status="The uploaded file is illegal. Please upload safe image file!"
                        )
                    feed_dict               = {images_placeholder: images , phase_train_placeholder:False}
                    emb                     = sess.run(embeddings, feed_dict=feed_dict)
                    classifier_filename_exp = os.path.expanduser(classifier_filename)
                    if images is not None: 
                        with open(classifier_filename_exp, 'rb') as infile:
                            (model, class_names) = pickle.load(infile)
                            if model:
                                print('Loaded classifier model from file "%s"\n' % classifier_filename_exp)
                                predictions                 = model.predict_proba(emb)
                                best_class_indices          = np.argmax(predictions, axis=1)
                                best_class_probabilities    = predictions[np.arange(len(best_class_indices)), best_class_indices]
                                k = 0
                                for j in range(count_per_image[0]):
                                    sentence        = str(static_path) + ","
                                    sentence        = sentence + str(count_per_image[0]) + " people detected!,"
                                    print("\npeople in image %s :" %(filename), '%s: %.3f' % (class_names[best_class_indices[k]], best_class_probabilities[k]))
                                    identity        = class_names[best_class_indices[k]]
                                    probabilities   = best_class_probabilities[k]
                                    k+=1
                                    probabilities   = "Similarity: " + str(probabilities).split('.')[0] + '.' + str(probabilities).split('.')[1][:3]
                                    spent           = str(time.time() - start_time)
                                    spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                                    sentence        = sentence + "Person " + str(k) + ": " + str(identity) + "," + str(probabilities) + "," + str(spent)
                                    one_image.append(sentence)
                            else:
                                sentence        = str(static_path) + ","
                                sentence        = sentence + "No embedding classifier was detected!,"
                                identity        = ""
                                probabilities   = ""
                                spent           = str(time.time() - start_time)
                                spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                                sentence        = sentence + str(identity) + "," + str(probabilities) + "," + str(spent)
                                one_image.append(sentence)
                    else:
                        sentence        = str(static_path) + ","
                        sentence        = sentence + "No human face was detected!,"
                        identity        = ""
                        probabilities   = ""
                        spent           = str(time.time() - start_time)
                        spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                        sentence        = sentence + str(identity) + "," + str(probabilities) + "," + str(spent)
                        one_image.append(sentence)
                else:
                    sentence        = str(static_path) + ","
                    sentence        = sentence + "No human face was detected!,"
                    identity        = ""
                    probabilities   = ""
                    spent           = str(time.time() - start_time)
                    spent           = "Time consuming: " + str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]  + " seconds"
                    sentence        = sentence + str(identity) + "," + str(probabilities) + "," + str(spent)
                    one_image.append(sentence)
            display.append(one_image)
        return render_template(
            template_name_or_list='predict_batch_result.html',
            display=display
        )      
    else:
        return render_template(
            template_name_or_list="warning.html",
            status="POST HTTP method required!"
        )

@app.route("/findSimilarKOLPage")
def find_similar_kol_page(): # manually upload single image file to find tip k similar identities
    return render_template(template_name_or_list="find_similar_kol.html")

@app.route('/findSimilarKOLResult', methods=['POST', 'GET'])
def find_similar_kol_result(): # get tip k similar identities
    images_savedir = "static/"
    if  os.path.exists(images_savedir):
        shutil.rmtree(images_savedir)
    if not os.path.exists(images_savedir):
        os.makedirs(images_savedir)
    if request.method == 'POST':
        if 'file' not in request.files:
            return render_template(
                template_name_or_list="warning.html",
                status="No 'file' field in POST request!"
            )
        file        = request.files['file']                                    # <FileStorage: 'download.jpg' ('image/jpeg')>
        filename    = secure_filename(''.join(lazy_pinyin(file.filename))) # download.jpg
        if filename == "":
            return render_template(
                template_name_or_list="warning.html",
                status="No selected file!"
            )
        upload_path = os.path.join(uploads_path, filename)
        file.save(upload_path)
        static_path             = "static/" + filename
        image_paths = []
        image_paths.append(static_path)
        kthKOL_similarity_timeused = []
        display   = kthKOL_similarity_timeused
        if file and allowed_file(filename=filename, allowed_set=allowed_set):
            tf.reset_default_graph()
            start_time  = time.time()
            images, count_per_image = load_and_align_data(image_paths) # count_per_image = [x] | x = 0时代表没有一个人脸被检测到            
            if count_per_image[0] != 0:
                if count_per_image[0] == "0":
                    return render_template(
                        template_name_or_list="warning.html",
                        status="The uploaded file is illegal. Please upload safe image file!"
                    )
                if count_per_image[0] != 1:
                    return render_template(
                        template_name_or_list="warning.html",
                        status="Please upload image which contains only one KOL face!"
                    )
                feed_dict               = {images_placeholder: images , phase_train_placeholder:False}
                emb                     = sess.run(embeddings, feed_dict=feed_dict)
                classifier_filename_exp = os.path.expanduser(classifier_filename)
                if images is not None: 
                    with open(classifier_filename_exp, 'rb') as infile:
                        (model, class_names) = pickle.load(infile)
                    if model:
                        print('Loaded classifier model from file "%s"\n' % classifier_filename_exp)
                        predictions                 = model.predict_proba(emb)
                        top_k_class_indices         = predictions[0].argsort()[-5:][::-1] # 最相似的前5个KOL
                        k = 0
                        for i in list(top_k_class_indices):
                            class_indices           = []
                            class_indices.append(i)
                            class_probabilities     = predictions[np.arange(len(class_indices)), class_indices] 
                            identity                = class_names[class_indices[0]]
                            probabilities           = class_probabilities[0]
                            probabilities           = str(probabilities).split('.')[0] + '.' + str(probabilities).split('.')[1][:3]
                            spent                   = str(time.time() - start_time)
                            spent                   = str(spent).split('.')[0] + '.' + str(spent).split('.')[1][:2]
                            k                       += 1
                            kth_kol                 = str(static_path) + "," + str(k) + "th KOL: " + identity + "," + "Similarity: " + probabilities + "," + "Time consuming: " + spent + " seconds"
                            display.append(kth_kol)
                    else:
                        return render_template(
                            template_name_or_list="warning.html",
                            status="No embedding classifier was detected!"
                        )
                else:
                    return render_template(
                        template_name_or_list="warning.html",
                        status="No human face was detected!"
                    )
            else:
                return render_template(
                    template_name_or_list="warning.html",
                    status="No human face was detected!"
                )
        return render_template(
            template_name_or_list='find_similar_kol_result.html',
            display=display
        )      
    else:
        return render_template(
            template_name_or_list="warning.html",
            status="POST HTTP method required!"
        )

if __name__ == '__main__':
    # tf.reset_default_graph()
    with tf.Graph().as_default():
        os.environ["CUDA_DEVICE_ORDER"] = "PCI_BUS_ID"
        os.environ['CUDA_VISIBLE_DEVICES'] = "0" # 指定使用第一块GPU
        config                                              =  tf.ConfigProto()
        config.allow_soft_placement                         = True
        config.gpu_options.per_process_gpu_memory_fraction  = 0.7
        config.gpu_options.allow_growth                     = True
        with tf.Session(config=config) as sess:
            model               = "models/20180402-114759/"
            classifier_filename = "models/kol.pkl"
            image_size          = 160
            facenet.load_model(model)
            images_placeholder      = tf.get_default_graph().get_tensor_by_name("input:0")
            embeddings              = tf.get_default_graph().get_tensor_by_name("embeddings:0")
            phase_train_placeholder = tf.get_default_graph().get_tensor_by_name("phase_train:0")
            serve(app=app, host='0.0.0.0', port=5000)

utils.py

utils.py

import os
import numpy as np
from scipy import misc
import tensorflow as tf
import align.detect_face
from six.moves import xrange
import facenet.facenet as facenet
from scipy.misc import imresize, imsave
from tensorflow.python.platform import gfile
from align.detect_face import detect_face
from align.detect_face import create_mtcnn

allowed_set = set(['png', 'jpg', 'jpeg'])           # allowed image formats for upload


def allowed_file(filename, allowed_set):
    check = '.' in filename and filename.rsplit('.', 1)[1].lower() in allowed_set
    return check


def get_face(img, pnet, rnet, onet, image_size):
    minsize             = 20
    threshold           = [0.6, 0.7, 0.7]
    factor              = 0.709
    margin              = 44
    input_image_size    = image_size
    img_size            = np.asarray(img.shape)[0:2]
    bounding_boxes, _   = detect_face(
        img=img, minsize=minsize, pnet=pnet, rnet=rnet,
        onet=onet, threshold=threshold, factor=factor
    )

    if not len(bounding_boxes) == 0:
        for face in bounding_boxes:
            det         = np.squeeze(face[0:4])
            bb          = np.zeros(4, dtype=np.int32)
            bb[0]       = np.maximum(det[0] - margin / 2, 0)
            bb[1]       = np.maximum(det[1] - margin / 2, 0)
            bb[2]       = np.minimum(det[2] + margin / 2, img_size[1])
            bb[3]       = np.minimum(det[3] + margin / 2, img_size[0])
            cropped     = img[bb[1]: bb[3], bb[0]:bb[2], :]
            face_img    = imresize(arr=cropped, size=(input_image_size, input_image_size), mode='RGB')
            return face_img
    else:
        return None


def load_image(img, do_random_crop, do_random_flip, image_size, do_prewhiten=True):
    image = np.zeros((1, image_size, image_size, 3))
    if img.ndim == 2:
            img = to_rgb(img)
    if do_prewhiten:
            img = prewhiten(img)
    img = crop(img, do_random_crop, image_size)
    img = flip(img, do_random_flip)
    image[:, :, :, :] = img
    return image


def forward_pass(img, session, images_placeholder, phase_train_placeholder, embeddings, image_size):
    if img is not None:
        image = load_image(
            img=img, do_random_crop=False, do_random_flip=False,
            do_prewhiten=True, image_size=image_size
        )
        feed_dict = {images_placeholder: image, phase_train_placeholder: False}
        embedding = session.run(embeddings, feed_dict=feed_dict)
        return embedding
    else:
        return None


def load_embeddings():
    embedding_dict = defaultdict()  
    for embedding in glob.iglob(pathname='embeddings/*.npy'):  
        name                    = remove_file_extension(embedding)
        dict_embedding          = np.load(embedding)
        embedding_dict[name]    = dict_embedding
    return embedding_dict


def remove_file_extension(filename):
    filename = os.path.splitext(filename)[0]
    return filename


def identify_face(embedding, embedding_dict):
    min_distance = 100
    try:
        for (name, dict_embedding) in embedding_dict.items():
            distance = np.linalg.norm(embedding - dict_embedding)
            if distance < min_distance:
                min_distance = distance
                identity = name
        if min_distance <= 1.1:
            identity = identity[11:]
            result = str(identity) + " with distance: " + str(min_distance)
            return result
        else:
            result = "Not in the database, the distance is " + str(min_distance)
            return result
    except Exception as e:
        print(str(e))
        return str(e)


def load_model(model, input_map=None):
    model_exp = os.path.expanduser(model)
    if (os.path.isfile(model_exp)):
        print('Model filename: %s' % model_exp)
        with gfile.FastGFile(model_exp,'rb') as f:
            graph_def = tf.GraphDef()
            graph_def.ParseFromString(f.read())
            tf.import_graph_def(graph_def, input_map=input_map, name='')
    else:
        print('Model directory: %s' % model_exp)
        meta_file, ckpt_file = get_model_filenames(model_exp)
        print('Metagraph file: %s' % meta_file)
        print('Checkpoint file: %s' % ckpt_file)
        saver = tf.train.import_meta_graph(os.path.join(model_exp, meta_file), input_map=input_map)
        saver.restore(tf.get_default_session(), os.path.join(model_exp, ckpt_file))


def load_and_align_data(image_paths):

    minsize     = 20 
    threshold   = [ 0.6, 0.7, 0.7 ]  
    factor      = 0.709 
    image_size  = 160
    margin      = 44
    
    print('Creating networks and loading parameters')
    with tf.Graph().as_default():
        config                                              =  tf.ConfigProto()
        config.allow_soft_placement                         = True
        config.gpu_options.per_process_gpu_memory_fraction  = 0.7
        config.gpu_options.allow_growth                     = True
        sess                                                = tf.Session(config=config)
        with sess.as_default():
            pnet, rnet, onet = create_mtcnn(sess, None)

    nrof_samples = len(image_paths)
    img_list = [] 
    count_per_image = []
    for i in xrange(nrof_samples):
        # img = misc.imread(name=file, mode='RGB')
        img = misc.imread(os.path.expanduser(image_paths[i])) # (157, 320, 3)
        if img.shape[2] !=3:
            print("Cannot feed value of shape (x, y, z, 4) for Tensor 'pnet/input:0', which has shape '(?, ?, ?, 3)'")
            images = "0"
            count_per_image.append("0")
            return images, count_per_image
        img_size = np.asarray(img.shape)[0:2] 
        bounding_boxes, _ = detect_face(img, minsize, pnet, rnet, onet, threshold, factor) # (3, 5) | 3代表检测到3个人脸
        count_per_image.append(len(bounding_boxes)) # bounding_boxes.shape = (x, 5) | x = 0时代表没有一个人脸被检测到
        if len(bounding_boxes) == 0:
            print("No person detected in this image!")
            images = "0"
            return images, count_per_image
        else:
            for j in range(len(bounding_boxes)):	
                det = np.squeeze(bounding_boxes[j,0:4])
                bb = np.zeros(4, dtype=np.int32)
                bb[0] = np.maximum(det[0]-margin/2, 0)
                bb[1] = np.maximum(det[1]-margin/2, 0)
                bb[2] = np.minimum(det[2]+margin/2, img_size[1])
                bb[3] = np.minimum(det[3]+margin/2, img_size[0])
                cropped = img[bb[1]:bb[3],bb[0]:bb[2],:]
                aligned = misc.imresize(cropped, (image_size, image_size), interp='bilinear')
                prewhitened = facenet.prewhiten(aligned) # (160, 160, 3)
                img_list.append(prewhitened)	
            images = np.stack(img_list) # (3, 160, 160, 3)
            return images, count_per_image

templates

index.html

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Upload Image</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
</head>

<body>
    <div class="container text-center">
            <br><br>
        <h1>Server Online</h1>
            <br><br><br>
            <br><br><br><br><br>
        <a href="{{ url_for('predict_single_page') }}" class="btn btn-outline-dark">Click here for single image identity prediction!</a>
            <br><br><br>
        <a href="{{ url_for('predict_batch_page') }}" class="btn btn-outline-dark">Click here for batch image identity prediction!</a>
        <br><br><br>
        <a href="{{ url_for('find_similar_kol_page') }}" class="btn btn-outline-dark">Click here to find similar KOL!</a>
    </div>
</body>
</html>
warning.html

warning.html

<!DOCTYPE html>
<html>
<head>
    <title>Image identity prediction</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
</head>

<body>
    <div class="container text-center">
            <br><br><br><br><br><br><br>
        <h3>Warning!</h3>
            <br><br>
        <p>{{ status }}</p>
            <br><br>
        <a href="{{ url_for('index_page') }}" class="btn btn-outline-dark">Back</a>
    </div>
</body>
</html>
predict_single.html

predict_single.html

<!DOCTYPE html>
<html>
<head>
    <title>Image identity prediction</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
</head>

<body>
    <div class="container text-center">
            <br><br><br><br><br><br><br>
        <h3>Upload your image file for identity prediction:</h3>
            <br><br>
        <form method=POST enctype=multipart/form-data action="{{ url_for('predict_single_image') }}">
            <input type=file name=file class="btn btn-outline-dark">
            <input type="submit" class="btn btn-outline-dark">
        </form>
            <br><br>
        <a href="{{ url_for('index_page') }}" class="btn btn-outline-dark">Back</a>
    </div>
</body>
</html>
predict_single_result.html

predict_single_result.html

<!DOCTYPE html>
<html>
    <head>
        <title>Image identity prediction</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
    </head>

    <body>
        <div class="container text-center">
                <br><br><br><br><br><br><br>
            <h3>Result of prediction:</h3>
                <br><br>
            <p><font color="#ff0033" size="5"><b> {{ display[0].split(',')[1] }} </b></font></p>
            <p>{{ display[0].split(',')[4] }} </p>
            <p> <img style="height: 150px" src="{{display[0].split(',')[0]}}"/> </p>
                <ul>
                    {% for record in display %}
                        <p> <font color="#ff0033" size="3"> {{ record.split(',')[2] }} </font> &nbsp;&nbsp; {{ record.split(',')[3] }} </p>
                    {% endfor %}
                </ul>
                <br><br>
            <a href="{{ url_for('predict_single_page') }}" class="btn btn-outline-dark">Back</a>
        </div>
    </body>
</html>
predict_batch.html

predict_batch.html

<!DOCTYPE html>
<html>
<head>
    <title>Image identity prediction</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
</head>

<body>
    <div class="container text-center">
            <br><br><br><br><br><br><br>
        <h3>Upload your image file for identity prediction:</h3>
            <br><br>
        <form method=POST enctype=multipart/form-data action="{{ url_for('predict_batch_image') }}">
            <input type=file name=file multiple="multiple" class="btn btn-outline-dark">
            <input type="submit" class="btn btn-outline-dark">
        </form>
            <br><br>
        <a href="{{ url_for('index_page') }}" class="btn btn-outline-dark">Back</a>
    </div>
</body>
</html>
predict_batch_result.html

predict_batch_result.html

<!DOCTYPE html>
<html>
    <head>
        <title>Image identity prediction</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
    </head>

    <body>
        <div class="container text-center">
                <br><br><br><br><br><br><br>
            <h3>Result of prediction:</h3>
                <br><br>
                {% for image in display %}  <!-- 列出每一张图片 -->
                    <p> <img style="height: 150px" src="{{image[0].split(',')[0]}}"/> </p> 
                    <p> <font color="#ff0033" size="3"> {{ image[0].split(',')[1] }} </font> &nbsp;&nbsp; {{image[0].split(',')[4]}} </p>
                    {% for record in image %} <!-- 识别每张图片中的每个人(可能不止一个) -->
                        <p> {{ record.split(',')[2] }} &nbsp;&nbsp; {{ record.split(',')[3] }} </p>
                    {% endfor %}
                {% endfor %}
                <br><br>
            <a href="{{ url_for('predict_batch_page') }}" class="btn btn-outline-dark">Back</a>
        </div>
    </body>
</html>
find_similar_kol.html
<!DOCTYPE html>
<html>
<head>
    <title>Find similar KOL</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
</head>

<body>
<div class="container text-center">
        <br><br><br><br><br><br><br>
    <h3>Upload your image file to find the similar KOL:</h3>
        <br><br>
    <form method=POST enctype=multipart/form-data action="{{ url_for('find_similar_kol_result') }}">
        <input type=file name=file class="btn btn-outline-dark">
        <input type="submit" class="btn btn-outline-dark">
    </form>
        <br><br>
    <a href="{{ url_for('index_page') }}" class="btn btn-outline-dark">Back</a>
</div>

</body>
</html>
find_similar_kol_result.html
<!DOCTYPE html>
<html>
    <head>
        <title>Similar KOL Finding Results</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css">
    </head>

    <body>
        <div class="container text-center">
                <br><br><br><br><br><br><br>
            <h3>Similar KOL Finding Results:</h3>
                <br><br>
            <p>{{ display[0].split(',')[3] }} </p>
            <p> <img style="height: 150px" src="{{display[0].split(',')[0]}}"/> </p>
                <ul>
                    {% for record in display %}
                        <p> <font color="#ff0033" size="3"> {{ record.split(',')[1] }} </font> &nbsp;&nbsp; {{ record.split(',')[2] }} </p>
                    {% endfor %}
                </ul>
                <br><br>
            <a href="{{ url_for('predict_single_page') }}" class="btn btn-outline-dark">Back</a>
        </div>
    </body>
</html>

Program Tricks

处理中文文件名

用secure_filename获取中文文件名时,中文会被省略,因为secure_filename()函数只返回ASCII字符,非ASCII字符会被过滤掉。

  1. 修改源码C:\Users\PC\.conda\envs\haha\Lib\site-packages\werkzeug\utils.py
    # 使用时: from werkzeug.utils import secure_filename
    
    # *****************Action1:修改代码
    # filename = str(_filename_ascii_strip_re.sub("", "_".join(filename.split()))).strip("._")
    
    _filename_ascii_add_strip_re = re.compile(r'[^A-Za-z0-9_\u4E00-\u9FBF.-]')
    filename = str(_filename_ascii_add_strip_re.sub('', '_'.join( 
                    filename.split()))).strip('._')
  2. 使用第三方库(pypinyin),将中文名转换成拼音
    from pypinyin import lazy_pinyin
    from werkzeug.utils import secure_filename
    filename = secure_filename(''.join(lazy_pinyin(file.filename))) # filename = secure_filename(file.filename)

保存文件信息

  • 保存文本内容到txt文件:
    path_identity_probability = blablabla
    with open("batch.txt","w") as f:
        f.writelines(path_identity_probability)
  • 保存图片到指定目录:
    upload_path = os.path.join(uploads_path, filename)
    file.save(upload_path)

排查错误

print("0")
# code snippet 1
print("1")
# code snippet 1
# 通过命令行逐一观察,到哪个数字停止,就是前一步出错