Course 4:人脸识别与神经风格迁移
课程简介
Siamese 网络、Triplet Loss、神经风格迁移原理。
一、人脸识别
人脸识别分为两种场景:
1. 人脸验证(Face Verification):1:1 匹配,判断两张人脸是否是同一个人
2. 人脸识别(Face Recognition):1:K 匹配,判断一张人脸是K个人中的哪一个
验证比识别简单,因为识别需要在更大的候选集中查找。
1.1 One-Shot学习
人脸识别面临的特殊挑战是One-Shot学习——每个目标只有一张参考照片,无法为每个人训练一个多类别分类器(因为新用户加入时需要不断重新训练)。
解决方案:学习一个相似度函数 $d(img1, img2)$,输出两张人脸的差异度。如果 $d < \tau$ 判定为同一人。
1.2 Siamese网络
Siamese网络由两个共享权重的相同网络组成,将输入图像映射到嵌入向量 $f(x)$:
$$|f(x^{(i)}) - f(x^{(j)})|^2$$
同一人的两张照片嵌入距离小,不同人的距离大。
1.3 Triplet Loss
Triplet Loss是训练Siamese网络的标准损失函数:
$$\mathcal{L} = \max\left(|f(A) - f(P)|^2 - |f(A) - f(N)|^2 + \alpha, 0\right)$$
其中 A=Anchor(锚点),P=Positive(同一人),N=Negative(不同人),$\alpha$ 是边距(margin)。
Loss的含义:锚点与正样本的距离加上边距要小于锚点与负样本的距离。
$$|f(A) - f(P)|^2 + \alpha \leq |f(A) - f(N)|^2$$
三元组选择技巧:选择困难的三元组(正样本不太像、负样本比较像)可以加速训练。如果随机选择,大多数三元组已经满足条件,loss为0。
FaceNet使用Triplet Loss在大规模人脸数据集上训练,在LFW数据集上达到99.63%的准确率。
1.4 人脸验证与二分类
另一种训练方法是将人脸验证视为二分类问题:输入两张人脸的嵌入向量,输出是否同一人。
$$y = \sigma\left(\sum_{k=1}^{K} w_k |f(x^{(i)})_k - f(x^{(j)})_k| + b\right)$$
这种方法在部署时更简单——只需预先计算所有用户的嵌入向量存到数据库,新用户注册时计算嵌入并查找最近邻。
1.5 人脸识别的挑战
- 光照变化:不同光照条件下同一人脸差异可能很大
- 姿态变化:正面和侧面人脸差异大
- 遮挡:眼镜、口罩、围巾
- 年龄变化:随着年龄增长人脸变化
- 化妆和整容
二、神经风格迁移
2.1 问题定义
神经风格迁移(Neural Style Transfer)将一张图片的内容与另一张图片的风格结合,生成新的图像。
输入:内容图像 $C$ 和风格图像 $S$
输出:生成图像 $G$
2.2 成本函数
总成本函数由内容成本和风格成本两部分组成:
$$J(G) = \alpha J_{\text{content}}(C, G) + \beta J_{\text{style}}(S, G)$$
$\alpha$ 和 $\beta$ 控制内容和风格的权重。
2.3 内容成本
使用预训练的CNN(如VGG-16)提取特征:
- 选择网络中某一层 $l$ 的特征图
- 内容图像 $C$ 和生成图像 $G$ 在该层的激活值应接近
$$J_{\text{content}}(C, G) = \frac{1}{2}|a^{l} - a^{l}|^2$$
层 $l$ 的选择很重要:浅层保留更多细节,深层保留更多语义内容。
2.4 风格成本
风格被定义为特征图中通道间的相关性,使用Gram矩阵表示:
$$G^{l}{kk'} = \sum{i}\sum_{j} A^{l}{ijk} \cdot A^{l}{ijk'}$$
Gram矩阵的第 $(k, k')$ 个元素表示第 $k$ 个特征图与第 $k'$ 个特征图之间的相关性。
风格成本:
$$J_{\text{style}}(S, G) = \frac{1}{4n_C^2 n_H^2 n_W^2} |G^{l} - G^{l}|_F^2$$
实践中会在多个层上计算风格成本,以捕获不同尺度的风格信息。
2.5 实现流程
# 神经风格迁移简化实现
def neural_style_transfer(content_img, style_img, num_iterations=1000):
# 加载预训练VGG模型
model = load_pretrained_vgg()
# 初始化生成图像为内容图像的拷贝
generated = content_img.copy()
generated = tf.Variable(generated)
optimizer = tf.optimizers.Adam(learning_rate=10.0)
for i in range(num_iterations):
with tf.GradientTape() as tape:
# 提取各层特征
content_features = model(content_img)
style_features = model(style_img)
gen_features = model(generated)
# 计算内容成本
J_content = compute_content_cost(content_features, gen_features)
# 计算风格成本
J_style = compute_style_cost(style_features, gen_features)
# 总成本
J_total = alpha * J_content + beta * J_style
# 更新生成图像的像素值
grads = tape.gradient(J_total, generated)
optimizer.apply_gradients([(grads, generated)])
return generated
注意:风格迁移优化的是生成图像的像素值,而不是网络参数。
2.6 1D和3D卷积推广
卷积不仅适用于2D图像:
- 1D卷积:时间序列、音频、文本($n_H \times n_C$ 的1D特征图)
- 3D卷积:视频、医学CT扫描($n_H \times n_W \times n_T \times n_C$)
延伸阅读
- 📺 B 站播放列表:Deep Learning Specialization — 深度学习专项课程
- 📚 更多学习资源,请访问 deeplearning.ai 官网