【实战】轻轻松松使⽤StyleGAN(三):基于ResNet50构造
StyleGAN的逆向。。。
在前⾯的⽂章⾥,我们可以利⽤StyleGAN创建令⼈惊讶的黄种⼈脸和专属于⾃⼰的⽼婆动漫头像,内容请参考:
并且,我们知道,可以对特征码进⾏混合与编辑,改变这些⼈脸和动漫头像的风格,内容请参考:
那么,能不能给出⼀个⽬标图像,使⽤神经⽹络⾃动提取出它的特征码呢?
如果可以,那么我们就可以⽅便地对这些图像进⾏编辑,创造出各种各样“酷炫”的风格⼈像。
这个⼯作可以分为两步:
(1)先利⽤StyleGAN⽣成的特征码和⽣成的⼈脸图像训练⼀个⽹络,把⼈脸图像作为输⼊,把特征码作为输出,理论上可以得到⼀个StyleGAN的逆向⽹络模型,如果训练成功的话,这个模型可以⾃动将⼈脸图像转换为特征码;
(2)利⽤真实⼈脸图像对上⾯得到的模型进⾏进⼀步训练和“微调”,使之能够⽤于真实⼈脸的特征码提取。
这篇⽂章先说明第⼀步的⼯作。
我们构造了⼀个如下图所⽰的神经⽹络,并计划对它进⾏训练。这个⽹络的输⼊为256x256的图⽚,输出为18x512的
dlatents(StyleGAN的中间数据层,即:w)
构建这个模型的代码如下,我们这⾥把这个模型叫做lotus_body():
# 定义StyleGAN的逆向⽹络模型lotus
# 下⾯的功能函数均使⽤keras原⽣函数构造
def lotus_body(x):
# input: (none, 256, 256, 3), output: (none, 8, 8,2048)
# 必须设定include_top=False, weights=None, 才能将输⼊设为256x256x3
# resnet输出C5,C5的shape是(none, 8, 8, 2048)
resnet = snet50.ResNet50(include_top=False, weights=None, input_tensor=x, input_shape=(256,256,3))
y = resnet.output
print('ResNet50 C5 shape : ', y.shape)
# output: (none, 8, 8, 144)
# 输出feature maps = filters = 144, 2D卷积输出的⾼和宽8 - 1 + 1 = 8
y = volutional.Conv2D(144, (1, 1), padding='same', activation='relu')(y)
# output: (none, 96, 96)
y = keras.layers.Reshape((96, 96))(y)
for i in range(3):
# output: (none, 96, 96)
汽车引擎盖能烤鱼# 输出feature maps = filters = 96, 1D卷积输出的长度96 - 1 + 1 = 96
y = keras.layers.local.LocallyConnected1D(96, 1)(y)
# output: (none, 96, 96),(2,1)代表将输⼊的第⼆个维度重拍到输出的第⼀个维度,⽽将输⼊的第⼀个维度重排到第⼆个维度
y = keras.layers.Permute((2, 1))(y)
# output:(none, 96, 96)
y = keras.layers.local.LocallyConnected1D(96, 1)(y)
# output: (none, 18, 512)
y = keras.layers.Reshape((18, 512))(y)
print('lotus body output shape : ', y.shape)
return y
为了训练这个模型,我们⽤StyleGAN⽣成了1200个dlatents以及它们所对应的⼈脸图⽚,并记录到⽂件中,训练时我们从⽂件中加载数据,并训练lotus模型。
由于lotus模型输出的不是⽬标的分类,⽽是18x512的张量,因此我们使⽤mean_squared_error作为损失函数loss,优化器(optimizer)使⽤的是adam,epochs = 503,batch_size = 6,最后训练得到的accuracy = 0.8933。
我们⽤StyleGAN⽣成了1280个dlatents以及它们所对应的⼈脸图⽚,使⽤mean_squared_error作为损失函数loss,优化器(optimizer)使⽤的是adam,epochs = 599,batch_size = 8,最后训练得到的accuracy = 0.9201。
完整的lotus⽹络模型训练及⽣成测试样⽚的源代码如下(带中⽂注释):
# -*- coding: UTF-8 -*-
import os,sys
import numpy as np
import scipy
from scipy import ndimage
import tensorflow as tf
import matplotlib.pyplot as plt
from tensorflow.keras.preprocessing import image
import keras
import pickle
import PIL.Image
import random
import dnnlib
import dnnlib.tflib as tflib
import config
import glob
# 设置已训练好的模型,⽤于StyleGAN正向⽣成⼈脸图⽚
Model = './cache/generator_yellow.pkl'
#Model = './cache/karras2019stylegan-ffhq-1024x1024.pkl'
#Model = './cache/2019-03-08-stylegan-animefaces-network-02051-021980.pkl'
synthesis_kwargs = dict(output_transform=dict(vert_images_to_uint8, nchw_to_nhwc=True), minibatch_size=8) _Gs_cache = dict()
# 设置图⽚⽂件的前缀
PREFIX = 'Person'
#PREFIX = 'Animation'
# 训练集和测试集中的图⽚数量
NUM_FIGURES = 1200
# 定义训练集和测试集的路径,这⾥创建了 train ,和 test ⽂件夹
train_path_face = './dataset/train/face/'
train_path_face_dlatents = './dataset/train/face/dlatents/'
test_path_face = './dataset/test/face/'
test_path_face_dlatents = './dataset/test/face/dlatents/'
# 加载StyleGAN已训练好的⽹络模型
海马小王子def load_Gs(model):
def load_Gs(model):
汽车凹坑修复
if model not in _Gs_cache:
model_file = glob.glob(Model)
if len(model_file) == 1:
model_file = open(model_file[0], "rb")
else:
raise Exception('Failed to find the model')
_G, _D, Gs = pickle.load(model_file)
# _G = Instantaneous snapshot of the generator. Mainly useful for resuming a previous training run.
# _D = Instantaneous snapshot of the discriminator. Mainly useful for resuming a previous training run.
# Gs = Long-term average of the generator. Yields higher-quality results than the instantaneous snapshot.
# Print network details.
Gs.print_layers()
_Gs_cache[model] = Gs
return _Gs_cache[model]
# ⽤StyleGAN⽣成图像,保存dlatents和图像到⽂件
def generate_dlatents_and_figures(Gs, w, h, num):
# ⽣成的latents,⼤⼩是512
for i in range(num):
# ⽣成latents.
SEED = i
rnd = np.random.RandomState(SEED)
latents = rnd.randn(1, Gs.input_shape[1])
# 按照StyleGAN的⽹络架构,从z变换到w
dlatents = Gsponents.mapping.run(latents, None)
# 保存dlatents到⽂件
save_name = PREFIX + '_' + str(SEED) + '.npy'
os.makedirs(train_path_face_dlatents, exist_ok=True)
save_path = os.path.join(train_path_face_dlatents, save_name)
np.save(save_path, dlatents)
os.makedirs(test_path_face_dlatents, exist_ok=True)
save_path = os.path.join(test_path_face_dlatents, save_name)
np.save(save_path, dlatents)
汽车贴纸# 从w⽣成图像
images = Gsponents.synthesis.run(dlatents, randomize_noise=False, **synthesis_kwargs)
# 保存图像到⽂件
save_name = PREFIX + '_' + str(SEED) + '.png'
os.makedirs(train_path_face, exist_ok=True)
save_path = os.path.join(train_path_face, save_name)
PIL.Image.fromarray(images[0], 'RGB').save(save_path)
os.makedirs(test_path_face, exist_ok=True)
save_path = os.path.join(test_path_face, save_name)
PIL.Image.fromarray(images[0], 'RGB').save(save_path)
# 准备Resnet50的训练集,把StyleGAN⽣成的图⽚作为输⼊,对应的dlatents作为输出的验证,训练Resnet50,⽣成StyleGAN的⼀个反向⽹络def DataSet():
# os.listdir(path) 是 python 中的函数,它会列出 path 下的所有⽂件名
os.makedirs(train_path_face, exist_ok=True)
imglist_train_face = os.listdir(train_path_face)
os.makedirs(train_path_face_dlatents, exist_ok=True)
os.makedirs(train_path_face_dlatents, exist_ok=True)
# 读取 /test/face 下的所有图⽚⽂件名
os.makedirs(test_path_face, exist_ok=True)
imglist_test_face = os.listdir(test_path_face)
os.makedirs(test_path_face_dlatents, exist_ok=True)
# 定义两个 numpy 对象,X_train 和 Y_train
# X_train 对象⽤来存放训练集的图⽚。每张图⽚都需要转换成 numpy 向量形式    # X_train 的 shape 是 (,256,256,3)
# resnet50 缺省的输⼊图⽚尺⼨是 (224,224) ,我们这⾥设置为(256,256)
# 3 是图⽚的通道数(rgb)
# Y_train ⽤来存放训练集中每张图⽚对应的dlatents
# Y_train 的 shape 是(,18,512),与StyleGAN的dlatents⼀致
X_train = np.empty((len(imglist_train_face), 256, 256, 3))
Y_train = np.empty((len(imglist_train_face), 18, 512))
# count 对象⽤来计数,每添加⼀张图⽚便加 1
count = 0
# 遍历 /train/face 下所有图⽚,即训练集下所有的图⽚
for img_name in imglist_train_face:
# 得到图⽚的路径
if dswith('png') or dswith('jpg'):
img_path = os.path.join(train_path_face, img_name)
# 通过 image.load_img() 函数读取对应的图⽚,并转换成⽬标⼤⼩
# image 是 tensorflow.keras.preprocessing 中的⼀个对象
img = image.load_img(img_path, target_size=(256, 256))
# 将图⽚转换成 numpy 数组,并除以 255 ,归⼀化
# 转换之后 img 的 shape 是(256,256,3)
img = image.img_to_array(img) / 255.0
# 将处理好的图⽚装进定义好的 X_train 对象中
X_train[count] = img
# 将对应的标签装进 Y_train 对象中
# 这⾥需要载⼊StyleGAN⽣成图⽚s时对应的dlatents
only_name = os.path.splitext(img_name)[0]
img_name = only_name + '.npy'
img_path = os.path.join(train_path_face_dlatents, img_name)
Y_train[count] = np.load(img_path)
count += 1
# 准备测试集的数据
X_test = np.empty((len(imglist_test_face), 256, 256, 3))
Y_test = np.empty((len(imglist_test_face), 18, 512))
count = 0
for img_name in imglist_test_face:
if dswith('png') or dswith('jpg'):
img_path = os.path.join(test_path_face, img_name)
img = image.load_img(img_path, target_size=(256, 256))
海马7x
img = image.img_to_array(img) / 255.0
X_test[count] = img
only_name = os.path.splitext(img_name)[0]
img_name = only_name + '.npy'
img_path = os.path.join(test_path_face_dlatents, img_name)
Y_test[count] = np.load(img_path)
count += 1
# 打乱训练集中的数据
index = [i for i in range(len(X_train))]
random.shuffle(index)
X_train = X_train[index]东风风行4s店
Y_train = Y_train[index]