文章目录
声明
本文参考何宽
前言
本文的结构:
- 人脸识别:1对多
- 人脸验证:1对1
- 神经风格迁移
人脸识别
- 人脸验证:这是不是本人,比如在某些机场你能够让系统扫描你的面部并验证你是否为本人从而使得你免人工检票通过海关,又或者某些手机能够使用人脸解锁功能。这都是1对1匹配问题。
- 人脸识别:这个人是谁,比如说,在视频中的百度员工进入办公室时的脸部识别视频的介绍,无需使用另外的id卡。这是1对多的匹配问题
facenet可以将人脸图像编码为一个128位数字的向量从而进行学习,通过比较两个这样的向量,那么就可以确定这两张图片是否是属于同一个人。
本节的结构: - 实现三元组损失函数
- 使用一个已经训练好了的模型来将人脸图像映射到一个128位数字的向量
- 使用这些编码来执行人脸验证和人脸识别
from keras.models import Sequential
from keras.layers import Conv2D, ZeroPadding2D, Activation, Input, concatenate
from keras.models import Model
from keras.layers.normalization import BatchNormalization
from keras.layers.pooling import MaxPooling2D, AveragePooling2D
from keras.layers.merge import Concatenate
from keras.layers.core import Lambda, Flatten, Dense
from keras.initializers import glorot_uniform
from keras.engine.topology import Layer
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
#------------------------------------------------#
K.set_image_data_format('channels_first')
import time
import cv2
import os
import sys
import scipy.io
import scipy.misc
import numpy as np
from numpy import genfromtxt
import pandas as pd
import tensorflow as tf
import fr_utils
from inception_blocks_v2 import *
%matplotlib inline
%load_ext autoreload
%autoreload 2
np.set_printoptions(threshold=np.nan)
简单的人脸验证
在人脸验证中,需要给出两张照片并想知道是否是同一个人,最简单的方法是逐像素地比较这两幅图像,如果图片之间的误差小于选择的阈值,那么则可能是同一个人。
将人脸图像编码为128位的向量
使用卷积网络来进行编码
- 该网络使用了96x96的RGB图像作为输入数据,图像数量为m,输入的数据维度为 ( m , n c , n h , n w ) = ( m , 3 , 96 , 96 ) (m,n_c,n_h,n_w)=(m,3,96,96) (m,nc,nh,nw)=(m,3,96,96)
- 输出为(m,128)的已经编码的m个128位的向量
FRmodel = faceRecoModel(input_shape=(3,96,96))
print(FRmodel.count_params())
结果:
3743280
绘制模型细节:
plot_model(FRmodel,to_file="FRmodel.png")
SVG(model_to_dot(FRmodel).create(prog='dot',format='svg'))
结果:
略。
通过使用128神经元全连接层作为最后一层,该模型确保输出是大小为128的编码向量,然后使用比较两个人脸图像的编码如下:
如果满足下面两个条件,编码是一个比较好的方法:
- 同一个人的两个图像的编码非常相似
- 两个不同人物的图像的编码非常不同
三元组损失函数将实现上面的内容,它会是图将同一个人的两个图像编码拉近,同时将两个不同的人的图像进一步分离。
三元组损失函数
对于给定的图像x,其编码为f(x),其中f为神经网络的计算函数。
将使用三元组图像(A,P,N)进行训练:
- A是anchor,是一个人的图像
- P是positive,是相对于anchor的同一个人的另外一张图像
- N是negative,是相对于anchor的不同的人的另外一张图像。
- 这些三元组来自训练集,使用 ( A ( i ) , P ( i ) , N ( i ) ) (A^{(i)},P^{(i)},N^{(i)}) (A(i),P(i),N(i))来表示第i个训练样本。要保证 A ( i ) A^{(i)} A(i)与图像 P ( i ) P^{(i)} P(i)的差值至少比图像 N ( i ) N^{(i)} N(i)的差值相差 α \alpha α:
∣
∣
f
(
A
(
i
)
−
f
(
P
(
i
)
)
∣
∣
2
2
+
α
<
∣
∣
f
(
A
(
i
)
−
f
(
N
(
i
)
)
∣
∣
2
2
||f(A^{(i)}-f(P^{(i)})||_2^2+\alpha < ||f(A^{(i)}-f(N^{(i)})||_2^2
∣∣f(A(i)−f(P(i))∣∣22+α<∣∣f(A(i)−f(N(i))∣∣22
让三元组损失变为最小:
J
=
∑
i
=
1
m
[
∣
∣
f
(
A
(
i
)
−
f
(
P
(
i
)
)
∣
∣
2
2
−
∣
∣
f
(
A
(
i
)
−
f
(
N
(
i
)
)
∣
∣
2
2
+
α
]
+
J=\sum_{i=1}^m[||f(A^{(i)}-f(P^{(i)})||_2^2-||f(A^{(i)}-f(N^{(i)})||_2^2+\alpha]_+
J=i=1∑m[∣∣f(A(i)−f(P(i))∣∣22−∣∣f(A(i)−f(N(i))∣∣22+α]+
- 使用 [ . . . ] + [...]_+ [...]+来表示函数max(z,0)
- 给定三元组A与正例P之间的距离的平方,要让它变小
- 给定三元组A与负例N之间的距离的平方,要让它变大
- α \alpha α是间距,这个需要手动选择,这里使用 α = 0.2 \alpha=0.2 α=0.2
def triplet_loss(y_true, y_pred, alpha = 0.2):
"""
根据公式(4)实现三元组损失函数
参数:
y_true -- true标签,当你在Keras里定义了一个损失函数的时候需要它,但是这里不需要。
y_pred -- 列表类型,包含了如下参数:
anchor -- 给定的“anchor”图像的编码,维度为(None,128)
positive -- “positive”图像的编码,维度为(None,128)
negative -- “negative”图像的编码,维度为(None,128)
alpha -- 超参数,阈值
返回:
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"之间编码的距离,这里需要使用axis=-1
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)
#通过取带零的最大值和对训练样本的求和来计算整个公式
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_pred = (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_pred)
print("loss = " + str(loss.eval()))
结果:
loss = 528.1432
加载训练好了的模型
#开始时间
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)) + "秒")
结果:
执行了:1分48秒
模型的应用
人脸验证
将用字典来存储数据,代表一个数据库。
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)
当有人出现在你的门前刷他们的身份证时,你可以在数据库中查找他们的编码,用它来检测站在门前的人是否与身份证上的名字匹配。
要实现verify()函数来验证摄像头的照片(image_path)是否与身份证上的名称匹配。
- 根据image_path来计算编码
- 计算与存储在数据库中的身份图像的编码的差距
- 如果差距小于0.7,那么就开门,否则不开门
def verify(image_path,identity,database,model):
"""
对“identity”与“image_path”的编码进行人脸验证
参数:
imgae_path - 摄像头的图片
identity - 字符类型,想要验证的人的名字
database - 字典类型,包含了成员的名字信息与对应的编码
model - 在keras的模型的实例
返回:
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
测试:
verify("images/camera_0.jpg","younes",database,FRmodel)
结果:
欢迎younes回家!
(0.06747335, True)
测试:
verify("images/camera_2.jpg","kian",database,FRmodel)
结果:
经验证,您与kian不符!
(0.86224037, False)
人脸识别
步骤:
1、根据image_path计算图像的编码
2、从数据库中找出与目标编码具有最小差距的编码
- 计算目标编码与当前数据库编码之间的L2差距
- 如果差距小于min_dist,那么就更新名字与编码到identity与min_dist中。
def who_is_it(image_path,database,model):
"""
根据指定的图片来进行人脸识别
参数:
images_path - 图像地址
database - 包含了名字与编码的字典
model - 在keras中的模型的实例
返回:
min_dist - 在数据库中与指定图像最相近的编码
identity - 字符串类型,与min_dist编码相对应的名字
"""
encoding = fr_utils.img_to_encoding(image_path,model)
min_dist = 100
for(name,db_enc) in database.items():
dist = np.linalg.norm(encoding-db_enc)
if dist<min_dist:
min_dist = dist
identity = name
if min_dist > 0.7:
print("抱歉,您的信息不在数据库中。")
else:
print("姓名"+str(identity)+" 差距:"+str(min_dist))
return min_dist,identity
测试:
who_is_it("images/camera_0.jpg",database,FRmodel)
结果:
姓名younes 差距:0.06747335
(0.06747335, 'younes')
神经风格转移
本节结构:
- 实现神经风格转换算法
- 用算法生成新的艺术图像
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
%matplotlib inline
迁移学习
迁移学习:神经风格转换(NST)使用先前训练好了的卷积网络,并在此基础之上进行构建,使用在不同任务上训练的网络并将其应用于新任务的想法。
因此我们将使用VGG网络,先从VGG模型加载参数。
model = nst_utils.load_vgg_model("pretrained-model/imagenet-vgg-verydeep-19.mat")
print(model)
结果:
{'input': <tf.Variable 'Variable:0' shape=(1, 300, 400, 3) dtype=float32_ref>, 'conv1_1': <tf.Tensor 'Relu:0' shape=(1, 300, 400, 64) dtype=float32>, 'conv1_2': <tf.Tensor 'Relu_1:0' shape=(1, 300, 400, 64) dtype=float32>, 'avgpool1': <tf.Tensor 'AvgPool:0' shape=(1, 150, 200, 64) dtype=float32>, 'conv2_1': <tf.Tensor 'Relu_2:0' shape=(1, 150, 200, 128) dtype=float32>, 'conv2_2': <tf.Tensor 'Relu_3:0' shape=(1, 150, 200, 128) dtype=float32>, 'avgpool2': <tf.Tensor 'AvgPool_1:0' shape=(1, 75, 100, 128) dtype=float32>, 'conv3_1': <tf.Tensor 'Relu_4:0' shape=(1, 75, 100, 256) dtype=float32>, 'conv3_2': <tf.Tensor 'Relu_5:0' shape=(1, 75, 100, 256) dtype=float32>, 'conv3_3': <tf.Tensor 'Relu_6:0' shape=(1, 75, 100, 256) dtype=float32>, 'conv3_4': <tf.Tensor 'Relu_7:0' shape=(1, 75, 100, 256) dtype=float32>, 'avgpool3': <tf.Tensor 'AvgPool_2:0' shape=(1, 38, 50, 256) dtype=float32>, 'conv4_1': <tf.Tensor 'Relu_8:0' shape=(1, 38, 50, 512) dtype=float32>, 'conv4_2': <tf.Tensor 'Relu_9:0' shape=(1, 38, 50, 512) dtype=float32>, 'conv4_3': <tf.Tensor 'Relu_10:0' shape=(1, 38, 50, 512) dtype=float32>, 'conv4_4': <tf.Tensor 'Relu_11:0' shape=(1, 38, 50, 512) dtype=float32>, 'avgpool4': <tf.Tensor 'AvgPool_3:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_1': <tf.Tensor 'Relu_12:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_2': <tf.Tensor 'Relu_13:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_3': <tf.Tensor 'Relu_14:0' shape=(1, 19, 25, 512) dtype=float32>, 'conv5_4': <tf.Tensor 'Relu_15:0' shape=(1, 19, 25, 512) dtype=float32>, 'avgpool5': <tf.Tensor 'AvgPool_4:0' shape=(1, 10, 13, 512) dtype=float32>}
该模型存储在一个python字典中,其中每个变量名都是键,相应的值是一个包含该变量值的张量,要通过此网络运行图像,只需将图像提供给模型,在tensorflow中,可以使用tf.assign函数来实现。
model["input"].assign(image)
这将图片作为输入给模型,在此之后,如果想要访问某个特定层的激活,比如4_2,
sess.run(model["conv4_2"])
神经风格转换
步骤:
- 构建内容损失函数J_{content}(C,G)
- 构建风格损失函数J_{style}(S,G)
- 把它放在一起得到 J ( G ) = α J c o n t e n t ( C , G ) + β J ( s t y l e ) ( S , G ) J(G)=\alpha J_{content}(C,G)+\beta J_{(style)}(S,G) J(G)=αJcontent(C,G)+βJ(style)(S,G)
计算内容损失
content_image = scipy.misc.imread("images/louvre.jpg")
imshow(content_image)
如何确保生成的图像G与图像C的内容匹配
浅层的一个卷积网络往往检测到较低层次的特征,如边缘和简单的纹理,更深层的往往检测更高层次的特征,如更复杂的纹理以及对象分类等。
希望生成的图像G具有与输入图像C相似的内容,假设我们选择了一些层的激活来表示图像的内容,在实践中,如果你在网络中间选择一个层–既不太浅也不太深,你会得到最好的视觉结果。
假设你选择了一个特殊的隐藏层,将图像C作为已经训练好的VGG网络的输入,然后进行前向传播,让
a
(
C
)
a^{(C)}
a(C)成为你选择的层中的隐藏层激活,激活值为
n
H
,
n
W
,
n
C
n_H,n_W,n_C
nH,nW,nC的张量,然后用图像G重复这个过程:将G设置为输入数据,并进行前向传播,让
a
(
G
)
a^{(G)}
a(G)成为相应的隐层激活,将把内容成本函数定义为:
J
c
o
n
t
e
n
t
(
C
,
G
)
=
1
4
×
n
H
×
n
W
×
n
C
∑
所
有
条
目
(
a
(
C
)
−
a
(
G
)
)
2
J_{content}(C,G)=\frac{1}{4 \times n_H \times n_W \times n_C}\sum_{所有条目}(a^{(C)}-a^{(G)})^2
Jcontent(C,G)=4×nH×nW×nC1所有条目∑(a(C)−a(G))2
步骤:
- 从a_G中获取维度信息:
- 从张量X中获取维度信息,可以使用:X.get_shape().as_list()
- 将a_C与a_G如上图一样降维:
- 计算内容代价
def compute_content_cost(a_C,a_G):
"""
计算内容代价的函数
参数:
a_C - tensor类型,维度为(1,n_H,n_W,n_C),表示隐藏层中图像C的内容的激活值
a_G - tensor类型,维度为(1,n_H,n_W,n_C),表示隐藏层中图像G的内容的激活值
返回:
J_content - 实数,用上面的公式1计算的值。
"""
m,n_H,n_W,n_C = a_G.get_shape().as_list()
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.eval())
test.close()
结果:
6.7655935
计算风格损失
先看看风格图片:
style_image = scipy.misc.imread("images/monet_800600.jpg")
imshow(style_image)
风格矩阵
风格矩阵也被叫做gram矩阵, G i j = v i T v j = n p . d o t ( v i , v j ) G_{ij}=v_i^Tv_j=np.dot(v_i,v_j) Gij=viTvj=np.dot(vi,vj),换句话说, G i j G_{ij} Gij比较了 V i V_i Vi和 V j V_j Vj的相似度,如果非常相似,则 G i j G_{ij} Gij会非常大。
def gram_matrix(A):
"""
参数:
A - 维度(n_C,n_H*n_W)
返回:
GA - 维度(n_C,n_C)
"""
GA = tf.matmul(A,A,transpose_b = True)
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.eval())
test.close()
结果:
[[ 6.422305 -4.429122 -2.096682]
[-4.429122 19.465837 19.563871]
[-2.096682 19.563871 20.686462]]
风格代价值
def compute_layer_style_cost(a_S,a_G):
"""
参数:
a_S - tensor类型,维度为(1,n_H,n_W,n_C),表示隐藏层中图像S的内容的激活值
a_G - tensor类型,维度为(1,n_H,n_W,n_C),表示隐藏层中图像G的内容的激活值
返回:
J_style_layer - 实数
"""
m,n_H,n_W,n_C = a_G.get_shape().as_list()
a_S = tf.transpose(tf.reshape(a_S,[n_H*n_W,n_C]))
a_G = tf.transpose(tf.reshape(a_G,[n_H*n_W,n_C]))
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(GS,GG)))
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.eval())
test.close()
结果:
9.190278
风格权重
到目前为止你已经从一层捕捉了风格,我们将得到更好的结果如果我们合成不同层的风格代价
STYLE_LAYERS = [
('conv1_1',0.2),
('conv2_1',0.2),
('conv3_1',0.2),
('conv4_1',0.2),
('conv5_1',0.2)
]
测试:
def compute_style_cost(model,STYLE_LAYERS):
"""
对一些所选的层计算整体风格代价值
参数:
model - 我们的tensorflow模型
STYLE_LAYERS - 一个python字典,包含:
- 我们将要抽取的层的名字
- 每层的系数
返回:
J_style - 实数,风格代价值
"""
J_style = 0
for layer_name,coeff in STYLE_LAYERS:
out = model[layer_name]
a_S = sess.run(out)
a_G = out
J_style_layer = compute_layer_style_cost(a_S,a_G)
J_style += coeff*J_style_layer
return J_style
计算总的代价值
def total_cost(J_content,J_style,alpha=10,beta=40):
"""
计算总体代价值
参数:
J_content - 内容图片的代价函数值
J_style - 风格图片的代价函数值
返回:
J - 生成图片的代价函数值
"""
J = alpha*J_content+beta*J_style
return J
测试:
tf.reset_default_graph()
with tf.Session() as test:
tf.set_random_seed(3)
J_content = np.random.randn()
J_style = np.random.randn()
J = total_cost(J_content,J_style)
print(J)
test.close()
结果:
7.777902537135693
整合模型
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])
model = nst_utils.load_vgg_model("pretrained-model/imagenet-vgg-verydeep-19.mat")
def model_nn(sess, input_image, num_iterations = 200):
# Initialize global variables (you need to run the session on the initializer)
### START CODE HERE ### (1 line)
sess.run(tf.global_variables_initializer())
### END CODE HERE ###
# Run the noisy input image (initial generated image) through the model. Use assign().
### START CODE HERE ### (1 line)
sess.run(model["input"].assign(input_image))
### END CODE HERE ###
for i in range(num_iterations):
# Run the session on the train_step to minimize the total cost
### START CODE HERE ### (1 line)
sess.run(train_step)
### END CODE HERE ###
# Compute the generated image by running the session on the current model['input']
### START CODE HERE ### (1 line)
generated_image = sess.run(model["input"])
### END CODE HERE ###
# Print every 20 iteration.
if i%20 == 0:
Jt, Jc, Js = sess.run([J, J_content, J_style])
print("Iteration " + str(i) + " :")
print("total cost = " + str(Jt))
print("content cost = " + str(Jc))
print("style cost = " + str(Js))
# save current generated image in the "/output" directory
save_image("output/" + str(i) + ".png", generated_image)
# save last generated image
save_image('output/generated_image.jpg', generated_image)
return generated_image
测试:
model_nn(sess,generated_image)
结果:
Iteration 0 :
total cost = 5050363000.0
content cost = 7877.6846
style cost = 126257096.0
Iteration 20 :
total cost = 943313150.0
content cost = 15187.001
style cost = 23579032.0
Iteration 40 :
total cost = 484989150.0
content cost = 16786.635
style cost = 12120532.0
Iteration 60 :
total cost = 312592860.0
content cost = 17466.723
style cost = 7810455.5
Iteration 80 :
total cost = 228137340.0
content cost = 17715.082
style cost = 5699005.0
Iteration 100 :
total cost = 180674600.0
content cost = 17899.586
style cost = 4512390.0
Iteration 120 :
total cost = 149986480.0
content cost = 18028.668
style cost = 3745154.8
Iteration 140 :
total cost = 127735700.0
content cost = 18187.12
style cost = 3188845.5
Iteration 160 :
total cost = 110698030.0
content cost = 18348.55
style cost = 2762863.5
Iteration 180 :
total cost = 97329440.0
content cost = 18492.033
style cost = 2428613.0
array([[[[ -47.661247 , -61.453 , 48.561188 ],
[ -26.120216 , -40.544724 , 26.910763 ],
[ -41.77308 , -28.989634 , 11.353765 ],
...,
[ -26.790583 , -9.504678 , 14.301171 ],
[ -30.215399 , -2.8596127 , 24.095652 ],
[ -42.33818 , -4.012277 , 49.331726 ]],
[[ -61.118427 , -51.75015 , 24.926647 ],
[ -33.086437 , -31.065102 , -1.5538673 ],
[ -27.143728 , -30.632084 , 15.239879 ],
...,
[ -26.743525 , -5.237683 , 25.955444 ],
[ -21.374044 , -16.847229 , 14.027954 ],
[ -40.673164 , -5.9413986 , 9.634444 ]],
[[ -52.304245 , -51.58418 , 13.43408 ],
[ -37.243927 , -41.448124 , -6.3468766 ],
[ -34.209667 , -25.22445 , 7.3981633 ],
...,
[ -10.557919 , -37.255943 , 12.633695 ],
[ -12.206678 , -20.861652 , 17.274206 ],
[ -22.465885 , -18.398954 , 14.384765 ]],
...,
[[ -48.962467 , -54.37402 , -37.02361 ],
[ -98.60941 , -77.45595 , -268.76196 ],
[ -76.595856 , -72.34538 , -142.87724 ],
...,
[ -70.069145 , -70.115875 , -28.93214 ],
[ -79.74132 , -87.88023 , -22.402292 ],
[ 1.5839 , -39.094883 , 24.227371 ]],
[[ -0.35573652, -74.69128 , 14.360605 ],
[-174.65245 , -102.71579 , -30.584835 ],
[ 5.712225 , -71.335175 , -20.298792 ],
...,
[ -95.97943 , -84.18833 , -47.554455 ],
[-102.51056 , -102.616905 , -59.301193 ],
[ -65.34055 , -95.03536 , 2.391805 ]],
[[ 50.225525 , -21.05201 , 52.79938 ],
[ 31.640673 , -84.51524 , 26.54396 ],
[ 29.929949 , -40.645756 , 17.317034 ],
...,
[ -99.936295 , -108.1952 , -17.179901 ],
[-117.799355 , -144.73357 , -27.593529 ],
[ -25.0283 , -104.69802 , 21.219383 ]]]], dtype=float32)