本次项目的文件
main.py主程序如下
导入必要的库和模块:
FaceAging
模块。定义 str2bool
函数:
创建命令行参数解析器:
argparse.ArgumentParser
创建解析器,设置命令行参数的相关信息,如是否训练、轮数、数据集名称等。主函数 main(_)
入口:
在 with tf.Session(config=config) as session
中:
FaceAging
模型实例,传入会话、训练模式标志、保存路径和数据集名称。判断是否训练模式:
如果不是训练模式:
在 __name__ == '__main__'
中执行程序:
import tensorflow as tf from FaceAging import FaceAging # 导入自定义的 FaceAging 模块 from os import environ import argparse # 设置环境变量,控制 TensorFlow 输出日志等级 environ['TF_CPP_MIN_LOG_LEVEL'] = '3' # 自定义一个函数用于将字符串转换为布尔值 def str2bool(v): if v.lower() in ('yes', 'true', 't', 'y', '1'): return True elif v.lower() in ('no', 'false', 'f', 'n', '0'): return False else: raise argparse.ArgumentTypeError('Boolean value expected.') # 创建命令行参数解析器 parser = argparse.ArgumentParser(description='CAAE') parser.add_argument('--is_train', type=str2bool, default=True, help='是否进行训练') parser.add_argument('--epoch', type=int, default=50, help='训练的轮数') parser.add_argument('--dataset', type=str, default='UTKFace', help='存储在./data目录中的训练数据集名称') parser.add_argument('--savedir', type=str, default='save', help='保存检查点、中间训练结果和摘要的目录') parser.add_argument('--testdir', type=str, default='None', help='测试图像所在的目录') parser.add_argument('--use_trained_model', type=str2bool, default=True, help='是否使用已有的模型进行训练') parser.add_argument('--use_init_model', type=str2bool, default=True, help='如果找不到已有模型,是否从初始模型开始训练') FLAGS = parser.parse_args() # 主函数入口 def main(_): # 打印设置参数 import pprint pprint.pprint(FLAGS) # 配置 TensorFlow 会话 config = tf.ConfigProto() config.gpu_options.allow_growth = True with tf.Session(config=config) as session: # 创建 FaceAging 模型实例 model = FaceAging( session, # TensorFlow 会话 is_training=FLAGS.is_train, # 是否为训练模式的标志 save_dir=FLAGS.savedir, # 保存检查点、样本和摘要的路径 dataset_name=FLAGS.dataset # 存储在 ./data 目录中的数据集名称 ) if FLAGS.is_train: print ('\n\t训练模式') if not FLAGS.use_trained_model: print ('\n\t预训练网络') model.train( num_epochs=10, # 训练轮数 use_trained_model=FLAGS.use_trained_model, use_init_model=FLAGS.use_init_model, weights=(0, 0, 0) ) print ('\n\t预训练完成!训练将开始。') model.train( num_epochs=FLAGS.epoch, # 训练轮数 use_trained_model=FLAGS.use_trained_model, use_init_model=FLAGS.use_init_model ) else: print ('\n\t测试模式') model.custom_test( testing_samples_dir=FLAGS.testdir + '/*jpg' ) if __name__ == '__main__': # 在主程序中执行命令行解析和执行主函数 tf.app.run()
2.FaceAging.py
主要流程
导入必要的库和模块:
定义 FaceAging
类:
train
方法:
encoder
方法:
generator
方法:
discriminator_z
和 discriminator_img
方法:
save_checkpoint
和 load_checkpoint
方法:
sample
和 test
方法:
custom_test
方法:
from __future__ import division
import os
import time
from glob import glob
import tensorflow as tf
import numpy as np
from scipy.io import savemat
from ops import *
class FaceAging(object):
def __init__(self,
session, # TensorFlow session
size_image=128, # size the input images
size_kernel=5, # size of the kernels in convolution and deconvolution
size_batch=100, # mini-batch size for training and testing, must be square of an integer
num_input_channels=3, # number of channels of input images
num_encoder_channels=64, # number of channels of the first conv layer of encoder
num_z_channels=50, # number of channels of the layer z (noise or code)
num_categories=10, # number of categories (age segments) in the training dataset
num_gen_channels=1024, # number of channels of the first deconv layer of generator
enable_tile_label=True, # enable to tile the label
tile_ratio=1.0, # ratio of the length between tiled label and z
is_training=True, # flag for training or testing mode
save_dir='./save', # path to save checkpoints, samples, and summary
dataset_name='UTKFace' # name of the dataset in the folder ./data
):
self.session = session
self.image_value_range = (-1, 1)
self.size_image = size_image
self.size_kernel = size_kernel
self.size_batch = size_batch
self.num_input_channels = num_input_channels
self.num_encoder_channels = num_encoder_channels
self.num_z_channels = num_z_channels
self.num_categories = num_categories
self.num_gen_channels = num_gen_channels
self.enable_tile_label = enable_tile_label
self.tile_ratio = tile_ratio
self.is_training = is_training
self.save_dir = save_dir
self.dataset_name = dataset_name
# ************************************* input to graph ********************************************************
self.input_image = tf.placeholder(
tf.float32,
[self.size_batch, self.size_image, self.size_image, self.num_input_channels],
name='input_images'
)
self.age = tf.placeholder(
tf.float32,
[self.size_batch, self.num_categories],
name='age_labels'
)
self.gender = tf.placeholder(
tf.float32,
[self.size_batch, 2],
name='gender_labels'
)
self.z_prior = tf.placeholder(
tf.float32,
[self.size_batch, self.num_z_channels],
name='z_prior'
)
# ************************************* build the graph *******************************************************
print ('\n\tBuilding graph ...')
# encoder: input image --> z
self.z = self.encoder(
image=self.input_image
)
# generator: z + label --> generated image
self.G = self.generator(
z=self.z,
y=self.age,
gender=self.gender,
enable_tile_label=self.enable_tile_label,
tile_ratio=self.tile_ratio
)
# discriminator on z
self.D_z, self.D_z_logits = self.discriminator_z(
z=self.z,
is_training=self.is_training
)
# discriminator on G
self.D_G, self.D_G_logits = self.discriminator_img(
image=self.G,
y=self.age,
gender=self.gender,
is_training=self.is_training
)
# discriminator on z_prior
self.D_z_prior, self.D_z_prior_logits = self.discriminator_z(
z=self.z_prior,
is_training=self.is_training,
reuse_variables=True
)
# discriminator on input image
self.D_input, self.D_input_logits = self.discriminator_img(
image=self.input_image,
y=self.age,
gender=self.gender,
is_training=self.is_training,
reuse_variables=True
)
# ************************************* loss functions *******************************************************
# loss function of encoder + generator
#self.EG_loss = tf.nn.l2_loss(self.input_image - self.G) / self.size_batch # L2 loss
self.EG_loss = tf.reduce_mean(tf.abs(self.input_image - self.G)) # L1 loss
# loss function of discriminator on z
self.D_z_loss_prior = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=self.D_z_prior_logits, labels=tf.ones_like(self.D_z_prior_logits))
)
self.D_z_loss_z = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=self.D_z_logits, labels=tf.zeros_like(self.D_z_logits))
)
self.E_z_loss = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=self.D_z_logits, labels=tf.ones_like(self.D_z_logits))
)
# loss function of discriminator on image
self.D_img_loss_input = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=self.D_input_logits, labels=tf.ones_like(self.D_input_logits))
)
self.D_img_loss_G = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=self.D_G_logits, labels=tf.zeros_like(self.D_G_logits))
)
self.G_img_loss = tf.reduce_mean(
tf.nn.sigmoid_cross_entropy_with_logits(logits=self.D_G_logits, labels=tf.ones_like(self.D_G_logits))
)
# total variation to smooth the generated image
tv_y_size = self.size_image
tv_x_size = self.size_image
self.tv_loss = (
(tf.nn.l2_loss(self.G[:, 1:, :, :] - self.G[:, :self.size_image - 1, :, :]) / tv_y_size) +
(tf.nn.l2_loss(self.G[:, :, 1:, :] - self.G[:, :, :self.size_image - 1, :]) / tv_x_size)) / self.size_batch
# *********************************** trainable variables ****************************************************
trainable_variables = tf.trainable_variables()
# variables of encoder
self.E_variables = [var for var in trainable_variables if 'E_' in var.name]
# variables of generator
self.G_variables = [var for var in trainable_variables if 'G_' in var.name]
# variables of discriminator on z
self.D_z_variables = [var for var in trainable_variables if 'D_z_' in var.name]
# variables of discriminator on image
self.D_img_variables = [var for var in trainable_variables if 'D_img_' in var.name]
# ************************************* collect the summary ***************************************
self.z_summary = tf.summary.histogram('z', self.z)
self.z_prior_summary = tf.summary.histogram('z_prior', self.z_prior)
self.EG_loss_summary = tf.summary.scalar('EG_loss', self.EG_loss)
self.D_z_loss_z_summary = tf.summary.scalar('D_z_loss_z', self.D_z_loss_z)
self.D_z_loss_prior_summary = tf.summary.scalar('D_z_loss_prior', self.D_z_loss_prior)
self.E_z_loss_summary = tf.summary.scalar('E_z_loss', self.E_z_loss)
self.D_z_logits_summary = tf.summary.histogram('D_z_logits', self.D_z_logits)
self.D_z_prior_logits_summary = tf.summary.histogram('D_z_prior_logits', self.D_z_prior_logits)
self.D_img_loss_input_summary = tf.summary.scalar('D_img_loss_input', self.D_img_loss_input)
self.D_img_loss_G_summary = tf.summary.scalar('D_img_loss_G', self.D_img_loss_G)
self.G_img_loss_summary = tf.summary.scalar('G_img_loss', self.G_img_loss)
self.D_G_logits_summary = tf.summary.histogram('D_G_logits', self.D_G_logits)
self.D_input_logits_summary = tf.summary.histogram('D_input_logits', self.D_input_logits)
# for saving the graph and variables
self.saver = tf.train.Saver(max_to_keep=2)
def train(self,
num_epochs=200, # number of epochs
learning_rate=0.0002, # learning rate of optimizer
beta1=0.5, # parameter for Adam optimizer
decay_rate=1.0, # learning rate decay (0, 1], 1 means no decay
enable_shuffle=True, # enable shuffle of the dataset
use_trained_model=True, # use the saved checkpoint to initialize the network
use_init_model=True, # use the init model to initialize the network
weigts=(0.0001, 0, 0) # the weights of adversarial loss and TV loss
):
# *************************** load file names of images ******************************************************
file_names = glob(os.path.join('./data', self.dataset_name, '*.jpg'))
size_data = len(file_names)
np.random.seed(seed=2017)
if enable_shuffle:
np.random.shuffle(file_names)
# *********************************** optimizer **************************************************************
# over all, there are three loss functions, weights may differ from the paper because of different datasets
self.loss_EG = self.EG_loss + weigts[0] * self.G_img_loss + weigts[1] * self.E_z_loss + weigts[2] * self.tv_loss # slightly increase the params
self.loss_Dz = self.D_z_loss_prior + self.D_z_loss_z
self.loss_Di = self.D_img_loss_input + self.D_img_loss_G
# set learning rate decay
self.EG_global_step = tf.Variable(0, trainable=False, name='global_step')
EG_learning_rate = tf.train.exponential_decay(
learning_rate=learning_rate,
global_step=self.EG_global_step,
decay_steps=size_data / self.size_batch * 2,
decay_rate=decay_rate,
staircase=True
)
# optimizer for encoder + generator
with tf.variable_scope('opt', reuse=tf.AUTO_REUSE):
self.EG_optimizer = tf.train.AdamOptimizer(
learning_rate=EG_learning_rate,
beta1=beta1
).minimize(
loss=self.loss_EG,
global_step=self.EG_global_step,
var_list=self.E_variables + self.G_variables
)
# optimizer for discriminator on z
self.D_z_optimizer = tf.train.AdamOptimizer(
learning_rate=EG_learning_rate,
beta1=beta1
).minimize(
loss=self.loss_Dz,
var_list=self.D_z_variables
)
# optimizer for discriminator on image
self.D_img_optimizer = tf.train.AdamOptimizer(
learning_rate=EG_learning_rate,
beta1=beta1
).minimize(
loss=self.loss_Di,
var_list=self.D_img_variables
)
# *********************************** tensorboard *************************************************************
# for visualization (TensorBoard): $ tensorboard --logdir path/to/log-directory
self.EG_learning_rate_summary = tf.summary.scalar('EG_learning_rate', EG_learning_rate)
self.summary = tf.summary.merge([
self.z_summary, self.z_prior_summary,
self.D_z_loss_z_summary, self.D_z_loss_prior_summary,
self.D_z_logits_summary, self.D_z_prior_logits_summary,
self.EG_loss_summary, self.E_z_loss_summary,
self.D_img_loss_input_summary, self.D_img_loss_G_summary,
self.G_img_loss_summary, self.EG_learning_rate_summary,
self.D_G_logits_summary, self.D_input_logits_summary
])
self.writer = tf.summary.FileWriter(os.path.join(self.save_dir, 'summary'), self.session.graph)
# ************* get some random samples as testing data to visualize the learning process *********************
sample_files = file_names[0:self.size_batch]
file_names[0:self.size_batch] = []
sample = [load_image(
image_path=sample_file,
image_size=self.size_image,
image_value_range=self.image_value_range,
is_gray=(self.num_input_channels == 1),
) for sample_file in sample_files]
if self.num_input_channels == 1:
sample_images = np.array(sample).astype(np.float32)[:, :, :, None]
else:
sample_images = np.array(sample).astype(np.float32)
sample_label_age = np.ones(
shape=(len(sample_files), self.num_categories),
dtype=np.float32
) * self.image_value_range[0]
sample_label_gender = np.ones(
shape=(len(sample_files), 2),
dtype=np.float32
) * self.image_value_range[0]
for i, label in enumerate(sample_files):
label = int(str(sample_files[i]).split('/')[-1].split('_')[0])
if 0 <= label <= 5:
label = 0
elif 6 <= label <= 10:
label = 1
elif 11 <= label <= 15:
label = 2
elif 16 <= label <= 20:
label = 3
elif 21 <= label <= 30:
label = 4
elif 31 <= label <= 40:
label = 5
elif 41 <= label <= 50:
label = 6
elif 51 <= label <= 60:
label = 7
elif 61 <= label <= 70:
label = 8
else:
label = 9
sample_label_age[i, label] = self.image_value_range[-1]
gender = int(str(sample_files[i]).split('/')[-1].split('_')[1])
sample_label_gender[i, gender] = self.image_value_range[-1]
# ******************************************* training *******************************************************
# initialize the graph
tf.global_variables_initializer().run()
# load check point
if use_trained_model:
if self.load_checkpoint():
print("\tSUCCESS ^_^")
else:
print("\tFAILED >__
3.data,一共23708张照片
from __future__ import division
import os
import time
from glob import glob
import tensorflow as tf
import numpy as np
from scipy.io import savemat
from ops import *
#https://mbd.pub/o/bread/ZJ2UmJpp