TensorFlow.org'da görüntüleyin | Google Colab'da çalıştırın | Kaynağı GitHub'da görüntüleyin | Not defterini indir |
Bu not defteri, MNIST veri kümesi üzerinde bir Değişken Otomatik Kodlayıcının (VAE) ( 1 , 2 ) nasıl eğitileceğini gösterir. Bir VAE, yüksek boyutlu girdi verilerini alan ve daha küçük bir temsile sıkıştıran bir model olan otomatik kodlayıcının olasılıksal bir yorumudur. Girişi gizli bir vektöre eşleyen geleneksel bir otomatik kodlayıcının aksine, bir VAE, giriş verilerini bir Gauss'un ortalaması ve varyansı gibi bir olasılık dağılımının parametrelerine eşler. Bu yaklaşım, görüntü üretimi için yararlı olan sürekli, yapılandırılmış bir gizli alan üretir.
Kurmak
tutucu35 l10n-yerpip install tensorflow-probability
# to generate gifs
pip install imageio
pip install git+https://github.com/tensorflow/docs
from IPython import display
import glob
import imageio
import matplotlib.pyplot as plt
import numpy as np
import PIL
import tensorflow as tf
import tensorflow_probability as tfp
import time
MNIST veri kümesini yükleyin
Her MNIST görüntüsü orijinal olarak her biri 0-255 arasında olan ve bir pikselin yoğunluğunu temsil eden 784 tamsayıdan oluşan bir vektördür. Modelimizde her pikseli bir Bernoulli dağılımıyla modelleyin ve veri kümesini statik olarak ikili hale getirin.
(train_images, _), (test_images, _) = tf.keras.datasets.mnist.load_data()
Downloading data from https://storage.googleapis.com/tensorflow/tf-keras-datasets/mnist.npz 11493376/11490434 [==============================] - 0s 0us/step 11501568/11490434 [==============================] - 0s 0us/stepyer tutucu38 l10n-yer
def preprocess_images(images):
images = images.reshape((images.shape[0], 28, 28, 1)) / 255.
return np.where(images > .5, 1.0, 0.0).astype('float32')
train_images = preprocess_images(train_images)
test_images = preprocess_images(test_images)
train_size = 60000
batch_size = 32
test_size = 10000
Verileri gruplamak ve karıştırmak için tf.data kullanın
train_dataset = (tf.data.Dataset.from_tensor_slices(train_images)
.shuffle(train_size).batch(batch_size))
test_dataset = (tf.data.Dataset.from_tensor_slices(test_images)
.shuffle(test_size).batch(batch_size))
tf.keras.Sequential ile kodlayıcı ve kod çözücü ağlarını tanımlayın
Bu VAE örneğinde, kodlayıcı ve kod çözücü ağları için iki küçük ConvNet kullanın. Literatürde bu ağlara sırasıyla çıkarım/tanıma ve üretici modeller de denilmektedir. Uygulamayı basitleştirmek için tf.keras.Sequential
kullanın. Aşağıdaki açıklamalarda l10n-yer \(x\) ve \(z\) sırasıyla gözlemi ve gizli değişkeni göstersin.
Enkoder ağı
Bu, girdi olarak bir gözlem alan ve gizli temsil \(z\)koşullu dağılımını belirtmek için bir dizi parametre çıktısı veren yaklaşık sonsal dağılımı \(q(z|x)\)tanımlar. Bu örnekte, dağılımı basitçe bir köşegen Gauss olarak modelleyin ve ağ, çarpanlara ayrılmış bir Gauss'un ortalama ve log-varyans parametrelerini verir. Sayısal kararlılık için doğrudan varyans yerine çıkış log-varyansı.
dekoder ağı
Bu, gizli bir \(z\) örneğini girdi olarak alan ve gözlemin koşullu dağılımı için parametreleri veren \(p(x|z)\)gözleminin koşullu dağılımını tanımlar. \(p(z)\) önceki gizli dağılımı bir Gauss birimi olarak modelleyin.
yeniden parametrelendirme hilesi
Eğitim sırasında kod çözücü için bir \(z\) örneği oluşturmak için, bir giriş gözlemi \(x\)verildiğinde kodlayıcı tarafından çıktı olarak verilen parametreler tarafından tanımlanan gizli dağılımdan örnek alabilirsiniz. Ancak, bu örnekleme işlemi bir darboğaz yaratır çünkü geri yayılım rastgele bir düğümden geçemez.
Bunu ele almak için bir yeniden parametrelendirme hilesi kullanın. Örneğimizde, kod çözücü parametrelerini ve başka bir \(\epsilon\) parametresini aşağıdaki gibi kullanarak \(z\) yaklaşık olarak hesaplarsınız:
\[z = \mu + \sigma \odot \epsilon\]
burada \(\mu\) tutucu13 ve \(\sigma\) sırasıyla bir Gauss dağılımının ortalamasını ve standart sapmasını temsil eder. Dekoder çıkışından elde edilebilirler. \(\epsilon\) , \(z\)stokastikliğini korumak için kullanılan rastgele bir gürültü olarak düşünülebilir. Standart bir normal dağılımdan \(\epsilon\) oluşturun.
Gizli değişken \(z\) şimdi, \(\mu\), \(\sigma\) ve \(\epsilon\)bir fonksiyonu tarafından üretilir; bu, modelin kodlayıcıdaki gradyanları sırasıyla \(\mu\) ve \(\sigma\) aracılığıyla geri yaymasını sağlarken, stokastikliği korurken \(\epsilon\).
Ağ mimarisi
Kodlayıcı ağı için, iki evrişimli katman ve ardından tam bağlantılı bir katman kullanın. Kod çözücü ağında, tam bağlı bir katman ve ardından üç evrişim devrik katman (bazı bağlamlarda evrişimsiz katmanlar olarak da bilinir) kullanarak bu mimariyi yansıtın. VAE'leri eğitirken toplu normalleştirmeyi kullanmaktan kaçınmanın yaygın bir uygulama olduğunu unutmayın, çünkü mini yığınların kullanılmasından kaynaklanan ek stokastiklik, örneklemeden elde edilen stokastikliğin üzerine kararsızlığı ağırlaştırabilir.
class CVAE(tf.keras.Model):
"""Convolutional variational autoencoder."""
def __init__(self, latent_dim):
super(CVAE, self).__init__()
self.latent_dim = latent_dim
self.encoder = tf.keras.Sequential(
[
tf.keras.layers.InputLayer(input_shape=(28, 28, 1)),
tf.keras.layers.Conv2D(
filters=32, kernel_size=3, strides=(2, 2), activation='relu'),
tf.keras.layers.Conv2D(
filters=64, kernel_size=3, strides=(2, 2), activation='relu'),
tf.keras.layers.Flatten(),
# No activation
tf.keras.layers.Dense(latent_dim + latent_dim),
]
)
self.decoder = tf.keras.Sequential(
[
tf.keras.layers.InputLayer(input_shape=(latent_dim,)),
tf.keras.layers.Dense(units=7*7*32, activation=tf.nn.relu),
tf.keras.layers.Reshape(target_shape=(7, 7, 32)),
tf.keras.layers.Conv2DTranspose(
filters=64, kernel_size=3, strides=2, padding='same',
activation='relu'),
tf.keras.layers.Conv2DTranspose(
filters=32, kernel_size=3, strides=2, padding='same',
activation='relu'),
# No activation
tf.keras.layers.Conv2DTranspose(
filters=1, kernel_size=3, strides=1, padding='same'),
]
)
@tf.function
def sample(self, eps=None):
if eps is None:
eps = tf.random.normal(shape=(100, self.latent_dim))
return self.decode(eps, apply_sigmoid=True)
def encode(self, x):
mean, logvar = tf.split(self.encoder(x), num_or_size_splits=2, axis=1)
return mean, logvar
def reparameterize(self, mean, logvar):
eps = tf.random.normal(shape=mean.shape)
return eps * tf.exp(logvar * .5) + mean
def decode(self, z, apply_sigmoid=False):
logits = self.decoder(z)
if apply_sigmoid:
probs = tf.sigmoid(logits)
return probs
return logits
Kayıp fonksiyonunu ve optimize ediciyi tanımlayın
VAE'ler, marjinal log-olasılığı üzerinde kanıt alt sınırını (ELBO) maksimize ederek eğitim alır:
\[\log p(x) \ge \text{ELBO} = \mathbb{E}_{q(z|x)}\left[\log \frac{p(x, z)}{q(z|x)}\right].\]
Uygulamada, bu beklentinin tek örnek Monte Carlo tahminini optimize edin:
\[\log p(x| z) + \log p(z) - \log q(z|x),\]
burada \(z\) , \(q(z|x)\)örneklenir.
optimizer = tf.keras.optimizers.Adam(1e-4)
def log_normal_pdf(sample, mean, logvar, raxis=1):
log2pi = tf.math.log(2. * np.pi)
return tf.reduce_sum(
-.5 * ((sample - mean) ** 2. * tf.exp(-logvar) + logvar + log2pi),
axis=raxis)
def compute_loss(model, x):
mean, logvar = model.encode(x)
z = model.reparameterize(mean, logvar)
x_logit = model.decode(z)
cross_ent = tf.nn.sigmoid_cross_entropy_with_logits(logits=x_logit, labels=x)
logpx_z = -tf.reduce_sum(cross_ent, axis=[1, 2, 3])
logpz = log_normal_pdf(z, 0., 0.)
logqz_x = log_normal_pdf(z, mean, logvar)
return -tf.reduce_mean(logpx_z + logpz - logqz_x)
@tf.function
def train_step(model, x, optimizer):
"""Executes one training step and returns the loss.
This function computes the loss and gradients, and uses the latter to
update the model's parameters.
"""
with tf.GradientTape() as tape:
loss = compute_loss(model, x)
gradients = tape.gradient(loss, model.trainable_variables)
optimizer.apply_gradients(zip(gradients, model.trainable_variables))
Eğitim
- Veri kümesini yineleyerek başlayın
- Her yineleme sırasında, yaklaşık posterior \(q(z|x)\)bir dizi ortalama ve log-varyans parametresini elde etmek için görüntüyü kodlayıcıya iletin29
- daha sonra \(q(z|x)\)örneğe yeniden parametrelendirme hilesini uygulayın
- Son olarak, üretici dağılımın \(p(x|z)\)tutucu31 logitlerini elde etmek için yeniden parametrelendirilmiş örnekleri kod çözücüye iletin.
- Not: Keras tarafından yüklenen veri setini eğitim setinde 60k veri noktası ve test setinde 10k veri noktası ile kullandığınız için, test setinde elde ettiğimiz ELBO, Larochelle'nin MNIST dinamik ikilileştirmesini kullanan literatürde bildirilen sonuçlardan biraz daha yüksektir.
Görüntü oluşturma
- Eğitimden sonra, bazı görüntüler oluşturmanın zamanı geldi
- Gauss ön dağılımı \(p(z)\)biriminden bir dizi gizli vektör örnekleyerek başlayın.
- Üreteç daha sonra \(z\) tutucu33 gizli örneğini gözlem logitlerine dönüştürerek bir l10n-yer \(p(x|z)\)dağılımı verir.
- Burada, Bernoulli dağılımlarının olasılıklarını çizin
epochs = 10
# set the dimensionality of the latent space to a plane for visualization later
latent_dim = 2
num_examples_to_generate = 16
# keeping the random vector constant for generation (prediction) so
# it will be easier to see the improvement.
random_vector_for_generation = tf.random.normal(
shape=[num_examples_to_generate, latent_dim])
model = CVAE(latent_dim)
def generate_and_save_images(model, epoch, test_sample):
mean, logvar = model.encode(test_sample)
z = model.reparameterize(mean, logvar)
predictions = model.sample(z)
fig = plt.figure(figsize=(4, 4))
for i in range(predictions.shape[0]):
plt.subplot(4, 4, i + 1)
plt.imshow(predictions[i, :, :, 0], cmap='gray')
plt.axis('off')
# tight_layout minimizes the overlap between 2 sub-plots
plt.savefig('image_at_epoch_{:04d}.png'.format(epoch))
plt.show()
# Pick a sample of the test set for generating output images
assert batch_size >= num_examples_to_generate
for test_batch in test_dataset.take(1):
test_sample = test_batch[0:num_examples_to_generate, :, :, :]
generate_and_save_images(model, 0, test_sample)
for epoch in range(1, epochs + 1):
start_time = time.time()
for train_x in train_dataset:
train_step(model, train_x, optimizer)
end_time = time.time()
loss = tf.keras.metrics.Mean()
for test_x in test_dataset:
loss(compute_loss(model, test_x))
elbo = -loss.result()
display.clear_output(wait=False)
print('Epoch: {}, Test set ELBO: {}, time elapse for current epoch: {}'
.format(epoch, elbo, end_time - start_time))
generate_and_save_images(model, epoch, test_sample)
Epoch: 10, Test set ELBO: -156.4964141845703, time elapse for current epoch: 4.854437351226807
Son eğitim döneminden oluşturulan bir görüntüyü göster
def display_image(epoch_no):
return PIL.Image.open('image_at_epoch_{:04d}.png'.format(epoch_no))
plt.imshow(display_image(epoch))
plt.axis('off') # Display images
-yer tutucu50 l10n-yer(-0.5, 287.5, 287.5, -0.5)
Kaydedilen tüm görüntülerin animasyonlu bir GIF'ini görüntüleyin
anim_file = 'cvae.gif'
with imageio.get_writer(anim_file, mode='I') as writer:
filenames = glob.glob('image*.png')
filenames = sorted(filenames)
for filename in filenames:
image = imageio.imread(filename)
writer.append_data(image)
image = imageio.imread(filename)
writer.append_data(image)
tutucu52 l10n-yerimport tensorflow_docs.vis.embed as embed
embed.embed_file(anim_file)
Gizli alandan bir 2B basamak manifoldu görüntüleyin
Aşağıdaki kodu çalıştırmak, her bir rakamın 2B gizli uzayda diğerine dönüşmesiyle farklı rakam sınıflarının sürekli bir dağılımını gösterecektir. Gizli alan için standart bir normal dağılım oluşturmak üzere TensorFlow Olasılığını kullanın.
def plot_latent_images(model, n, digit_size=28):
"""Plots n x n digit images decoded from the latent space."""
norm = tfp.distributions.Normal(0, 1)
grid_x = norm.quantile(np.linspace(0.05, 0.95, n))
grid_y = norm.quantile(np.linspace(0.05, 0.95, n))
image_width = digit_size*n
image_height = image_width
image = np.zeros((image_height, image_width))
for i, yi in enumerate(grid_x):
for j, xi in enumerate(grid_y):
z = np.array([[xi, yi]])
x_decoded = model.sample(z)
digit = tf.reshape(x_decoded[0], (digit_size, digit_size))
image[i * digit_size: (i + 1) * digit_size,
j * digit_size: (j + 1) * digit_size] = digit.numpy()
plt.figure(figsize=(10, 10))
plt.imshow(image, cmap='Greys_r')
plt.axis('Off')
plt.show()
tutucu54 l10n-yerplot_latent_images(model, 20)
Sonraki adımlar
Bu öğretici, TensorFlow kullanarak bir evrişimsel değişken otomatik kodlayıcının nasıl uygulanacağını göstermiştir.
Bir sonraki adım olarak, ağ boyutunu artırarak model çıktısını iyileştirmeyi deneyebilirsiniz. Örneğin, Conv2D
ve Conv2DTranspose
katmanlarının her biri için filter
parametrelerini 512'ye ayarlamayı deneyebilirsiniz. Son 2B gizli görüntü grafiğini oluşturmak için latent_dim
2'de tutmanız gerektiğini unutmayın. Ayrıca, eğitim süresi artacaktır. ağ boyutu arttıkça.
CIFAR-10 gibi farklı bir veri kümesi kullanarak bir VAE uygulamayı da deneyebilirsiniz.
VAE'ler birkaç farklı stilde ve değişen karmaşıklıkta uygulanabilir. Ek uygulamaları aşağıdaki kaynaklarda bulabilirsiniz:
- Varyasyonlu Otomatik Kodlayıcı (keras.io)
- "Özel katmanlar ve modeller yazma" kılavuzundan VAE örneği (tensorflow.org)
- TFP Olasılık Katmanları: Değişken Otomatik Kodlayıcı
VAE'lerin ayrıntıları hakkında daha fazla bilgi edinmek isterseniz, lütfen Varyasyonlu Otomatik Kodlayıcılara Giriş bölümüne bakın.