{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"nvidiaTeslaT4","dataSources":[{"sourceId":65471,"databundleVersionId":7211464,"sourceType":"competition"}],"dockerImageVersionId":30616,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# <div> <center> <b> NN Arabic Handwriting Recognition </b> </center> </div>\n\n## <b> Credentials: </b> (E-JUST Submission)\n- Name: Yousef Ibrahim Gomaa Mahmoud\n- ID: 320210207\n- Group: 1\n- Section: AID 3\n\n## <b> Notebook Workflow: </b>\n- Environment Setup\n- Data Pre-processing\n- Data Visualization\n- Model Definition\n- Training the Model\n- Prediction\n\n## <b> Introduction: </b>\n<p> In this notebook, a model is to be trained to <b> recognize/classify arabic letters </b> using a convolutional neural network (CNN) through Keras, a TensorFlow tool. </p>\n\n## <b> Dataset: </b>\nhttps://www.kaggle.com/competitions/arabic-letters-classification","metadata":{}},{"cell_type":"markdown","source":"## Environment Setup:\n### Importing Packages","metadata":{}},{"cell_type":"code","source":"import os\nimport numpy as np\nimport pandas as pd\nfrom matplotlib import pyplot as plt\nimport tensorflow as tf\nfrom tensorflow import keras","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:07.146112Z","iopub.execute_input":"2025-08-30T18:56:07.146817Z","iopub.status.idle":"2025-08-30T18:56:18.183107Z","shell.execute_reply.started":"2025-08-30T18:56:07.146767Z","shell.execute_reply":"2025-08-30T18:56:18.182184Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### GPU Checkup:\nKaggle provides 2 GPUs which are relied upon heavily during the training phase.","metadata":{}},{"cell_type":"code","source":"for device in tf.config.list_physical_devices():\n    print(\": {}\".format(device.name))","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:18.185065Z","iopub.execute_input":"2025-08-30T18:56:18.186012Z","iopub.status.idle":"2025-08-30T18:56:18.603954Z","shell.execute_reply.started":"2025-08-30T18:56:18.185978Z","shell.execute_reply":"2025-08-30T18:56:18.602997Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Loading the Dataset(s):","metadata":{}},{"cell_type":"code","source":"datasets = ['/kaggle/input/arabic-letters-classification/Final_Arabic_Alpha_dataset/Final_Arabic_Alpha_dataset/train',\\\n            '/kaggle/input/arabic-letters-classification/Final_Arabic_Alpha_dataset/Final_Arabic_Alpha_dataset/test',\n           '/kaggle/input/arabic-letters-classification/Final_Arabic_Alpha_dataset/Final_Arabic_Alpha_dataset']\nNUM_CLASS = 65\nIMAGE_SIZE = (160,160)\nBATCH_SIZE = 512\nSEED = 43\nEPOCHS = 50","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:18.605221Z","iopub.execute_input":"2025-08-30T18:56:18.605487Z","iopub.status.idle":"2025-08-30T18:56:18.619104Z","shell.execute_reply.started":"2025-08-30T18:56:18.605465Z","shell.execute_reply":"2025-08-30T18:56:18.618245Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- There are 65 classes to classify into (0-64)\n- Images must be resized to a consistent size in order to train properly.","metadata":{}},{"cell_type":"code","source":"# str = \"user001_heh_middle_042.png\"\n# str[8:18]","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:18.62038Z","iopub.execute_input":"2025-08-30T18:56:18.620968Z","iopub.status.idle":"2025-08-30T18:56:18.627619Z","shell.execute_reply.started":"2025-08-30T18:56:18.620937Z","shell.execute_reply":"2025-08-30T18:56:18.626677Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Pre-processing:\nSplitting training data into 'Training' and 'Validation' for the model to train on.","metadata":{}},{"cell_type":"code","source":"train_images = tf.keras.utils.image_dataset_from_directory(\n    datasets[0],\n    validation_split=0.2,\n    color_mode='grayscale',\n    label_mode=\"categorical\",\n    subset=\"training\",\n    shuffle=True,\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE)\n","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:18.630178Z","iopub.execute_input":"2025-08-30T18:56:18.630398Z","iopub.status.idle":"2025-08-30T18:56:32.064594Z","shell.execute_reply.started":"2025-08-30T18:56:18.63038Z","shell.execute_reply":"2025-08-30T18:56:32.063953Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"train_validation = tf.keras.utils.image_dataset_from_directory(\n    datasets[0],\n    validation_split=0.2,\n    label_mode=\"categorical\",\n    color_mode='grayscale',\n    subset=\"validation\",\n    shuffle=True,\n    seed=SEED,\n    image_size=IMAGE_SIZE,\n    batch_size=BATCH_SIZE)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:32.065507Z","iopub.execute_input":"2025-08-30T18:56:32.065803Z","iopub.status.idle":"2025-08-30T18:56:38.645033Z","shell.execute_reply.started":"2025-08-30T18:56:32.065781Z","shell.execute_reply":"2025-08-30T18:56:38.644197Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Data Visualization:","metadata":{}},{"cell_type":"code","source":"class_names = train_images.class_names\nplt.figure(figsize=(10, 10))\nfor images, labels in train_images.take(1): # Takes a batch and shows the first 9 images\n    for i in range(9):\n        ax = plt.subplot(3, 3, i + 1)\n        plt.imshow(images[i].numpy().astype(\"uint8\"), cmap=plt.cm.Greys_r)\n        plt.title(class_names[np.where(np.array(labels[i])==1)[0][0]])\n        plt.axis(\"off\")","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:38.646202Z","iopub.execute_input":"2025-08-30T18:56:38.646526Z","iopub.status.idle":"2025-08-30T18:56:44.080305Z","shell.execute_reply.started":"2025-08-30T18:56:38.646496Z","shell.execute_reply":"2025-08-30T18:56:44.079434Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for images, labels in train_images:\n    print(images.shape)\n    print(labels.shape)\n    break","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:44.081278Z","iopub.execute_input":"2025-08-30T18:56:44.081534Z","iopub.status.idle":"2025-08-30T18:56:45.810922Z","shell.execute_reply.started":"2025-08-30T18:56:44.081513Z","shell.execute_reply":"2025-08-30T18:56:45.810025Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"- (Batch Size, Width, Height, 1 Image Tensor Channel (Greyscale))\n- (Batch Size, Number of Classes)","metadata":{}},{"cell_type":"markdown","source":"## Model Definition:","metadata":{}},{"cell_type":"code","source":"model = tf.keras.Sequential([\n    tf.keras.layers.Rescaling(1./255),\n    tf.keras.layers.experimental.preprocessing.RandomRotation(0.1),\n    tf.keras.layers.experimental.preprocessing.RandomZoom(0.1),\n    tf.keras.layers.Conv2D(32, (3, 3), activation = 'relu', padding = 'same'),\n    tf.keras.layers.MaxPooling2D(2, 2),\n    tf.keras.layers.Conv2D(64, (3, 3), activation = 'relu', padding = 'same'),\n    tf.keras.layers.MaxPooling2D(2, 2),\n    tf.keras.layers.Conv2D(64, (3, 3), activation = 'relu', padding = 'same'),\n    tf.keras.layers.MaxPooling2D(2, 2),\n    tf.keras.layers.Conv2D(128, (3, 3), activation = 'relu', padding = 'same'),\n    tf.keras.layers.MaxPooling2D(2, 2),\n    tf.keras.layers.Conv2D(256, (3, 3), activation = 'relu', padding = 'same'),\n    tf.keras.layers.MaxPooling2D(2, 2),\n    tf.keras.layers.Flatten(),\n    tf.keras.layers.Dense(1024, activation='relu',\n#                            kernel_regularizer=tf.keras.regularizers.l2(0.001)\n                         ),\n    tf.keras.layers.Dropout(0.2),\n    tf.keras.layers.Dense(NUM_CLASS, activation=tf.nn.softmax)\n])","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:45.81194Z","iopub.execute_input":"2025-08-30T18:56:45.812197Z","iopub.status.idle":"2025-08-30T18:56:45.855347Z","shell.execute_reply.started":"2025-08-30T18:56:45.812176Z","shell.execute_reply":"2025-08-30T18:56:45.854697Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Model Summary:","metadata":{}},{"cell_type":"code","source":"model.build(input_shape=(BATCH_SIZE, IMAGE_SIZE[0], IMAGE_SIZE[1], 1))\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:45.856223Z","iopub.execute_input":"2025-08-30T18:56:45.856428Z","iopub.status.idle":"2025-08-30T18:56:46.174068Z","shell.execute_reply.started":"2025-08-30T18:56:45.856409Z","shell.execute_reply":"2025-08-30T18:56:46.173157Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Model Optimization:","metadata":{}},{"cell_type":"code","source":"learning_rate = 0.001\nlr_mod = tf.keras.optimizers.schedules.ExponentialDecay(learning_rate, decay_steps=100000,\\\n                                                             decay_rate=0.96, staircase=True)\n\nmodel.compile(optimizer = tf.keras.optimizers.Adam(learning_rate=lr_mod), loss = 'categorical_crossentropy', metrics=['accuracy'])","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:46.17507Z","iopub.execute_input":"2025-08-30T18:56:46.175312Z","iopub.status.idle":"2025-08-30T18:56:46.201744Z","shell.execute_reply.started":"2025-08-30T18:56:46.175292Z","shell.execute_reply":"2025-08-30T18:56:46.201179Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Training the Model:","metadata":{}},{"cell_type":"code","source":"h1 = model.fit(train_images,\n              validation_data=train_validation,\n              epochs=EPOCHS)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T18:56:46.202872Z","iopub.execute_input":"2025-08-30T18:56:46.203477Z","iopub.status.idle":"2025-08-30T19:20:43.387736Z","shell.execute_reply.started":"2025-08-30T18:56:46.203447Z","shell.execute_reply":"2025-08-30T19:20:43.386981Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.plot(h1.history['loss'])\nplt.plot(h1.history['val_loss'])\nplt.title('Model Loss')\nplt.ylabel('loss')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:20:43.388923Z","iopub.execute_input":"2025-08-30T19:20:43.389158Z","iopub.status.idle":"2025-08-30T19:20:43.642235Z","shell.execute_reply.started":"2025-08-30T19:20:43.38914Z","shell.execute_reply":"2025-08-30T19:20:43.641525Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.plot(h1.history['accuracy'])\nplt.plot(h1.history['val_accuracy'])\nplt.title('Model Accuracy')\nplt.ylabel('accuracy')\nplt.xlabel('epoch')\nplt.legend(['train', 'test'], loc='upper left')\nplt.show()","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:20:43.645661Z","iopub.execute_input":"2025-08-30T19:20:43.646005Z","iopub.status.idle":"2025-08-30T19:20:43.817181Z","shell.execute_reply.started":"2025-08-30T19:20:43.645984Z","shell.execute_reply":"2025-08-30T19:20:43.816514Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Saving the Model:","metadata":{}},{"cell_type":"markdown","source":"Keras format:","metadata":{}},{"cell_type":"code","source":"model.save('nn-arabic-handwriting-320210207.keras')","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:20:43.818098Z","iopub.execute_input":"2025-08-30T19:20:43.818344Z","iopub.status.idle":"2025-08-30T19:20:44.195864Z","shell.execute_reply.started":"2025-08-30T19:20:43.818323Z","shell.execute_reply":"2025-08-30T19:20:44.195121Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"### Loading the Model:","metadata":{}},{"cell_type":"code","source":"# model = tf.keras.models.load_model('nn-arabic-handwriting-320210207.keras')","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:20:44.196974Z","iopub.execute_input":"2025-08-30T19:20:44.197214Z","iopub.status.idle":"2025-08-30T19:20:44.200957Z","shell.execute_reply.started":"2025-08-30T19:20:44.197194Z","shell.execute_reply":"2025-08-30T19:20:44.200137Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Prediction:","metadata":{}},{"cell_type":"markdown","source":"Loading the 'Test' dataset:","metadata":{}},{"cell_type":"code","source":"test_images = tf.keras.utils.image_dataset_from_directory(\n        datasets[1],\n        labels=None,\n        label_mode=\"categorical\",\n        color_mode='grayscale',\n        shuffle=False,\n        image_size=IMAGE_SIZE,\n        batch_size=BATCH_SIZE)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:20:44.202104Z","iopub.execute_input":"2025-08-30T19:20:44.202442Z","iopub.status.idle":"2025-08-30T19:21:05.600345Z","shell.execute_reply.started":"2025-08-30T19:20:44.202412Z","shell.execute_reply":"2025-08-30T19:21:05.599719Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"plt.figure(figsize=(10, 10))\nfor images in test_images.take(1): # Takes a batch and shows the first 9 images\n    for i in range(9):\n        ax = plt.subplot(3, 3, i + 1)\n        plt.imshow(images[i].numpy().astype(\"uint8\"), cmap=plt.cm.Greys_r)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:05.601266Z","iopub.execute_input":"2025-08-30T19:21:05.601515Z","iopub.status.idle":"2025-08-30T19:21:07.287455Z","shell.execute_reply.started":"2025-08-30T19:21:05.601493Z","shell.execute_reply":"2025-08-30T19:21:07.286632Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"for images in test_images:\n    print(images.shape)\n    break","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:07.289751Z","iopub.execute_input":"2025-08-30T19:21:07.290015Z","iopub.status.idle":"2025-08-30T19:21:07.487192Z","shell.execute_reply.started":"2025-08-30T19:21:07.289993Z","shell.execute_reply":"2025-08-30T19:21:07.486481Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions = model.predict(test_images)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:07.488167Z","iopub.execute_input":"2025-08-30T19:21:07.488387Z","iopub.status.idle":"2025-08-30T19:21:18.01214Z","shell.execute_reply.started":"2025-08-30T19:21:07.488368Z","shell.execute_reply":"2025-08-30T19:21:18.01141Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"img_list = os.listdir(datasets[1])","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:18.013323Z","iopub.execute_input":"2025-08-30T19:21:18.013575Z","iopub.status.idle":"2025-08-30T19:21:18.02047Z","shell.execute_reply.started":"2025-08-30T19:21:18.013554Z","shell.execute_reply":"2025-08-30T19:21:18.019843Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"labels_list = sorted(os.listdir(datasets[0]))\nprint(labels_list)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:18.021498Z","iopub.execute_input":"2025-08-30T19:21:18.021871Z","iopub.status.idle":"2025-08-30T19:21:18.028419Z","shell.execute_reply.started":"2025-08-30T19:21:18.021842Z","shell.execute_reply":"2025-08-30T19:21:18.027562Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"predictions[1][22]","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:18.029397Z","iopub.execute_input":"2025-08-30T19:21:18.029624Z","iopub.status.idle":"2025-08-30T19:21:18.037481Z","shell.execute_reply.started":"2025-08-30T19:21:18.029605Z","shell.execute_reply":"2025-08-30T19:21:18.036578Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"markdown","source":"## Making 'predictions.csv':","metadata":{}},{"cell_type":"code","source":"predictions.shape","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:18.03852Z","iopub.execute_input":"2025-08-30T19:21:18.039105Z","iopub.status.idle":"2025-08-30T19:21:18.047412Z","shell.execute_reply.started":"2025-08-30T19:21:18.039076Z","shell.execute_reply":"2025-08-30T19:21:18.046759Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_predictions = pd.DataFrame(columns=['ID','Label'], dtype=(np.int32,np.int32))\n\npredictions_mod = np.argmax(predictions, 1)\n\nfor idx,image in enumerate(sorted(img_list)):\n#     print(image, predictions_mod[idx])\n    df2 = pd.DataFrame([[int(image.split(\".\")[0]), int(labels_list[predictions_mod[idx]])]], columns=['ID','Label'])\n    df_predictions = pd.concat([df_predictions, df2])","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:18.04828Z","iopub.execute_input":"2025-08-30T19:21:18.048499Z","iopub.status.idle":"2025-08-30T19:21:21.72819Z","shell.execute_reply.started":"2025-08-30T19:21:18.048472Z","shell.execute_reply":"2025-08-30T19:21:21.727478Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"print(predictions_mod)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:21.729103Z","iopub.execute_input":"2025-08-30T19:21:21.72935Z","iopub.status.idle":"2025-08-30T19:21:21.734062Z","shell.execute_reply.started":"2025-08-30T19:21:21.729329Z","shell.execute_reply":"2025-08-30T19:21:21.733274Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_predictions.head()","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:21.735266Z","iopub.execute_input":"2025-08-30T19:21:21.735592Z","iopub.status.idle":"2025-08-30T19:21:21.753724Z","shell.execute_reply.started":"2025-08-30T19:21:21.735562Z","shell.execute_reply":"2025-08-30T19:21:21.752621Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_predictions.sort_values(by='ID', inplace=True)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:21.754983Z","iopub.execute_input":"2025-08-30T19:21:21.755229Z","iopub.status.idle":"2025-08-30T19:21:21.762123Z","shell.execute_reply.started":"2025-08-30T19:21:21.755193Z","shell.execute_reply":"2025-08-30T19:21:21.76131Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_predictions.reset_index(drop=True)","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:21.763084Z","iopub.execute_input":"2025-08-30T19:21:21.763293Z","iopub.status.idle":"2025-08-30T19:21:21.773568Z","shell.execute_reply.started":"2025-08-30T19:21:21.763276Z","shell.execute_reply":"2025-08-30T19:21:21.772735Z"},"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_predictions.to_csv('predictions.csv', index=False, header=True) ","metadata":{"execution":{"iopub.status.busy":"2025-08-30T19:21:21.774577Z","iopub.execute_input":"2025-08-30T19:21:21.774829Z","iopub.status.idle":"2025-08-30T19:21:21.794773Z","shell.execute_reply.started":"2025-08-30T19:21:21.774809Z","shell.execute_reply":"2025-08-30T19:21:21.793909Z"},"trusted":true},"outputs":[],"execution_count":null}]}