Chapter 1: Introduction to Deep Learning
1.4 Practical Exercises - Chapter 1: Introduction to Deep Learning
Exercise 1: Implement a Simple Neural Network
Task: Implement a simple neural network using the code provided in section 1.1.1. Modify the network to include an additional hidden layer and observe how the performance changes.
Solution:
import numpy as np
# Sigmoid activation function
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Derivative of sigmoid function
def sigmoid_derivative(x):
return x * (1 - x)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden1 = 2 * np.random.random((3, 4)) - 1
weights_hidden1_hidden2 = 2 * np.random.random((4, 4)) - 1
weights_hidden2_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer1 = sigmoid(np.dot(input_layer, weights_input_hidden1))
hidden_layer2 = sigmoid(np.dot(hidden_layer1, weights_hidden1_hidden2))
output_layer = sigmoid(np.dot(hidden_layer2, weights_hidden2_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * sigmoid_derivative(output_layer)
hidden_layer2_error = output_layer_delta.dot(weights_hidden2_output.T)
hidden_layer2_delta = hidden_layer2_error * sigmoid_derivative(hidden_layer2)
hidden_layer1_error = hidden_layer2_delta.dot(weights_hidden1_hidden2.T)
hidden_layer1_delta = hidden_layer1_error * sigmoid_derivative(hidden_layer1)
# Update weights
weights_hidden2_output += hidden_layer2.T.dot(output_layer_delta)
weights_hidden1_hidden2 += hidden_layer1.T.dot(hidden_layer2_delta)
weights_input_hidden1 += input_layer.T.dot(hidden_layer1_delta)
print("Output after training:")
print(output_layer)
Exercise 2: Implement a ReLU Activation Function
Task: Implement a ReLU activation function and apply it to a simple neural network. Compare the results with the sigmoid activation function.
Solution:
import numpy as np
# ReLU activation function
def relu(x):
return np.maximum(0, x)
# Derivative of ReLU function
def relu_derivative(x):
return np.where(x > 0, 1, 0)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden = 2 * np.random.random((3, 4)) - 1
weights_hidden_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer = relu(np.dot(input_layer, weights_input_hidden))
output_layer = relu(np.dot(hidden_layer, weights_hidden_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * relu_derivative(output_layer)
hidden_layer_error = output_layer_delta.dot(weights_hidden_output.T)
hidden_layer_delta = hidden_layer_error * relu_derivative(hidden_layer)
# Update weights
weights_hidden_output += hidden_layer.T.dot(output_layer_delta)
weights_input_hidden += input_layer.T.dot(hidden_layer_delta)
print("Output after training:")
print(output_layer)
Exercise 3: Fine-Tune a Pre-trained BERT Model
Task: Fine-tune a pre-trained BERT model for a text classification task using the provided example in section 1.3.2. Use a different dataset for this exercise.
Solution:
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
import tensorflow as tf
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had.", "It was okay, not great."]
labels = [1, 0, 1] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Convert labels to tensor
labels = tf.convert_to_tensor(labels)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Exercise 4: Implement a Basic GAN
Task:
Implement a basic GAN for generating synthetic data. Follow the example provided in section 1.3.3 and generate new samples after training the GAN.
Solution:
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
import numpy as np
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
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 = 128
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)
print(generated_images)
Exercise 5: Implement Q-Learning for a Simple Environment
Task: Implement Q-Learning for a simple grid world environment. Follow the example provided in section 1.3.4 and extend the grid size or modify the reward structure.
Solution:
import numpy as np
# Environment setup
grid_size = 5 # Extended grid size
rewards = np.zeros((grid_size, grid_size))
rewards[4, 4] = 1 # New goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action
selection
def choose_action(state):
if np.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (4, 4):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
These 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 deep learning techniques and their practical applications. Keep practicing, and don't hesitate to explore further on your own!
1.4 Practical Exercises - Chapter 1: Introduction to Deep Learning
Exercise 1: Implement a Simple Neural Network
Task: Implement a simple neural network using the code provided in section 1.1.1. Modify the network to include an additional hidden layer and observe how the performance changes.
Solution:
import numpy as np
# Sigmoid activation function
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Derivative of sigmoid function
def sigmoid_derivative(x):
return x * (1 - x)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden1 = 2 * np.random.random((3, 4)) - 1
weights_hidden1_hidden2 = 2 * np.random.random((4, 4)) - 1
weights_hidden2_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer1 = sigmoid(np.dot(input_layer, weights_input_hidden1))
hidden_layer2 = sigmoid(np.dot(hidden_layer1, weights_hidden1_hidden2))
output_layer = sigmoid(np.dot(hidden_layer2, weights_hidden2_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * sigmoid_derivative(output_layer)
hidden_layer2_error = output_layer_delta.dot(weights_hidden2_output.T)
hidden_layer2_delta = hidden_layer2_error * sigmoid_derivative(hidden_layer2)
hidden_layer1_error = hidden_layer2_delta.dot(weights_hidden1_hidden2.T)
hidden_layer1_delta = hidden_layer1_error * sigmoid_derivative(hidden_layer1)
# Update weights
weights_hidden2_output += hidden_layer2.T.dot(output_layer_delta)
weights_hidden1_hidden2 += hidden_layer1.T.dot(hidden_layer2_delta)
weights_input_hidden1 += input_layer.T.dot(hidden_layer1_delta)
print("Output after training:")
print(output_layer)
Exercise 2: Implement a ReLU Activation Function
Task: Implement a ReLU activation function and apply it to a simple neural network. Compare the results with the sigmoid activation function.
Solution:
import numpy as np
# ReLU activation function
def relu(x):
return np.maximum(0, x)
# Derivative of ReLU function
def relu_derivative(x):
return np.where(x > 0, 1, 0)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden = 2 * np.random.random((3, 4)) - 1
weights_hidden_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer = relu(np.dot(input_layer, weights_input_hidden))
output_layer = relu(np.dot(hidden_layer, weights_hidden_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * relu_derivative(output_layer)
hidden_layer_error = output_layer_delta.dot(weights_hidden_output.T)
hidden_layer_delta = hidden_layer_error * relu_derivative(hidden_layer)
# Update weights
weights_hidden_output += hidden_layer.T.dot(output_layer_delta)
weights_input_hidden += input_layer.T.dot(hidden_layer_delta)
print("Output after training:")
print(output_layer)
Exercise 3: Fine-Tune a Pre-trained BERT Model
Task: Fine-tune a pre-trained BERT model for a text classification task using the provided example in section 1.3.2. Use a different dataset for this exercise.
Solution:
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
import tensorflow as tf
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had.", "It was okay, not great."]
labels = [1, 0, 1] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Convert labels to tensor
labels = tf.convert_to_tensor(labels)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Exercise 4: Implement a Basic GAN
Task:
Implement a basic GAN for generating synthetic data. Follow the example provided in section 1.3.3 and generate new samples after training the GAN.
Solution:
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
import numpy as np
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
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 = 128
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)
print(generated_images)
Exercise 5: Implement Q-Learning for a Simple Environment
Task: Implement Q-Learning for a simple grid world environment. Follow the example provided in section 1.3.4 and extend the grid size or modify the reward structure.
Solution:
import numpy as np
# Environment setup
grid_size = 5 # Extended grid size
rewards = np.zeros((grid_size, grid_size))
rewards[4, 4] = 1 # New goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action
selection
def choose_action(state):
if np.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (4, 4):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
These 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 deep learning techniques and their practical applications. Keep practicing, and don't hesitate to explore further on your own!
1.4 Practical Exercises - Chapter 1: Introduction to Deep Learning
Exercise 1: Implement a Simple Neural Network
Task: Implement a simple neural network using the code provided in section 1.1.1. Modify the network to include an additional hidden layer and observe how the performance changes.
Solution:
import numpy as np
# Sigmoid activation function
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Derivative of sigmoid function
def sigmoid_derivative(x):
return x * (1 - x)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden1 = 2 * np.random.random((3, 4)) - 1
weights_hidden1_hidden2 = 2 * np.random.random((4, 4)) - 1
weights_hidden2_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer1 = sigmoid(np.dot(input_layer, weights_input_hidden1))
hidden_layer2 = sigmoid(np.dot(hidden_layer1, weights_hidden1_hidden2))
output_layer = sigmoid(np.dot(hidden_layer2, weights_hidden2_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * sigmoid_derivative(output_layer)
hidden_layer2_error = output_layer_delta.dot(weights_hidden2_output.T)
hidden_layer2_delta = hidden_layer2_error * sigmoid_derivative(hidden_layer2)
hidden_layer1_error = hidden_layer2_delta.dot(weights_hidden1_hidden2.T)
hidden_layer1_delta = hidden_layer1_error * sigmoid_derivative(hidden_layer1)
# Update weights
weights_hidden2_output += hidden_layer2.T.dot(output_layer_delta)
weights_hidden1_hidden2 += hidden_layer1.T.dot(hidden_layer2_delta)
weights_input_hidden1 += input_layer.T.dot(hidden_layer1_delta)
print("Output after training:")
print(output_layer)
Exercise 2: Implement a ReLU Activation Function
Task: Implement a ReLU activation function and apply it to a simple neural network. Compare the results with the sigmoid activation function.
Solution:
import numpy as np
# ReLU activation function
def relu(x):
return np.maximum(0, x)
# Derivative of ReLU function
def relu_derivative(x):
return np.where(x > 0, 1, 0)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden = 2 * np.random.random((3, 4)) - 1
weights_hidden_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer = relu(np.dot(input_layer, weights_input_hidden))
output_layer = relu(np.dot(hidden_layer, weights_hidden_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * relu_derivative(output_layer)
hidden_layer_error = output_layer_delta.dot(weights_hidden_output.T)
hidden_layer_delta = hidden_layer_error * relu_derivative(hidden_layer)
# Update weights
weights_hidden_output += hidden_layer.T.dot(output_layer_delta)
weights_input_hidden += input_layer.T.dot(hidden_layer_delta)
print("Output after training:")
print(output_layer)
Exercise 3: Fine-Tune a Pre-trained BERT Model
Task: Fine-tune a pre-trained BERT model for a text classification task using the provided example in section 1.3.2. Use a different dataset for this exercise.
Solution:
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
import tensorflow as tf
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had.", "It was okay, not great."]
labels = [1, 0, 1] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Convert labels to tensor
labels = tf.convert_to_tensor(labels)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Exercise 4: Implement a Basic GAN
Task:
Implement a basic GAN for generating synthetic data. Follow the example provided in section 1.3.3 and generate new samples after training the GAN.
Solution:
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
import numpy as np
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
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 = 128
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)
print(generated_images)
Exercise 5: Implement Q-Learning for a Simple Environment
Task: Implement Q-Learning for a simple grid world environment. Follow the example provided in section 1.3.4 and extend the grid size or modify the reward structure.
Solution:
import numpy as np
# Environment setup
grid_size = 5 # Extended grid size
rewards = np.zeros((grid_size, grid_size))
rewards[4, 4] = 1 # New goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action
selection
def choose_action(state):
if np.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (4, 4):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
These 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 deep learning techniques and their practical applications. Keep practicing, and don't hesitate to explore further on your own!
1.4 Practical Exercises - Chapter 1: Introduction to Deep Learning
Exercise 1: Implement a Simple Neural Network
Task: Implement a simple neural network using the code provided in section 1.1.1. Modify the network to include an additional hidden layer and observe how the performance changes.
Solution:
import numpy as np
# Sigmoid activation function
def sigmoid(x):
return 1 / (1 + np.exp(-x))
# Derivative of sigmoid function
def sigmoid_derivative(x):
return x * (1 - x)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden1 = 2 * np.random.random((3, 4)) - 1
weights_hidden1_hidden2 = 2 * np.random.random((4, 4)) - 1
weights_hidden2_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer1 = sigmoid(np.dot(input_layer, weights_input_hidden1))
hidden_layer2 = sigmoid(np.dot(hidden_layer1, weights_hidden1_hidden2))
output_layer = sigmoid(np.dot(hidden_layer2, weights_hidden2_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * sigmoid_derivative(output_layer)
hidden_layer2_error = output_layer_delta.dot(weights_hidden2_output.T)
hidden_layer2_delta = hidden_layer2_error * sigmoid_derivative(hidden_layer2)
hidden_layer1_error = hidden_layer2_delta.dot(weights_hidden1_hidden2.T)
hidden_layer1_delta = hidden_layer1_error * sigmoid_derivative(hidden_layer1)
# Update weights
weights_hidden2_output += hidden_layer2.T.dot(output_layer_delta)
weights_hidden1_hidden2 += hidden_layer1.T.dot(hidden_layer2_delta)
weights_input_hidden1 += input_layer.T.dot(hidden_layer1_delta)
print("Output after training:")
print(output_layer)
Exercise 2: Implement a ReLU Activation Function
Task: Implement a ReLU activation function and apply it to a simple neural network. Compare the results with the sigmoid activation function.
Solution:
import numpy as np
# ReLU activation function
def relu(x):
return np.maximum(0, x)
# Derivative of ReLU function
def relu_derivative(x):
return np.where(x > 0, 1, 0)
# Input data (4 samples, 3 features each)
inputs = np.array([[0, 0, 1],
[1, 1, 1],
[1, 0, 1],
[0, 1, 1]])
# Output labels (4 samples, 1 output each)
outputs = np.array([[0], [1], [1], [0]])
# Seed for reproducibility
np.random.seed(1)
# Initialize weights randomly with mean 0
weights_input_hidden = 2 * np.random.random((3, 4)) - 1
weights_hidden_output = 2 * np.random.random((4, 1)) - 1
# Training the neural network
for epoch in range(10000):
# Forward propagation
input_layer = inputs
hidden_layer = relu(np.dot(input_layer, weights_input_hidden))
output_layer = relu(np.dot(hidden_layer, weights_hidden_output))
# Error calculation
error = outputs - output_layer
# Backward propagation
output_layer_delta = error * relu_derivative(output_layer)
hidden_layer_error = output_layer_delta.dot(weights_hidden_output.T)
hidden_layer_delta = hidden_layer_error * relu_derivative(hidden_layer)
# Update weights
weights_hidden_output += hidden_layer.T.dot(output_layer_delta)
weights_input_hidden += input_layer.T.dot(hidden_layer_delta)
print("Output after training:")
print(output_layer)
Exercise 3: Fine-Tune a Pre-trained BERT Model
Task: Fine-tune a pre-trained BERT model for a text classification task using the provided example in section 1.3.2. Use a different dataset for this exercise.
Solution:
from transformers import BertTokenizer, TFBertForSequenceClassification
from tensorflow.keras.optimizers import Adam
import tensorflow as tf
# Load pre-trained BERT model and tokenizer
tokenizer = BertTokenizer.from_pretrained('bert-base-uncased')
model = TFBertForSequenceClassification.from_pretrained('bert-base-uncased')
# Sample data
texts = ["I love this product!", "This is the worst experience I've ever had.", "It was okay, not great."]
labels = [1, 0, 1] # 1 for positive, 0 for negative
# Tokenize the input texts
inputs = tokenizer(texts, return_tensors='tf', padding=True, truncation=True, max_length=128)
# Convert labels to tensor
labels = tf.convert_to_tensor(labels)
# Compile the model
optimizer = Adam(learning_rate=2e-5)
model.compile(optimizer=optimizer, loss=model.compute_loss, metrics=['accuracy'])
# Train the model
model.fit(inputs['input_ids'], labels, epochs=3, batch_size=8)
# Evaluate the model
predictions = model.predict(inputs['input_ids'])
print(predictions)
Exercise 4: Implement a Basic GAN
Task:
Implement a basic GAN for generating synthetic data. Follow the example provided in section 1.3.3 and generate new samples after training the GAN.
Solution:
import tensorflow as tf
from tensorflow.keras.layers import Dense, LeakyReLU, Reshape, Flatten
from tensorflow.keras.models import Sequential
import numpy as np
# Generator model
def build_generator():
model = Sequential([
Dense(128, input_dim=100),
LeakyReLU(alpha=0.01),
Dense(784, activation='tanh'),
Reshape((28, 28, 1))
])
return model
# Discriminator model
def build_discriminator():
model = Sequential([
Flatten(input_shape=(28, 28, 1)),
Dense(128),
LeakyReLU(alpha=0.01),
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 = 128
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)
print(generated_images)
Exercise 5: Implement Q-Learning for a Simple Environment
Task: Implement Q-Learning for a simple grid world environment. Follow the example provided in section 1.3.4 and extend the grid size or modify the reward structure.
Solution:
import numpy as np
# Environment setup
grid_size = 5 # Extended grid size
rewards = np.zeros((grid_size, grid_size))
rewards[4, 4] = 1 # New goal state
# Q-Learning parameters
gamma = 0.9 # Discount factor
alpha = 0.1 # Learning rate
epsilon = 0.1 # Exploration rate
q_table = np.zeros((grid_size, grid_size, 4)) # Q-table for 4 actions
# Action
selection
def choose_action(state):
if np.random.rand() < epsilon:
return np.random.randint(4)
return np.argmax(q_table[state])
# Q-Learning algorithm
for episode in range(1000):
state = (0, 0)
while state != (4, 4):
action = choose_action(state)
next_state = (max(0, min(grid_size-1, state[0] + (action == 1) - (action == 0))),
max(0, min(grid_size-1, state[1] + (action == 3) - (action == 2))))
reward = rewards[next_state]
td_target = reward + gamma * np.max(q_table[next_state])
td_error = td_target - q_table[state][action]
q_table[state][action] += alpha * td_error
state = next_state
print("Trained Q-Table:")
print(q_table)
These 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 deep learning techniques and their practical applications. Keep practicing, and don't hesitate to explore further on your own!