参考网址:
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))+"秒")