Pytorch & TensorFlow for beginners who has few experience of Deep Learning (2/2) — TensorFlow | by Ryosuke Iimura | Aug, 2024


In the previous article, I introduced Pytorch tutorial for the beignners who knew what Pytorch and TensorFlow were, but had no idea how they could be used for deep learning models. This note tells you how-to of TensorFlow.

If you have not read the previous note, it’s more effective to start learning from it.

https://medium.com/@ryosukeiimura/pytorch-tensorflow-for-beginners-who-has-few-experience-of-deep-learning-1-2-pytorch-2f39ee3e4e09

To compare to Pytorch, we will do the same thing with TensorFlow.

# load libraries
import numpy as np
import pandas as pd
import tensorflow as tf
import random
# load sample data
from sklearn.datasets import load_wine
wine_data = load_wine()
data = pd.DataFrame(wine_data.data, columns = wine_data.feature_names)
tensor = tf.convert_to_tensor(data.values)
print(tensor)

tf.convert_to_tensor transform a dataframe into tensor format whose data type is inferred from the type of value as a default. If you want to change the data type, you can set the dtype argument.

tensor_float = tf.convert_to_tensor(data.values, dtype=tf.float32) # float32
tensor_int = tf.convert_to_tensor(data.values, dtype = tf.int64) # int64

TensorFlow implements an embedding. This it a simple embedding with a weight matrix initialized randomly with random seed.

text = 'Hellow world this is an example'

tokens = text.lower().split()

vocab = {word : i for i, word in enumerate(set(tokens))}

token_indice = list(vocab.values())

indice_tensor = tf.convert_to_tensor(token_indice)

indice_tensor

# sample embedding
tf.random.set_seed(1234)

vocab_size = len(vocab) # the number of target tokens to be embedded
embedding_dim = 3 # hypter parameters
embedding = tf.keras.layers.Embedding(vocab_size, embedding_dim)

# sample input
indice_tensor = tf.constant([[1, 2, 3], [4, 5, 6]], dtype=tf.int32)

# output the embedding weights
embedding_weights = embedding.weights[0].numpy()
print('embedding W :{}'.format(embedding_weights))

# # 埋め込まれたテキストの出力
embedded_text = embedding(indice_tensor)
print('embedded text : {}'.format(embedded_text.numpy()))

Unlike Pytorch, it does not provide transform method to handle with image data such as resizing, normalizing and so on. Instead, it’s better to create custom functions.

from PIL import Image

image_path = '/content/sample_data/sample_image1.jpg'
# load and show the image
image = Image.open(image_path)

print('The original size of the image :{}'.format(image.size))
print('The original color of the image :{}'.format(image.mode))

To resize an image

resize = 256 # or you can designate as a tuple such as size = (235,256)

def reSize(image, resize):
# Check if resize is an integer, convert it to a tuple (resize, resize)
if isinstance(resize, int):
resize = (resize, resize)

# Use tf.image.resize to resize the image
resize_image = tf.image.resize(image,resize,method = 'bilinear')

print('The size of the image resized :{}'.format(resize_image.shape))

return resize_image

reSize(image,resize)

To change its color into gray scale

def gScale(image):
# Convert the image to grayscale using tf.image.rgb_to_grayscale
gray_image = tf.image.rgb_to_grayscale(image)

# Printing the color mode, for grayscale it should be (height, width, 1)
print('The color of the image changed :{}'.format(gray_image.shape))

return gray_image

# Example usage:
# Convert the image to grayscale
gray_image = gScale(image)

To transform an image into numeric data in tensor format

def to_tensor(image):
# Convert the image to a numpy array if it's a PIL Image
if isinstance(image, Image.Image):
image = np.array(image)

# Convert the numpy array to a TensorFlow tensor
tensor_image = tf.convert_to_tensor(image, dtype=tf.float32)

return tensor_image

to_tensor(image)

To normalize data

def to_tensor_normalize(image):
# Convert the image to a numpy array if it's a PIL Image
if isinstance(image, Image.Image):
image = np.array(image)

# Convert the numpy array to a TensorFlow tensor
tensor_image = tf.convert_to_tensor(image, dtype=tf.float32)

# Normalize the image to the range [0, 1] if needed
tensor_image = tensor_image / 255.0

return tensor_image

to_tensor_normalize(image)

def check_gpu_availability():
# List all physical devices of type 'GPU'
gpus = tf.config.list_physical_devices('GPU')

# Check if any GPUs are available
is_available = len(gpus) > 0

# Print whether GPUs are available
print("Is GPU available:", is_available)

# Print the number of available GPUs
print("Number of available GPUs:", len(gpus))

return is_available, len(gpus)

def set_device():
# List all physical devices of type 'GPU'
gpus = tf.config.list_physical_devices('GPU')

# Determine the device to use
device = '/GPU:0' if len(gpus) > 0 else '/CPU:0'

return device

# Check GPU availability and count
check_gpu_availability()

# Set the device based on availability
device = set_device()
print("Using device:", device)

Fix the random seed

def fix_seed(seed):
# Set the seed for Python's random module
random.seed(seed)

# Set the seed for NumPy
np.random.seed(seed)

# Set the seed for TensorFlow
tf.random.set_seed(seed)

# Fix the random seed for reproducibility
fix_seed(1234)

To create dataset for modelling

class Mydataset(tf.keras.utils.Sequence):
'''
In TensorFlow's dataset class, you define data preprocessing and prepare the training data as a data loader.
e.g.
train_dataset = MyDataset(train_X, train_y)
test_dataset = MyDataset(test_X, test_y)
'''
def __init__(self, X, y, transform=None):
self.X = self.convert_to_tensor(X, is_float=True)
self.y = self.convert_to_tensor(y, is_float=False)
self.transform = transform

def convert_to_tensor(self, data, is_float=True):
if isinstance(data, np.ndarray):
# In the case of a NumPy array
tensor = tf.convert_to_tensor(data)
elif isinstance(data, tf.Tensor):
# In the case of a TensorFlow tensor
tensor = data
elif isinstance(data, pd.DataFrame):
# In the case of a Pandas DataFrame
tensor = tf.convert_to_tensor(data.values)
else:
raise TypeError("Unsupported data type")

if is_float:
return tf.cast(tensor, dtype=tf.float32)
else:
return tf.cast(tensor, dtype=tf.int64)

# Required
def __len__(self):
return len(self.X)

# Required
def __getitem__(self, index):
'''
This method is defined as part of a custom dataset class that inherits from TensorFlow's dataset class.
It is used to retrieve data at a specific index in the dataset.
This method is automatically called when the data loader fetches data in batches from the dataset.
'''
feature = self.X[index]
label = self.y[index]

# Write preprocessing, etc. -----
if self.transform:
feature = self.transform(feature)
# --------------------
return feature, label

The order of tensor shapes in TensorFlow

Again, the order of tensor shapes is different from Pytorch. TensorFlow usually requires you to set the argument in the following order.

  • N: Number of samples (batch size)
  • H: Height of the features
  • W: Width of the features
  • C: Number of channels

Sample data creation

# Create random data tensor with shape (100, 3, 255, 255) and label tensor with shape (100,)
data = tf.random.normal((100, 3, 255, 255))
labels = tf.random.uniform((100,), minval=0, maxval=2, dtype=tf.int32)

# Create training and testing masks
train_mask = np.random.permutation(100) test_mask = ~train_mask # 20% for testing

# Split the dataset into training and testing sets using the created masks
train_X = tf.boolean_mask(data, train_mask)
train_y = tf.boolean_mask(labels, train_mask)
test_X = tf.boolean_mask(data, test_mask)
test_y = tf.boolean_mask(labels, test_mask)

# Print the shapes of the training and testing sets for verification
print('Train X shape:', train_X.shape)
print('Train y shape:', train_y.shape)
print('Test X shape:', test_X.shape)
print('Test y shape:', test_y.shape)

# To create Mydataset class instance
train_dataset = Mydataset(train_X, train_y)
test_dataset = Mydataset(test_X, test_y)

Split into Batches

Pytorch provides DataLoader for spliting data into batches, but TensorFlow does not have the similar module.

def set_seed(seed):
np.random.seed(seed)
tf.random.set_seed(seed)

def create_dataset(features, labels, batch_size, shuffle=True):
# Create a TensorFlow Dataset from the features and labels
dataset = tf.data.Dataset.from_tensor_slices((features, labels))

# Shuffle the dataset if specified
if shuffle:
dataset = dataset.shuffle(buffer_size=len(features), seed=1234)

# Batch the dataset
dataset = dataset.batch(batch_size)

return dataset

# Assuming train_X, train_y, test_X, and test_y are already defined
batch_size = 20

# Create train and test datasets
train_dataset = create_dataset(train_X, train_y, batch_size, shuffle=True)
test_dataset = create_dataset(test_X, test_y, batch_size, shuffle=False)

# Iterate over the train dataset to check batch sizes
for batch_idx, (features, labels) in enumerate(train_dataset):
print(f"Batch {batch_idx + 1}: Size {features.shape[0]}")

parameters

model_params = {
'conv1':{
'in_channels': 3,
'out_channels': 16,
'kernel_size': 5,
'stride': 1,
'padding':1,
'in_size':255, # input data feature size
'dropout':.25
},
'conv2':{
'out_channels': 10,
'kernel_size': 5,
'stride': 1,
'padding':1,
'dropout':.25
},
'dropout':.5,
'fc1_size':100,
'fc2_size':2
}

Modelling

As you remember, Pytorch has Sequential method to create your own network, TensorFlow has tf.keras.Sequential, which is also useful to build neural networks.

class MyModel(tf.keras.Model):
def __init__(self, **kwargs):
super().__init__()

c1 = kwargs.get('conv1')
c2 = kwargs.get('conv2')

self.conv1 = tf.keras.Sequential([
tf.keras.layers.Input(shape = (c1['in_size'],c1['in_size'],c1['in_channels'])),# height, width, channels
tf.keras.layers.Conv2D(filters=c1['out_channels'],
kernel_size=c1['kernel_size'], # int or tuple/list of 2 integer
strides=c1['stride'], # int or tuple/list of 2 integer
padding='same', # When padding="same" and strides=1, the output has the same size as the input.
),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.ReLU(),
tf.keras.layers.Dropout(rate=c1['dropout'])
])

self.conv2 = tf.keras.Sequential([
tf.keras.layers.Conv2D(filters=c2['out_channels'],
kernel_size=c2['kernel_size'],
strides=c2['stride'],
padding='same'),
tf.keras.layers.BatchNormalization(),
tf.keras.layers.ReLU(),
tf.keras.layers.Dropout(rate=c2['dropout'])
])

self.flatten = tf.keras.layers.Flatten()
self.fc1 = tf.keras.layers.Dense(kwargs.get('fc1_size'))
self.dropout = tf.keras.layers.Dropout(rate=kwargs.get('dropout'))
self.fc2 = tf.keras.layers.Dense(kwargs.get('fc2_size'))

def call(self, x):
x = tf.transpose(x, perm=[0, 2, 3, 1]) # From (batch, channels, height, width) to (batch, height, width, channels)
x = self.conv1(x)
x = self.conv2(x)
x = self.flatten(x)
x = self.fc1(x)
x = self.dropout(x)
x = self.fc2(x)
return x

To initiate the model, and to define loss function and optimization

# Instantiate the model
model = MyModel(**model_params)
criterion = tf.keras.losses.CategoricalCrossentropy(from_logits=True) # Define the loss function
optimizer = tf.keras.optimizers.SGD(learning_rate=0.01) # Define the optimizer
model.compile(optimizer=optimizer, loss=criterion) # Example of compiling the model (not necessary for custom training loops)

To define the training step

# Model training function
def train_model(model, train_dataset, test_dataset, optimizer, criterion, device='/GPU:0'):
# Train loop ----------------------------
train_batch_loss = []
for data, label in train_dataset:
'''
c.f. - data, label = data.to(device), label.to(device) in Pytorch
In TensorFlow, unlike PyTorch, you typically do not need to explicitly transfer data or models to the GPU using commands like data.to(device).
TensorFlow manages device placement automatically, which simplifies the process of utilizing GPU resources.

c.f. - optimizer.zero_grad() in Pytorch
you typically do not need an explicit equivalent to optimizer.zero_grad() in tensorFlow
'''
# Convert labels to one-hot encoding
label = tf.keras.utils.to_categorical(label, num_classes=2) # Assuming you have 2 classes

with tf.GradientTape() as tape:
# 1. Inference
output = model(data, training=True)
# 2. Calculate loss
loss = criterion(label, output)

# 3. Gradient computation
gradients = tape.gradient(loss, model.trainable_variables)
# 4. Update parameters
optimizer.apply_gradients(zip(gradients, model.trainable_variables))

# Retrieve train loss
train_batch_loss.append(loss.numpy())

# Test(val) loop ----------------------------
test_batch_loss = []
for data, label in test_dataset:
# Convert labels to one-hot encoding
label = tf.keras.utils.to_categorical(label, num_classes=2) # Assuming you have 2 classes
output = model(data, training=False)
loss = criterion(label, output)
test_batch_loss.append(loss.numpy())

return model, np.mean(train_batch_loss), np.mean(test_batch_loss)

To train the data

from tqdm.notebook import tqdm  
import ipywidgets as widgets
widgets.IntProgress(value=50, min=0, max=100)

# execute training
epoch = 100
train_loss = []
test_loss = []

for epoch in tqdm(range(epoch)):
model, train_l, test_l = train_model(model,train_dataset, test_dataset, optimizer = optimizer, criterion = criterion)
train_loss.append(train_l)
test_loss.append(test_l)
# show loss in every 10 epoch
if epoch % 10 == 0:
print("Train loss: {a:.3f}, Test loss: {b:.3f}".format(a=train_loss[-1], b = test_loss[-1]))

To show the training result

import matplotlib.pyplot as plt

plt.plot(train_loss, label='train_loss')
plt.plot(test_loss, label='test_loss')
plt.legend()

To show an image from image vectors

# Create a dummy image tensor (3 channels for a color image)
image_tensor = tf.random.normal((255, 255, 3)) # Height, Width, Channels

# Convert the image tensor to a NumPy array
image_np = image_tensor.numpy()

# Display the image using Matplotlib
plt.imshow(image_np)
plt.axis('off') # Hide axes
plt.show()

Function to Display Images in Bulk

Below is a function that displays images in a grid format.

# Create a batch of dummy images (10 images, 3 channels, 255x255)
images = tf.random.normal((10, 255, 255, 3)) # Height, Width, Channels

# Convert the tensor to a NumPy array
images_np = images.numpy()

def show_images(images, n_rows, n_cols):
fig, axes = plt.subplots(n_rows, n_cols, figsize=(n_cols * 3, n_rows * 3))
for i, ax in enumerate(axes.flat):
if i ax.imshow(images[i])
ax.axis('off')
plt.tight_layout()
plt.show()

# Display the images in a 2x5 grid
show_images(images_np, n_rows=2, n_cols=5)



Source link

Be the first to comment

Leave a Reply

Your email address will not be published.


*