1 Star 0 Fork 0

唐万强 / Advanced-Deep-Learning-with-Keras

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
cyclegan-7.1.1.py 21.20 KB
一键复制 编辑 原始数据 按行查看 历史
Rowel Atienza 提交于 2019-12-19 08:42 . code migration to tf.keras
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620
"""Builds and trains a CycleGAN
CycleGAN is a cross-domain GAN. Like other GANs, it can be trained
in unsupervised manner.
CycleGAN is made of two generators (G & F) and two discriminators.
Each generator is a U-Network. The discriminator is a
typical decoder network with the option to use PatchGAN structure.
There are 2 datasets: x = source, y = target.
The forward-cycle solves x'= F(y') = F(G(x)) where y' is
the predicted output in y-domain and x' is the reconstructed input.
The target discriminator determines if y' is fake/real.
The objective of the forward-cycle generator G is to learn
how to trick the target discriminator into believing that y'
is real.
The backward-cycle improves the performance of CycleGAN by doing
the opposite of forward cycle. It learns how to solve
y' = G(x') = G(F(y)) where x' is the predicted output in the
x-domain. The source discriminator determines if x' is fake/real.
The objective of the backward-cycle generator F is to learn
how to trick the target discriminator into believing that x'
is real.
References:
[1]Zhu, Jun-Yan, et al. "Unpaired Image-to-Image Translation Using
Cycle-Consistent Adversarial Networks." 2017 IEEE International
Conference on Computer Vision (ICCV). IEEE, 2017.
[2]Ronneberger, Olaf, Philipp Fischer, and Thomas Brox. "U-net:
Convolutional networks for biomedical image segmentation."
International Conference on Medical image computing and
computer-assisted intervention. Springer, Cham, 2015.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from tensorflow.keras.layers import Activation, Dense, Input
from tensorflow.keras.layers import Conv2D, Flatten
from tensorflow.keras.layers import Conv2DTranspose
from tensorflow.keras.layers import LeakyReLU
from tensorflow.keras.layers import concatenate
from tensorflow.keras.optimizers import RMSprop
from tensorflow.keras.models import Model
from tensorflow.keras.models import load_model
# from keras_contrib.layers.normalization import InstanceNormalization
# from keras_contrib.layers.normalization.instancenormalization import InstanceNormalization
# install: pip install tensorflow-addons
from tensorflow_addons.layers import InstanceNormalization
import numpy as np
import argparse
import cifar10_utils
import mnist_svhn_utils
import other_utils
import datetime
def encoder_layer(inputs,
filters=16,
kernel_size=3,
strides=2,
activation='relu',
instance_norm=True):
"""Builds a generic encoder layer made of Conv2D-IN-LeakyReLU
IN is optional, LeakyReLU may be replaced by ReLU
"""
conv = Conv2D(filters=filters,
kernel_size=kernel_size,
strides=strides,
padding='same')
x = inputs
if instance_norm:
x = InstanceNormalization()(x)
if activation == 'relu':
x = Activation('relu')(x)
else:
x = LeakyReLU(alpha=0.2)(x)
x = conv(x)
return x
def decoder_layer(inputs,
paired_inputs,
filters=16,
kernel_size=3,
strides=2,
activation='relu',
instance_norm=True):
"""Builds a generic decoder layer made of Conv2D-IN-LeakyReLU
IN is optional, LeakyReLU may be replaced by ReLU
Arguments: (partial)
inputs (tensor): the decoder layer input
paired_inputs (tensor): the encoder layer output
provided by U-Net skip connection &
concatenated to inputs.
"""
conv = Conv2DTranspose(filters=filters,
kernel_size=kernel_size,
strides=strides,
padding='same')
x = inputs
if instance_norm:
x = InstanceNormalization()(x)
if activation == 'relu':
x = Activation('relu')(x)
else:
x = LeakyReLU(alpha=0.2)(x)
x = conv(x)
x = concatenate([x, paired_inputs])
return x
def build_generator(input_shape,
output_shape=None,
kernel_size=3,
name=None):
"""The generator is a U-Network made of a 4-layer encoder
and a 4-layer decoder. Layer n-i is connected to layer i.
Arguments:
input_shape (tuple): input shape
output_shape (tuple): output shape
kernel_size (int): kernel size of encoder & decoder layers
name (string): name assigned to generator model
Returns:
generator (Model):
"""
inputs = Input(shape=input_shape)
channels = int(output_shape[-1])
e1 = encoder_layer(inputs,
32,
kernel_size=kernel_size,
activation='leaky_relu',
strides=1)
e2 = encoder_layer(e1,
64,
activation='leaky_relu',
kernel_size=kernel_size)
e3 = encoder_layer(e2,
128,
activation='leaky_relu',
kernel_size=kernel_size)
e4 = encoder_layer(e3,
256,
activation='leaky_relu',
kernel_size=kernel_size)
d1 = decoder_layer(e4,
e3,
128,
kernel_size=kernel_size)
d2 = decoder_layer(d1,
e2,
64,
kernel_size=kernel_size)
d3 = decoder_layer(d2,
e1,
32,
kernel_size=kernel_size)
outputs = Conv2DTranspose(channels,
kernel_size=kernel_size,
strides=1,
activation='sigmoid',
padding='same')(d3)
generator = Model(inputs, outputs, name=name)
return generator
def build_discriminator(input_shape,
kernel_size=3,
patchgan=True,
name=None):
"""The discriminator is a 4-layer encoder that outputs either
a 1-dim or a n x n-dim patch of probability that input is real
Arguments:
input_shape (tuple): input shape
kernel_size (int): kernel size of decoder layers
patchgan (bool): whether the output is a patch
or just a 1-dim
name (string): name assigned to discriminator model
Returns:
discriminator (Model):
"""
inputs = Input(shape=input_shape)
x = encoder_layer(inputs,
32,
kernel_size=kernel_size,
activation='leaky_relu',
instance_norm=False)
x = encoder_layer(x,
64,
kernel_size=kernel_size,
activation='leaky_relu',
instance_norm=False)
x = encoder_layer(x,
128,
kernel_size=kernel_size,
activation='leaky_relu',
instance_norm=False)
x = encoder_layer(x,
256,
kernel_size=kernel_size,
strides=1,
activation='leaky_relu',
instance_norm=False)
# if patchgan=True use nxn-dim output of probability
# else use 1-dim output of probability
if patchgan:
x = LeakyReLU(alpha=0.2)(x)
outputs = Conv2D(1,
kernel_size=kernel_size,
strides=2,
padding='same')(x)
else:
x = Flatten()(x)
x = Dense(1)(x)
outputs = Activation('linear')(x)
discriminator = Model(inputs, outputs, name=name)
return discriminator
def train_cyclegan(models,
data,
params,
test_params,
test_generator):
""" Trains the CycleGAN.
1) Train the target discriminator
2) Train the source discriminator
3) Train the forward and backward cyles of
adversarial networks
Arguments:
models (Models): Source/Target Discriminator/Generator,
Adversarial Model
data (tuple): source and target training data
params (tuple): network parameters
test_params (tuple): test parameters
test_generator (function): used for generating
predicted target and source images
"""
# the models
g_source, g_target, d_source, d_target, adv = models
# network parameters
batch_size, train_steps, patch, model_name = params
# train dataset
source_data, target_data, test_source_data, test_target_data\
= data
titles, dirs = test_params
# the generator image is saved every 2000 steps
save_interval = 2000
target_size = target_data.shape[0]
source_size = source_data.shape[0]
# whether to use patchgan or not
if patch > 1:
d_patch = (patch, patch, 1)
valid = np.ones((batch_size,) + d_patch)
fake = np.zeros((batch_size,) + d_patch)
else:
valid = np.ones([batch_size, 1])
fake = np.zeros([batch_size, 1])
valid_fake = np.concatenate((valid, fake))
start_time = datetime.datetime.now()
for step in range(train_steps):
# sample a batch of real target data
rand_indexes = np.random.randint(0,
target_size,
size=batch_size)
real_target = target_data[rand_indexes]
# sample a batch of real source data
rand_indexes = np.random.randint(0,
source_size,
size=batch_size)
real_source = source_data[rand_indexes]
# generate a batch of fake target data fr real source data
fake_target = g_target.predict(real_source)
# combine real and fake into one batch
x = np.concatenate((real_target, fake_target))
# train the target discriminator using fake/real data
metrics = d_target.train_on_batch(x, valid_fake)
log = "%d: [d_target loss: %f]" % (step, metrics[0])
# generate a batch of fake source data fr real target data
fake_source = g_source.predict(real_target)
x = np.concatenate((real_source, fake_source))
# train the source discriminator using fake/real data
metrics = d_source.train_on_batch(x, valid_fake)
log = "%s [d_source loss: %f]" % (log, metrics[0])
# train the adversarial network using forward and backward
# cycles. the generated fake source and target
# data attempts to trick the discriminators
x = [real_source, real_target]
y = [valid, valid, real_source, real_target]
metrics = adv.train_on_batch(x, y)
elapsed_time = datetime.datetime.now() - start_time
fmt = "%s [adv loss: %f] [time: %s]"
log = fmt % (log, metrics[0], elapsed_time)
print(log)
if (step + 1) % save_interval == 0:
test_generator((g_source, g_target),
(test_source_data, test_target_data),
step=step+1,
titles=titles,
dirs=dirs,
show=False)
# save the models after training the generators
g_source.save(model_name + "-g_source.h5")
g_target.save(model_name + "-g_target.h5")
def build_cyclegan(shapes,
source_name='source',
target_name='target',
kernel_size=3,
patchgan=False,
identity=False
):
"""Build the CycleGAN
1) Build target and source discriminators
2) Build target and source generators
3) Build the adversarial network
Arguments:
shapes (tuple): source and target shapes
source_name (string): string to be appended on dis/gen models
target_name (string): string to be appended on dis/gen models
kernel_size (int): kernel size for the encoder/decoder
or dis/gen models
patchgan (bool): whether to use patchgan on discriminator
identity (bool): whether to use identity loss
Returns:
(list): 2 generator, 2 discriminator,
and 1 adversarial models
"""
source_shape, target_shape = shapes
lr = 2e-4
decay = 6e-8
gt_name = "gen_" + target_name
gs_name = "gen_" + source_name
dt_name = "dis_" + target_name
ds_name = "dis_" + source_name
# build target and source generators
g_target = build_generator(source_shape,
target_shape,
kernel_size=kernel_size,
name=gt_name)
g_source = build_generator(target_shape,
source_shape,
kernel_size=kernel_size,
name=gs_name)
print('---- TARGET GENERATOR ----')
g_target.summary()
print('---- SOURCE GENERATOR ----')
g_source.summary()
# build target and source discriminators
d_target = build_discriminator(target_shape,
patchgan=patchgan,
kernel_size=kernel_size,
name=dt_name)
d_source = build_discriminator(source_shape,
patchgan=patchgan,
kernel_size=kernel_size,
name=ds_name)
print('---- TARGET DISCRIMINATOR ----')
d_target.summary()
print('---- SOURCE DISCRIMINATOR ----')
d_source.summary()
optimizer = RMSprop(lr=lr, decay=decay)
d_target.compile(loss='mse',
optimizer=optimizer,
metrics=['accuracy'])
d_source.compile(loss='mse',
optimizer=optimizer,
metrics=['accuracy'])
d_target.trainable = False
d_source.trainable = False
# build the computational graph for the adversarial model
# forward cycle network and target discriminator
source_input = Input(shape=source_shape)
fake_target = g_target(source_input)
preal_target = d_target(fake_target)
reco_source = g_source(fake_target)
# backward cycle network and source discriminator
target_input = Input(shape=target_shape)
fake_source = g_source(target_input)
preal_source = d_source(fake_source)
reco_target = g_target(fake_source)
# if we use identity loss, add 2 extra loss terms
# and outputs
if identity:
iden_source = g_source(source_input)
iden_target = g_target(target_input)
loss = ['mse', 'mse', 'mae', 'mae', 'mae', 'mae']
loss_weights = [1., 1., 10., 10., 0.5, 0.5]
inputs = [source_input, target_input]
outputs = [preal_source,
preal_target,
reco_source,
reco_target,
iden_source,
iden_target]
else:
loss = ['mse', 'mse', 'mae', 'mae']
loss_weights = [1., 1., 10., 10.]
inputs = [source_input, target_input]
outputs = [preal_source,
preal_target,
reco_source,
reco_target]
# build adversarial model
adv = Model(inputs, outputs, name='adversarial')
optimizer = RMSprop(lr=lr*0.5, decay=decay*0.5)
adv.compile(loss=loss,
loss_weights=loss_weights,
optimizer=optimizer,
metrics=['accuracy'])
print('---- ADVERSARIAL NETWORK ----')
adv.summary()
return g_source, g_target, d_source, d_target, adv
def graycifar10_cross_colorcifar10(g_models=None):
"""Build and train a CycleGAN that can do
grayscale <--> color cifar10 images
"""
model_name = 'cyclegan_cifar10'
batch_size = 32
train_steps = 100000
patchgan = True
kernel_size = 3
postfix = ('%dp' % kernel_size) \
if patchgan else ('%d' % kernel_size)
data, shapes = cifar10_utils.load_data()
source_data, _, test_source_data, test_target_data = data
titles = ('CIFAR10 predicted source images.',
'CIFAR10 predicted target images.',
'CIFAR10 reconstructed source images.',
'CIFAR10 reconstructed target images.')
dirs = ('cifar10_source-%s' % postfix, \
'cifar10_target-%s' % postfix)
# generate predicted target(color) and source(gray) images
if g_models is not None:
g_source, g_target = g_models
other_utils.test_generator((g_source, g_target),
(test_source_data, \
test_target_data),
step=0,
titles=titles,
dirs=dirs,
show=True)
return
# build the cyclegan for cifar10 colorization
models = build_cyclegan(shapes,
"gray-%s" % postfix,
"color-%s" % postfix,
kernel_size=kernel_size,
patchgan=patchgan)
# patch size is divided by 2^n since we downscaled the input
# in the discriminator by 2^n (ie. we use strides=2 n times)
patch = int(source_data.shape[1] / 2**4) if patchgan else 1
params = (batch_size, train_steps, patch, model_name)
test_params = (titles, dirs)
# train the cyclegan
train_cyclegan(models,
data,
params,
test_params,
other_utils.test_generator)
def mnist_cross_svhn(g_models=None):
"""Build and train a CycleGAN that can do mnist <--> svhn
"""
model_name = 'cyclegan_mnist_svhn'
batch_size = 32
train_steps = 100000
patchgan = True
kernel_size = 5
postfix = ('%dp' % kernel_size) \
if patchgan else ('%d' % kernel_size)
data, shapes = mnist_svhn_utils.load_data()
source_data, _, test_source_data, test_target_data = data
titles = ('MNIST predicted source images.',
'SVHN predicted target images.',
'MNIST reconstructed source images.',
'SVHN reconstructed target images.')
dirs = ('mnist_source-%s' \
% postfix, 'svhn_target-%s' % postfix)
# generate predicted target(svhn) and source(mnist) images
if g_models is not None:
g_source, g_target = g_models
other_utils.test_generator((g_source, g_target),
(test_source_data, \
test_target_data),
step=0,
titles=titles,
dirs=dirs,
show=True)
return
# build the cyclegan for mnist cross svhn
models = build_cyclegan(shapes,
"mnist-%s" % postfix,
"svhn-%s" % postfix,
kernel_size=kernel_size,
patchgan=patchgan)
# patch size is divided by 2^n since we downscaled the input
# in the discriminator by 2^n (ie. we use strides=2 n times)
patch = int(source_data.shape[1] / 2**4) if patchgan else 1
params = (batch_size, train_steps, patch, model_name)
test_params = (titles, dirs)
# train the cyclegan
train_cyclegan(models,
data,
params,
test_params,
other_utils.test_generator)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
help_ = "Load cifar10 source generator h5 model"
parser.add_argument("--cifar10_g_source", help=help_)
help_ = "Load cifar10 target generator h5 model"
parser.add_argument("--cifar10_g_target", help=help_)
help_ = "Load mnist_svhn source generator h5 model"
parser.add_argument("--mnist_svhn_g_source", help=help_)
help_ = "Load mnist_svhn target generator h5 model"
parser.add_argument("--mnist_svhn_g_target", help=help_)
help_ = "Train cifar10 colorization"
parser.add_argument("-c",
"--cifar10",
action='store_true',
help=help_)
help_ = "Train mnist-svhn cross domain cyclegan"
parser.add_argument("-m",
"--mnist-svhn",
action='store_true',
help=help_)
args = parser.parse_args()
# load pre-trained cifar10 source & target generators
if args.cifar10_g_source:
g_source = load_model(args.cifar10_g_source)
if args.cifar10_g_target:
g_target = load_model(args.cifar10_g_target)
g_models = (g_source, g_target)
graycifar10_cross_colorcifar10(g_models)
# load pre-trained mnist-svhn source & target generators
elif args.mnist_svhn_g_source:
g_source = load_model(args.mnist_svhn_g_source)
if args.mnist_svhn_g_target:
g_target = load_model(args.mnist_svhn_g_target)
g_models = (g_source, g_target)
mnist_cross_svhn(g_models)
# train a cifar10 CycleGAN
elif args.cifar10:
graycifar10_cross_colorcifar10()
# train a mnist-svhn CycleGAN
else:
mnist_cross_svhn()
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/tang_wan_qiang/Advanced-Deep-Learning-with-Keras.git
git@gitee.com:tang_wan_qiang/Advanced-Deep-Learning-with-Keras.git
tang_wan_qiang
Advanced-Deep-Learning-with-Keras
Advanced-Deep-Learning-with-Keras
master

搜索帮助

344bd9b3 5694891 D2dac590 5694891