«Это 17-й день моего участия в ноябрьском испытании обновлений. Подробную информацию об этом событии см.:Вызов последнего обновления 2021 г."
Принцип ИнфоГАН
Первоначальный GAN мог выдавать значимые выходные данные, но недостатком было то, что его свойства нельзя было контролировать. Например, генератор не может быть явно предложен для создания лица знаменитости, темноволосой, светлокожей, кареглазой и улыбающейся. Основная причина этого заключается в том, что используемый 100-мерный вектор шума включает в себя все существенные свойства выходного сигнала генератора.
Если исходный GAN можно изменить так, чтобы представление было разделено на объединенные и разделенные интерпретируемые векторы скрытого кодирования, то генератору можно указать, что синтезировать. Слияние и разделение кодировок можно выразить следующим образом:
GAN с разделенными представлениями также можно оптимизировать так же, как и обычные GAN. Выход генератора можно представить в виде:
кодированиесодержит два элемента,представляет собой комбинированное представление,Представляет отдельное закодированное представление. Чтобы усилить разделение кодировок, InfoGAN предлагает функцию регуляризации для исходной функции потерь, которая объединяет скрытое кодированиеиМаксимально увеличить взаимную информацию между:
Регулятор заставляет генератор учитывать скрытые кодировки. В области теории информации скрытое кодированиеиВзаимная информация между ними определяется как:
вэто скрытый кодэнтропия иэто выход генератораЗаднийУсловная энтропия . Максимизация взаимной информации означает, что при создании сгенерированного выводаМинимизируйте или уменьшите неопределенность в базовом кодировании.
Но из-за предполагаемогоТребуется апостериорное распределение, так что трудно оценить.
Обходной путь - использовать вспомогательный дистрибутивАпостериорная вероятность оценивается для оценки нижней границы взаимной информации, которая оценивается как:
В InfoGAN предполагается, чтоявляется константой. Следовательно, максимизация взаимной информации — это вопрос максимизации ожиданий. Генератор должен быть уверен, что он произвел выходные данные с определенными свойствами. Это желаемое максимальное значение равно нулю. Следовательно, максимальное значение нижней границы взаимной информации равно. В InfoGAN дискретные скрытые кодыможет быть представлен softmax. Ожидается отрицательная потеря categorical_crossentropy в tf.keras.
Ожидается, что для непрерывного кодирования 1DиДвойной интеграл из-за ожидания того, что выборки поступают как из распределения разделительного кодирования, так и из распределения генератора. Один из способов оценить ожидаемое значение — предположить, что выборка является хорошей мерой непрерывных данных. Таким образом, потери оцениваются как.
Чтобы завершить сеть InfoGAN, должен бытьреализация. Для простоты сеть Q является вспомогательной сетью, присоединенной к дискриминатору.
Функция потери дискриминатора:
Функция потерь генератора:
вположительная константа
Реализация ИнфоГАН
Применительно к набору данных MNIST InfoGAN может изучать отдельные дискретные и непрерывные кодировки для изменения выходных свойств генератора. Например, подобно CGAN и ACGAN, дискретные кодировки в виде 10-мерных однократных меток будут использоваться для указания генерируемых чисел. Однако можно добавить два последовательных кода: один для управления углом наклона письма, а другой — для регулировки ширины штриха. Кодировки меньшего размера зарезервированы для представления всех остальных свойств:
Строитель
def generator(inputs,image_size,activation='sigmoid',labels=None,codes=None):
image_resize = image_size // 4
kernel_size = 5
layer_filters = [128,64,32,1]
inputs = [inputs,labels] + codes
x = keras.layers.concatenate(inputs,axis=1)
x = keras.layers.Dense(image_resize*image_resize*layer_filters[0])(x)
x = keras.layers.Reshape((image_resize,image_resize,layer_filters[0]))(x)
for filters in layer_filters:
if filters > layer_filters[-2]:
strides = 2
else:
strides = 1
x = keras.layers.BatchNormalization()(x)
x = keras.layers.Activation('relu')(x)
x = keras.layers.Conv2DTranspose(filters=filters,
kernel_size=kernel_size,
strides=strides,
padding='same')(x)
if activation is not None:
x = keras.layers.Activation(activation)(x)
return keras.Model(inputs,x,name='generator')
Дискриминатор
def discriminator(inputs,activation='sigmoid',num_labels=None,num_codes=None):
kernel_size = 5
layer_filters = [32,64,128,256]
x = inputs
for filters in layer_filters:
if filters == layer_filters[-1]:
strides = 1
else:
strides = 2
x = keras.layers.LeakyReLU(0.2)(x)
x = keras.layers.Conv2D(filters=filters,
kernel_size=kernel_size,
strides=strides,
padding='same')(x)
x = keras.layers.Flatten()(x)
outputs = keras.layers.Dense(1)(x)
if activation is not None:
print(activation)
outputs = keras.layers.Activation(activation)(outputs)
if num_labels:
layer = keras.layers.Dense(layer_filters[-2])(x)
labels = keras.layers.Dense(num_labels)(layer)
labels = keras.layers.Activation('softmax',name='label')(labels)
# 1-dim continous Q of 1st c given x
code1 = keras.layers.Dense(1)(layer)
code1 = keras.layers.Activation('sigmoid',name='code1')(code1)
# 1-dim continous Q of 2nd c given x
code2 = keras.layers.Dense(1)(layer)
code2 = keras.layers.Activation('sigmoid',name='code2')(code2)
outputs = [outputs,labels,code1,code2]
return keras.Model(inputs,outputs,name='discriminator')
Построение модели
#mi_loss
def mi_loss(c,q_of_c_give_x):
"""mi_loss = -c * log(Q(c|x))
"""
return K.mean(-K.sum(K.log(q_of_c_give_x + K.epsilon()) * c,axis=1))
def build_and_train_models(latent_size=100):
"""Load the dataset, build InfoGAN models,
Call the InfoGAN train routine.
"""
(x_train,y_train),_ = keras.datasets.mnist.load_data()
image_size = x_train.shape[1]
x_train = np.reshape(x_train,[-1,image_size,image_size,1])
x_train = x_train.astype('float32') / 255.
num_labels = len(np.unique(y_train))
y_train = keras.utils.to_categorical(y_train)
#超参数
model_name = 'infogan_mnist'
batch_size = 64
train_steps = 40000
lr = 2e-4
decay = 6e-8
input_shape = (image_size,image_size,1)
label_shape = (num_labels,)
code_shape = (1,)
#discriminator model
inputs = keras.layers.Input(shape=input_shape,name='discriminator_input')
#discriminator with 4 outputs
discriminator_model = discriminator(inputs,num_labels=num_labels,num_codes=2)
optimizer = keras.optimizers.RMSprop(lr=lr,decay=decay)
loss = ['binary_crossentropy','categorical_crossentropy',mi_loss,mi_loss]
loss_weights = [1.0,1.0,0.5,0.5]
discriminator_model.compile(loss=loss,
loss_weights=loss_weights,
optimizer=optimizer,
metrics=['acc'])
discriminator_model.summary()
input_shape = (latent_size,)
inputs = keras.layers.Input(shape=input_shape,name='z_input')
labels = keras.layers.Input(shape=label_shape,name='labels')
code1 = keras.layers.Input(shape=code_shape,name='code1')
code2 = keras.layers.Input(shape=code_shape,name='code2')
generator_model = generator(inputs,image_size,labels=labels,codes=[code1,code2])
generator_model.summary()
optimizer = keras.optimizers.RMSprop(lr=lr*0.5,decay=decay*0.5)
discriminator_model.trainable = False
inputs = [inputs,labels,code1,code2]
adversarial_model = keras.Model(inputs,
discriminator_model(generator_model(inputs)),
name=model_name)
adversarial_model.compile(loss=loss,loss_weights=loss_weights,
optimizer=optimizer,
metrics=['acc'])
adversarial_model.summary()
models = (generator_model,discriminator_model,adversarial_model)
data = (x_train,y_train)
params = (batch_size,latent_size,train_steps,num_labels,model_name)
train(models,data,params)
обучение модели
def train(models,data,params):
generator,discriminator,adversarial = models
x_train,y_train = data
batch_size,latent_size,train_steps,num_labels,model_name = params
save_interval = 500
code_std = 0.5
noise_input = np.random.uniform(-1.0,1.,size=[16,latent_size])
noise_label = np.eye(num_labels)[np.arange(0,16) % num_labels]
noise_code1 = np.random.normal(scale=code_std,size=[16,1])
noise_code2 = np.random.normal(scale=code_std,size=[16,1])
train_size = x_train.shape[0]
for i in range(train_steps):
rand_indexes = np.random.randint(0,train_size,size=batch_size)
real_images = x_train[rand_indexes]
real_labels = y_train[rand_indexes]
#random codes for real images
real_code1 = np.random.normal(scale=code_std,size=[batch_size,1])
real_code2 = np.random.normal(scale=code_std,size=[batch_size,1])
#生成假图片,标签和编码
noise = np.random.uniform(-1.,1.,size=[batch_size,latent_size])
fake_labels = np.eye(num_labels)[np.random.choice(num_labels,batch_size)]
fake_code1 = np.random.normal(scale=code_std,size=[batch_size,1])
fake_code2 = np.random.normal(scale=code_std,size=[batch_size,1])
inputs = [noise,fake_labels,fake_code1,fake_code2]
fake_images = generator.predict(inputs)
x = np.concatenate((real_images,fake_images))
labels = np.concatenate((real_labels,fake_labels))
codes1 = np.concatenate((real_code1,fake_code1))
codes2 = np.concatenate((real_code2,fake_code2))
y = np.ones([2 * batch_size,1])
y[batch_size:,:] = 0
#train discriminator network
outputs = [y,labels,codes1,codes2]
# metrics = ['loss', 'activation_1_loss', 'label_loss',
# 'code1_loss', 'code2_loss', 'activation_1_acc',
# 'label_acc', 'code1_acc', 'code2_acc']
metrics = discriminator.train_on_batch(x, outputs)
fmt = "%d: [dis: %f, bce: %f, ce: %f, mi: %f, mi:%f, acc: %f]"
log = fmt % (i, metrics[0], metrics[1], metrics[2], metrics[3], metrics[4], metrics[6])
#train the adversarial network
noise = np.random.uniform(-1.,1.,size=[batch_size,latent_size])
fake_labels = np.eye(num_labels)[np.random.choice(num_labels,batch_size)]
fake_code1 = np.random.normal(scale=code_std,size=[batch_size,1])
fake_code2 = np.random.normal(scale=code_std,size=[batch_size,1])
y = np.ones([batch_size,1])
inputs = [noise,fake_labels,fake_code1,fake_code2]
outputs = [y,fake_labels,fake_code1,fake_code2]
metrics = adversarial.train_on_batch(inputs,outputs)
Показать результаты
steps = 500
steps = 16000
修改书写角度的分离编码