http://www.ai-start.com/dl2017/html/lesson4-week4.html
特殊应用:人脸识别和神经风格转换(Special applications: Face recognition &Neural style transfer)
什么是人脸识别?(What is face recognition?)
One-Shot学习(One-shot learning)
人脸识别所面临的一个挑战就是你需要解决一次学习问题,这意味着在大多数人脸识别应用中,你需要通过单单一张图片或者单单一个人脸样例就能去识别这个人。而历史上,当深度学习只有一个训练样例时,它的表现并不好,让我们看一个直观的例子,并讨论如何去解决这个问题。
假设你的数据库里有4张你们公司的员工照片,实际上他们确实是我们deeplearning.ai的员工,分别是Kian,Danielle,Younes和Tian。现在假设有个人(编号1所示)来到办公室,并且她想通过带有人脸识别系统的栅门,现在系统需要做的就是,仅仅通过一张已有的Danielle照片,来识别前面这个人确实是她。相反,如果机器看到一个不在数据库里的人(编号2所示),机器应该能分辨出她不是数据库中四个人之一。
所以在一次学习问题中,只能通过一个样本进行学习,以能够认出同一个人。大多数人脸识别系统都需要解决这个问题,因为在你的数据库中每个雇员或者组员可能都只有一张照片。
有一种办法是,将人的照片放进卷积神经网络中,使用softmax单元来输出4种,或者说5种标签,分别对应这4个人,或者4个都不是,所以softmax里我们会有5种输出。但实际上这样效果并不好,因为如此小的训练集不足以去训练一个稳健的神经网络。
而且,假如有新人加入你的团队,你现在将会有5个组员需要识别,所以输出就变成了6种,这时你要重新训练你的神经网络吗?这听起来实在不像一个好办法。
所以要让人脸识别能够做到一次学习,为了能有更好的效果,你现在要做的应该是学习Similarity函数。详细地说,你想要神经网络学习这样一个用
d
d
d表示的函数,
d
(
i
m
g
1
,
i
m
g
2
)
=
d
e
g
r
e
e
o
f
d
i
f
f
e
r
e
n
c
e
b
e
t
w
e
e
n
i
m
a
g
e
s
d(img1,img2) = degree\ of\ difference\ between\ images
d(img1,img2)=degree of difference between images,它以两张图片作为输入,然后输出这两张图片的差异值。如果你放进同一个人的两张照片,你希望它能输出一个很小的值,如果放进两个长相差别很大的人的照片,它就输出一个很大的值。所以在识别过程中,如果这两张图片的差异值小于某个阈值
τ
\tau
τ,它是一个超参数,那么这时就能预测这两张图片是同一个人,如果差异值大于τ,就能预测这是不同的两个人,这就是解决人脸验证问题的一个可行办法。
要将它应用于识别任务,你要做的是拿这张新图片(编号6),然后用
d
d
d函数去比较这两张图片(编号1和编号6),这样可能会输出一个非常大的数字,在该例中,比如说这个数字是10。之后你再让它和数据库中第二张图(编号2)片比较,因为这两张照片是同一个人,所以我们希望会输出一个很小的数。然后你再用它与数据库中的其他图片(编号3、4)进行比较,通过这样的计算,最终你能够知道,这个人确实是Danielle。
对应的,如果某个人(编号7)不在你的数据库中,你通过函数 d d d将他们的照片两两进行比较,最后我们希望 d d d会对所有的比较都输出一个很大的值,这就证明这个人并不是数据库中4个人的其中一个。
要注意在这过程中你是如何解决一次学习问题的,只要你能学习这个函数 d d d,通过输入一对图片,它将会告诉你这两张图片是否是同一个人。如果之后有新人加入了你的团队(编号5),你只需将他的照片加入你的数据库,系统依然能照常工作。
Siamese 网络(Siamese network)
建立一个人脸识别系统的方法就是,如果你要比较两个图片的话,例如这里的第一张(编号1)和第二张图片(编号2),你要做的就是把第二张图片喂给有同样参数的同样的神经网络,然后得到一个不同的128维的向量(编号3),这个向量代表或者编码第二个图片,我要把第二张图片的编码叫做 f ( x ( 2 ) ) f(x^{(2)}) f(x(2))。这里我用 x ( 1 ) x^{(1)} x(1)和 x ( 2 ) x^{(2)} x(2)仅仅代表两个输入图片,他们没必要非是第一个和第二个训练样本,可以是任意两个图片。
最后如果你相信这些编码很好地代表了这两个图片,你要做的就是定义 d d d,将 x ( 1 ) x^{(1)} x(1)和 x ( 2 ) x^{(2)} x(2)的距离定义为这两幅图片的编码之差的范数, d ( x ( 1 ) , x ( 2 ) ) = ∣ ∣ f ( x ( 1 ) ) − f ( x ( 2 ) ) ∣ ∣ 2 2 d( x^{( 1)},x^{( 2)}) =|| f( x^{( 1)}) - f( x^{( 2)})||_{2}^{2} d(x(1),x(2))=∣∣f(x(1))−f(x(2))∣∣22。
对于两个不同的输入,运行相同的卷积神经网络,然后比较它们,这一般叫做Siamese网络架构。这里提到的很多观点,都来自于Yaniv Taigman,Ming Yang,Marc’ Aurelio Ranzato,Lior Wolf的这篇论文,他们开发的系统叫做DeepFace。
怎么训练这个Siamese神经网络呢?不要忘了这两个网络有相同的参数,所以你实际要做的就是训练一个网络,它计算得到的编码可以用于函数
d
d
d,它可以告诉你两张图片是否是同一个人。更准确地说,神经网络的参数定义了一个编码函数
f
(
x
(
i
)
)
f(x^{(i)})
f(x(i)),如果给定输入图像
x
(
i
)
x^{(i)}
x(i),这个网络会输出
x
(
i
)
x^{(i)}
x(i)的128维的编码。你要做的就是学习参数,使得如果两个图片
x
(
i
)
x^{( i)}
x(i)和
x
(
j
)
x^{( j)}
x(j)是同一个人,那么你得到的两个编码的距离就小。前面几个幻灯片我都用的是
x
(
1
)
x^{(1)}
x(1)和
x
(
2
)
x^{( 2)}
x(2),其实训练集里任意一对
x
(
i
)
x^{(i)}
x(i)和
x
(
j
)
x^{(j)}
x(j)都可以。相反,如果
x
(
i
)
x^{(i)}
x(i)和
x
(
j
)
x^{(j)}
x(j)是不同的人,那么你会想让它们之间的编码距离大一点。
如果你改变这个网络所有层的参数,你会得到不同的编码结果,你要做的就是用反向传播来改变这些所有的参数,以确保满足这些条件。
Triplet 损失(Triplet 损失)
人脸识别:
三元组损失函数的定义基于三张图片,假如三张图片
A
A
A、
P
P
P、
N
N
N,即Anchor样本、Positive样本和Negative样本,其中Positive图片和Anchor图片是同一个人,但是Negative图片和Anchor不是同一个人。
接下来我们定义损失函数,这个例子的损失函数,它的定义基于三元图片组,我先从前一张幻灯片复制过来一些式子,就是
∣
∣
f
(
A
)
−
f
(
P
)
∣
∣
2
−
∣
∣
f
(
A
)
−
f
(
N
)
∣
∣
2
+
a
≤
0
|| f( A) - f( P)||^{2} -||f( A) - f( N)||^{2} +a \leq0
∣∣f(A)−f(P)∣∣2−∣∣f(A)−f(N)∣∣2+a≤0。所以为了定义这个损失函数,我们取这个和0的最大值:
L
(
A
,
P
,
N
)
=
m
a
x
(
∣
∣
f
(
A
)
−
f
(
P
)
∣
∣
2
−
∣
∣
f
(
A
)
−
f
(
N
)
∣
∣
2
+
a
,
0
)
L( A,P,N) = max(|| f( A) - f( P)||^{2} -|| f( A) - f( N)||^{2} + a,0)
L(A,P,N)=max(∣∣f(A)−f(P)∣∣2−∣∣f(A)−f(N)∣∣2+a,0)
这个 m a x max max函数的作用就是,只要这个 ∣ ∣ f ( A ) − f ( P ) ∣ ∣ 2 − ∣ ∣ f ( A ) − f ( N ) ∣ ∣ 2 + a ≤ 0 || f( A) - f( P)||^{2} -|| f( A) - f( N)||^{2} + a\leq0 ∣∣f(A)−f(P)∣∣2−∣∣f(A)−f(N)∣∣2+a≤0,那么损失函数就是0。只要你能使画绿色下划线部分小于等于0,只要你能达到这个目标,那么这个例子的损失就是0。
另一方面如果这个 ∣ ∣ f ( A ) − f ( P ) ∣ ∣ 2 − ∣ ∣ f ( A ) − f ( N ) ∣ ∣ 2 + a ≤ 0 || f( A) - f( P)||^{2} -|| f( A) - f( N)||^{2} + a\leq0 ∣∣f(A)−f(P)∣∣2−∣∣f(A)−f(N)∣∣2+a≤0,然后你取它们的最大值,最终你会得到绿色下划线部分(即 ∣ ∣ f ( A ) − f ( P ) ∣ ∣ 2 − ∣ ∣ f ( A ) − f ( N ) ∣ ∣ 2 + a || f(A) - f( P)||^{2} -|| f( A) - f( N)||^{2} +a ∣∣f(A)−f(P)∣∣2−∣∣f(A)−f(N)∣∣2+a)是最大值,这样你会得到一个正的损失值。通过最小化这个损失函数达到的效果就是使这部分 ∣ ∣ f ( A ) − f ( P ) ∣ ∣ 2 − ∣ ∣ f ( A ) − f ( N ) ∣ ∣ 2 + a || f( A) - f( P)||^{2} -||f( A) - f( N)||^{2} +a ∣∣f(A)−f(P)∣∣2−∣∣f(A)−f(N)∣∣2+a成为0,或者小于等于0。只要这个损失函数小于等于0,网络不会关心它负值有多大。
人脸验证与二分类(Face verification and binary classification)
Triplet loss是一个学习人脸识别卷积网络参数的好方法,还有其他学习参数的方法,让我们看看如何将人脸识别当成一个二分类问题。
另一个训练神经网络的方法是选取一对神经网络,选取Siamese网络,使其同时计算这些嵌入,比如说128维的嵌入(编号1),或者更高维,然后将其输入到逻辑回归单元,然后进行预测,如果是相同的人,那么输出是1,若是不同的人,输出是0。这就把人脸识别问题转换为一个二分类问题,训练这种系统时可以替换Triplet loss的方法。
最后的逻辑回归单元是怎么处理的?输出
y
^
\hat y
y^会变成,比如说sigmoid函数应用到某些特征上,相比起直接放入这些编码(
f
(
x
(
i
)
)
,
f
(
x
(
j
)
)
f(x^{(i)}),f( x^{(j)})
f(x(i)),f(x(j))),你可以利用编码之间的不同。
y ^ = σ ( ∑ k = 1 128 w i ∣ f ( x ( i ) ) k − f ( x ( j ) ) k ∣ + b ) \hat y = \sigma(\sum_{k = 1}^{128}{w_{i}| f( x^{( i)}){k} - f( x^{( j)}){k}| + b}) y^=σ(∑k=1128wi∣f(x(i))k−f(x(j))k∣+b)
我解释一下,符号
f
(
x
(
i
)
)
k
f( x^{( i)}){k}
f(x(i))k代表图片
x
(
i
)
x^{(i)}
x(i)的编码,下标
k
k
k代表选择这个向量中的第
k
k
k个元素,
∣
f
(
x
(
i
)
)
k
−
f
(
x
(
j
)
)
k
∣
| f(x^{( i)}){k} - f( x^{( j)}){k}|
∣f(x(i))k−f(x(j))k∣对这两个编码取元素差的绝对值。你可能想,把这128个元素当作特征,然后把他们放入逻辑回归中,最后的逻辑回归可以增加参数
w
i
w{i}
wi和
b
b
b,就像普通的逻辑回归一样。你将在这128个单元上训练合适的权重,用来预测两张图片是否是一个人,这是一个很合理的方法来学习预测0或者1,即是否是同一个人。
还有其他不同的形式来计算绿色标记的这部分公式(
∣
f
(
x
(
i
)
)
k
−
f
(
x
(
j
)
)
k
∣
| 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
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,这个公式也被叫做
χ
2
\chi^{2}
χ2公式,是一个希腊字母
χ
\chi
χ,也被称为
χ
\chi
χ平方相似度。
• Yaniv Taigman, Ming Yang, Marc’Aurelio Ranzato, Lior Wolf (2014). DeepFace:Closing the gap to human-level performance in face verification
这些公式及其变形在这篇DeepFace论文中有讨论,我之前也引用过。
但是在这个学习公式中,输入是一对图片,这是你的训练输入
x
x
x(编号1、2),输出
y
y
y是0或者1,取决于你的输入是相似图片还是非相似图片。与之前类似,你正在训练一个Siamese网络,意味着上面这个神经网络拥有的参数和下面神经网络的相同(编号3和4所示的网络),两组参数是绑定的,这样的系统效果很好。
之前提到一个计算技巧可以帮你显著提高部署效果,如果这是一张新图片(编号1),当员工走进门时,希望门可以自动为他们打开,这个(编号2)是在数据库中的图片,不需要每次都计算这些特征(编号6),不需要每次都计算这个嵌入,你可以提前计算好,那么当一个新员工走近时,你可以使用上方的卷积网络来计算这些编码(编号5),然后使用它,和预先计算好的编码进行比较,然后输出预测值 y ^ \hat y y^。
因为不需要存储原始图像,如果你有一个很大的员工数据库,你不需要为每个员工每次都计算这些编码。这个预先计算的思想,可以节省大量的计算,这个预训练的工作可以用在Siamese网路结构中,将人脸识别当作一个二分类问题,也可以用在学习和使用Triplet loss函数上,我在之前的视频中描述过。
总结一下,把人脸验证当作一个监督学习,创建一个只有成对图片的训练集,不是三个一组,而是成对的图片,目标标签是1表示一对图片是一个人,目标标签是0表示图片中是不同的人。利用不同的成对图片,使用反向传播算法去训练神经网络,训练Siamese神经网络。
什么是神经风格迁移?(What is neural style transfer?)
最近,卷积神经网络最有趣的应用是神经风格迁移,在编程作业中,你将自己实现这部分并创造出你的艺术作品。
什么是神经风格迁移?让我们来看几个例子,比如这张照片,照片是在斯坦福大学拍摄的,离我的办公室不远,你想利用右边照片的风格来重新创造原本的照片,右边的是梵高的星空,神经风格迁移可以帮你生成下面这张照片。
CNN特征可视化(What are deep ConvNets learning?)
深度卷积网络到底在学什么?在这个视频中我将展示一些可视化的例子,可以帮助你理解卷积网络中深度较大的层真正在做什么,这样有助于理解如何实现神经风格迁移。
来看一个例子,假如你训练了一个卷积神经网络,是一个Alexnet,轻量级网络,你希望将看到不同层之间隐藏单元的计算结果。
你可以这样做,从第一层的隐藏单元开始,假设你遍历了训练集,然后找到那些使得单元激活最大化的一些图片,或者是图片块。换句话说,将你的训练集经过神经网络,然后弄明白哪一张图片最大限度地激活特定的单元。注意在第一层的隐藏单元,只能看到小部分卷积神经,如果要画出来哪些激活了激活单元,只有一小块图片块是有意义的,因为这就是特定单元所能看到的全部。你选择一个隐藏单元,发现有9个图片最大化了单元激活,你可能找到这样的9个图片块(编号1),似乎是图片浅层区域显示了隐藏单元所看到的,找到了像这样的边缘或者线(编号2),这就是那9个最大化地激活了隐藏单元激活项的图片块。
然后你可以选一个另一个第一层的隐藏单元,重复刚才的步骤,这是另一个隐藏单元,似乎第二个由这9个图片块(编号1)组成。看来这个隐藏单元在输入区域,寻找这样的线条(编号2),我们也称之为接受域。
对其他隐藏单元也进行处理,会发现其他隐藏单元趋向于激活类似于这样的图片。这个似乎对垂直明亮边缘左边有绿色的图片块(编号1)感兴趣,这一个隐藏单元倾向于橘色,这是一个有趣的图片块(编号2),红色和绿色混合成褐色或者棕橙色,但是神经元仍可以激活它。
以此类推,这是9个不同的代表性神经元,每一个不同的图片块都最大化地激活了。你可以这样理解,第一层的隐藏单元通常会找一些简单的特征,比如说边缘或者颜色阴影。
我在这个视频中使用的所有例子来自于Matthew Zener和Rob Fergus的这篇论文,题目是(Zeiler M D, Fergus R.Visualizing and Understanding Convolutional Networks[J]. 2013, 8689:818-833.)《可视化理解卷积神经网络》,我会使用一种更简单的方法来可视化神经网络隐藏单元的计算内容。如果你读过他们的论文,他们提出了一些更复杂的方式来可视化卷积神经网络的计算。
你已经在第一层的9个隐藏单元重复了这个过程好几遍,如果在深层的隐藏单元中进行这样的计算呢?卷积神经网络的深层部分学到了什么?在深层部分,一个隐藏单元会看到一张图片更大的部分,在极端的情况下,可以假设每一个像素都会影响到神经网络更深层的输出,靠后的隐藏单元可以看到更大的图片块,我还会画出和这页中的大小相同的图片块。
但如果我们重复这一过程,这(Layer 1所示图片)是之前第一层得到的,这个(Layer 2所示图片)是可视化的第2层中最大程度激活的9个隐藏单元。我想解释一下这个可视化,这是(编号2所示)使一个隐藏单元最大激活的9个图片块,每一个组合,这是另一组(编号2),使得一个隐藏单元被激活的9个图片块,这个可视化展示了第二层的9个隐藏单元,每一个又有9个图片块使得隐藏单元有较大的输出或是较大的激活。
在更深的层上,你可以重复这个过程。
在这页里很难看清楚,这些微小的浅层图片块,让我们放大一些,这是第一层,这是第一个被高度激活的单元,你能在输入图片的区域看到,大概是这个角度的边缘(编号1)放大第二层的可视化图像。
有意思了,第二层似乎检测到更复杂的形状和模式,比如说这个隐藏单元(编号1),它会找到有很多垂线的垂直图案,这个隐藏单元(编号2)似乎在左侧有圆形图案时会被高度激活,这个的特征(编号3)是很细的垂线,以此类推,第二层检测的特征变得更加复杂。
看看第三层我们将其放大,放得更大一点,看得更清楚一点,这些东西激活了第三层。再放大一点,这又很有趣了,这个隐藏单元(编号1)似乎对图像左下角的圆形很敏感,所以检测到很多车。这一个(编号2)似乎开始检测到人类,这个(编号3)似乎检测特定的图案,蜂窝形状或者方形,类似这样规律的图案。有些很难看出来,需要手动弄明白检测到什么,但是第三层明显,检测到更复杂的模式。
下一层呢?这是第四层,检测到的模式和特征更加复杂,这个(编号1)学习成了一个狗的检测器,但是这些狗看起来都很类似,我并不知道这些狗的种类,但是你知道这些都是狗,他们看起来也类似。第四层中的这个(编号2)隐藏单元它检测什么?水吗?这个(编号3)似乎检测到鸟的脚等等。
第五层检测到更加复杂的事物,注意到这(编号1)也有一个神经元,似乎是一个狗检测器,但是可以检测到的狗似乎更加多样性。这个(编号2)可以检测到键盘,或者是键盘质地的物体,可能是有很多点的物体。我认为这个神经元(编号3)可能检测到文本,但是很难确定,这个(编号4)检测到花。我们已经有了一些进展,从检测简单的事物,比如说,第一层的边缘,第二层的质地,到深层的复杂物体。
我希望这让你可以更直观地了解卷积神经网络的浅层和深层是如何计算的,接下来让我们使用这些知识开始构造神经风格迁移算法。
神经风格迁移系统代价函数(Cost function)
记住我们的问题,给你一个内容图像
C
C
C,给定一个风格图片
S
S
S,而你的目标是生成一个新图片
G
G
G。为了实现神经风格迁移,你要做的是定义一个关于
G
G
G的代价函数
J
J
J用来评判某个生成图像的好坏,我们将使用梯度下降法去最小化
J
(
G
)
J(G)
J(G),以便于生成这个图像。
怎么判断生成图像的好坏呢?我们把这个代价函数定义为两个部分。
J content ( C , G ) J_{\text{content}}(C,G) Jcontent(C,G)
第一部分被称作内容代价,这是一个关于内容图片和生成图片的函数,它是用来度量生成图片 G G G的内容与内容图片 C C C的内容有多相似。
J style ( S , G ) J_{\text{style}}(S,G) Jstyle(S,G)
然后我们会把结果加上一个风格代价函数,也就是关于 S S S和 G G G的函数,用来度量图片 G G G的风格和图片 S S S的风格的相似度。
J ( G ) = a J content ( C , G ) + β J style ( S , G ) J( G) = a J_{\text{content}}( C,G) + \beta J_{\text{style}}(S,G) J(G)=aJcontent(C,G)+βJstyle(S,G)
最后我们用两个超参数 a a a和 β \beta β来来确定内容代价和风格代价,两者之间的权重用两个超参数来确定。两个代价的权重似乎是多余的,我觉得一个超参数似乎就够了,但提出神经风格迁移的原始作者使用了两个不同的超参数,我准备保持一致。
关于神经风格迁移算法我将在接下来几段视频中展示的,是基于Leon Gatys, Alexandra Ecker和Matthias Bethge的这篇论文。这篇论文并不是很难读懂,如果你愿意,看完这些视频,我也非常推荐你去看看他们的论文。
Leon A. Gatys, Alexander S. Ecker, Matthias Bethge, (2015). A Neural Algorithm of Artistic Style (https://arxiv.org/abs/1508.06576)
算法的运行是这样的,对于代价函数
J
(
G
)
J(G)
J(G),为了生成一个新图像,你接下来要做的是随机初始化生成图像
G
G
G,它可能是100×100×3,可能是500×500×3,又或者是任何你想要的尺寸。
然后使用在之前的幻灯片上定义的代价函数
J
(
G
)
J(G)
J(G),你现在可以做的是使用梯度下降的方法将其最小化,更新
G
:
=
G
−
∂
∂
G
J
(
G
)
G:= G - \frac{\partial}{\partial G}J(G)
G:=G−∂G∂J(G)。在这个步骤中,你实际上更新的是图像
G
G
G的像素值,也就是100×100×3,比如RGB通道的图片。
这里有个例子,假设你从这张内容图片(编号1)和风格(编号2)图片开始,这是另一张公开的毕加索画作,当你随机初始化
G
G
G,你随机初始化的生成图像就是这张随机选取像素的白噪声图(编号3)。接下来运行梯度下降算法,最小化代价函数
J
(
G
)
J(G)
J(G),逐步处理像素,这样慢慢得到一个生成图片(编号4、5、6),越来越像用风格图片的风格画出来的内容图片。
内容代价函数(Content cost function)
风格迁移网络的代价函数有一个内容代价部分,还有一个风格代价部分。
J ( G ) = α J content ( C , G ) + β J style ( S , G ) J( G) = \alpha J_{\text{content}}( C,G) + \beta J_{\text{style}}(S,G) J(G)=αJcontent(C,G)+βJstyle(S,G)
我们先定义内容代价部分,不要忘了这就是我们整个风格迁移网络的代价函数,我们看看内容代价函数应该是什么。
假如说,你用隐含层
l
l
l来计算内容代价,如果
l
l
l是个很小的数,比如用隐含层1,这个代价函数就会使你的生成图片像素上非常接近你的内容图片。然而如果你用很深的层,那么那就会问,内容图片里是否有狗,然后它就会确保生成图片里有一个狗。所以在实际中,这个层
l
l
l在网络中既不会选的太浅也不会选的太深。因为你要自己做这周结束的编程练习,我会让你获得一些直觉,在编程练习中的具体例子里通常
l
l
l会选择在网络的中间层,既不太浅也不很深,然后用一个预训练的卷积模型,可以是VGG网络或者其他的网络也可以。
现在你需要衡量假如有一个内容图片和一个生成图片他们在内容上的相似度,我们令这个
a
[
l
]
[
C
]
a^{[l][C]}
a[l][C]和
a
[
l
]
[
G
]
a^{[l][G]}
a[l][G],代表这两个图片
C
C
C和
G
G
G的
l
l
l层的激活函数值。如果这两个激活值相似,那么就意味着两个图片的内容相似。
我们定义这个
J content ( C , G ) = 1 2 ∣ ∣ a [ l ] [ C ] − a [ l ] [ G ] ∣ ∣ 2 J_{\text{content}}( C,G) = \frac{1}{2}|| a^{[l][C]} - a^{[l][G]}||^{2} Jcontent(C,G)=21∣∣a[l][C]−a[l][G]∣∣2
为两个激活值不同或者相似的程度,我们取 l l l层的隐含单元的激活值,按元素相减,内容图片的激活值与生成图片相比较,然后取平方,也可以在前面加上归一化或者不加,比如 1 2 \frac{1}{2} 21或者其他的,都影响不大,因为这都可以由这个超参数 a a a来调整( J ( G ) = a J content ( C , G ) + β J style ( S , G ) J(G) =a J_{\text{content}}( C,G) + \beta J_{\text{style}}(S,G) J(G)=aJcontent(C,G)+βJstyle(S,G))。
要清楚我这里用的符号都是展成向量形式的,这个就变成了这一项( a [ l ] [ C ] a^{[l]\lbrack C\rbrack} a[l][C])减这一项( a [ l ] [ C ] a^{[l]\lbrack C\rbrack} a[l][C])的 L 2 L2 L2范数的平方,在把他们展成向量后。这就是两个激活值间的差值平方和,这就是两个图片之间 l l l层激活值差值的平方和。后面如果对 J ( G ) J(G) J(G)做梯度下降来找 G G G的值时,整个代价函数会激励这个算法来找到图像 G G G,使得隐含层的激活值和你内容图像的相似。
这就是如何定义风格迁移网络的内容代价函数,接下来让我们学习风格代价函数。
风格代价函数(Style cost function)
在上节视频中,我们学习了如何为神经风格迁移定义内容代价函数,这节课我们来了解风格代价函数。那么图片的风格到底是什么意思呢?
这么说吧,比如你有这样一张图片,你可能已经对这个计算很熟悉了,它能算出这里是否含有不同隐藏层。现在你选择了某一层
l
l
l(编号1),比如这一层去为图片的风格定义一个深度测量,现在我们要做的就是将图片的风格定义为
l
l
l层中各个通道之间激活项的相关系数。
我来详细解释一下,现在你将
l
l
l层的激活项取出,这是个$ n_{H} \times n_{W} \times n_{C}$的激活项,它是一个三维的数据块。现在问题来了,如何知道这些不同通道之间激活项的相关系数呢?
为了解释这些听起来很含糊不清的词语,现在注意这个激活块,我把它的不同通道渲染成不同的颜色。在这个例子中,假如我们有5个通道为了方便讲解,我将它们染成了五种颜色。一般情况下,我们在神经网络中会有许多通道,但这里只用5个通道,会更方便我们理解。
为了能捕捉图片的风格,你需要进行下面这些操作,首先,先看前两个通道,前两个通道(编号1、2)分别是图中的红色和黄色部分,那我们该如何计算这两个通道间激活项的相关系数呢?
举个例子,在视频的左下角在第一个通道中含有某个激活项,第二个通道也含有某个激活项,于是它们组成了一对数字(编号1所示)。然后我们再看看这个激活项块中其他位置的激活项,它们也分别组成了很多对数字(编号2,3所示),分别来自第一个通道,也就是红色通道和第二个通道,也就是黄色通道。现在我们得到了很多个数字对,当我们取得这两个
n
H
×
n
W
n_{H}\times n_{W}
nH×nW的通道中所有的数字对后,现在该如何计算它们的相关系数呢?它是如何决定图片风格的呢?
我们来看一个例子,这是之前视频中的一个可视化例子,它来自一篇论文,作者是Matthew Zeile和Rob Fergus 我之前有提到过。我们知道,这个红色的通道(编号1)对应的是这个神经元,它能找出图片中的特定位置是否含有这些垂直的纹理(编号3),而第二个通道也就是黄色的通道(编号2),对应这个神经元(编号4),它可以粗略地找出橙色的区域。什么时候两个通道拥有高度相关性呢?如果它们有高度相关性,那么这幅图片中出现垂直纹理的地方(编号2),那么这块地方(编号4)很大概率是橙色的。如果说它们是不相关的,又是什么意思呢?显然,这意味着图片中有垂直纹理的地方很大概率不是橙色的。而相关系数描述的就是当图片某处出现这种垂直纹理时,该处又同时是橙色的可能性。
相关系数这个概念为你提供了一种去测量这些不同的特征的方法,比如这些垂直纹理,这些橙色或是其他的特征去测量它们在图片中的各个位置同时出现或不同时出现的频率。
如果我们在通道之间使用相关系数来描述通道的风格,你能做的就是测量你的生成图像中第一个通道(编号1)是否与第二个通道(编号2)相关,通过测量,你能得知在生成的图像中垂直纹理和橙色同时出现或者不同时出现的频率,这样你将能够测量生成的图像的风格与输入的风格图像的相似程度。
现在我们来证实这种说法,对于这两个图像,也就是风格图像与生成图像,你需要计算一个风格矩阵,说得更具体一点就是用
l
l
l层来测量风格。
我们设
a
i
,
j
,
k
[
l
]
a_{i,\ j,\ k}^{[l]}
ai, j, k[l],设它为隐藏层l中
(
i
,
j
,
k
)
(i,j,k)
(i,j,k)位置的激活项,
i
i
i,
j
j
j,
k
k
k分别代表该位置的高度、宽度以及对应的通道数。现在你要做的就是去计算一个关于
l
l
l层和风格图像的矩阵,即
G
l
G^{l}
Gl(
l
l
l表示层数,
S
S
S表示风格图像),这(
G
l
G^{l}
Gl)是一个
n
c
×
n
c
n_{c} \times n_{c}
nc×nc的矩阵,同样地,我们也对生成的图像进行这个操作。
但是现在我们先来定义风格图像,设这个关于 l l l层和风格图像的, G G G是一个矩阵,这个矩阵的高度和宽度都是 l l l层的通道数。在这个矩阵中 k k k和 k ′ k' k′元素被用来描述 k k k通道和 k ′ k' k′通道之间的相关系数。具体地:
G k k ′ l = ∑ i = 1 n H [ l ] ∑ j = 1 n W [ l ] a i , j , k l a i , j , k ′ l G_{kk^{'}}^{l} = \sum_{i = 1}^{n_{H}^{[l]}}{\sum_{j = 1}^{n_{W}^{[l]}}{a_{i,\ j,\ k}^{l}a_{i,\ j,\ k^{'}}^{l}}} Gkk′l=∑i=1nH[l]∑j=1nW[l]ai, j, klai, j, k′l
用符号
i
i
i,
j
j
j表示下界,对
i
i
i,
j
j
j,
k
k
k位置的激活项
a
i
,
j
,
k
[
l
]
a_{i,\ j,\ k}^{[l]}
ai, j, k[l],乘以同样位置的激活项,也就是
i
i
i,$ j
,
,
,k’
位
置
的
激
活
项
,
即
位置的激活项,即
位置的激活项,即a_{i,j,k{’}}{[l]}
,
将
它
们
两
个
相
乘
。
然
后
,将它们两个相乘。然后
,将它们两个相乘。然后i
和
和
和j
分
别
加
到
l
层
的
高
度
和
宽
度
,
即
分别加到l层的高度和宽度,即
分别加到l层的高度和宽度,即n_{H}{[l]}$和$n_{W}{[l]}
,
将
这
些
不
同
位
置
的
激
活
项
都
加
起
来
。
,将这些不同位置的激活项都加起来。
,将这些不同位置的激活项都加起来。(i,j,k)
和
和
和(i,j,k’)
中
中
中x
坐
标
和
坐标和
坐标和y
坐
标
分
别
对
应
高
度
和
宽
度
,
将
坐标分别对应高度和宽度,将
坐标分别对应高度和宽度,将k
通
道
和
通道和
通道和k’$通道上这些位置的激活项都进行相乘。我一直以来用的这个公式,严格来说,它是一种非标准的互相关函数,因为我们没有减去平均数,而是将它们直接相乘。
这就是输入的风格图像所构成的风格矩阵,然后,我们再对生成图像做同样的操作。
G k k ′ l = ∑ i = 1 n H [ l ] ∑ j = 1 n W [ l ] a i , j , k l a i , j , k ′ l G_{kk^{'}}^{l} = \sum_{i = 1}^{n_{H}^{[l]}}{\sum_{j = 1}^{n_{W}^{[l]}}{a_{i,\ j,\ k}^{l}a_{i,\ j,\ k^{'}}^{l}}} Gkk′l=∑i=1nH[l]∑j=1nW[l]ai, j, klai, j, k′l
a i , j , k l a_{i,\ j,\ k}^{l} ai, j, kl和 a i , j , k l a_{i, j,k}^{l} ai,j,kl中的上标 ( S ) (S) (S)和 ( G ) (G) (G)分别表示在风格图像 S S S中的激活项和在生成图像 G G G的激活项。我们之所以用大写字母 G G G来代表这些风格矩阵,是因为在线性代数中这种矩阵有时也叫Gram矩阵,但在这里我只把它们叫做风格矩阵。
所以你要做的就是计算出这张图像的风格矩阵,以便能够测量出刚才所说的这些相关系数。更正规地来表示,我们用 a i , j , k [ l ] a_{i,j,k}^{[l]} ai,j,k[l]来记录相应位置的激活项,也就是 l l l层中的 i , j , k i,j,k i,j,k位置,所以 i i i代表高度, j j j代表宽度, k k k代表着 l l l中的不同通道。之前说过,我们有5个通道,所以 k k k就代表这五个不同的通道。
对于这个风格矩阵,你要做的就是计算这个矩阵也就是
G
[
l
]
G^{[l]}
G[l]矩阵,它是个
n
c
×
n
c
n_{c} \times n_{c}
nc×nc的矩阵,也就是一个方阵。记住,因为这里有
n
c
n_{c}
nc个通道,所以矩阵的大小是
n
c
×
n
c
n_{c}\times n_{c}
nc×nc。以便计算每一对激活项的相关系数,所以
G
kk
′
[
l
]
G_{\text{kk}^{'}}^{[l]}
Gkk′[l]可以用来测量
k
k
k通道与
k
′
k'
k′通道中的激活项之间的相关系数,
k
k
k和
k
′
k'
k′会在1到
n
c
n_{c}
nc之间取值,
n
c
n_{c}
nc就是
l
l
l层中通道的总数量。
当在计算
G
[
l
]
G^{[l]}
G[l]时,我写下的这个符号(下标
k
k
’
kk’
kk’)只代表一种元素,所以我要在右下角标明是
k
k
′
kk'
kk′元素,和之前一样
i
i
i,
j
j
j从一开始往上加,对应
(
i
,
j
,
k
)
(i,j,k)
(i,j,k)位置的激活项与对应
(
i
,
j
,
k
′
)
(i, j, k')
(i,j,k′)位置的激活项相乘。记住,这个
i
i
i和
j
j
j是激活块中对应位置的坐标,也就是该激活项所在的高和宽,所以
i
i
i会从1加到
n
H
[
l
]
n_{H}^{[l]}
nH[l],
j
j
j会从1加到
n
W
[
l
]
n_{W}^{[l]}
nW[l],
k
k
k和
k
′
k'
k′则表示对应的通道,所以
k
k
k和
k
′
k'
k′值的范围是从1开始到这个神经网络中该层的通道数量
n
C
[
l
]
n_{C}^{[l]}
nC[l]。这个式子就是把图中各个高度和宽度的激活项都遍历一遍,并将
k
k
k和
k
′
k'
k′通道中对应位置的激活项都进行相乘,这就是
G
k
k
′
[
l
]
G_{{kk}^{'}}^{[l]}
Gkk′[l]的定义。通过对
k
k
k和
k
′
k'
k′通道中所有的数值进行计算就得到了
G
G
G矩阵,也就是风格矩阵。
G k k ′ [ l ] = ∑ i = 1 n H [ l ] ∑ j = 1 n W [ l ] a i , j , k [ l ] a i , j , k ′ [ l ] G_{kk^{'}}^{[l]} = \sum_{i = 1}^{n_{H}^{[l]}}{\sum_{j = 1}^{n_{W}^{[l]}}{a_{i,\ j,\ k}^{[l]}a_{i,\ j,\ k^{'}}^{[l]}}} Gkk′[l]=∑i=1nH[l]∑j=1nW[l]ai, j, k[l]ai, j, k′[l]
要注意,如果两个通道中的激活项数值都很大,那么
G
k
k
′
[
l
]
G_{{kk}^{'}}^{[l]}
Gkk′[l]也会变得很大,对应地,如果他们不相关那么
G
k
k
′
[
l
]
G_{{kk}^{'}}^{[l]}
Gkk′[l]就会很小。严格来讲,我一直使用这个公式来表达直觉想法,但它其实是一种非标准的互协方差,因为我们并没有减去均值而只是把这些元素直接相乘,这就是计算图像风格的方法。
G
k
k
′
l
=
∑
i
=
1
n
H
[
l
]
∑
j
=
1
n
W
[
l
]
a
i
,
j
,
k
l
a
i
,
j
,
k
′
l
G_{kk^{'}}^{l} = \sum_{i = 1}^{n_{H}^{[l]}}{\sum_{j = 1}^{n_{W}^{[l]}}{a_{i,\ j,\ k}^{l}a_{i,\ j,\ k^{'}}^{l}}}
Gkk′l=∑i=1nH[l]∑j=1nW[l]ai, j, klai, j, k′l
你要同时对风格图像 S S S和生成图像 G G G都进行这个运算,为了区分它们,我们在它的右上角加一个 ( S ) (S) (S),表明它是风格图像 S S S,这些都是风格图像S中的激活项,之后你需要对生成图像也做相同的运算。
G k k ′ l = ∑ i = 1 n H [ l ] ∑ j = 1 n W [ l ] a i , j , k l a i , j , k ′ l G_{kk^{'}}^{l} = \sum_{i = 1}^{n_{H}^{[l]}}{\sum_{j = 1}^{n_{W}^{[l]}}{a_{i,\ j,\ k}^{l}a_{i,\ j,\ k^{'}}^{l}}} Gkk′l=∑i=1nH[l]∑j=1nW[l]ai, j, klai, j, k′l
和之前一样,再把公式都写一遍,把这些都加起来,为了区分它是生成图像,在这里放一个
(
G
)
(G)
(G)。
现在,我们有2个矩阵,分别从风格图像
S
S
S和生成图像
G
G
G。
再提醒一下,我们一直使用大写字母
G
G
G来表示矩阵,是因为在线性代数中,这种矩阵被称为Gram矩阵,但在本视频中我把它叫做风格矩阵,我们取了Gram矩阵的首字母
G
G
G来表示这些风格矩阵。
最后,如果我们将
S
S
S和
G
G
G代入到风格代价函数中去计算,这将得到这两个矩阵之间的误差,因为它们是矩阵,所以在这里加一个
F
F
F(Frobenius范数,编号1所示),这实际上是计算两个矩阵对应元素相减的平方的和,我们把这个式子展开,从
k
k
k和
k
′
k'
k′开始作它们的差,把对应的式子写下来,然后把得到的结果都加起来,作者在这里使用了一个归一化常数,也就是
1
2
n
H
[
l
]
l
n
W
[
l
]
n
C
[
l
]
\frac{1}{2n_{H}^{[l]l}n_{W}^{[l]}n_{C}^{[l]}}
2nH[l]lnW[l]nC[l]1,再在外面加一个平方,但是一般情况下你不用写这么多,一般我们只要将它乘以一个超参数
β
\beta
β就行。
最后,这是对
l
l
l层定义的风格代价函数,和之前你见到的一样,这是两个矩阵间一个基本的Frobenius范数,也就是
S
S
S图像和
G
G
G图像之间的范数再乘上一个归一化常数,不过这不是很重要。实际上,如果你对各层都使用风格代价函数,会让结果变得更好。如果要对各层都使用风格代价函数,你可以这么定义代价函数,把各个层的结果(各层的风格代价函数)都加起来,这样就能定义它们全体了。我们还需要对每个层定义权重,也就是一些额外的超参数,我们用
λ
[
l
]
\lambda^{[l]}
λ[l]来表示,这样将使你能够在神经网络中使用不同的层,包括之前的一些可以测量类似边缘这样的低级特征的层,以及之后的一些能测量高级特征的层,使得我们的神经网络在计算风格时能够同时考虑到这些低级和高级特征的相关系数。这样,在基础的训练中你在定义超参数时,可以尽可能的得到更合理的选择。
为了把这些东西封装起来,你现在可以定义一个全体代价函数:
J ( G ) = a J content ( C , G ) + β J s t y l e ( S , G ) J(G) = a J_{\text{content}( C,G)} + \beta J_{{style}}(S,G) J(G)=aJcontent(C,G)+βJstyle(S,G)
之后用梯度下降法,或者更复杂的优化算法来找到一个合适的图像 G G G,并计算 J ( G ) J(G) J(G)的最小值,这样的话,你将能够得到非常好看的结果,你将能够得到非常漂亮的结果。
这节神经风格迁移的内容就讲到这里,希望你能愉快地在本周的基础训练中进行实践。在本周结束之前,还有最后一节内容想告诉你们,就是如何对1D和3D的数据进行卷积,之前我们处理的都是2D图片,我们下节视频再见。
一维到三维推广(1D and 3D generalizations of models)
你已经学习了许多关于卷积神经网络(ConvNets)的知识,从卷积神经网络框架,到如何使用它进行图像识别、对象检测、人脸识别与神经网络转换。即使我们大部分讨论的图像数据,某种意义上而言都是2D数据,考虑到图像如此普遍,许多你所掌握的思想不仅局限于2D图像,甚至可以延伸至1D,乃至3D数据。
让我们回头看看在第一周课程中你所学习关于2D卷积,你可能会输入一个14×14的图像,并使用一个5×5的过滤器进行卷积,接下来你看到了14×14图像是如何与5×5的过滤器进行卷积的,通过这个操作你会得到10×10的输出。
如果你使用了多通道,比如14×14×3,那么相匹配的过滤器可能是5×5×3,如果你使用了多重过滤,比如16,最终你得到的是10×10×16。
事实证明早期想法也同样可以用于1维数据,举个例子,左边是一个EKG信号,或者说是心电图,当你在你的胸部放置一个电极,电极透过胸部测量心跳带来的微弱电流,正因为心脏跳动,产生的微弱电波能被一组电极测量,这就是人心跳产生的EKG,每一个峰值都对应着一次心跳。
如果你想使用EKG信号,比如医学诊断,那么你将处理1维数据,因为EKG数据是由时间序列对应的每个瞬间的电压组成,这次不是一个14×14的尺寸输入,你可能只有一个14尺寸输入,在这种情况下你可能需要使用一个1维过滤进行卷积,你只需要一个1×5的过滤器,而不是一个5×5的。
二维数据的卷积是将同一个5×5特征检测器应用于图像中不同的位置(编号1所示),你最后会得到10×10的输出结果。1维过滤器可以取代你的5维过滤器(编号2所示),可在不同的位置中应用类似的方法(编号3,4,5所示)。
当你对这个1维信号使用卷积,你将发现一个14维的数据与5维数据进行卷积,并产生一个10维输出。
再一次如果你使用多通道,在这种场景下可能会获得一个14×1的通道。如果你使用一个EKG,就是5×1的,如果你有16个过滤器,可能你最后会获得一个10×16的数据,这可能会是你卷积网络中的某一层。
对于卷积网络的下一层,如果输入一个10×16数据,你也可以使用一个5维过滤器进行卷积,这需要16个通道进行匹配,如果你有32个过滤器,另一层的输出结果就是6×32,如果你使用了32个过滤器的话。
对于2D数据而言,当你处理10×10×16的数据时也是类似的,你可以使用5×5×16进行卷积,其中两个通道数16要相匹配,你将得到一个6×6的输出,如果你用的是32过滤器,输出结果就是6×6×32,这也是32的来源。
所有这些方法也可以应用于1维数据,你可以在不同的位置使用相同的特征检测器,比如说,为了区分EKG信号中的心跳的差异,你可以在不同的时间轴位置使用同样的特征来检测心跳。
所以卷积网络同样可以被用于1D数据,对于许多1维数据应用,你实际上会使用递归神经网络进行处理,这个网络你会在下一个课程中学到,但是有些人依旧愿意尝试使用卷积网络解决这些问题。
下一门课将讨论序列模型,包括递归神经网络、LCM与其他类似模型。我们将探讨使用1D卷积网络的优缺点,对比于其它专门为序列数据而精心设计的模型。
这也是2D向1D的进化,对于3D数据来说如何呢?什么是3D数据?与1D数列或数字矩阵不同,你现在有了一个3D块,一个3D输入数据。以你做CT扫描为例,这是一种使用X光照射,然后输出身体的3D模型,CT扫描实现的是它可以获取你身体不同片段(图片信息)。
当你进行CT扫描时,与我现在做的事情一样,你可以看到人体躯干的不同切片(整理者注:图中所示为人体躯干中不同层的切片,附CT扫描示意图,图片源于互联网),本质上这个数据是3维的。
一种对这份数据的理解方式是,假设你的数据现在具备一定长度、宽度与高度,其中每一个切片都与躯干的切片对应。
如果你想要在3D扫描或CT扫描中应用卷积网络进行特征识别,你也可以从第一张幻灯片(Convolutions in 2D and 1D)里得到想法,并将其应用到3D卷积中。为了简单起见,如果你有一个3D对象,比如说是14×14×14,这也是输入CT扫描的宽度与深度(后两个14)。再次提醒,正如图像不是必须以矩形呈现,3D对象也不是一定是一个完美立方体,所以长和宽可以不一样,同样CT扫描结果的长宽高也可以是不一致的。为了简化讨论,我仅使用14×14×14为例。
如果你现在使用5×5×5过滤器进行卷积,你的过滤器现在也是3D的,这将会给你一个10×10×10的结果输出,技术上来说你也可以再×1(编号1所示),如果这有一个1的通道。这仅仅是一个3D模块,但是你的数据可以有不同数目的通道,那种情况下也是乘1(编号2所示),因为通道的数目必须与过滤器匹配。如果你使用16过滤器处理5×5×5×1,接下来的输出将是10×10×10×16,这将成为你3D数据卷积网络上的一层。
如果下一层卷积使用5×5×5×16维度的过滤器再次卷积,通道数目也与往常一样匹配,如果你有32个过滤器,操作也与之前相同,最终你得到一个6×6×6×32的输出。