人脸识别

人脸验证指输入图片后验证是否是对应的人。而人脸识别则是输入一副图片,在数据库中寻找符合输入的图片,并识别输出。大多数人脸识别系统存在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