课程笔记地址:https://blog.csdn.net/column/details/26931.html
课程代码地址:https://github.com/duboya/DeepLearning.ai-pragramming-code/tree/master
欢迎大家fork及star!(-O-)
人脸验证(Verification):
人脸识别(Recognition):
人脸识别问题对于人脸验证问题来说,具有更高的难度。如对于一个验证系统来说,如果我们拥有99%的精确度,那么这个验证系统已经具有了很高的精度;但是假设在另外一个识别系统中,如果我们把这个验证系统应用在具有K个人的识别系统中,那么系统犯错误的机会就变成了K倍。所以如果我们想在识别系统中得到更高的精度,那么就需要得到一个具有更高精度的验证系统。
对于大多数的人脸识别系统都存在的一个问题就是one shot learning。
什么是one shot learning:
对于一个人脸识别系统,我们需要仅仅通过先前的一张人脸的图片或者说一个人脸的样例,就能够实现该人的识别,那么这样的问题就是one shot 问题。对于存在于数据库中的人脸图片,系统能够识别到对应的人;而不在数据库中的人脸图片,则系统给出无法通过识别的结果。
对于one shot learning 问题,因为只有单个样本,是不足以训练一个稳健的卷积神经网络来进行不同人的识别过程。而且,在有新的样本成员加入的时候,往往还需要对网络进行重新训练。所以我们不能以传统的方法来实现识别系统。
Similarity 函数:
为了能够让人脸识别系统实现一次学习,需要让神经网络学习 Similarity 函数:
对于人脸识别系统,通过将输入的人脸图片与数据库中所拥有的图片成对输入Similarity函数,两两对比,则可解决one shot problem。如果有新的人加入团队,则只需将其图片添加至数据库即可。
利用Siamese 网络来实现 Similarity 函数。
构建网络:
对于一个卷积神经网络结构,我们去掉最后的softmax层,将图片样本1输入网络,最后由网络输出一个N维的向量(图中实例以128表示),这N维向量则代表输入图片样本1的编码。将不同人的图片样本输入相同参数的网络结构,得到各自相应的图片编码。
Similarity 函数实现:
将Similarity 函数表示成两幅图片编码之差的范数:
d ( x 1 , x 2 ) = ∥ f ( x 1 ) − f ( x 2 ) ∥ 2 2 d(x1,x2) = {\parallel f(x1) - f(x2)\parallel}_2^2 d(x1,x2)=∥f(x1)−f(x2)∥22
那么也就是说:
我们的神经网络的参数定义了图片的编码;
学习网络的参数,使我们得到好的Similarity 函数:
如果x1,x2是同一个人的图片,那么得到的 ∥ f ( x 1 ) − f ( x 2 ) ∥ 2 2 {\parallel f(x1) - f(x2)\parallel}_2^2 ∥f(x1)−f(x2)∥22很小;
如果x1,x2不是同一个人的图片,那么得到的 ∥ f ( x 1 ) − f ( x 2 ) ∥ 2 2 {\parallel f(x1) - f(x2)\parallel}_2^2 ∥f(x1)−f(x2)∥22很大。
如何通过学习神经网络的参数,得到优质的人脸图片的编码?方法之一就是定义 Triplet 损失函数,并在其之上运用梯度下降。
学习目标:
为了使用Triplet 损失函数,我们需要比较成对的图像(三元组术语):
对于Anchor 和 Positive,我们希望二者编码的差异小一些;对于Anchor 和Negative,我们希望他们编码的差异大一些。所以我们的目标以编码差的范数来表示为:
d ( A , P ) = ∥ f ( A ) − f ( P ) ∥ 2 ≤ ∥ f ( A ) − f ( N ) ∥ 2 = d ( A , N ) d(A,P) = {\parallel f(A) - f(P)\parallel}^2 \le {\parallel f(A) - f(N)\parallel}^2 = d(A,N) d(A,P)=∥f(A)−f(P)∥2≤∥f(A)−f(N)∥2=d(A,N)
也就是:
∥ f ( A ) − f ( P ) ∥ 2 − ∥ f ( A ) − f ( N ) ∥ 2 ≤ 0 {\parallel f(A) - f(P)\parallel}^2 - {\parallel f(A) - f(N)\parallel}^2 \le 0 ∥f(A)−f(P)∥2−∥f(A)−f(N)∥2≤0
上面的公式存在一个问题就是,当 f ( A ) = f ( P ) = f ( N ) = 0 f(A )= f(P) = f(N) = 0 f(A)=f(P)=f(N)=0时,也就是神经网络学习到的函数总是输出0时,或者 f ( A ) = f ( P ) = f ( N ) = 0 f(A )= f(P) = f(N) = 0 f(A)=f(P)=f(N)=0时,也满足上面的公式,但却不是我们想要的目标结果。所以为了防止出现这种情况,我们对上式进行修改,使得两者差要小于一个较小的负数:
∥ f ( A ) − f ( P ) ∥ 2 − ∥ f ( A ) − f ( N ) ∥ 2 ≤ − α {\parallel f(A) - f(P)\parallel}^2 - {\parallel f(A) - f(N)\parallel}^2 \le -\alpha ∥f(A)−f(P)∥2−∥f(A)−f(N)∥2≤−α
一般将 − α -\alpha −α写成 + α +\alpha +α,称为“margin”。即:
∥ f ( A ) − f ( P ) ∥ 2 − ∥ f ( A ) − f ( N ) ∥ 2 + α ≤ 0 {\parallel f(A) - f(P)\parallel}^2 - {\parallel f(A) - f(N)\parallel}^2 + \alpha \le 0 ∥f(A)−f(P)∥2−∥f(A)−f(N)∥2+α≤0
不同 margin值的设置对模型学习具有不同的效果,margin 的作用就是拉大了 Anchor与Positive 图片对和 Anchor与Negative 图片对之间的差距。
Triplet 损失函数:
Triplet 损失函数的定义基于三张图片:Anchor、Positive、Negative。
L ( A , P , N ) = m a x ( ∥ f ( A ) − f ( P ) ∥ 2 − ∥ f ( A ) − f ( N ) ∥ 2 + α , 0 ) L(A,P,N) = max({\parallel f(A) - f(P)\parallel}^2 - {\parallel f(A) - f(N)\parallel}^2 + \alpha, 0) L(A,P,N)=max(∥f(A)−f(P)∥2−∥f(A)−f(N)∥2+α,0)
整个网络的代价函数:
J = ∑ i = 1 m L ( A ( i ) , P ( i ) , N ( i ) ) J = \sum_{i=1}^{m}L(A^{(i)},P^{(i)},N^{(i)}) J=i=1∑mL(A(i),P(i),N(i))
注:这里triplet损失函数:当前者<=0时,则L(A, P, N) = 0,即损失函数为0;当前者>0时,则L(A, P, N) = 前者,此时,利用梯度下降降低损失函数,正好达到训练参数的目的,最终训练好的参数只训练到d(A, N) - d(A, P) >= alpha,即满足margin要求。
假设我们有一个10000张片的训练集,里面是1000个不同的人的照片样本。我们需要做的就是从这10000张训练集中抽取图片生成(A,P,N)的三元组,来训练我们的学习算法,并在Triplet 损失函数上进行梯度下降。
注意:为了训练我们的网络,我们必须拥有Anchor和Positive对,所以这里我们必须有每个人的多张照片,而不能仅仅是一张照片,否则无法训练网络。
三元组(A,P,N)的选择:
在训练的过程中,如果我们随机地选择图片构成三元组(A,P,N),那么对于下面的条件是很容易满足的:
d ( A , P ) + α ≤ d ( A , N ) d(A,P) + \alpha \le d(A,N) d(A,P)+α≤d(A,N)
所以,为了更好地训练网络,我们需要选择那些训练有“难度”的三元组,也就是选择的三元组满足:
d ( A , P ) ≈ d ( A , N ) d(A,P) \approx d(A,N) d(A,P)≈d(A,N)
算法将会努力使得 d ( A , N ) d(A,N) d(A,N)变大,或者使得 d ( A , P ) d(A,P) d(A,P)变小,从而使两者之间至少有一个 α \alpha α的间隔;
增加学习算法的计算效率,避免那些太简单的三元组。
注:这里提到选择 d(A, P) 约等于 d(A, N),好处之一是提升学习算法的计算效率,原因正是在于当随机选择的时候d(A, N)普遍偏大,此时,很容易满足 d ( A , P ) + α < = d ( A , N ) d(A, P) + \alpha <= d(A,N) d(A,P)+α<=d(A,N),这时候网络并不会进行训练,只有当不满足此条件时候才会进行梯度下降进行参数训练,故而这里找不容易满足的放进去训练模型正好起到提升计算效率的功能。
最终通过训练,我们学习到的参数,会使得对于同一个人的图片,编码的距离很小;对不同人的图片,编码的距离就很大。
对于大型的人脸识别系统,常常具有上百万甚至上亿的训练数据集,我们并我容易得到。所以对于该领域,我们常常是下载别人在网上上传的预训练模型,而不是从头开始。
除了利用 Triplet 损失函数来学习人脸识别卷积网络参数的方法外,还有其他的方式。我们可以将人脸识别问题利用Siamese网络当成一个二分类问题,同样可以实现参数的学习。
Siamese 二分类改进:
对两张图片应用Siamese 网络,计算得到两张图片的N维编码,然后将两个编码输入到一个logistic regression 单元中,然后进行预测。如果是相同的人,那么输出是1;如果是不同的人,输出是0。那么这里我们就将人脸识别的问题,转化为一个二分类问题。
对于最后的sigmoid函数,我们可以进行如下计算:
y ^ = σ ( ∑ k = 1 N w i ∣ f ( x ( i ) ) k − f ( x ( j ) ) k ∣ + b ) \hat{y} = \sigma(\sum_{k=1}^{N}w_i \mid f(x^{(i)})_k - f(x^{(j)})_k \mid + b) y^=σ(k=1∑Nwi∣f(x(i))k−f(x(j))k∣+b)
其中, f ( x ( i ) ) f(x^{(i)}) f(x(i))代表图片 x ( i ) x^{(i)} x(i)的编码,下标k代表选择N维编码向量中的第k个元素。
我们以两个图片编码向量对应元素之间的差值作为特征输入到logistic regression 的单元中,增加参数 w i w_i wi 和 b,通过训练得到合适的参数权重和偏置,进而判断两张图片是否为同一个人。
同时输入逻辑回归单元的特征可以进行更改,如还可以是:
( f ( x ( i ) ) k − f ( x ( j ) ) k ) 2 f ( x ( i ) ) k + f ( x ( j ) ) k \frac{(f(x^{(i)})_k - f(x^{(j)})_k)^2}{f(x^{(i)})_k + f(x^{(j)})_k} f(x(i))k+f(x(j))k(f(x(i))k−f(x(j))k)2
上式也被称为 c h i chi chi方公式,有时也称为 c h i chi chi方相似度。
在实际的人脸验证系统中,我们可以对数据库的人脸图片进行预计算,存储卷积网络得到的编码。当有图片进行识别时,运用卷积网络计算新图片的编码,与预计算保存好的编码输入到逻辑回归单元中进行预测。这样可以提高我们系统预测的效率,节省计算时间。
注:该trick也同样可以用到上面triplet loss function中,即提前将图片进行预编码(走一遍Siamese网络,得到去掉softmax层的输出神经元的值),就避免了后续再对数据库image进行计算编码,提升了效率,如果不存储图片,同时还能节省空间(对于大型数据集)。
总结:
利用Siamese 网络,我们可以将人脸验证当作一个监督学习,创建成对的训练集和是否同一个人的输出标签。
我们利用不同的图片对,使用反向传播的算法对Siamese网络进行训练,进而得到人脸验证系统。
注:不管是利用Siamese网络进行编码+triplet损失函数,还是采用Siamese网络编码+logistic regression二分类,其最重要的是学到一个有效的Siamese网络,即对同一个人照片之间的编码近似(距离较近),不同人之间的编码差异较大(距离较远)。
如何可视化:
假设我们训练了一个卷积神经网络如下所示:
我们希望看到不同层的隐藏单元的计算结果。依次对各个层进行如下操作:
对于在第一层的隐藏单元中,其只能看到卷积网络的小部分内容,也就是最后我们找到的那些最大化激活第一层隐层单元的是一些小的图片块。我们可以理解为第一层的神经单元通常会寻找一些简单的特征,如边缘或者颜色阴影等。
各层网络可视化:
对于卷积网络的各层单元,随着网络深度的增加,隐藏层计算单元随着层数的增加,从简单的事物逐渐到更加复杂的事物。
代价函数:
为了实现神经风格迁移,我们需要为生成的图片定义一个代价函数。
对于神经风格迁移,我们的目标是由内容图片C和风格图片S,生成最终的风格迁移图片G:
所以为了实现神经风格迁移,我们需要定义关于G的代价函数J,以用来评判生成图片的好坏:
J ( G ) = α J c o n t e n t ( C , G ) + β J s t y l e ( S , G ) J(G) = \alpha J_{content}(C,G) + \beta J_{style}(S,G) J(G)=αJcontent(C,G)+βJstyle(S,G)
其中
J c o n t e n t ( C , G ) J_{content}(C,G) Jcontent(C,G)代表生成图片G的内容和内容图片C的内容的相似度;
J s t y l e ( S , G ) J_{style}(S,G) Jstyle(S,G)代表生成图片G的风格和风格图片S的风格的相似度;
α \alpha α, β \beta β两个超参数用来表示以上两者之间的权重。
执行过程:
随机初始化生成图片G,如大小为 100 × 100 × 3 100 \times 100 \times 3 100×100×3;
使用梯度下降算法最小化上面定义的代价函数 J(G), G : = G − ∂ ∂ G J ( G ) G := G - \frac{\partial}{\partial G}J(G) G:=G−∂G∂J(G);
对于上图的内容图片C和风格图片S,通过梯度下降算法一次次的训练,我们可以由初始的噪声图片得到最终的风格迁移图片G。
假设我们使用隐藏层l来计算内容代价。
使用一个预训练的卷积网络。(如,VGG或其他);
令 a [ l ] ( C ) a^{[l](C)} a[l](C)和 a [ l ] ( G ) a^{[l](G)} a[l](G)分别代表内容图片C和生成图片G的 l l l 层的激活值;
如果 a [ l ] ( C ) a^{[l](C)} a[l](C)和 a [ l ] ( G ) a^{[l](G)} a[l](G)相似,那么两张图片就有相似的内容;
注:如果选择的l 太小,那么代价函数就会使得我们的生成图片G在像素上非常接近内容图片;然而用很深的网络,那么生成图片G中就会产生与内容图片中所拥有的物体。所以对于 l l l 一般选在网络的中间层,既不深也不浅;
定义内容代价函数如下:
J c o n t e n t ( C , G ) = 1 2 ∥ a [ l ] ( C ) − a [ l ] ( G ) ∥ 2 J_{content}(C,G) = \frac{1}{2}{\parallel a^{[l](C)} - a^{[l](G)}\parallel}^2 Jcontent(C,G)=21∥a[l](C)−a[l](G)∥2
在对代价函数运行梯度下降算法时,会激励这里的内容代价函数,努力使得生成图片G隐含层 l l l 的激活值和内容图片C隐含层 l l l 的激活值相似。
“Style”的含义:
对于一个卷积网络中,我们选择网络的中间层 l l l, 定义“Style”表示 l l l 层的各个通道激活项之间的相关性。
注:上述定义是风格转义的本质,即保证生成图片各通道激活项之间的相关性与参考图片各通道激活函数项之间的相关性近似。
相关性大小的度量:
上面是我们选出的 l l l 层的激活项,对于不同的通道值,代表不同的神经元所学习到的特征,这里假如红色的通道可以找到图片中含有垂直纹理特征的区域,黄色通道可以找出橙色的区域。
而相关性大小的含义就是,如假设中,图片出现垂直纹理特征的区域显示橙色可能的大小。
我们将相关系数应用到风格图片S和生成图片G的对应通道上,就可以度量风格图片和生成图片的相似度。
Style 矩阵:
注: J s t y l e J_{style} Jstyle 前的 λ \lambda λ 指的是不同层的权重,是另外一个超参;
风格代价函数旨在增大生成函数与风格函数之间各自对应channel之间的相关性;
在我们上面学过的卷积中,多数是对图形应用2D的卷积运算。同时,我们所应用的卷积运算还可以推广到1D和3D的情况。
2D和1D卷积:
- 2D卷积中 14x14x3,其中3代表图像channel,这种是神经网络中最常见的情况,故而keras做卷积运算时候也是以2Dconvolution来命名。
- 1D卷积中14*1中的1也可以理解为channel,1D则对应一个channel,卷积核则必须对应具有相同的channel,一共有 n c n_c nc个卷积核,故而最终结果是 10 × n c 10 \times n_c 10×nc。
3D卷积:
注:3D data,如医疗CT扫描,一个图片对应输入的一个channel,不同切片对应不同channel,最后就是有图片对应第三维度有多少channel,故而输入是3D data,对应电影也是,每一帧图片是一个channel,对应多少帧图片就是多少channel。
注:参考补充自:
https://blog.csdn.net/koala_tree/article/details/78647528