FaceNet: A Unified Embedding for Face Recognition and Clustering 阅读笔记

简介

该论文试图去解决人脸识别的课题,人脸识别有三个研究方向,分别为

  1. 验证(verification),用于判断两个图像是否为同一个人
  2. 识别(recognition),用于识别一个图像是哪个人
  3. 聚类(clustering),在一系列图片中,找到一个经常出现的人

通常的人脸识别算法流程是这样的——

与之相反,论文提出的模型,以直接生产特征向量为目标,也就是说,特征向量不再作为一个中间信息,而是一个模型实实在在优化的目标。论文中的这种损失函数称为Triplet Loss。

模型

我们希望构造函数f(x),将图片x映射到特征空间\mathbb{R}^d,使得所有相同身份的人脸拥有较小的欧几里得距离,反之不同人脸的距离较大。在Triplet Loss的构造中,为了满足尺度的恒定,还额外规定f(x)位于单位球体上,即|f(x)| = 1

模型希望相同人脸特征距离近,不同人脸特征距离远,量化到数学公式上就是

|f(x_i^a)-f(x_i^p)|^2 + \alpha < |f(x_i^a)-f(x_i^n)|^2

其中x_i^a为基准图片(anchor),x_i^p为同一身份的不同图片(positive),x_i^n为不同身份的不同图片(negative). 而\alpha被称为TripletLoss的边缘距离(margin)

由于算力的原因,不能计算出所有可能的x_i^ax_i^px_i^n组合,即所有Triplet。因此,选择有代表性的Triplet是非常有必要的。一种非常暴力的做法是,找到上述不等式中前半部分的最大值以及后半部分的最小值,即argmax|f(x_i^a)-f(x_i^p)|^2argmin|f(x_i^a)-f(x_i^n)|^2,他们的组合一定是优化的瓶颈部分。我们将他们作为Hard Triplet,单纯优化他们就可以较快收敛。

由于图片画质以及误标记等原因,全局的argmax和argmin表现并不好,不过将其改成mini-batch内部的argmax与argmin就好了。

从零开始编写并托管WordPress插件:NoBaidu抵制百度插件

最近被一篇称为《搜索引擎百度已死》的文章被刷屏了,笔者本身就非常不喜欢百度,趁放寒假没有事情干,琢磨这在自己的网站上写一个插件。当访问者从百度进入网站,则在页面顶部显示一段用户自定义的阻止标语。顺便搞清楚Wordpress插件的原理。

上图就是NoBaidu的一个截图,本文按照我的编写顺序,分为——

  • 如何编写一个Wordpress插件
  • 如何将编好的插件托管到云端

另外,插件的源代码在我的github中开源,读者可以对照理解。

如何编写一个wordpress插件

编写Wordpress插件的教程网上很多,我就不重新造轮子了,我自己是在 如何开发一个WordPress如何开发一个WordPress插件 这篇文章中学到的,其核心要义就是钩子函数——

在Wordpress渲染一个页面的时候,在很多关键位置设置钩子(比如页面渲染开头,渲染器启动后等位置),而插件可以自定义的在任何钩子上挂载自己的函数,这样当页面渲染到了指定位置时,插件可以做一些自己的事情。

比如下面代码就可以在robots.txt渲染时,运行 $this->robots_txt_edit 指定的插件处理模块。

而以NoBaidu插件为例,我们需要支持如下逻辑——

  • 当普通用户访问时,检查用户的请求头的REFERER域是不是baidu域名下的URL
  • 当用户来自百度时,注册一个钩子函数,在页面渲染开始时,加入一个固定在页面顶部的,包含了自定义文字的HTML元素。
  • 当请求robots.txt时,返回禁止Baidu爬虫的robots文字

除此之外,还需要一些额外的代码,来保证我们的插件可以正常使用

  • 在用户是管理员的时候,生成插件设置页面,在其中可以设置显示的自定义抵制文字,用户选择是否组织百度爬虫,以及抵制文字是显示在顶端还是页面中部等等…
  • 编写插件安装、卸载时的逻辑,保证安装时初始化配置信息,卸载时删除配置信息,做到无残留

上述的逻辑,都集中到了下面的代码中,该代码使用一个类包裹了所有的函数(其作用类似于C++中的Namespace,用于最小化命名冲突)

看懂了上面的代码,是不是觉得Wordpress编写插件非常简单? 上述代码中引用的其他php代码,大家可以在我的github中自行查阅。

将自己写好的插件托管到wordpress云端

WordPress官网的Plugin目录最低端,有一个Add Your Plugin分区,这个分区详细介绍了该如何将你的Wordpress插件托管至wordpress.org,一般来说,读完上述的文章,你就已经能够自主把自己的插件丢到云端了。不过我准备从自己的经历出发,来大致讲一讲流程。

WordPress.org自己维护了一个svn仓库,最终会获得自己插件的专属仓库,你可以使用svn提供的版本控制,自行升级、修订等等。而想要获得自己插件的svn仓库,你就需要把自己的插件的最初版提交给官方审核。提交入口在这里。这里写几个提交的注意事项吧——

  • 最重要的是要知道php没有命名空间之说,所以一切全局变量都应该防止冲突。一个比较取巧的做法就是像上文一样,将你自己的程序包裹在一个class里面,而class以自己的插件名称命名。(我就因为配置的class命名为MySettingClass被官方打回来了)
  • 不要有额外的文件,之前写插件的时候,把网上别人插件的代码拷贝到了自己的目录,本来只是想照着抄一抄,结果忘删了,这个也被审核人发现了。
  • README.txt按照官方的标准格式写。

网站上面说的是7个工作日,结果我第一天晚上提交了初版的zip包,第二天早上就收到了审核者的邮件。按照要求修改了一些类名,并删除了无关文件后,我直接把zip包附在了回复中,第三天早上就拿到了通过的消息,并且获得了svn仓库的访问权限。开源社区真的非常强大👍

第一次要求修改的邮件
成功通过审核

接下来就是上传仓库了,如果读者会使用git的话,就别自己去学svn了,照着官方指南走一遍,就完全学会了。和git的pull, add, commit, push, tag如出一辙。

当你的first commit上传到远程仓库,并且readme.txt没有什么问题的话,你应该是可以立即在插件商店中搜索到自己写的插件的。

到这里我们在wordpress上面第一个插件就做好啦!

如果你觉得本文有用的话,欢迎到我的github仓库Star或Contribute~

让我们一起抵制Baidu吧~

参考文章

  • 【一个前人的代码】:http://ju.outofmemory.cn/entry/222836
  • 【如何开发Wordpress插件】:https://www.wpdaxue.com/writing_a_plugin.html
  • 【官方文档】:https://wordpress.org/plugins/developers/

Identity-Aware Convolutional Neural Network for Facial Expression Recognition 阅读笔记

这篇论文核心目标是提高人脸表情识别的精度。经典的表情识别算法,会将表情图片映射到特征空间(feature space)中的特征向量,而两个特征向量的欧几里得距离来衡量两个表情的相似度。

作者发现常规模型容易将“同一个人(indentity)”的特征向量放在一起而非“同一个表情(expression)”。这个问题表现在模型训练的时候,模型容易“偷偷”记录下训练数据中人物的面貌,并基于此进行判断。倘若随便换一个测试集,模型的准确度就有非常大的下降。(比如模型会记录下某黑人胖子A的吃惊的表情特征,但是之后测试集中,倘若是一个黑人瘦子B吃惊的表情,那么模型就不怎么容易判别出来了)

如下图,现有的模型的特征向量如图(a)所示依赖于人物,而作者的模型能挖掘和人物无关的表情特征,反映到特征空间中如图(b)所示。

文章提出的模型称为identity-aware convolutional neural network (IACNN) ,其核心思想是学习与表情有关(expression-related)的特征,并在同时,故意学习一个和人物有关(identity-related)的特征,后者虽然不利于网络的实际识别,但是却有利于让前者变得与身份无关(identity-invariant).

同时,该文章提出了一种距离的计算法则,能够让相似的表情的特征向量相距较近,而不相似的表情的特征向量相距较远。(不过这个计算法则感觉并不算亮点或是创新)

首先定义欧几里得距离作为接下来的距离公式——

D(f^E(I_1), f^E(I_2)) = |f^E(I_1), f^E(I_2)|^2

下面是一个自定义的损失函数公式——

L^{exp}_{Contrastive} = \frac{z_i^E}{2} * D(f^E(I_{i, 1}), f^E(I_{i,2})) + \frac{1-z_i^E}{2} * max(0, \alpha^E - D(f^E(I_{i, 1}), f^E(I_{i,2})))

这个公式看着很长,实际上非常简单,当两个图片属于同一个表情(exp),那么z_i=1 ,优化损失函数倾向于让他们的特征向量靠近;而倘若不属于同一个表情,z_i=0,优化损失函数倾向于让他们至少有\alpha^E的距离。这个损失函数可以用于训练提取表情特征的网络f^E(x)。顺便一提f^E是一个基于CNN的网络。

与上面相似,我们也可以创造一个L^{ID}_{Contrastive}专门用来训练提取身份特征的网络F^{ID}

L^{ID}_{Contrastive} = \frac{z_i^E}{2} * D(f^{ID}(I_{i, 1}), f^{ID}(I_{i,2})) + \frac{1-z_i^{ID}}{2} * max(0, \alpha^{ID} - D(f^{ID}(I_{i, 1}), f^{ID}(I_{i,2})))

通过上述的网络f^{ID}f^E,我们对于一个图片能够生成表情相关的特征FC_{exp}和身份相关特征FC_{ID},令他们的组合特征为FC_{feat}.

使用FC_{feat}进行预测,得到损失函数L^1_{softmax}L^2_{softmax}(1和2分别是两张图片),使用FC_{exp}进行预测,得到L^{exp1}_{softmax}L^{exp2}_{softmax}

最终损失函数如下

L = \lambda_1 L^{ID}_{Contrastive} + \lambda_2 L^{Exp}_{Contrastive} + \lambda_3 L^1_{softmax}+ \lambda_4 L^2_{softmax}+ \lambda_5 L^{exp1}_{softmax}+ \lambda_6 L^{exp2}_{softmax}

可以发现,优化损失函数L,身份特征相关的信息倾向于聚集在FC_{ID}中,从而使FC_{Exp}的信息是纯粹的面部无关信息。那么最后测试集上,单纯使用FC_{Exp}进行预测,就不会存在模型对于训练集人物过于依赖的问题了。

Visdom教程(3):绘制折线图

这是Visdom教程的第三篇文章,主要讲了绘制折线图的方法。


Visdom中,使用函数vis.line绘制折线图,该函数参数表如下——

折线的Y坐标是必要参数。它可以是一个一维数组表示一条直线,也可以是二维数组表示多条折线。该函数的调用可以看示例代码的demo1函数。

line函数还可以设置update参数为’append’。在这种情况下,可以每次只传入新增的数据。示例代码demo2即使一个使用append优化了的绘制深度学习loss函数的例子。

Visdom教程(2):实时展示图片

这是Visdom教程的第二篇文章。主要讲了如何使用Visdom在本地查看服务器上的一个图片。


我们常常使用Visdom实时展示模型训练时刻的图像输出。Visdom支持展示numpy数组形式的若干张图片。展示图片用到了如下两个函数

  • visdom.image(img, win)
  • visdom.images(imgs, win)

第一个函数在win指定的窗口,展示单张图片,而第二个函数则用来展示一系列图片。

  • img是一个表示图片的numpy数组,形状(3,width,height)或者(width,height)形状
  • imgs是表示一系列图片的numpy数组,形状为(batch, 3, width, height)。
  • win表示窗口id,应该可以为任意类型。如果不指定win,服务端将自动新建一个窗口放图片,然后你的浏览器中将堆满密密麻麻的图片窗口。

下面是一个例子——

Visdom教程(1):开启Visdom服务

这是Visdom教程系列文章的第一篇,主要介绍了Visdom的安装。


Visdom是一个Python的远程可视化工具,经常用于配合深度学习训练进行可视化。

Visdom的工作原理是先在远端服务器上面运行一个Server,该服务器将绑定localhost:8097,同时,任意远端运行的Python程序可以通过引入visdom包,实现与该Server的交互。

Visdom服务端假设在服务器上的8097端口,我们只需要执行

安装visdom,并且执行

打开服务端,此时服务端将自动监听8097端口。

下面是一个最简单的visdom程序,该程序在example环境中,新建了一个text_window号窗口,里面写上了Here is the text这句话。

值得注意的是,Visdom中的环境可以理解为工作区,比如一个训练神经网络的程序,训练loss折线图可以输出到train环境中,而模型的内部可视化信息则输出到model环境中。这样可以使我们的工程井井有条。

我们既可以直接通过 server_of_hostname:8097 访问可视化界面,也可以通过ssh命令,将服务器上面的端口映射到本地,并进一步通过 localhost:8097 访问。

ssh name@host.com -p 3522 -L 8097:*:8097 .

 

An Introduction To Compressive Sampling 阅读笔记

通常我们讲的采样都要求采样率至少两倍于带宽,这是香农在1948年就提出了的。而本文讲的则恰恰相反,本文试图介绍如何使用压缩感知(Compressive Sampling/Sencing)的方式,使用尽量少的样本,刻画出信号本身。

从实用性角度来看,尽管本文的所有算法都可以拓展至连续信号,但是本文将单纯讲述离散信号在欠采样时的CS,毕竟这才是对工业界最有意义的。因为在离散前采样的信号中,有一个很重要的问题——

压缩感知的原理

假设有一个离散信号f \in R^n,是否有可能构造出m \ll n个波形,能够“几乎”完整刻画原来f所携带的信息?

首先向讲一讲关于稀疏性(Sparsity)的问题,令f =  \Psi x,其中\Psi是一个n\times n的矩阵。我们可以理解为将f使用特征x进行表征,而倘若我们提取x中前S大的值,而将其他位置强行置0(这样便于压缩,且不会显著对于结果产生影响),则称呼得到的向量x_S为S稀疏向量(S-sparse). 此时,我们可以通过x_S计算出f_S  = \Psi x_S

那么,接下来,m的取指大概为多少可以保证信号能够被“几乎”完整恢复呢?论文通过一系列推导,给出了一个式子

m \geq C \cdot \mu^2(\Phi, \Psi) \cdot S \cdot log n

其中\mu函数表征了感知基\Phi和表征基\Psi的相关程度,S是指x是S-sparse向量,C是某个常数。

而上述公式带入实际的图像压缩中,每一个缺失项倘若对应至少四个不连续的采样点,就能准确恢复原来的图像。

若干难题

文中提出并解决了基于上述算法的两个问题——

  1. 对于极其欠采样的样本,是否可以恢复如此高的分辨率?
  2. 实际的检测设备都存在噪音,噪音将如何影响该算法?

为了解决上述两个问题,作者提出了一个叫做restricted isometry property(RIP)的限制条件,倘若条件满足,不仅欠采样样本可以被完整恢复,而且还可以将误差限制在一个较小的区间中。而RIP的细节这里不叙述。

感知基的选择

感知基选择非常随便,只要满足RIP准则就可以。而文中提到了在单位球体上面随机取点,或者使用标准正交基与一个随机矩阵的乘积生成。

应用

  1. 数据压缩
  2. 信道编码
  3. 数据获取

等等

参考文献

  • E. J. Candès and M. B. Wakin, An introduction to compressive sampling, IEEE Signal Processing Magazine, 21-30, March 2008.

ICMP攻击及防范

ICMP协议概述

IP协议本身无法处理诸如“得到一个包发送失败的事实”,“对网络进行诊断”这些操作。ICMP协议就是为了上述操作而包含在IP协议中的一个子协议。

ICMP协议(Internet Control Message Protocol)全称Internet控制报文协议,是TCP/IP协议族的一个子协议,用于在主机,路由器之间传递控制信息。最常见的是我们使用的ping命令就是通过发送ICMP包实现的。

ICMP包位于IP包内部,ICMP的包头一般来说紧跟着IP包头(通常IP包头为20字节,则ICMP包头从第21字节开始)。

ICMP包包含Type, Code, Checksum和Data域,其中Type指定了ICMP的类别,可大致分为两类——一种是请求及其回应,另一种是错误信息反馈。

以Echo请求为例,一个Type=8的包发向目标IP,如果目标服务器成功收到,回复一个Type=0请求。倘若中间出现路由错误,则中间路由器返回一个Type=3的包。

ICMP攻击及其防范

ICMP攻击有非常多不同的种类,接下来将列举一些常见的攻击。

ICMP隧道

首先讲一讲ping指令的原理,ping指令首先发送一个ICMP的ECHO REQUEST包给目标IP,并在data域包含一个唯一的16位标识符(这个标识符在Linux中是顺序生成的,而在Windows中是随机生成的),通常操作系统还会在data域后面添加一些无用的信息使得ICMP包长到达一个指定的长度。目标主机收到了ping指令后,将data域原封不动包裹在一个ECHO REPLY包中发回来。

对于一个ICMP ECHO REQUEST/REPLY包,由于其做的是类似于网络控制方面的事情,故通常不会被防火墙在意(除非防火墙一一分析每个包的内在data域有没有异常),而其data域可以携带任意的数据,因此可以借助ICMP包实现一个从服务器发送消息的程序。我在github上面也找到了一个实现ICMP隧道的程序,大家可以参考一下。

对于ICMP tunneling的攻击,有一些非常原始的防范方式,包括限制包的大小以及直接丢弃所有的ICMP包。但是在实际上这些方式都不可行。也有一些比较复杂的防范方式,包括在本机监听包,并在发现异常是,通过网络中的控制节点拦截攻击的流量。

DDOS攻击

PING FLOOD攻击

两个对称的主机,攻击者在一个主机上使用多线程发送大量包向目标主机。只要攻击者的主机的带宽大于被攻击者,被攻击者的带宽就会被完全占满,而真正的包无法到达。

对于这类攻击,可以在子网入口的路由器上设置过滤表,拦截攻击者IP。

Smurf攻击

Smurf-attack是一种原始的DOS攻击,攻击者伪造目标主机的IP为source IP,并发送一个广播的ICMP ECHO REQUEST指令。当指令被网络的其他主机回应后,回应的ECHO REPLY将涌入目标主机,导致目标主机瘫痪。

一般Smurf-attack攻击的解决方式有——对于主机,设置相应的网段,不在该网段的ICMP ECHO REQUEST都不会被相应。或者是对于一个子网路由器,丢弃掉发入子网的广播ECHO REQUEST。由于现在新的路由器都默认disable广播流量,因此smurf攻击基本已经成为历史了。

伪造IP的方式不一定在攻击者主机上面实现,比如在 DNS Amplification Attack 攻击中,攻击者可以通过UDP包,将目标主机的真实地址关联一个无关的IP,注入DNS服务器,将大量无关请求全部导向目标主机。

网络信息收集

通过ICMP攻击可以用来收集网络拓扑结构,首先,通过ECHO REQUEST可以探知一个主机的存在性,而通过一个TTL依次递减的一系列ECHO REQUEST包,观察其ICMP Time Exceeded 包的发送源即可获得一条网络的边。

甚至通过ECHO REPLY包,我们还可以探知目标机器的OS类型。返回TTL=128的机器是Windows系的,而TTL=64的机器是Linux系的。同时,对于一个任意Code域非空的ICMP包,Windows系主机将返回一个Code域为0的包,而Linux则不会。进一步,不同的系统版本对于TIMESTAMP REQ包的回应也不尽相同,我们可以借此更进一步确认。

这类行为因为不会造成严重的后果,故一般没有进行防范。

Ping of Death攻击

通过发送若干段IP超大包,并让目标主机接受后,拼接出一个大小超过65536的包,导致栈溢出。这种攻击仅发生在早起服务器上,新的服务器都通过修改其收包算法的边界条件判定解决了这个攻击。

总结

对于ICMP攻击的防范一般都需要用户服务器精细配置防火墙,或是在边界路由器中设置过滤规则。普通的丢弃ICMP包也是可行的,但是会使得很多网络诊断工具不可用。在参考了有限资料后,我认为ICMPv4和ICMPv6在上述攻击的防御上几乎没有区别,ICMPv6协议本身没有增加额外的防范机制的支持。

参考文献

ICMPv4 and ICMPv6: https://notes.shichao.io/tcpv1/ch8/

ICMP attacks:  https://resources.infosecinstitute.com/icmp-attacks/#gref

ICMP tunnels: https://www.notsosecure.com/icmp-tunnels-a-case-study/

ICMP floods: https://www.cloudflare.com/learning/ddos/ping-icmp-flood-ddos-attack/

Express的Session不同步问题

近期使用express做项目,发现遇到了一个神奇的bug,从现象上来看是用户的session并不同步。具体来说有如下现象——

  • 页面A在session中加入了某个字段。并输出session,发现字段已经成功加入。
  • 页面A通过setTimeout设置5秒后再次输出session,发现字段仍然存在。
  • 页面B在页面A加载后的5秒内访问同用户的session,发现字段不见了。

经过研究,解决方式为在session创建时,指明resave选项为false——

resave字段为false的时候,倘若一个请求未修改session本身,则session不会重新写回内存。通常倘若两个同时发生的请求,只有一个修改了session。当resave为true时,未修改session的请求的session写回操作很容易覆盖另一个请求对于session的修改。

Structural Deep Embedding for Hyper-Networks 阅读笔记

所谓的Network Embedding就是指,对于一个网络(或者简单地说,图),给每个节点一个特征向量X_i,而该特征向量能够表征网络中节点的结构信息。这篇文章是Network-Embedding中的一篇较为重要的文章。


Abstract

网络嵌入(Network Embedding)近年来吸引了很多人的注意,传统的方法多局限于由点与点二元组关系形成的图,但是,在实际生活中,图本身很难是标准的二元组关系,在有些情况下,一条关系(relationship)会涉及到三个甚至更多的主体,我们可以称之为超边(hyperedge)。当超边成为一个不可分割的独立整体,则传统算法会遇到不少困难。本文将介绍一种算法,用于对于超网络(hyper-network)的嵌入。并且证明,传统算法不可能成功Embedding存在不可分割超边的网络。

Introduction

对于hyper-network的embedding,一个非常简单的方式是将其转化为普通图,这里有两种常用做法,一种是直接将图中的超边转化为一个“团(clique)”,另一种是为超边本身分配一个虚拟节点,并向其包含的所有元素连边。他们分别被称作clique expansion和star expansion。在这些算法中,我们都假设了超边是可分的,即,一个超边包含的点集的子集仍然是有意义的,这在实际生活中是不存在的。上述的算法的诸多问题便是这篇文章需要解决的——

  • 节点的不可分解性:如电影的推荐中的<user, moive, tag>三元组的子集 <user,tag>是不合法的。
  • 结构保留特性:实现局部细节的embed是相对简单的,但是网络整体结构的embed却相对复杂。而我们还需要解决同时embed局部、全局的特征。

对于第一个问题,文章设计了不可分解的多元组相似性函数,这种函数本身的结构保证了超边的子集不会存在。我们从理论上证明该函数是非线性性的,如果使用带非线性层的神经网络实现,则可以实现强力的非线性性。

对于第二个问题,我们使用自编码器来保证相似的节点拥有相似的embedding。

Related Work

早期的图Embedding使用了线性代数的方式,使用图的主特征向量来作为图的编码。由于线性代数中的特征向量分解是一个相对昂贵的方式,因此实际上这种做法并不常用

近年来,一个叫做RamdomWalk的算法提出新的思路,通过刻画一系列随机游走的路径来Embed一个图。在此之后,很多学者都在其基础上进行了创新,诸如使用更好的刻画函数,或者引入深度学习来优化。

然而,上述所有的算法,假设了图是标准的由边的二元组构成的。在存在超边的途中,现有的算法不是无法保证超边的不可分解性,就是过于低效。

Notations and Definitions

术语

如右图所示,相比于图论中的“无向图”,我们的超图更类似于

一个“有向图”。也可类比于之前提到的<user,movie,tag>三元组,节点本身是有类别的。我们定义点集

V = \{V_t\}_{t=1}^{T}

表示所有节点可以按照类别分为T类。

超边边集则可以表示为

E=\{(v_1,v_2,\dots,v_{n_i})\} (n_i \geq 2)

最终第j类的第i个节点的Embedding可以计算为

X_i^j

对于N个节点,我们可以定义相似性函数

\mathcal{S}(X_1,X_2,\dots,X_n)

相似性

节点的一阶相似性(First-order Proximity)刻画了N个节点的直接相似性,其定义非常简单,只要这N个节点组成一个超边,则这N个节点的一阶相似性是1。当然,这里就隐含定义了这N个节点的任意子集都不存在一阶相似性。

显然,单纯的一阶相似性定义是不完善的,这时,我们可以进一步定义二阶相似性。

二阶相似性定义在任意两个节点上,当两个节点x_ix_j共有邻居时,他们应该是二阶相似的。这也应该反映到节点的Embedding上面。举一个例子,在上面那张图上,A_1的邻居应该是\{(L_2,U_1),(L_1,U_2)\},而由于A_1A_2拥有共同的邻居(L_1,U_2)则他们应该是二阶相似的。

Deep Hyper-Network Embedding

模型设计

我们计算的网络Embedding需要保证一阶相似性,即对于模型生成的Embedding值X_i,倘若一个点集组成了超边,则他们的X_i的一阶相似性函数应该尽可能大,反之尽可能小——

特征1:我们令X_i为模型生成的Embedding值,则

  • (v_1, v_2, \dots , v_n) \in E, \mathcal(S)(X_1, X_2, \dots, X_n)应该尽可能大(或者说至少得大于某个比较大的阈值)
  • (v_1, v_2, \dots , v_n) \notin E, \mathcal(S)(X_1, X_2, \dots, X_n)应该尽可能小(或者说至少得小于某个比较小的阈值)

有了上述定义,文章进一步通过列举反例,证明了倘若\mathcal(S)仅仅是依赖于X_1,X_2,\dots, X_n的一个线性函数\mathcal(S) = \sum_i W_i X_i,是无法正确刻画特征1的。当然,了解过深度学习的读者应该就能发现,这恰好是深度学习中的非线性层引入的动机。

后续讨论我们默认N=3,更大的N对应算法很容易在N=3的情况修改得到。下图是网络的结构图——

在输入节点Embeding (X_i, X_j, X_k),我们可以通过非线性函数将他们映射到一个非线性空间L中。

L_{ijk} = \sigma(W_a^{(2)} X_i^a + W_b^{(2)} X_j^b + W_c^{(2)} X_k^c + b^{(2)})

而我们进一步将L映射到一个概率空间S

S_{ijk} = \mathcal{S} (X_i^a, X_j^b, X_k^c) =  \sigma(W^{(3)} * L_{ijk} + b^{(3)})

上面两层共同构成了我们的非线性相似性函数\mathcal{S},最终的目标函数定义如下

\mathcal{L}_1 = -(R_{ijk} \log S_{ijk} + (1-R_{ijk}) \log (1-S_{ijk}))

上式中的R_{ijk}被定义为“节点i,j,k是否组成一个超边”的0\1值函数。

接下来,我们考虑如何生成二阶相似性。之前我们介绍了模型中的第二层和第三层,而模型的第一层就是用来保证二阶相似性的。从图中,我们可以发现,模型使用了一个AutoEncoder,那么任何AutoEncoder都需要一个可供编码的东西,我们这里使用AutoEncoder编码什么呢?作者设计了一个编码矩阵A_i.

H表示在图G(V,E)的关联矩阵,h(v,e)=1当且仅当v \in e,而D表示图的度数矩阵。令A = H H^T -D。在离散数学中,可以证明,A矩阵的第i行A_i刻画了节点i的邻居的信息。而AutoEncoder就可以如下构造——

X_i = \sigma(W^{(1)} * A_i + b_i)

\hat{A}_i = \sigma(\hat{W}^{(1)} * X_i + b_i)

对于AutoEncoder的损失函数,我们有一个小优化,即只计算A_i不等于0的项。

\mathcal{L}_2 = \sum_t ||sign(A_i^t) \cdot (A_i^t-\hat{A}_i) ||_F^2

最终模型的损失为

\mathcal{L} = \mathcal{L}_1 + \alpha \mathcal{L}_2

模型训练

使用SGD算法训练。

Experiment

Conclusion