Github Link
https://github.com/Natan-Asrat/tensorflow_data_pipeline
Contact
- LinkedIn: Natan Asrat
- Gmail: nathanyilmaasrat@gmail.com
- Telegram: Natan Asrat
- X: Natan Asrat Yilma
- Youtube: Natville
The Setup
Introduction
In this project, i explore TensorFlow data pipeline by accomplishing three tasks concerned with building data generators, image augmentation, and dataset generation and training.
Libraries Used
- TensorFlow
- Numpy
- Pandas
- scikit-learn
Imports
import tensorflow as tf
import os
import numpy as np
import pandas as pd
from tensorflow.keras import Sequential
from tensorflow.keras.datasets import cifar10
from tensorflow.keras import Model
from tensorflow.keras.layers import Dense, Input, BatchNormalization
from tensorflow.keras.preprocessing.image import ImageDataGenerator
from sklearn.preprocessing import LabelBinarizer
Data Generator
Load the fertility dataset
headers = ['Season', 'Age', 'Diseases', 'Trauma', 'Surgery', 'Fever', 'Alcohol', 'Smoking', 'Sitting', 'Output']
fertility = pd.read_csv('data/fertility_diagnosis.txt', delimiter=',', header=None, names=headers)
Process the Data
Map the 'Output' feature from 'N' to 0 and from 'O' to 1:
fertility['Output'] = fertility['Output'].map(lambda x : 0.0 if x=='N' else 1.0)
Convert the DataFrame so that the features are mapped to floats:
fertility = fertility.astype('float32')
Shuffle the DataFrame:
fertility = fertility.sample(frac=1).reset_index(drop=True)
Convert the field Season to a one-hot encoded vector:
fertility = pd.get_dummies(fertility, prefix='Season', columns=['Season'])
Move the Output column such that it is the last column in the DataFrame:
fertility = fertility.reindex(columns = [col for col in fertility.columns if col != 'Output'] + ['Output'])
Convert the DataFrame to a numpy array:
fertility = fertility.to_numpy()
Split the Data
Split the dataset into training and validation set:
training = fertility[0:70]
validation = fertility[70:100]
Separate the features and labels for the validation and training data:
training_features = training[:,0:-1]
training_labels = training[:,-1]
validation_features = validation[:,0:-1]
validation_labels = validation[:,-1]
Create the Generator
def get_generator(features, labels, batch_size=1):
for n in range(int(len(features)/batch_size)):
yield (features[n*batch_size: (n+1)*batch_size], labels[n*batch_size: (n+1)*batch_size])
Apply the function to our training features and labels with a batch size of 10:
train_generator = get_generator(training_features, training_labels, batch_size=10)
Test the generator using the next() function:
next(train_generator)
Build the Model
Create a model using Keras with 3 layers:
input_shape = (12,)
output_shape = (1,)
model_input = Input(input_shape)
batch_1 = BatchNormalization(momentum=0.8)(model_input)
dense_1 = Dense(100, activation='relu')(batch_1)
batch_2 = BatchNormalization(momentum=0.8)(dense_1)
output = Dense(1, activation='sigmoid')(batch_2)
model = Model([model_input], output)
Compile the Model
Create the optimizer object:
optimizer = tf.keras.optimizers.Adam(learning_rate=1e-2)
Compile the model with loss function and metric:
model.compile(optimizer=optimizer, loss='binary_crossentropy', metrics=['accuracy'])
Fit the Model
Calculate the number of training steps per epoch for the given batch size:
batch_size = 5
train_steps = len(training) // batch_size
Train the model:
epochs = 3
for epoch in range(epochs):
train_generator=get_generator(training_features, training_labels, batch_size =batch_size)
validation_generator =get_generator(validation_features,validation_labels, batch_size=batch_size)
model.fit_generator(train_generator,steps_per_epoch= train_steps, validation_data =validation_generator, validation_steps=1)
Evaluate and get Predictions
Obtain a validation data generator:
validation_generator = get_generator(validation_features, validation_labels, batch_size=30)
Evaluate the model:
predictions = model.evaluate_generator(validation_generator, steps=1)
Image Data Augmentation
Load the CIFAR-10 Dataset:
(training_features, training_labels), (test_features, test_labels) = cifar10.load_data()
Convert the labels to a one-hot encoding:
num_classes = 10
training_labels = tf.keras.utils.to_categorical(training_labels, num_classes)
test_labels = tf.keras.utils.to_categorical(test_labels, num_classes)
Create a Generator Function
def get_generator(features, labels, batch_size=1):
for n in range(int(len(features)/batch_size)):
yield (features[n*batch_size:(n+1)*batch_size], labels[n*batch_size:(n+1)*batch_size])
Use the function we created to get a training data generator with a batch size of 1:
training_generator = get_generator(training_features, training_labels)
Create a Data Augmentation Generator
Create a function to convert an image to monochrome:
def monochrome(x):
def func_bw(a):
average_colour = np.mean(a)
return [average_colour, average_colour, average_colour]
x = np.apply_along_axis(func_bw, -1, x)
return x
Create an ImageDataGenerator object:
image_generator = ImageDataGenerator(
preprocessing_function=monochrome, rotation_range=180, rescale=(1/255.0) )
image_generator.fit(training_features)
Create an iterable generator using the `flow` function:
image_generator_iterable = image_generator.flow(training_features, training_labels, batch_size=1, shuffle=False)
Flow from Directory
train_path = 'data/flowers-recognition-split/train'
val_path = 'data/flowers-recognition-split/val'
datagenerator = ImageDataGenerator(rescale=(1/255.0))
classes = ['daisy', 'dandelion', 'rose', 'sunflower', 'tulip']
train_generator = datagenerator.flow_from_directory(train_path, batch_size = 64, classes=classes, target_size=(16,16))
val_generator = datagenerator.flow_from_directory(val_path, batch_size = 64, classes=classes, target_size=(16,16))
The Dataset Class
Simple Dataset
x = np.zeros((100,10,2,2))
dataset1 = tf.data.Dataset.from_tensor_slices(x)
x2 = [np.zeros((10,2,2)), np.zeros((5,2,2))]
dataset2 = tf.data.Dataset.from_tensor_slices(x2)
Zipped Dataset
dataset_zipped = tf.data.Dataset.zip((dataset1, dataset2))
Create a Dataset from Numpy Arrays
(train_features, train_labels), (test_features, test_labels) = tf.keras.datasets.mnist.load_data()
mnist_dataset = tf.data.Dataset.from_tensor_slices((train_features, train_labels))
Create a Dataset from Text Data
text_files = sorted([f.path for f in os.scandir('data/shakespeare')])
with open(text_files[0], 'r') as fil:
contents = [fil.readline() for i in range(5)]
for line in contents:
print(line)
shakespeare_dataset = tf.data.TextLineDataset(text_files)
Use the take method to get and print the first 5 lines of the dataset:
first_5_lines_dataset = iter(shakespeare_dataset.take(5))
lines = [line for line in first_5_lines_dataset]
for line in lines:
print(line)
Interleave Lines from the Text Data Files
text_files_dataset = tf.data.Dataset.from_tensor_slices(text_files)
files = [file for file in text_files_dataset]
for file in files:
print(file)
Interleave the lines from the text files:
interleaved_shakespeare_dataset=text_files_dataset.interleave(tf.data.TextLineDataset, cycle_length=9)
Training with Datasets
Load the UCI Bank Marketing Dataset
bank_dataframe = pd.read_csv('data/bank/bank-full.csv', delimiter=';')
Select features from the DataFrame:
features = ['age', 'job', 'marital', 'education', 'default', 'balance', 'housing',
'loan', 'contact', 'campaign', 'pdays', 'poutcome']
labels = ['y']
bank_dataframe = bank_dataframe.filter(features + labels)
Preprocess the Data
Convert the categorical features in the DataFrame to one-hot encodings:
encoder = LabelBinarizer()
categorical_features = ['default', 'housing', 'job', 'loan', 'education', 'contact', 'poutcome']
for feature in categorical_features:
bank_dataframe[feature] = tuple(encoder.fit_transform(bank_dataframe[feature]))
Shuffle the DataFrame:
bank_dataframe = bank_dataframe.sample(frac=1).reset_index(drop=True)
Create the Dataset Object
Convert the DataFrame to a Dataset:
bank_dataset = tf.data.Dataset.from_tensor_slices(dict(bank_dataframe))
Filter the Dataset to retain only entries with a ‘divorced’ marital status:
bank_dataset = bank_dataset.filter(lambda x : tf.equal(x['marital'], tf.constant([b'divorced']))[0] )
Convert the label (‘y’) to an integer instead of ‘yes’ or ‘no’:
def map_label(x):
x['y'] = 0 if (x['y'] == tf.constant([b'no'], dtype= tf.string)) else 1
return x
bank_dataset = bank_dataset.map(map_label)
Remove the ‘marital’ column:
bank_dataset = bank_dataset.map(lambda x: {key:val for key,val in x.items() if key!= 'marital'})
Create Input and Output Data Tuples
def map_feature_label(x):
features = [[x['age']], [x['balance']], [x['campaign']], x['contact'], x['default'],
x['education'], x['housing'], x['job'], x['loan'], [x['pdays']], x['poutcome']]
return (tf.concat(features, axis=0), x['y'])
bank_dataset = bank_dataset.map(map_feature_label)
Split into a Training and a Validation Set
dataset_length = 0
for _ in bank_dataset:
dataset_length += 1
print(dataset_length)
training_elements = int(dataset_length * 0.7)
train_dataset = bank_dataset.take(training_elements)
validation_dataset = bank_dataset.skip(training_elements)
Build a Classification Model
model = Sequential()
model.add(Input(shape=(30,)))
model.add(BatchNormalization(momentum=0.8))
model.add(Dense(400, activation='relu'))
model.add(BatchNormalization(momentum=0.8))
model.add(Dense(400, activation='relu'))
model.add(BatchNormalization(momentum=0.8))
model.add(Dense(1, activation='sigmoid'))
Compile the Model
optimizer = tf.keras.optimizers.Adam(1e-4)
model.compile(optimizer=optimizer, loss='binary_crossentropy', metrics=['accuracy'])
Fit the Model
Create batched training and validation datasets:
train_dataset = train_dataset.batch(20, drop_remainder=True)
validation_dataset = validation_dataset.batch(100)
Shuffle the training data:
train_dataset = train_dataset.shuffle(1000)
Fit the model:
history = model.fit(train_dataset, validation_data = validation_dataset, epochs=5)