Menu iconMenu iconGenerative Deep Learning Updated Edition
Generative Deep Learning Updated Edition

Chapter 2: Understanding Generative Models

2.4 Practical Exercises - Chapter 2: Understanding Generative Models

Exercise 1: Implement a Simple GAN

Task: Implement a simple GAN to generate new samples from the MNIST dataset. Use the example provided in section 2.2.1 and modify it to include an additional layer in both the generator and discriminator.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten, Dropout
from tensorflow.keras.models import Sequential
import numpy as np
import matplotlib.pyplot as plt

# Generator model
def build_generator():
    model = Sequential([
        Dense(256, input_dim=100),
        LeakyReLU(alpha=0.2),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dense(28 * 28, activation='tanh'),
        Reshape((28, 28, 1))
    ])
    return model

# Discriminator model
def build_discriminator():
    model = Sequential([
        Flatten(input_shape=(28, 28, 1)),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(256),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(1, activation='sigmoid')
    ])
    return model

# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')

# Training the GAN
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5  # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 64
epochs = 10000

for epoch in range(epochs):
    # Train discriminator
    idx = np.random.randint(0, x_train.shape[0], batch_size)
    real_images = x_train[idx]
    noise = np.random.normal(0, 1, (batch_size, 100))
    fake_images = generator.predict(noise)
    d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
    d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
    d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

    # Train generator
    noise = np.random.normal(0, 1, (batch_size, 100))
    g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))

    # Print progress
    if epoch % 1000 == 0:
        print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")

# Generate new samples
noise = np.random.normal(0, 1, (10, 100))
generated_images = generator.predict(noise)

# Plot generated images
fig, axs = plt.subplots(1, 10, figsize=(20, 2))
for i, img in enumerate(generated_images):
    axs[i].imshow(img.squeeze(), cmap='gray')
    axs[i].axis('off')
plt.show()

Exercise 2: Fine-Tune a Pre-trained GPT-4 Model

Task: Fine-tune a pre-trained GPT-4 model for a specific text generation task using the OpenAI API. Use a custom prompt and generate text based on that prompt.

Solution:

import openai

# Set your OpenAI API key
openai.api_key = 'your-api-key-here'

# Define the prompt for GPT-4
prompt = "In a futuristic city, the AI robots started to develop their own consciousness. One day,"

# Generate text using GPT-4
response = openai.Completion.create(
    engine="gpt-4",
    prompt=prompt,
    max_tokens=100,
    n=1,
    stop=None,
    temperature=0.7
)

# Extract the generated text
generated_text = response.choices[0].text.strip()
print(generated_text)

Exercise 3: Implement a Simple VAE

Task: Implement a simple VAE to generate new samples from the MNIST dataset. Use the example provided in section 2.2.2 and modify it to include additional layers in both the encoder and decoder.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten, Reshape, Lambda, Input, Conv2D, Conv2DTranspose
from tensorflow.keras.models import Model
from tensorflow.keras.losses import binary_crossentropy
from tensorflow.keras import backend as K
import numpy as np
import matplotlib.pyplot as plt

# Sampling function
def sampling(args):
    z_mean, z_log_var = args
    batch = tf.shape(z_mean)[0]
    dim = tf.shape(z_mean)[1]
    epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
    return z_mean + K.exp(0.5 * z_log_var) * epsilon

# Encoder model
input_img = Input(shape=(28, 28, 1))
x = Conv2D(32, 3, activation='relu', padding='same')(input_img)
x = Conv2D(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2D(128, 3, activation='relu', padding='same', strides=2)(x)
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
z_mean = Dense(2)(x)
z_log_var = Dense(2)(x)
z = Lambda(sampling, output_shape=(2,))([z_mean, z_log_var])
encoder = Model(input_img, z)

# Decoder model
decoder_input = Input(shape=(2,))
x = Dense(7*7*128, activation='relu')(decoder_input)
x = Reshape((7, 7, 128))(x)
x = Conv2DTranspose(128, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(32, 3, activation='relu', padding='same')(x)
output_img = Conv2DTranspose(1, 3, activation='sigmoid', padding='same')(x)
decoder = Model(decoder_input, output_img)

# VAE model
output_img = decoder(encoder(input_img))
vae = Model(input_img, output_img)

# VAE loss function
reconstruction_loss = binary_crossentropy(K.flatten(input_img), K.flatten(output_img))
reconstruction_loss *= 28 * 28
kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
kl_loss = K.sum(kl_loss, axis=-1)
kl_loss *= -0.5
vae_loss = K.mean(reconstruction_loss + kl_loss)
vae.add_loss(vae_loss)
vae.compile(optimizer='adam')

# Training the VAE
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) / 255.0) - 0.5
x_train = np.expand_dims(x_train, axis=-1)
vae.fit(x_train, epochs=50, batch_size=128, verbose=1)

# Generate new samples
z_sample = np.array([[0.0, 0.0]])
generated_image = decoder.predict(z_sample)

# Plot generated image
plt.imshow(generated_image[0].squeeze(), cmap='gray')
plt.axis('off')
plt.show()

Exercise 4: Apply Spectral Normalization to a Discriminator

Task: Implement spectral normalization in a simple discriminator model. Use the example provided in section 2.3.2 and ensure the discriminator is applied to the MNIST dataset.

Solution:

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# Define a simple discriminator with spectral normalization
class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.utils.spectral_norm(nn.Conv2d(1, 64, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.utils.spectral_norm(nn.Conv2d(64, 128, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Flatten(),
            nn.utils.spectral_norm(nn.Linear(128 * 7 * 7, 1))
        )

    def forward(self, x):
        return self.model(x)

# Load MNIST dataset
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,),

 (0.5,))
])
dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)

# Instantiate the discriminator
discriminator = Discriminator()
optimizer = optim.Adam(discriminator.parameters(), lr=0.0002)
criterion = nn.BCELoss()

# Training loop
num_epochs = 5
for epoch in range(num_epochs):
    for images, _ in dataloader:
        optimizer.zero_grad()
        labels = torch.ones(images.size(0), 1)
        outputs = discriminator(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# Generate fake data (for demonstration purposes)
noise = torch.randn(64, 1, 28, 28)
fake_images = noise

# Evaluate discriminator on fake data
with torch.no_grad():
    fake_outputs = discriminator(fake_images)
    print("Discriminator output on fake images:", fake_outputs[:5])

Exercise 5: Implement a 3D GAN for Voxel-Based Object Generation

Task: Implement a simple 3D GAN to generate voxel-based objects. Use the example provided in section 2.3.3 and visualize the generated 3D objects.

Solution:

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Define a simple 3D GAN for voxel-based object generation
class VoxelGenerator(nn.Module):
    def __init__(self):
        super(VoxelGenerator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(100, 128),
            nn.ReLU(inplace=True),
            nn.Linear(128, 256),
            nn.ReLU(inplace=True),
            nn.Linear(256, 512),
            nn.ReLU(inplace=True),
            nn.Linear(512, 32*32*32),
            nn.Tanh()
        )

    def forward(self, z):
        return self.model(z).view(-1, 32, 32, 32)

# Instantiate the generator
voxel_generator = VoxelGenerator()

# Generate random latent vectors
num_voxels = 5
latent_vectors = torch.randn(num_voxels, 100)

# Generate 3D voxel objects
generated_voxels = voxel_generator(latent_vectors)

# Visualize the generated 3D objects
fig = plt.figure(figsize=(15, 15))
for i in range(num_voxels):
    ax = fig.add_subplot(1, num_voxels, i+1, projection='3d')
    ax.voxels(generated_voxels[i].detach().numpy() > 0, edgecolor='k')
    ax.axis('off')

plt.show()

These practical exercises should help reinforce your understanding of the concepts covered in this chapter. By implementing these models and experimenting with different configurations, you'll gain hands-on experience with generative models and their practical applications. Keep practicing, and don't hesitate to explore further on your own!

2.4 Practical Exercises - Chapter 2: Understanding Generative Models

Exercise 1: Implement a Simple GAN

Task: Implement a simple GAN to generate new samples from the MNIST dataset. Use the example provided in section 2.2.1 and modify it to include an additional layer in both the generator and discriminator.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten, Dropout
from tensorflow.keras.models import Sequential
import numpy as np
import matplotlib.pyplot as plt

# Generator model
def build_generator():
    model = Sequential([
        Dense(256, input_dim=100),
        LeakyReLU(alpha=0.2),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dense(28 * 28, activation='tanh'),
        Reshape((28, 28, 1))
    ])
    return model

# Discriminator model
def build_discriminator():
    model = Sequential([
        Flatten(input_shape=(28, 28, 1)),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(256),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(1, activation='sigmoid')
    ])
    return model

# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')

# Training the GAN
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5  # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 64
epochs = 10000

for epoch in range(epochs):
    # Train discriminator
    idx = np.random.randint(0, x_train.shape[0], batch_size)
    real_images = x_train[idx]
    noise = np.random.normal(0, 1, (batch_size, 100))
    fake_images = generator.predict(noise)
    d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
    d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
    d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

    # Train generator
    noise = np.random.normal(0, 1, (batch_size, 100))
    g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))

    # Print progress
    if epoch % 1000 == 0:
        print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")

# Generate new samples
noise = np.random.normal(0, 1, (10, 100))
generated_images = generator.predict(noise)

# Plot generated images
fig, axs = plt.subplots(1, 10, figsize=(20, 2))
for i, img in enumerate(generated_images):
    axs[i].imshow(img.squeeze(), cmap='gray')
    axs[i].axis('off')
plt.show()

Exercise 2: Fine-Tune a Pre-trained GPT-4 Model

Task: Fine-tune a pre-trained GPT-4 model for a specific text generation task using the OpenAI API. Use a custom prompt and generate text based on that prompt.

Solution:

import openai

# Set your OpenAI API key
openai.api_key = 'your-api-key-here'

# Define the prompt for GPT-4
prompt = "In a futuristic city, the AI robots started to develop their own consciousness. One day,"

# Generate text using GPT-4
response = openai.Completion.create(
    engine="gpt-4",
    prompt=prompt,
    max_tokens=100,
    n=1,
    stop=None,
    temperature=0.7
)

# Extract the generated text
generated_text = response.choices[0].text.strip()
print(generated_text)

Exercise 3: Implement a Simple VAE

Task: Implement a simple VAE to generate new samples from the MNIST dataset. Use the example provided in section 2.2.2 and modify it to include additional layers in both the encoder and decoder.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten, Reshape, Lambda, Input, Conv2D, Conv2DTranspose
from tensorflow.keras.models import Model
from tensorflow.keras.losses import binary_crossentropy
from tensorflow.keras import backend as K
import numpy as np
import matplotlib.pyplot as plt

# Sampling function
def sampling(args):
    z_mean, z_log_var = args
    batch = tf.shape(z_mean)[0]
    dim = tf.shape(z_mean)[1]
    epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
    return z_mean + K.exp(0.5 * z_log_var) * epsilon

# Encoder model
input_img = Input(shape=(28, 28, 1))
x = Conv2D(32, 3, activation='relu', padding='same')(input_img)
x = Conv2D(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2D(128, 3, activation='relu', padding='same', strides=2)(x)
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
z_mean = Dense(2)(x)
z_log_var = Dense(2)(x)
z = Lambda(sampling, output_shape=(2,))([z_mean, z_log_var])
encoder = Model(input_img, z)

# Decoder model
decoder_input = Input(shape=(2,))
x = Dense(7*7*128, activation='relu')(decoder_input)
x = Reshape((7, 7, 128))(x)
x = Conv2DTranspose(128, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(32, 3, activation='relu', padding='same')(x)
output_img = Conv2DTranspose(1, 3, activation='sigmoid', padding='same')(x)
decoder = Model(decoder_input, output_img)

# VAE model
output_img = decoder(encoder(input_img))
vae = Model(input_img, output_img)

# VAE loss function
reconstruction_loss = binary_crossentropy(K.flatten(input_img), K.flatten(output_img))
reconstruction_loss *= 28 * 28
kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
kl_loss = K.sum(kl_loss, axis=-1)
kl_loss *= -0.5
vae_loss = K.mean(reconstruction_loss + kl_loss)
vae.add_loss(vae_loss)
vae.compile(optimizer='adam')

# Training the VAE
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) / 255.0) - 0.5
x_train = np.expand_dims(x_train, axis=-1)
vae.fit(x_train, epochs=50, batch_size=128, verbose=1)

# Generate new samples
z_sample = np.array([[0.0, 0.0]])
generated_image = decoder.predict(z_sample)

# Plot generated image
plt.imshow(generated_image[0].squeeze(), cmap='gray')
plt.axis('off')
plt.show()

Exercise 4: Apply Spectral Normalization to a Discriminator

Task: Implement spectral normalization in a simple discriminator model. Use the example provided in section 2.3.2 and ensure the discriminator is applied to the MNIST dataset.

Solution:

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# Define a simple discriminator with spectral normalization
class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.utils.spectral_norm(nn.Conv2d(1, 64, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.utils.spectral_norm(nn.Conv2d(64, 128, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Flatten(),
            nn.utils.spectral_norm(nn.Linear(128 * 7 * 7, 1))
        )

    def forward(self, x):
        return self.model(x)

# Load MNIST dataset
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,),

 (0.5,))
])
dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)

# Instantiate the discriminator
discriminator = Discriminator()
optimizer = optim.Adam(discriminator.parameters(), lr=0.0002)
criterion = nn.BCELoss()

# Training loop
num_epochs = 5
for epoch in range(num_epochs):
    for images, _ in dataloader:
        optimizer.zero_grad()
        labels = torch.ones(images.size(0), 1)
        outputs = discriminator(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# Generate fake data (for demonstration purposes)
noise = torch.randn(64, 1, 28, 28)
fake_images = noise

# Evaluate discriminator on fake data
with torch.no_grad():
    fake_outputs = discriminator(fake_images)
    print("Discriminator output on fake images:", fake_outputs[:5])

Exercise 5: Implement a 3D GAN for Voxel-Based Object Generation

Task: Implement a simple 3D GAN to generate voxel-based objects. Use the example provided in section 2.3.3 and visualize the generated 3D objects.

Solution:

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Define a simple 3D GAN for voxel-based object generation
class VoxelGenerator(nn.Module):
    def __init__(self):
        super(VoxelGenerator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(100, 128),
            nn.ReLU(inplace=True),
            nn.Linear(128, 256),
            nn.ReLU(inplace=True),
            nn.Linear(256, 512),
            nn.ReLU(inplace=True),
            nn.Linear(512, 32*32*32),
            nn.Tanh()
        )

    def forward(self, z):
        return self.model(z).view(-1, 32, 32, 32)

# Instantiate the generator
voxel_generator = VoxelGenerator()

# Generate random latent vectors
num_voxels = 5
latent_vectors = torch.randn(num_voxels, 100)

# Generate 3D voxel objects
generated_voxels = voxel_generator(latent_vectors)

# Visualize the generated 3D objects
fig = plt.figure(figsize=(15, 15))
for i in range(num_voxels):
    ax = fig.add_subplot(1, num_voxels, i+1, projection='3d')
    ax.voxels(generated_voxels[i].detach().numpy() > 0, edgecolor='k')
    ax.axis('off')

plt.show()

These practical exercises should help reinforce your understanding of the concepts covered in this chapter. By implementing these models and experimenting with different configurations, you'll gain hands-on experience with generative models and their practical applications. Keep practicing, and don't hesitate to explore further on your own!

2.4 Practical Exercises - Chapter 2: Understanding Generative Models

Exercise 1: Implement a Simple GAN

Task: Implement a simple GAN to generate new samples from the MNIST dataset. Use the example provided in section 2.2.1 and modify it to include an additional layer in both the generator and discriminator.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten, Dropout
from tensorflow.keras.models import Sequential
import numpy as np
import matplotlib.pyplot as plt

# Generator model
def build_generator():
    model = Sequential([
        Dense(256, input_dim=100),
        LeakyReLU(alpha=0.2),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dense(28 * 28, activation='tanh'),
        Reshape((28, 28, 1))
    ])
    return model

# Discriminator model
def build_discriminator():
    model = Sequential([
        Flatten(input_shape=(28, 28, 1)),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(256),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(1, activation='sigmoid')
    ])
    return model

# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')

# Training the GAN
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5  # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 64
epochs = 10000

for epoch in range(epochs):
    # Train discriminator
    idx = np.random.randint(0, x_train.shape[0], batch_size)
    real_images = x_train[idx]
    noise = np.random.normal(0, 1, (batch_size, 100))
    fake_images = generator.predict(noise)
    d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
    d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
    d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

    # Train generator
    noise = np.random.normal(0, 1, (batch_size, 100))
    g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))

    # Print progress
    if epoch % 1000 == 0:
        print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")

# Generate new samples
noise = np.random.normal(0, 1, (10, 100))
generated_images = generator.predict(noise)

# Plot generated images
fig, axs = plt.subplots(1, 10, figsize=(20, 2))
for i, img in enumerate(generated_images):
    axs[i].imshow(img.squeeze(), cmap='gray')
    axs[i].axis('off')
plt.show()

Exercise 2: Fine-Tune a Pre-trained GPT-4 Model

Task: Fine-tune a pre-trained GPT-4 model for a specific text generation task using the OpenAI API. Use a custom prompt and generate text based on that prompt.

Solution:

import openai

# Set your OpenAI API key
openai.api_key = 'your-api-key-here'

# Define the prompt for GPT-4
prompt = "In a futuristic city, the AI robots started to develop their own consciousness. One day,"

# Generate text using GPT-4
response = openai.Completion.create(
    engine="gpt-4",
    prompt=prompt,
    max_tokens=100,
    n=1,
    stop=None,
    temperature=0.7
)

# Extract the generated text
generated_text = response.choices[0].text.strip()
print(generated_text)

Exercise 3: Implement a Simple VAE

Task: Implement a simple VAE to generate new samples from the MNIST dataset. Use the example provided in section 2.2.2 and modify it to include additional layers in both the encoder and decoder.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten, Reshape, Lambda, Input, Conv2D, Conv2DTranspose
from tensorflow.keras.models import Model
from tensorflow.keras.losses import binary_crossentropy
from tensorflow.keras import backend as K
import numpy as np
import matplotlib.pyplot as plt

# Sampling function
def sampling(args):
    z_mean, z_log_var = args
    batch = tf.shape(z_mean)[0]
    dim = tf.shape(z_mean)[1]
    epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
    return z_mean + K.exp(0.5 * z_log_var) * epsilon

# Encoder model
input_img = Input(shape=(28, 28, 1))
x = Conv2D(32, 3, activation='relu', padding='same')(input_img)
x = Conv2D(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2D(128, 3, activation='relu', padding='same', strides=2)(x)
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
z_mean = Dense(2)(x)
z_log_var = Dense(2)(x)
z = Lambda(sampling, output_shape=(2,))([z_mean, z_log_var])
encoder = Model(input_img, z)

# Decoder model
decoder_input = Input(shape=(2,))
x = Dense(7*7*128, activation='relu')(decoder_input)
x = Reshape((7, 7, 128))(x)
x = Conv2DTranspose(128, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(32, 3, activation='relu', padding='same')(x)
output_img = Conv2DTranspose(1, 3, activation='sigmoid', padding='same')(x)
decoder = Model(decoder_input, output_img)

# VAE model
output_img = decoder(encoder(input_img))
vae = Model(input_img, output_img)

# VAE loss function
reconstruction_loss = binary_crossentropy(K.flatten(input_img), K.flatten(output_img))
reconstruction_loss *= 28 * 28
kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
kl_loss = K.sum(kl_loss, axis=-1)
kl_loss *= -0.5
vae_loss = K.mean(reconstruction_loss + kl_loss)
vae.add_loss(vae_loss)
vae.compile(optimizer='adam')

# Training the VAE
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) / 255.0) - 0.5
x_train = np.expand_dims(x_train, axis=-1)
vae.fit(x_train, epochs=50, batch_size=128, verbose=1)

# Generate new samples
z_sample = np.array([[0.0, 0.0]])
generated_image = decoder.predict(z_sample)

# Plot generated image
plt.imshow(generated_image[0].squeeze(), cmap='gray')
plt.axis('off')
plt.show()

Exercise 4: Apply Spectral Normalization to a Discriminator

Task: Implement spectral normalization in a simple discriminator model. Use the example provided in section 2.3.2 and ensure the discriminator is applied to the MNIST dataset.

Solution:

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# Define a simple discriminator with spectral normalization
class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.utils.spectral_norm(nn.Conv2d(1, 64, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.utils.spectral_norm(nn.Conv2d(64, 128, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Flatten(),
            nn.utils.spectral_norm(nn.Linear(128 * 7 * 7, 1))
        )

    def forward(self, x):
        return self.model(x)

# Load MNIST dataset
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,),

 (0.5,))
])
dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)

# Instantiate the discriminator
discriminator = Discriminator()
optimizer = optim.Adam(discriminator.parameters(), lr=0.0002)
criterion = nn.BCELoss()

# Training loop
num_epochs = 5
for epoch in range(num_epochs):
    for images, _ in dataloader:
        optimizer.zero_grad()
        labels = torch.ones(images.size(0), 1)
        outputs = discriminator(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# Generate fake data (for demonstration purposes)
noise = torch.randn(64, 1, 28, 28)
fake_images = noise

# Evaluate discriminator on fake data
with torch.no_grad():
    fake_outputs = discriminator(fake_images)
    print("Discriminator output on fake images:", fake_outputs[:5])

Exercise 5: Implement a 3D GAN for Voxel-Based Object Generation

Task: Implement a simple 3D GAN to generate voxel-based objects. Use the example provided in section 2.3.3 and visualize the generated 3D objects.

Solution:

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Define a simple 3D GAN for voxel-based object generation
class VoxelGenerator(nn.Module):
    def __init__(self):
        super(VoxelGenerator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(100, 128),
            nn.ReLU(inplace=True),
            nn.Linear(128, 256),
            nn.ReLU(inplace=True),
            nn.Linear(256, 512),
            nn.ReLU(inplace=True),
            nn.Linear(512, 32*32*32),
            nn.Tanh()
        )

    def forward(self, z):
        return self.model(z).view(-1, 32, 32, 32)

# Instantiate the generator
voxel_generator = VoxelGenerator()

# Generate random latent vectors
num_voxels = 5
latent_vectors = torch.randn(num_voxels, 100)

# Generate 3D voxel objects
generated_voxels = voxel_generator(latent_vectors)

# Visualize the generated 3D objects
fig = plt.figure(figsize=(15, 15))
for i in range(num_voxels):
    ax = fig.add_subplot(1, num_voxels, i+1, projection='3d')
    ax.voxels(generated_voxels[i].detach().numpy() > 0, edgecolor='k')
    ax.axis('off')

plt.show()

These practical exercises should help reinforce your understanding of the concepts covered in this chapter. By implementing these models and experimenting with different configurations, you'll gain hands-on experience with generative models and their practical applications. Keep practicing, and don't hesitate to explore further on your own!

2.4 Practical Exercises - Chapter 2: Understanding Generative Models

Exercise 1: Implement a Simple GAN

Task: Implement a simple GAN to generate new samples from the MNIST dataset. Use the example provided in section 2.2.1 and modify it to include an additional layer in both the generator and discriminator.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten, Dropout
from tensorflow.keras.models import Sequential
import numpy as np
import matplotlib.pyplot as plt

# Generator model
def build_generator():
    model = Sequential([
        Dense(256, input_dim=100),
        LeakyReLU(alpha=0.2),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dense(28 * 28, activation='tanh'),
        Reshape((28, 28, 1))
    ])
    return model

# Discriminator model
def build_discriminator():
    model = Sequential([
        Flatten(input_shape=(28, 28, 1)),
        Dense(1024),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(512),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(256),
        LeakyReLU(alpha=0.2),
        Dropout(0.3),
        Dense(1, activation='sigmoid')
    ])
    return model

# Build and compile the GAN
generator = build_generator()
discriminator = build_discriminator()
discriminator.compile(optimizer='adam', loss='binary_crossentropy', metrics=['accuracy'])

# GAN model
discriminator.trainable = False
gan_input = tf.keras.Input(shape=(100,))
gan_output = discriminator(generator(gan_input))
gan = tf.keras.Model(gan_input, gan_output)
gan.compile(optimizer='adam', loss='binary_crossentropy')

# Training the GAN
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) - 127.5) / 127.5  # Normalize to [-1, 1]
x_train = np.expand_dims(x_train, axis=-1)
batch_size = 64
epochs = 10000

for epoch in range(epochs):
    # Train discriminator
    idx = np.random.randint(0, x_train.shape[0], batch_size)
    real_images = x_train[idx]
    noise = np.random.normal(0, 1, (batch_size, 100))
    fake_images = generator.predict(noise)
    d_loss_real = discriminator.train_on_batch(real_images, np.ones((batch_size, 1)))
    d_loss_fake = discriminator.train_on_batch(fake_images, np.zeros((batch_size, 1)))
    d_loss = 0.5 * np.add(d_loss_real, d_loss_fake)

    # Train generator
    noise = np.random.normal(0, 1, (batch_size, 100))
    g_loss = gan.train_on_batch(noise, np.ones((batch_size, 1)))

    # Print progress
    if epoch % 1000 == 0:
        print(f"{epoch} [D loss: {d_loss[0]}, acc.: {d_loss[1] * 100}%] [G loss: {g_loss}]")

# Generate new samples
noise = np.random.normal(0, 1, (10, 100))
generated_images = generator.predict(noise)

# Plot generated images
fig, axs = plt.subplots(1, 10, figsize=(20, 2))
for i, img in enumerate(generated_images):
    axs[i].imshow(img.squeeze(), cmap='gray')
    axs[i].axis('off')
plt.show()

Exercise 2: Fine-Tune a Pre-trained GPT-4 Model

Task: Fine-tune a pre-trained GPT-4 model for a specific text generation task using the OpenAI API. Use a custom prompt and generate text based on that prompt.

Solution:

import openai

# Set your OpenAI API key
openai.api_key = 'your-api-key-here'

# Define the prompt for GPT-4
prompt = "In a futuristic city, the AI robots started to develop their own consciousness. One day,"

# Generate text using GPT-4
response = openai.Completion.create(
    engine="gpt-4",
    prompt=prompt,
    max_tokens=100,
    n=1,
    stop=None,
    temperature=0.7
)

# Extract the generated text
generated_text = response.choices[0].text.strip()
print(generated_text)

Exercise 3: Implement a Simple VAE

Task: Implement a simple VAE to generate new samples from the MNIST dataset. Use the example provided in section 2.2.2 and modify it to include additional layers in both the encoder and decoder.

Solution:

import tensorflow as tf
from tensorflow.keras.layers import Dense, Flatten, Reshape, Lambda, Input, Conv2D, Conv2DTranspose
from tensorflow.keras.models import Model
from tensorflow.keras.losses import binary_crossentropy
from tensorflow.keras import backend as K
import numpy as np
import matplotlib.pyplot as plt

# Sampling function
def sampling(args):
    z_mean, z_log_var = args
    batch = tf.shape(z_mean)[0]
    dim = tf.shape(z_mean)[1]
    epsilon = tf.keras.backend.random_normal(shape=(batch, dim))
    return z_mean + K.exp(0.5 * z_log_var) * epsilon

# Encoder model
input_img = Input(shape=(28, 28, 1))
x = Conv2D(32, 3, activation='relu', padding='same')(input_img)
x = Conv2D(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2D(128, 3, activation='relu', padding='same', strides=2)(x)
x = Flatten()(x)
x = Dense(256, activation='relu')(x)
z_mean = Dense(2)(x)
z_log_var = Dense(2)(x)
z = Lambda(sampling, output_shape=(2,))([z_mean, z_log_var])
encoder = Model(input_img, z)

# Decoder model
decoder_input = Input(shape=(2,))
x = Dense(7*7*128, activation='relu')(decoder_input)
x = Reshape((7, 7, 128))(x)
x = Conv2DTranspose(128, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(64, 3, activation='relu', padding='same', strides=2)(x)
x = Conv2DTranspose(32, 3, activation='relu', padding='same')(x)
output_img = Conv2DTranspose(1, 3, activation='sigmoid', padding='same')(x)
decoder = Model(decoder_input, output_img)

# VAE model
output_img = decoder(encoder(input_img))
vae = Model(input_img, output_img)

# VAE loss function
reconstruction_loss = binary_crossentropy(K.flatten(input_img), K.flatten(output_img))
reconstruction_loss *= 28 * 28
kl_loss = 1 + z_log_var - K.square(z_mean) - K.exp(z_log_var)
kl_loss = K.sum(kl_loss, axis=-1)
kl_loss *= -0.5
vae_loss = K.mean(reconstruction_loss + kl_loss)
vae.add_loss(vae_loss)
vae.compile(optimizer='adam')

# Training the VAE
(x_train, _), (_, _) = tf.keras.datasets.mnist.load_data()
x_train = (x_train.astype(np.float32) / 255.0) - 0.5
x_train = np.expand_dims(x_train, axis=-1)
vae.fit(x_train, epochs=50, batch_size=128, verbose=1)

# Generate new samples
z_sample = np.array([[0.0, 0.0]])
generated_image = decoder.predict(z_sample)

# Plot generated image
plt.imshow(generated_image[0].squeeze(), cmap='gray')
plt.axis('off')
plt.show()

Exercise 4: Apply Spectral Normalization to a Discriminator

Task: Implement spectral normalization in a simple discriminator model. Use the example provided in section 2.3.2 and ensure the discriminator is applied to the MNIST dataset.

Solution:

import torch
import torch.nn as nn
import torch.optim as optim
from torchvision import datasets, transforms
from torch.utils.data import DataLoader
import matplotlib.pyplot as plt

# Define a simple discriminator with spectral normalization
class Discriminator(nn.Module):
    def __init__(self):
        super(Discriminator, self).__init__()
        self.model = nn.Sequential(
            nn.utils.spectral_norm(nn.Conv2d(1, 64, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.utils.spectral_norm(nn.Conv2d(64, 128, 4, stride=2, padding=1)),
            nn.LeakyReLU(0.2, inplace=True),
            nn.Flatten(),
            nn.utils.spectral_norm(nn.Linear(128 * 7 * 7, 1))
        )

    def forward(self, x):
        return self.model(x)

# Load MNIST dataset
transform = transforms.Compose([
    transforms.ToTensor(),
    transforms.Normalize((0.5,),

 (0.5,))
])
dataset = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
dataloader = DataLoader(dataset, batch_size=64, shuffle=True)

# Instantiate the discriminator
discriminator = Discriminator()
optimizer = optim.Adam(discriminator.parameters(), lr=0.0002)
criterion = nn.BCELoss()

# Training loop
num_epochs = 5
for epoch in range(num_epochs):
    for images, _ in dataloader:
        optimizer.zero_grad()
        labels = torch.ones(images.size(0), 1)
        outputs = discriminator(images)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

    print(f'Epoch [{epoch+1}/{num_epochs}], Loss: {loss.item():.4f}')

# Generate fake data (for demonstration purposes)
noise = torch.randn(64, 1, 28, 28)
fake_images = noise

# Evaluate discriminator on fake data
with torch.no_grad():
    fake_outputs = discriminator(fake_images)
    print("Discriminator output on fake images:", fake_outputs[:5])

Exercise 5: Implement a 3D GAN for Voxel-Based Object Generation

Task: Implement a simple 3D GAN to generate voxel-based objects. Use the example provided in section 2.3.3 and visualize the generated 3D objects.

Solution:

import torch
import torch.nn as nn
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# Define a simple 3D GAN for voxel-based object generation
class VoxelGenerator(nn.Module):
    def __init__(self):
        super(VoxelGenerator, self).__init__()
        self.model = nn.Sequential(
            nn.Linear(100, 128),
            nn.ReLU(inplace=True),
            nn.Linear(128, 256),
            nn.ReLU(inplace=True),
            nn.Linear(256, 512),
            nn.ReLU(inplace=True),
            nn.Linear(512, 32*32*32),
            nn.Tanh()
        )

    def forward(self, z):
        return self.model(z).view(-1, 32, 32, 32)

# Instantiate the generator
voxel_generator = VoxelGenerator()

# Generate random latent vectors
num_voxels = 5
latent_vectors = torch.randn(num_voxels, 100)

# Generate 3D voxel objects
generated_voxels = voxel_generator(latent_vectors)

# Visualize the generated 3D objects
fig = plt.figure(figsize=(15, 15))
for i in range(num_voxels):
    ax = fig.add_subplot(1, num_voxels, i+1, projection='3d')
    ax.voxels(generated_voxels[i].detach().numpy() > 0, edgecolor='k')
    ax.axis('off')

plt.show()

These practical exercises should help reinforce your understanding of the concepts covered in this chapter. By implementing these models and experimenting with different configurations, you'll gain hands-on experience with generative models and their practical applications. Keep practicing, and don't hesitate to explore further on your own!