学习时间:2020/06/29 - 2020/07/05
0 主要学习内容
所有相关资料和我写的代码可以在此处下载:Download
- Coursera深度学习风格迁移编程作业
- Coursera深度学习人脸识别编程作业
1 Neural Style Transfer(神经网络风格迁移)
神经网络
风格迁移
就是一次(也可以的一个batch生成多张)利用一张内容图片(content image, notaion:C)和一张风格图片(style image, notation:S)去生成一张内容与C类似,风格与S类似的图片G。
1.1 风格迁移特点概述
根据我对神经网络风格迁移的理解以及编程实现中所遇到的一些问题,现总结神经网络风格迁移的特点如下:
- 风格迁移需要借助一个pre-trained的网络实现(GitHub代码中借助的是
VGG-19
) - 风格迁移训练时更新的权重是input层中初始时随机生成图片的像元(这里原来所学习的更新网络权重参数的方式有所不同,这也是为什么要用pre-trained网络的原因)
- 风格迁移计算内容损失(Content Cost)时,只考虑一个隐含层的输出。而计算风格损失(Style Cost)时则需要考虑多个,甚至是全部隐含层的输出。
1.2 风格迁移构建步骤
- 挑选Content Cost所考虑的一层隐含层;挑选Style Cost所考虑的几层隐含层以及其对应的权重 λ i lambda_i λi(tips:Content Cost考虑的隐含层一般选取中间的层,从而提取不浅也不太深的特征即可;Style Cost所考虑的几层权重应满足 ∑ i = 1 n λ = 1 sum_{i = 1}^{n} lambda = 1 ∑i=1nλ=1)
- 随机初始化一张图片,作为待更新图片。(tips:为了加快收敛速度,其实可以用Content Image + Random Noise的方式初始化)
- 计算Content Cost
- 计算Style Cost
- 计算总损失 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) + eta J_{style}(S, G) J(G)=αJcontent(C,G)+βJstyle(S,G)
- 根据总损失 J ( G ) J(G) J(G)更新G中的像素
1.3 风格迁移重点知识
1.3.1 Content Cost
从Visualizing and understanding convolutional networks这篇发表在ECCV2014的论文中可以了解到,卷积神经网络较浅层次学习的是颜色、边缘等低层特征,而较深层次学习的是纹理、网格特征甚至是一些完整的、具有辨别性的关键特征。
因此,为了能够度量两张图片的内容相似程度,我们就可以选择网络中间层的输出(目的是提取不是太浅,也不是太深的特征)
a
C
a_C
aC和
a
G
a_G
aG来计算两张图片的相似程度。计算公式如下:
J
c
o
n
t
e
n
t
(
C
,
G
)
=
1
4
×
n
H
×
n
W
×
n
C
∑
i
m
(
a
C
(
i
)
−
a
C
(
i
)
)
2
J_{content}(C,G) = frac{1}{4 imes n_H imes n_W imes n_C} sum_i^m(a_C^{(i)} - a_C^{(i)})^2
Jcontent(C,G)=4×nH×nW×nC1i∑m(aC(i)−aC(i))2
1.3.2 Style Cost
1.3.2.1 Gram Matrix
为了更好的理解Style Cost,我们需要先理解一下什么叫做Gram Matrix。
Gram Matrix被定义为n维欧式空间中任意k个向量
α
1
,
α
2
,
α
3
…
α
k
alpha_1,alpha_2, alpha_3 dots alpha_k
α1,α2,α3…αk的内积所组成的矩阵。公式如下:
△
(
α
1
,
α
2
,
…
α
k
)
=
[
(
α
1
,
α
1
)
(
α
1
,
α
2
)
⋯
(
α
1
,
α
k
)
(
α
2
,
α
1
)
(
α
2
,
α
2
)
⋯
(
α
2
,
α
k
)
⋮
⋮
⋱
⋮
(
α
k
,
α
1
)
(
α
k
,
α
2
)
⋯
(
α
k
,
α
k
)
]
riangle(alpha_1, alpha_2, dots alpha_k)= left[ egin{matrix} (alpha_1, alpha_1) & (alpha_1, alpha_2) & cdots & (alpha_1, alpha_k)\ (alpha_2, alpha_1) & (alpha_2, alpha_2) & cdots & (alpha_2, alpha_k)\ vdots & vdots & ddots & vdots\ (alpha_k, alpha_1) & (alpha_k, alpha_2) & cdots & (alpha_k, alpha_k) end{matrix}
ight]
△(α1,α2,…αk)=⎣⎢⎢⎢⎡(α1,α1)(α2,α1)⋮(αk,α1)(α1,α2)(α2,α2)⋮(αk,α2)⋯⋯⋱⋯(α1,αk)(α2,αk)⋮(αk,αk)⎦⎥⎥⎥⎤
其中
(
α
1
,
α
1
)
(alpha_1, alpha_1)
(α1,α1)表示两个向量的内积。
由于在向量2范数固定之后(可以想象一下全部都是单位向量),向量的内积大小其实是由两个向量夹角的cos值所决定的。即Gram Matrix实际上的每一个元素都反映着对应两个向量方向的相似程度。所以我们采用Gram Matrix去度量两张图片的风格相似程度。
1.3.2.2 Unrolling trick
有了上面Gram Matrix的知识,我们想要用它去代表一张图片的风格,这个时候该怎么办呢?
首先根据Gram Matrix的定义,我们需要准备k个向量去两两做内积吧。根据这一点,同时考虑到图片本身就是由一个个通道stack起来的,所以图片风格其实是可以由各个通道之间的关联性度量的。因此,为了保持每个通道包含信息完整且用Gram Matrix度量的是每个通道之间的关联性,我们就需要把这个
n
H
×
n
W
×
n
C
n_H imes n_W imes n_C
nH×nW×nC的图片变成
n
C
n_C
nC个向量。即从上图的左边变到右边,以方便我们用矩阵的乘法构建Gram Matrix。
至此,如上图所示,我们只需要把
a
G
a_G
aG和
a
S
a_S
aS变换一下,就可以用矩阵乘法方便地计算出Gram Matrix。
编程tips:TensorFlow中完成上述操作可以查看一下tf.transpose和tf.reshape的官方API
1.3.2.3 Style Cost的计算
有了Gram Matrix实际上就很好计算Style Cost了,但这里要注意的是计算Style Cost需要考虑多层隐含层的输出。因此下面给出Style Cost计算的公式:
J
s
t
y
l
e
[
l
]
(
S
,
G
)
=
1
4
×
n
C
2
×
(
n
H
×
n
W
)
2
∑
i
=
1
n
C
∑
j
=
1
n
C
(
G
(
g
r
a
m
)
i
,
j
[
l
]
(
S
)
−
G
(
g
r
a
m
)
i
,
j
[
l
]
(
G
)
)
2
(1)
J_{style}^{[l]}(S,G) = frac{1}{4 imes {n_C}^2 imes (n_H imes n_W)^2} sum _{i=1}^{n_C}sum_{j=1}^{n_C}(G^{[l](S)}_{(gram)i,j} - G^{[l](G)}_{(gram)i,j})^2 ag{1}
Jstyle[l](S,G)=4×nC2×(nH×nW)21i=1∑nCj=1∑nC(G(gram)i,j[l](S)−G(gram)i,j[l](G))2(1)
J
s
t
y
l
e
(
S
,
G
)
=
∑
l
λ
[
l
]
J
s
t
y
l
e
[
l
]
(
S
,
G
)
(2)
J_{style}(S,G) = sum_{l} lambda^{[l]} J^{[l]}_{style}(S,G) ag{2}
Jstyle(S,G)=l∑λ[l]Jstyle[l](S,G)(2)
其中公式(1)计算的是两图片第l层输出结果的Style Cost,而公式(2)是总体Style Cost(即各个层的加权平均)
1.3.3 总体Cost计算
有了Content Cost和Style Cost,取两者的加权平均就很容易计算出总体的Cost。公式如下:
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) + eta J_{style}(S, G)
J(G)=αJcontent(C,G)+βJstyle(S,G)
1.3.4 迭代更新
风格迁移的迭代更新与之前所学习的神经网络不太一样的地方就是,风格迁移利用的是一个pre-trained的网络,在迭代更新的时候并不会更新网络的权重参数,而是会更新输入层的输入G。
1.4 风格迁移结果展示
Python代码有需要请自行在Download处下载。
Content image:
Style image:
Generated image:
2 Face Verification & Face Recognition
2.1 人脸验证和人脸识别的区别
我们拿一个公司打卡的场景来举例说明这两者的区别。人脸验证需要员工把脸对准摄像头并且刷自己的ID Card,让机器判断输入的脸与输入的ID Card是否对应,也就是在整个数据库中进行了一个一对一的验证;人脸识别就是直接让员工把脸对准摄像头,机器则会将输入的图片与存在数据库中的身份图片进行对比,从而找出与输入图片最相似的一个员工。当然,如果相似程度太低了则会判定这个人并不是该公司的员工。
2.2 Encoding
从上面的那个例子我们可以看出来,不论是人脸验证还是人脸识别都需要把输入的图片与数据库中的图片进行对比。所以,如何有效地进行对比就变成了我们实现该系统的第一步。首先,很容易想到的是用图片一个像素一个像素地进行对比,然后计算像素之间的差距作为相似度。但是,其实对于人脸验证和识别来说,人脸其实也只是占了整个图片的一部分,更多的可能是背景像素,我们完全没有必要一个像素一个像素地进行对比。所以,就诞生了给图片编码的方式。本次编程作业就参考了 FaceNet 中的编码方式,将图片编码为一个128维的向量。
tips:一句话总结编码就是用神经网络(可以是普通的卷积神经网络,本编程作业用的是一个Inception Network)把图片输入转化为一个128维的向量输出。
2.3 The Triplet Loss
notation:
Anchor Image
就是存入数据库,用于做识别对比的员工证件照,通常情况下只有一张
Positive Image
就是与Anchor Image是同一人的照片,训练时需要多张
Negative Image
就是与Anchor Image不是同一人的照片,训练时需要多张
我们希望我们的人脸验证和识别系统能够让Anchor Image 和 Positive Image更加接近的同时,也使得Anchor Image和Negative Image更加相离。所以为了达到这个目的,我们就引入了Triplet Loss来来作为网络需要优化的损失。
我们来看一下上面这张图片,最左边的就是Anchor Image,中间的是Positive Image,最右边的就是Negative Image。这三张图片就形成了一个三元组,我们在训练的时候就是以一个三元组作为sample进行训练。而三元组的损失度量函数如下:
J
=
∑
i
=
1
m
[
∣
∣
f
(
A
(
i
)
)
−
f
(
P
(
i
)
)
∣
∣
2
2
⏟
(1)
−
∣
∣
f
(
A
(
i
)
)
−
f
(
N
(
i
)
)
∣
∣
2
2
⏟
(2)
+
α
]
+
mathcal{J} = sum^{m}_{i=1} large[ small underbrace{mid mid f(A^{(i)}) - f(P^{(i)}) mid mid_2^2}_ ext{(1)} - underbrace{mid mid f(A^{(i)}) - f(N^{(i)}) mid mid_2^2}_ ext{(2)} + alpha large ] small_+
J=i=1∑m[(1)
∣∣f(A(i))−f(P(i))∣∣22−(2)
∣∣f(A(i))−f(N(i))∣∣22+α]+
其中, “
[
z
]
+
[z]_+
[z]+” 就是
m
a
x
(
z
,
0
)
max(z,0)
max(z,0);
α
alpha
α则是一个常数,目的是为了让
∣
∣
f
(
A
(
i
)
)
−
f
(
P
(
i
)
)
∣
∣
2
2
mid mid f(A^{(i)}) - f(P^{(i)}) mid mid_2^2
∣∣f(A(i))−f(P(i))∣∣22 比
∣
∣
f
(
A
(
i
)
)
−
f
(
N
(
i
)
)
∣
∣
2
2
mid mid f(A^{(i)}) - f(N^{(i)}) mid mid_2^2
∣∣f(A(i))−f(N(i))∣∣22 小得更多,从而避免了输出编码全部是0的退化解。
tips:个人感觉这种大间距划分决策边界的思想与SVM十分类似。
2.4 Coding Tips
对于人脸验证来说,只需要从根据ID Card中的信息在数据库中取出Anchor Image的编码再与输入图片的编码进行对比即可。而对于人脸识别来说,在未经优化地情况下是会把数据中所有Anchor Image编码与输入图片编码进行对比,再找出最相似的Anchor Image编码(本次编程作业就是这样),这也就导致了整个算法运行十分缓慢,无法大规模应用。因此,为了加速人脸识别的速度,我们需要在数据库中构建索引。这一部分和我以前使用的sklearn库中的K临近算法十分类似,所以可以通过构建KD树去加快搜索速度。