Course 4 - 卷积神经网络 - 第四周作业 - 人脸识别与神经风格转换

参考网址:

https://blog.csdn.net/u013733326/article/details/80767079#commentBox

https://blog.csdn.net/ljp1919/article/details/79112622

1.人脸验证和人脸识别

  • 1.1定义三元损失函数,之所以要定义三元损失函数,是因为它解决了一次学习的问题,如果没有他,则咱们需要训练的网络就是对于许多人的许多图片进行训,然后对于新输入的图片输入到网络中,判断是哪个人,但是当公司的员工新加入一个人时,就需要重新训练网络参数,这样是不符合的,因此需要训练这样的网络:他可以提取图片中的特征的网络,可以将图片转为128为的向量进行输入,然后定义这个三元损失函数,使得Anchor和positive的distance尽可能的小,而Anchor和negative的distance尽可能的大。
  • 1.2加载已经训练好的模型
  • 1.3验证函数:将输入的图片放到模型中进行前向传播,得到128维的向量,判断和identity的128的向量的相似度。超过阈值则认为是一个人。
  • 1.4识别函数,相对于验证,少了需要输入identity,因为需要遍历数据库中的每个identity
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import sys
from keras import backend as K


#------------用于绘制模型细节,可选--------------#
from IPython.display import SVG
from keras.utils.vis_utils import model_to_dot
from keras.utils import plot_model
#------------------------------------------------#
import time
import cv2
import os
import numpy as np
from numpy import genfromtxt
import pandas as pd
import tensorflow as tf
import fr_utils
from inception_blocks_v2 import *
np.set_printoptions(threshold=sys.maxsize)
K.set_image_data_format('channels_first')
from Face_Recongnition.inception_blocks_v2 import *


# 1.获取模型
FRmodel=faceRecoModel(input_shape=(3,96,96))
# 打印模型的总参数数量
# print("参数数量:"+str(FRmodel.count_params()))

#绘制模型细节
# plot_model(FRmodel,to_file="FRmodel.png")
# SVG(model_to_dot(FRmodel).create(prog='dot',format='svg'))


# 2.根据神经网络输出的编码,计算triplet_loss损失
def triplet_loss(y_true,y_pred,alpha=0.2):
    '''
    实现三元损失函数
    :param y_true:  true标签,当你在Keras里定义了一个损失函数的时候需要它,但是这里不需要
    :param y_pred:-- 列表类型,包含了如下参数:
            anchor -- 给定的“anchor”图像的编码,维度为(None,128)
            positive -- “positive”图像的编码,维度为(None,128)
            negative -- “negative”图像的编码,维度为(None,128)
    :param alpha:超参数,阈值
    :return:loss,损失值
    '''
    # 获取anchor、positive、negative的图像编码
    anchor,positive,negative=y_pred[0],y_pred[1],y_pred[2]

    # 第一步:计算anchor和positive的编码距离,axis=-1
    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)

    # 第三步:减去之前的两个距离,并加上alpha
    basic_loss=tf.add(tf.subtract(pos_dist,neg_dist),alpha)

    # 和0比较,取最大,并对所有的basec_loss求和
    loss=tf.reduce_sum(tf.maximum(basic_loss,0))

    return loss
# 测试
# with tf.Session() as test:
#     tf.set_random_seed(1)
#     y_true=(None,None,None)
#     y_predict=(
#         tf.random_normal([3,128],mean=6,stddev=0.1,seed=1),
#         tf.random_normal([3, 128], mean=1, stddev=1, seed=1),
#         tf.random_normal([3, 128], mean=3, stddev=4, seed=1)
#     )
#
#     loss=triplet_loss(y_true,y_predict)
#     print("loss="+str(loss.eval()))


# 3.加载已经训练好的模型

# 开始时间
start_time= time.clock()

# 编译模型
FRmodel.compile(optimizer='adam',loss=triplet_loss,metrics=['accuracy'])

# 加载权值
fr_utils.load_weights_from_FaceNet(FRmodel)

# 结束时间
end_time=time.clock()

# 计算时差
minium=end_time-start_time

print("加载权值执行了:"+str(int(minium/60))+"分"+str(int(minium%60))+"秒")


# 3.模型的应用
## 3.1人脸验证
### 3.1.1构建数据库---加载图片,在模型中执行一次向前传播得到128维的向量
database = {}
database["danielle"] = fr_utils.img_to_encoding("images/danielle.png", FRmodel)
database["younes"] = fr_utils.img_to_encoding("images/younes.jpg", FRmodel)
database["tian"] = fr_utils.img_to_encoding("images/tian.jpg", FRmodel)
database["andrew"] = fr_utils.img_to_encoding("images/andrew.jpg", FRmodel)
database["kian"] = fr_utils.img_to_encoding("images/kian.jpg", FRmodel)
database["dan"] = fr_utils.img_to_encoding("images/dan.jpg", FRmodel)
database["sebastiano"] = fr_utils.img_to_encoding("images/sebastiano.jpg", FRmodel)
database["bertrand"] = fr_utils.img_to_encoding("images/bertrand.jpg", FRmodel)
database["kevin"] = fr_utils.img_to_encoding("images/kevin.jpg", FRmodel)
database["felix"] = fr_utils.img_to_encoding("images/felix.jpg", FRmodel)
database["benoit"] = fr_utils.img_to_encoding("images/benoit.jpg", FRmodel)
database["arnaud"] = fr_utils.img_to_encoding("images/arnaud.jpg", FRmodel)

### 3.1.2验证函数,根据id,将采集到的图片与数据库中对应id的图片进行相似度对比,相似超过一定的阈值,则认为是一个人
def verify(image_path,identity,database,model):
    '''
    对“identity”与“image_path”的编码进行验证。
    :param image_path:摄像头的图片
    :param identity:字符类型,想要验证的人的名字
    :param database: 字典类型,包含了成员的名字信息与对应的编码。
    :param model:在Keras的模型的实例
    :return:  dist -- 摄像头的图片与数据库中的图片的编码的差距。
        is_open_door -- boolean,是否该开门。
    '''

    # 第一步:计算图像的编码
    encoding= fr_utils.img_to_encoding(image_path, model)

    # 第二步:计算与数据库中保存的编码的差距
    dist=np.linalg.norm(encoding-database[identity])

    # 判断是否开门
    if dist<0.7:
        print("欢迎"+str(identity)+"回家!")
        is_door_open=True
    else:
        print("经验证,您与"+str(identity)+"不符")
        is_door_open=False
    return dist,is_door_open
# 测试
# print(verify("images/camera_0.jpg","younes",database,FRmodel))


## 3.2人脸识别
### 遍历数据库中的人员,找到距离最小的人员
def who_is_it(image_path,database,model):
    '''
    根据指定图片进行人脸识别
    :param image_path:图像地址
    :param database: 包含了名字与编码的字典
    :param model: 在Keras中的模型的实例
    :return:  min_dist -- 在数据库中与指定图像最相近的编码。
        identity -- 字符串类型,与min_dist编码相对应的名字。
    '''

    # 第一步:计算指定图像的编码
    encoding= fr_utils.img_to_encoding(image_path, model)

    # 第二步:找到最相似的编码
    mini_dist=100
    for name,db_enc in database.items():
        ### 计算目标编码与当前数据库编码之间的L2差距。
        dist=np.linalg.norm(encoding-db_enc)

        if dist<mini_dist:
            mini_dist=dist
            identity=name
    if mini_dist>0.7:
        print("抱歉,您的信息不在数据库中。")
    else:
        print("姓名"+str(identity)+",差距:"+str(mini_dist))
    return mini_dist,identity
# 测试
print(who_is_it("images/camera_0.jpg",database,FRmodel))


2.神经风格转换

2.1首先需要定义J_content和J_style,其中的区别,绘图如下:,计算得到前两者之后就可以得到J总的损失函数

2.2然后具体的步骤如下进行实现

1.创建交互会话
2.加载内容图像
3.加载风格图像
4.随机初始化生成的图像
5.加载VGG16模型
6.构建TensorFlow图: 
    1.使用VGG16模型来运行内容图并计算内容成本
    2.使用VGG16模型来运行风格图并计算风格成本
    3.计算总成本
    4.定义优化器与学习速率
7.初始化TensorFlow图,进行多次迭代,每次迭代更新生成的图像。
#!/usr/bin/env python
# _*_ coding:utf-8 _*_
import time
import os
import sys
import scipy.io
import scipy.misc
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
from PIL import Image
import nst_utils
import numpy as np
import tensorflow as tf

STYLE_LAYERS = [
    ('conv1_1', 0.2),
    ('conv2_1', 0.2),
    ('conv3_1', 0.2),
    ('conv4_1', 0.2),
    ('conv5_1', 0.2)]
# 1.从vgg模型中加载参数
#model=nst_utils.load_vgg_model("pretrained-model/imagenet-vgg-verydeep-19.mat")
# print(model)

# 将图像作为输入给模型,进行运行
# model["input"].assign(image)

# 访问 4_2的激活层
# sess.run(model["conv4_2"])


# 2.查看内容图片代码
# content_image=scipy.misc.imread("images/louvre.jpg")
# imshow(content_image)

# 3.计算内容代价函数 将特定层的C和G激活值(数据立方体)做均方误差
def compute_content_cost(a_C,a_G):
    '''

    :param a_C:tensor类型,维度为(1, n_H, n_W, n_C),表示隐藏层中图像C的内容的激活值
    :param a_G:tensor类型,维度为(1, n_H, n_W, n_C),表示隐藏层中图像G的内容的激活值
    :return:J_content 内容损失值
    '''
    # 获取a_G的维度信息
    m,n_H,n_W,n_C=a_G.get_shape().as_list()

    # 对a_C与a_G从3维降到2维
    a_C_unrolled=tf.transpose(tf.reshape(a_C,[n_H*n_W,n_C]))
    a_G_unrolled=tf.transpose(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

# 测试
# tf.reset_default_graph()
#
# with tf.Session() as test:
#     tf.set_random_seed(1)
#     a_C = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4)
#     a_G = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4)
#     J_content = compute_content_cost(a_C, a_G)
#     print("J_content = " + str(J_content.eval()))
#
#     test.close()

# 查看风格图片代码
# style_image=scipy.misc.imread("images/monet_800600.jpg")
# imshow(style_image)

# 4.计算风格代价函数
## 4.1计算格拉姆矩阵G(i,j)=v(i).T*v(j)
def gram_matrix(A):
    '''
    计算矩阵A的风格矩阵
    :param A: 矩阵,维度为(n_C, n_H * n_W)
    :return:A的风格矩阵,维度为(n_C, n_C)
    '''
    GA=tf.matmul(A,tf.transpose(A))
    # tf.matmul是矩阵乘法
    return GA
# 测试
# tf.reset_default_graph()
#
# with tf.Session() as test:
#     tf.set_random_seed(1)
#     A = tf.random_normal([3, 2*1], mean=1, stddev=4)
#     GA = gram_matrix(A)
#
#     print("GA = " + str(GA.eval()))

## 4.2计算一层的风格损失函数
### 在公式中需要计算一个立方体的通道之间的乘积,不需要两重循环,只需要把
### 每个通道转为一行数据,立方体就转为了二维的数据,将二维数据,乘上二维数据的转置就实现了
### 每个通道和每个通道之间的乘法
def compute_layer_style_cost(a_S,a_G):
    '''

    :param a_S: tensor类型,维度为(1, n_H, n_W, n_C),表示隐藏层中图像S的风格的激活值
    :param a_G:tensor类型,维度为(1, n_H, n_W, n_C),表示隐藏层中图像G的风格的激活值
    :return:J_style_layer
    '''
    # 第一步:从a_G中获取维度信息
    m,n_H,n_W,n_C=a_G.get_shape().as_list()

    # 第二步 将a_S和a_G的维度重构为(n_C, n_H * n_W)
    a_S=tf.transpose(tf.reshape(a_S,[n_H*n_W,n_C]))
    a_G=tf.transpose(tf.reshape(a_G,[n_W*n_H,n_C]))

    # 第三步:计算S和G的风格矩阵
    GS=gram_matrix(a_S)
    GG=gram_matrix(a_G)

    # 第四步:计算风格损失函数
    J_style_layer=1/(4*n_C*n_C*n_H*n_H*n_W*n_W)*tf.reduce_sum(tf.square(tf.subtract(GG,GS)))

    return J_style_layer
# 测试
# tf.reset_default_graph()
#
# with tf.Session() as test:
#     tf.set_random_seed(1)
#     a_S = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4)
#     a_G = tf.random_normal([1, 4, 4, 3], mean=1, stddev=4)
#     J_style_layer = compute_layer_style_cost(a_S, a_G)
#
#     print("J_style_layer = " + str(J_style_layer.eval()))
#
#     test.close()

## 4.3计算多层的风格损失函数,需要指定每层的权值
def compute_style_cost(model,STYLE_LAYERS):
    '''
    计算几个选定层的总体风格成本
    sess:运行会话
    :param model:加载了的tensorflow模型
    :param STYLE_LAYERS:字典,包含了:
                        - 我们希望从中提取风格的层的名称
                        - 每一层的系数(coeff)
    :return:J_style - tensor类型,实数,由公式(2)定义的成本计算方式来计算的值。
    '''
    # 1.初始化所有的成本值
    J_style=0
    for layer_name,coeff in STYLE_LAYERS:

        # 选择当前选定层的输出
        out=model[layer_name]

        # 运行会话,将a_S设置为我们选择的隐藏层的激活值
        a_S=sess.run(out)

        # 将a_G设置为来自统一图层的隐藏层激活,这里a_G引用model[layer_name],并且还没有计算,
        # 在后面的代码中,我们将图像G指定为模型输入,这样当我们运行会话时,
        # 这将是以图像G作为输入,从隐藏层中获取的激活值
        # a_G是张量,还没有被计算,
        # 当我们在下面的model_nn()中运行TensorFlow图时,将在每次迭代中对其进行评估和更新。
        a_G=out

        # 计算当前层的风格成本
        J_style_layer=compute_layer_style_cost(a_S,a_G)

        #计算总风格成本,同时考虑到系数
        J_style+=coeff*J_style_layer
    return J_style

# 5.计算总成本函数
def total_cost(J_content,J_style,alpha=10,beta=40):
    '''
    计算总成本
    :param J_content:内容成本函数的输出
    :param J_style:风格成本函数的输出
    :param alpha:- 超参数,内容成本的权值
    :param beta:超参数,风格成本的权值
    :return:J总体成本值
    '''
    J=alpha*J_content+beta*J_style
    return J
# 测试
# tf.reset_default_graph()
# with tf.Session() as test:
#     np.random.seed(3)
#     J_content=np.random.randn()
#     J_style=np.random.randn()
#     J=total_cost(J_content,J_style)
#     print("J="+str(J))
#
#     test.close()

# 6.整合
'''
最后,让我们一起来实现神经风格转换,它由以下几步构成:

1.创建交互会话
2.加载内容图像
3.加载风格图像
4.随机初始化生成的图像
5.加载VGG16模型
6.构建TensorFlow图: 
    1.使用VGG16模型来运行内容图并计算内容成本
    2.使用VGG16模型来运行风格图并计算风格成本
    3.计算总成本
    4.定义优化器与学习速率
7.初始化TensorFlow图,进行多次迭代,每次迭代更新生成的图像。
'''
# 重设图
tf.reset_default_graph()

# 第一步:创建交互会话
sess=tf.InteractiveSession()

# 第二步:加载内容图像,并归一化图像
content_image=scipy.misc.imread("images/louvre_small.jpg")
content_image=nst_utils.reshape_and_normalize_image(content_image)

# 第三步:加载风格图像,并归一化图像
style_image=scipy.misc.imread("images/monet.jpg")
style_image=nst_utils.reshape_and_normalize_image(style_image)

# 第四步:随机初始化生成的图像,通过在内容图像中添加随机噪声来产生噪声图像
generated_image=nst_utils.generate_noise_image(content_image)
imshow(generated_image[0])

# 第五步:加载VGG16模型
model=nst_utils.load_vgg_model("pretrained-model/imagenet-vgg-verydeep-19.mat")

'''
为了让程序计算内容成本,现在我们将把a_C与a_G作为选定的隐藏层的激活值,我们将使用conv4_2 层来计算内容成本,下面的代码将做以下事:
将内容图像作为VGG模型的输入。
将a_C设置为“conv4_2”隐藏层的激活值。
将a_G设置为同一隐藏层的激活值。
使用 a_C与a_G计算内容成本
'''

# 第六步:构建TensorFolw图
##============ 1.将内容图像作为VGG模型的输入
sess.run(model["input"].assign(content_image))

## 1.1获取conv4_2层的输出
out=model["conv4_2"]

## 1.2将a_C设置为"conv4_2"隐藏层的激活值
a_C=sess.run(out)

## 1.3将a_G设置为来自同一图层的隐藏层激活,这里a_G引用model["conv4_2"],并且还没有计算,
## 在后面的代码中,我们将图像G指定为模型输入,这样当我们运行会话时,
## 这将是以图像G作为输入,从隐藏层中获取的激活值。
## a_G并没有计算
a_G=out

# 2.计算内容成本
J_content=compute_content_cost(a_C,a_G)

##=============== 将风格图像作为VGG模型的输入
sess.run(model["input"].assign(style_image))

## 3.计算风格成本
J_style=compute_style_cost(model,STYLE_LAYERS)

## 4.计算总成本
J=total_cost(J_content,J_style,alpha=10,beta=40)

## 5.定义优化器,设置学习率为2.0
optimizer=tf.train.AdamOptimizer(2.0)

## 6.定义学习目标:最小化成本
train_step=optimizer.minimize(J)



# 第七步:初始化TensorFlow图,进行多次迭代,每次迭代更新生成的图像
def model_nn(sess,input_image,num_iterations=100,is_print_info=True,
             is_plot=True,is_save_process_image=True,
             save_lase_image_to="output/generated_image.jpg"):
    # 初始化全局变量
    sess.run(tf.global_variables_initializer())

    # 运行带噪声的输入图像 ===根据输入的内容图像加入一定的噪声,初始化的generate_image
    sess.run(model["input"].assign(input_image))

    for i in range(num_iterations):
        # 运行最小化目标
        sess.run(train_step)
        # 产生把数据输入模型后生成的图像
        generated_image=sess.run(model["input"])

        if is_print_info and i%20==0:
            Jt,Jc,Js=sess.run([J,J_content,J_style])
            print("第"+str(i)+"轮训练,"+
                  "总成本为:"+str(Jt)+
                  ",内容成本为:"+str(Jc)+
                  ",风格成本为:"+str(Js))
        if is_save_process_image:
            nst_utils.save_image("output/"+str(i)+".png",generated_image)
    nst_utils.save_image(save_lase_image_to,generated_image)

    return generated_image


# 第八步:开始训练
# 开始时间
start_time=time.clock()

# 非GPU版本 25-30min
generated_image=model_nn(sess,generated_image)

# GPU版本 1-2min
# with tf.device("/gpu:0"):
#     generated_image=model_nn(sess,generated_image)

# 结束时间
end_time=time.clock()

# 计算时差
minium=end_time-start_time

print("执行了:"+str(int(minium/60))+"分"+str(int(minium%60))+"秒")

 

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值