<jupyter_start><jupyter_text>Data Exploration Import libraries necessary for this project.<jupyter_code>! pip install pytesseract
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cv2
import seaborn as sn
from PIL import Image
from scipy.ndimage import rotate
from IPython.display import display 
from sklearn.metrics import classification_report

import keras
from keras.models import Model
from keras.layers import Dense, Conv2D, MaxPooling2D, Flatten, Dropout, Input
from keras.layers.normalization import BatchNormalization
from keras.callbacks import ModelCheckpoint

from keras.utils.np_utils import to_categorical
from keras.preprocessing.image import ImageDataGenerator
from sklearn.model_selection import train_test_split
from sklearn.metrics import confusion_matrix<jupyter_output><empty_output><jupyter_text>Load Arabic Letters dataset files into dataframes<jupyter_code># Training letters images and labels files
letters_training_images_file_path = ("csvTrainImages 13440x1024.csv")
letters_training_labels_file_path = ("csvTrainLabel 13440x1.csv")
# Testing letters images and labels files
letters_testing_images_file_path = ("csvTestImages 3360x1024.csv")
letters_testing_labels_file_path = ("csvTestLabel 3360x1.csv")
# Loading dataset into dataframes
training_letters_images = pd.read_csv(letters_training_images_file_path, header=None)
training_letters_labels = pd.read_csv(letters_training_labels_file_path, header=None)
testing_letters_images = pd.read_csv(letters_testing_images_file_path, header=None)
testing_letters_labels = pd.read_csv(letters_testing_labels_file_path, header=None)

# print statistics about the dataset
print("There are %d training arabic letter images of 32x32 pixels." %training_letters_images.shape[0])
print("There are %d testing arabic letter images of 32x32 pixels." %testing_letters_images.shape[0])
training_letters_images.head()
letters_testing_labels_file_path = ("csvTestLabel 3360x1.csv")

# Loading dataset into dataframes
training_letters_images = pd.read_csv(letters_training_images_file_path, header=None)
training_letters_labels = pd.read_csv(letters_training_labels_file_path, header=None)
testing_letters_images = pd.read_csv(letters_testing_images_file_path, header=None)
testing_letters_labels = pd.read_csv(letters_testing_labels_file_path, header=None)

# print statistics about the dataset
print("There are %d training arabic letter images of 32x32 pixels." %training_letters_images.shape[0])
print("There are %d testing arabic letter images of 32x32 pixels." %testing_letters_images.shape[0])
training_letters_images.head()<jupyter_output>There are 13440 training arabic letter images of 32x32 pixels.
There are 3360 testing arabic letter images of 32x32 pixels.
There are 13440 training arabic letter images of 32x32 pixels.
There are 3360 testing arabic letter images of 32x32 pixels.<jupyter_text>Convert csv values to an image Writting a method to be used later if we want visualization of an image from its pixels values.<jupyter_code>def convert_values_to_image(image_values, display=False):
    image_array = np.asarray(image_values)
    image_array = image_array.reshape(32,32).astype('uint8')
    # The original dataset is reflected so we will flip it then rotate for a better view only.
    image_array = np.flip(image_array, 0)
    image_array = rotate(image_array, -90)
    new_image = Image.fromarray(image_array)
    if display == True:
        new_image.show()
    return new_image
convert_values_to_image(training_letters_images.loc[0], True)<jupyter_output><empty_output><jupyter_text>Data Preprocessing Image Normalization<jupyter_code>training_letters_images_scaled = training_letters_images.values.astype('float32')/255
training_letters_labels = training_letters_labels.values.astype('int32')
testing_letters_images_scaled = testing_letters_images.values.astype('float32')/255
testing_letters_labels = testing_letters_labels.values.astype('int32')
print("Training images of letters after scaling")
print(training_letters_images_scaled.shape)
training_letters_images_scaled[0:5]<jupyter_output>Training images of letters after scaling
(13440, 1024)<jupyter_text>Encoding Categorical Labels<jupyter_code>number_of_classes = 28

training_letters_labels_encoded = to_categorical(training_letters_labels-1, num_classes=number_of_classes)
testing_letters_labels_encoded = to_categorical(testing_letters_labels-1, num_classes=number_of_classes)

print(training_letters_labels_encoded)<jupyter_output>[[1. 0. 0. ... 0. 0. 0.]
 [1. 0. 0. ... 0. 0. 0.]
 [1. 0. 0. ... 0. 0. 0.]
 ...
 [0. 0. 0. ... 0. 0. 1.]
 [0. 0. 0. ... 0. 0. 1.]
 [0. 0. 0. ... 0. 0. 1.]]<jupyter_text>Reshaping Input Images to 32x32x1<jupyter_code>training_letters_images_scaled = training_letters_images_scaled.reshape([-1, 32, 32, 1])
testing_letters_images_scaled = testing_letters_images_scaled.reshape([-1, 32, 32, 1])

print(training_letters_images_scaled.shape, training_letters_labels_encoded.shape, testing_letters_images_scaled.shape, testing_letters_labels_encoded.shape)<jupyter_output>(13440, 32, 32, 1) (13440, 28) (3360, 32, 32, 1) (3360, 28)<jupyter_text>Designing Model Architecture<jupyter_code>from keras.models import Sequential
from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D, BatchNormalization, Dropout, Dense
def create_model(optimizer='adam', kernel_initializer='he_normal', activation='relu'):
    # create model
    model = Sequential()
    model.add(Conv2D(filters=16, kernel_size=3, padding='same', input_shape=(32, 32, 1), kernel_initializer=kernel_initializer, activation=activation))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=2))
    model.add(Dropout(0.2))

    model.add(Conv2D(filters=32, kernel_size=3, padding='same', kernel_initializer=kernel_initializer, activation=activation))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=2))
    model.add(Dropout(0.2))

    model.add(Conv2D(filters=64, kernel_size=3, padding='same', kernel_initializer=kernel_initializer, activation=activation))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=2))
    model.add(Dropout(0.2))

    model.add(Conv2D(filters=128, kernel_size=3, padding='same', kernel_initializer=kernel_initializer, activation=activation))
    model.add(BatchNormalization())
    model.add(MaxPooling2D(pool_size=2))
    model.add(Dropout(0.2))
    model.add(GlobalAveragePooling2D())

    #Fully connected final layer
    model.add(Dense(28, activation='softmax'))

    # Compile model
    model.compile(loss='categorical_crossentropy', metrics=['accuracy'], optimizer=optimizer)
    return model<jupyter_output><empty_output><jupyter_text>Model Summary And Visualization<jupyter_code>model = create_model()
model.summary()
from keras.utils import plot_model
plot_model(model, to_file="model.png", show_shapes=True)
from IPython.display import Image as IPythonImage
display(IPythonImage('model.png'))<jupyter_output><empty_output><jupyter_text>Parameters Tuning<jupyter_code>seed = 7
np.random.seed(seed)

# define the grid search parameters
optimizer = ['RMSprop', 'Adam', 'Adagrad', 'Nadam']
kernel_initializer = ['normal', 'uniform']
activation = ['relu', 'linear', 'tanh']

param_grid = dict(optimizer=optimizer, kernel_initializer=kernel_initializer, activation=activation)

# count number of different parameters values combinations
parameters_number = 1
for x in param_grid:
    parameters_number = parameters_number * len(param_grid[x]) 
print("Number of different parameter combinations = {}".format(parameters_number))
datagen = ImageDataGenerator(
        featurewise_center=False,  # set input mean to 0 over the dataset
        samplewise_center=False,  # set each sample mean to 0
        featurewise_std_normalization=False,  # divide inputs by std of the dataset
        samplewise_std_normalization=False,  # divide each input by its std
        zca_whitening=False,  # apply ZCA whitening
        rotation_range=10,  # randomly rotate images in the range (degrees, 0 to 180)
        zoom_range = 0.1, # Randomly zoom image 
        width_shift_range=0.1,  # randomly shift images horizontally (fraction of total width)
        height_shift_range=0.1,  # randomly shift images vertically (fraction of total height)
        horizontal_flip=False,  # randomly flip images
        vertical_flip=False)  # randomly flip images

epochs = 200
batch_size = 64
model = create_model(optimizer='Adam', kernel_initializer='uniform', activation='relu')<jupyter_output><empty_output><jupyter_text>Training the Model Fitting the Model<jupyter_code>batch_size = 64
epochs = 200

train_gen = datagen.flow(x_train, y_train, batch_size=batch_size)
test_gen = datagen.flow(x_test, y_test, batch_size=batch_size)
from keras.callbacks import ModelCheckpoint  

model_checkpoint_callback = ModelCheckpoint(
    filepath="best.hdf5",
    monitor='val_accuracy', 
    verbose=1, 
    save_best_only=True, 
    mode='max')


history = model.fit_generator(train_gen, 
                              epochs = epochs, 
                              steps_per_epoch = x_train.shape[0] // batch_size,
                              validation_data = test_gen,
                              validation_steps = x_test.shape[0] // batch_size,
                              callbacks=[model_checkpoint_callback])<jupyter_output>/usr/local/lib/python3.7/dist-packages/tensorflow/python/keras/engine/training.py:1844: UserWarning: `Model.fit_generator` is deprecated and will be removed in a future version. Please use `Model.fit`, which supports generators.
  warnings.warn('`Model.fit_generator` is deprecated and '<jupyter_text>Plotting Loss and Accuracy Curves with Epochs<jupyter_code>def plot_loss_accuracy(history):
    # Loss Curves
    plt.figure(figsize=[8,6])
    plt.plot(history.history['loss'],'r',linewidth=3.0)
    plt.plot(history.history['val_loss'],'b',linewidth=3.0)
    plt.legend(['Training loss', 'Validation Loss'],fontsize=18)
    plt.xlabel('Epochs ',fontsize=16)
    plt.ylabel('Loss',fontsize=16)
    plt.title('Loss Curves',fontsize=16)

    # Accuracy Curves
    plt.figure(figsize=[8,6])
    plt.plot(history.history['accuracy'],'r',linewidth=3.0)
    plt.plot(history.history['val_accuracy'],'b',linewidth=3.0)
    plt.legend(['Training Accuracy', 'Validation Accuracy'],fontsize=18)
    plt.xlabel('Epochs ',fontsize=16)
    plt.ylabel('Accuracy',fontsize=16)
    plt.title('Accuracy Curves',fontsize=16)
plot_loss_accuracy(history)<jupyter_output><empty_output><jupyter_text>Load the Model with the Best Validation Loss<jupyter_code>model.load_weights('weights.hdf5')<jupyter_output><empty_output><jupyter_text>Test the Model<jupyter_code>metrics = model.evaluate(testing_letters_images_scaled, testing_letters_labels_encoded, verbose=1)
print("Test Accuracy: {}".format(metrics[1]))
print("Test Loss: {}".format(metrics[0]))
epochs = 30
batch_size = 20

checkpointer = ModelCheckpoint(filepath='weights.hdf5', verbose=1, save_best_only=True)

history = model.fit(training_letters_images_scaled, training_letters_labels_encoded, 
                    validation_data=(testing_letters_images_scaled, testing_letters_labels_encoded),
                    epochs=epochs, batch_size=batch_size, verbose=1, callbacks=[checkpointer])
          
model.load_weights('weights.hdf5')
plot_loss_accuracy(history)
plot_loss_accuracy(history)
plt.figure(figsize=(10,10))
plt.plot(history.history["accuracy"])
plt.plot(history.history["val_accuracy"])
plt.legend(["accuracy","val_accuracy"])
plt.show()<jupyter_output><empty_output><jupyter_text>Testing the Model again<jupyter_code># Final evaluation of the model
metrics = model.evaluate(testing_letters_images_scaled, testing_letters_labels_encoded, verbose=1)
print("Test Accuracy: {}".format(metrics[1]))
print("Test Loss: {}".format(metrics[0]))<jupyter_output>105/105 [==============================] - 2s 15ms/step - loss: 0.0948 - accuracy: 0.9783
Test Accuracy: 0.9782738089561462
Test Loss: 0.09481441974639893<jupyter_text>Saving the Final Model<jupyter_code>from keras.models import model_from_yaml
model_yaml = model.to_yaml()
with open("model.yaml", "w") as yaml_file:
    yaml_file.write(model_yaml)<jupyter_output><empty_output><jupyter_text>Save the model weights to file.<jupyter_code># serialize weights to HDF5
model.save_weights("model.h5")
print("Saved model to disk")
# load YAML and create model
yaml_file = open('model.yaml', 'r')
loaded_model_yaml = yaml_file.read()
yaml_file.close()
loaded_model = model_from_yaml(loaded_model_yaml)
# load weights into new model
loaded_model.load_weights("model.h5")
print("Loaded model from disk")
 
# compile the loaded model
loaded_model.compile(loss='binary_crossentropy', optimizer='Adam', metrics=['accuracy'])<jupyter_output>Loaded model from disk<jupyter_text>Predict Image Classes<jupyter_code>def get_predicted_classes(model, data, labels=None):
    image_predictions = model.predict(data)
    predicted_classes = np.argmax(image_predictions, axis=1)
    true_classes = np.argmax(labels, axis=1)
    return predicted_classes, true_classes, image_predictions
def get_classification_report(y_true, y_pred):
    print(classification_report(y_true, y_pred))
!pip install Pillow
y_pred, y_true, image_predictions = get_predicted_classes(model, testing_letters_images_scaled, testing_letters_labels_encoded)
get_classification_report(y_true, y_pred)
errors = (y_pred - y_true != 0)


Y_pred_classes_errors = y_pred[errors]
Y_pred_errors = image_predictions[errors]
Y_true_errors = y_true[errors]
X_val_errors = testing_letters_images_scaled[errors]


def display_errors(errors_index,img_errors,pred_errors, obs_errors):
    """ This function shows 6 images with their predicted and real labels"""
    n = 0
    nrows = 2
    ncols = 3
    fig, ax = plt.subplots(nrows,ncols,sharex=True,sharey=True)
    for row in range(nrows):
        for col in range(ncols):
            error = errors_index[n]
            
            image_array = img_errors[error]
            image_array = np.flip(image_array, 0)
            image_array = rotate(image_array, -90)
            
            
            ax[row,col].imshow((image_array).reshape((32,32)))
            ax[row,col].set_title("Predicted label :{}\nTrue label :{}".format(pred_errors[error],obs_errors[error]))
            n += 1

# Probabilities of the wrong predicted letters
Y_pred_errors_prob = np.max(Y_pred_errors,axis = 1)

# Predicted probabilities of the true values in the error set
true_prob_errors = np.diagonal(np.take(Y_pred_errors, Y_true_errors, axis=1))

# Difference between the probability of the predicted label and the true label
delta_pred_true_errors = Y_pred_errors_prob - true_prob_errors

# Sorted list of the delta prob errors
sorted_dela_errors = np.argsort(delta_pred_true_errors)

# Top 6 errors 
most_important_errors = sorted_dela_errors[-6:]

# Show the top 6 errors
display_errors(most_important_errors, X_val_errors, Y_pred_classes_errors, Y_true_errors)
fig = plt.figure(0, figsize=(18,18))
indices = np.random.randint(0, testing_letters_labels.shape[0], size=49)
y_pred = np.argmax(model.predict(training_letters_images_scaled), axis=1)

for i, idx in enumerate(indices):
    plt.subplot(7,7,i+1)
        
    image_array = training_letters_images_scaled[idx][:,:,0]
    image_array = np.flip(image_array, 0)
    image_array = rotate(image_array, -90)
       
    plt.imshow(image_array, cmap='gray')
    plt.title("Pred: {} - Label: {}".format(y_pred[idx], (training_letters_labels[idx] -1)))
    plt.xticks([])
    plt.yticks([])
plt.show()

x_train = pd.read_csv("csvTrainImages 13440x1024.csv",header=None).to_numpy()
y_train = pd.read_csv("csvTrainLabel 13440x1.csv",header=None).to_numpy()-1 

x_test = pd.read_csv("csvTestImages 3360x1024.csv",header=None).to_numpy()
y_test = pd.read_csv("csvTestLabel 3360x1.csv",header=None).to_numpy()-1

print("x_train.shape =", x_train.shape, "\ny_train.shape =", y_train.shape, "\nx_test.shape =", x_test.shape, "\ny_test.shape =", y_test.shape)
arabic_labels = ['alef', 'beh', 'teh', 'theh', 'jeem', 'hah', 'khah', 'dal', 'thal',
                'reh', 'zain', 'seen', 'sheen', 'sad', 'dad', 'tah', 'zah', 'ain', 
                'ghain', 'feh', 'qaf', 'kaf', 'lam', 'meem', 'noon', 'heh', 'waw', 'yeh']
x_train = x_train.reshape(-1,32,32,1)
x_test = x_test.reshape(-1,32,32,1)

x_train = x_train / 255.0
x_test = x_test / 255.0

print(x_train.shape, x_test.shape)

ra = np.random.randint(0, 13440, size=25)
plt.figure(figsize=(10,10))
for i in range(25):
    plt.subplot(5,5,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.imshow(x_train[ra[i]].squeeze().T,"gray")
    plt.xlabel(arabic_characters[int(y_train[ra[i]][0])])
plt.show()

import random
x = random.randint(0, 13440)
plt.imshow(x_train[x].squeeze().T)
plt.title(arabic_labels[y_train[x][0]])
import random
x = random.randint(0, 13440)
plt.imshow(x_train[x].squeeze().T)
plt.title(arabic_labels[y_train[x][0]])
import random
x = random.randint(0, 13440)
plt.imshow(x_train[x].squeeze().T)
plt.title(arabic_labels[y_train[x][0]])
import random
x = random.randint(0, 13440)
plt.imshow(x_train[x].squeeze().T)
plt.title(arabic_labels[y_train[x][0]])
import random
x = random.randint(0, 13440)
plt.imshow(x_train[x].squeeze().T)
plt.title(arabic_labels[y_train[x][0]])
import random
x = random.randint(0, 13440)
plt.imshow(x_train[x].squeeze().T)
plt.title(arabic_labels[y_train[x][0]])
import random
x = random.randint(0, 13440)
plt.imshow(x_train[x].squeeze().T)
plt.title(arabic_labels[y_train[x][0]])
# Converting the class vector in integers to binary class matrix
y_train = to_categorical(y_train)
y_test = to_categorical(y_test)
print(y_train.shape, y_test.shape)
for i in zip(y_train[0], arabic_characters):
    print(i)
y_preds = model.predict(x_test)
y_pred_classes = np.argmax(y_preds, axis=1)
y_true = np.argmax(y_test, axis=1)
cm = confusion_matrix(y_true, y_pred_classes)

plt.figure(figsize=(10,15))
sn.heatmap(cm, annot=True, fmt="d")<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.11508130744547779, 'fraction_numerical': 0.03040191439604068, 'mean_word_length': 5.201686340640809, 'pattern_counts': {'":': 0, '<': 62, '<?xml version=': 0, '>': 62, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '17', 'dir': 'jupyter-structured-clean-dedup', 'id': '542753', 'license': 'Apache-2.0', 'n_tokens_mistral': 6748, 'n_tokens_neox': 6216, 'n_words': 1211, 'path': 'Arabic_Characters_Recognition.ipynb', 'repo_name': 'Akshatpattiwar512/Convolutional-Neural-Network', 'size': '18387'}