人脸识别
人脸验证指输入图片后验证是否是对应的人。而人脸识别则是输入一副图片,在数据库中寻找符合输入的图片,并识别输出。大多数人脸识别系统存在One-shot learning问题。
对于人脸识别系统,我们需要仅通过一张人脸图片实现该人的识别。因为只有单个样本,不足以训练一个健壮的卷积神经网络识别不同的人。为了让系统实现一次学习,需要让神经网络学习Similarity函数,将人脸图片与数据库中拥有的图片成对输入Similarity函数,比较两幅图片之间的差异,则可解决One-shot问题。
Siamese网络
Similarity函数可通过Siamese网络实现。去掉卷积神经网络结构最后的Softmax层,样本最后会输出一个N维向量,代表图片编码。再将Similarity函数表示成两幅图片编码之差的范数,可根据范数大小判断图片差异。
以下为人脸识别的简单实现:
def who_is_it(image_path, database, model):
# 使用CNN网络计算图片编码
encoding = img_to_encoding(image_path, model)
# 初始化最小距离
min_dist = 100
# 遍历数据库(名称和编码)
for (name, db_enc) in database.items():
# 计算数据库图片和输入图片编码的L2距离
dist = np.linalg.norm(db_enc - encoding, ord=2)
# 寻找最小值
if dist < min_dist:
min_dist = dist
identity = name
# 根据min_dist判断是否是同一个人
if min_dist > 0.7:
print("Not in the database.")
else:
print ("it's " + str(identity) + ", the distance is " + str(min_dist))
return min_dist, identity
Siamese网络可进行二分类改进。应用Siamese网络,得到两张图片的N维编码后,可输入到一个逻辑回归单元中进行预测。这样可以将人脸识别问题,转化为二分类问题。
Triplet损失
在神经网络中使用Triplet损失函数,进行梯度下降,学习参数。函数的定义基于三张图片(三元组):
- Anchor(A):目标图片
- Positive(P):与Anchor属于同一人的图片
- Negative(N):与Anchor不属于同一人的图片
函数公式表示:
$$ L(A,P,N) = \max (||f(A) - f(P)||^{2} - ||f(A) - f(N)||^{2} + \alpha, \ 0) $$
在TensorFlow中可进行如下表示:
def triplet_loss(y_true, y_pred, alpha = 0.2):
anchor, positive, negative = y_pred[0], y_pred[1], y_pred[2]
# 计算anchor和positive的编码距离
pos_dist = tf.reduce_sum(tf.square(tf.subtract(anchor, positive)),axis=-1)
# 计算anchor和negative的编码距离
neg_dist = tf.reduce_sum(tf.square(tf.subtract(anchor, negative)),axis=-1)
# 剩余运算
basic_loss = tf.add(tf.subtract(pos_dist, neg_dist), alpha)
loss = tf.reduce_sum(tf.maximum(basic_loss, 0))
return loss
为了训练网络,必须拥有一个人的多张照片,否则无法进行训练。对于三元组的选择,要尽量选择那些训练有难度,也就是编码差异较小的三元组,这样梯度下降可以最大限度地扩大那些不同人图片的差异,可以提高算法的计算效率。
神经风格迁移
神经风格迁移的目标是由内容图片C和风格图片S,生成最终的风格迁移图片G:
$$ J(G) = \alpha J_{content}(C, G) + \beta J_{style}(S,G) $$
其中,$J_{content}(C, G)$ 代表内容图片C和生成图片G的内容的相似度;$J_{style}(S,G)$ 代表风格图片S和生成图片G和内容相似度;$\alpha$、$\beta$ 两个超参数分别表示两者的权重。执行时,随机初始化生成图片G的参数,然后使用梯度下降算法最小化代价函数。
内容代价函数
$$ J_{content}(C, G) = \dfrac{1}{2}||a^{[l]C} - a^{[l]G} ||^{2} $$
$ a^{[l]C} $和$ a^{[l]G} $分别代表内容图片C和生成图片G的第l层的激活值。如果两个值相似,说明两张图片就有相似的内容。
在TensorFlow中可进行如下表示:
def compute_content_cost(a_C, a_G):
# 获取生成图片G隐藏层的各个维度大小并Reshape
m, n_H, n_W, n_C = a_G.get_shape().as_list()
a_C_unrolled = tf.reshape(a_C, (n_H * n_W,n_C))
a_G_unrolled = tf.reshape(a_G, (n_H * n_W,n_C))
# 计算代价函数
J_content = 1/(4 * n_H * n_W * n_C) * tf.reduce_sum(tf.square(tf.subtract(a_C_unrolled, a_G_unrolled)))
return J_content
风格代价函数
“风格”表示l层的各个通道激活项之间的相关性。首先定义风格矩阵,$a^{[l]}_{i,j,k}$表示(i,j,k)位置(高、宽、通道)的激活值,$ G^{[l]} $是一个$ n_c^{l}\times n_c^{l} $大小的矩阵,风格矩阵用于风格图片S和生成图片G,其在线性代数中又称为Gram矩阵。有了风格矩阵以后,就可以定义风格代价函数,这里列出各层的风格函数的和。
$$ J_{style}(S,G) = \sum_{l}\lambda^{[l]}J_{style}^{[l]}(S,G) $$
TensorFlow表示如下:
def compute_layer_style_cost(a_S, a_G):
m, n_H, n_W, n_C = a_G.get_shape().as_list()
a_S = tf.reshape(a_S, (n_H * n_W, n_C))
a_G = tf.reshape(a_G, (n_H * n_W, n_C))
# 计算风格图片S和生成图片G的Gram矩阵
GS = gram_matrix(tf.transpose(a_S))
GG = gram_matrix(tf.transpose(a_G))
# 计算代价函数
J_style_layer =1/(4 * (n_C **2) * ((n_H * n_W) **2)) * tf.reduce_sum(tf.square(tf.subtract(GS,GG)))
return J_style_layer