Commit 81f84197 authored by Rathnayaka R.M.D.S's avatar Rathnayaka R.M.D.S

Trained Model Dev

parent 20ae9acf
{
"cells": [
{
"cell_type": "code",
"execution_count": 11,
"id": "10a87b96-d9bc-47b6-85b2-fa1cd7197c31",
"metadata": {
"tags": []
},
"outputs": [],
"source": [
"test_path = 'data/test/'\n",
"train_path = 'data/train/'\n",
"model_weights = 'weights/emotion-detection.h5'\n",
"model_converter = 'weights/emotion-detection.tflite'\n",
"\n",
"batch_size = 64\n",
"valid_size = 32\n",
"color_mode = 'rgb'\n",
"\n",
"width = 71\n",
"height = 71\n",
"\n",
"target_size = (width, height)\n",
"input_shape = (width, height, 3)\n",
"\n",
"zoom_range = 0.3\n",
"shear_range = 0.3\n",
"shift_range = 0.3\n",
"rotation_range = 30\n",
"\n",
"dense_1 = 512\n",
"dense_2 = 256\n",
"dense_3 = 64\n",
"num_classes = 4\n",
"\n",
"epochs = 80\n",
"rate = 0.3\n",
"learning_rate = 1e-3\n",
"\n",
"verbose = 1\n",
"\n",
"class_dict = {'happy': 0, 'sad': 1}\n",
"\n",
"class_dict_reverse = {v:k for k,v in class_dict.items()}"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "03021536-396a-403b-8e6b-5cf08028ee46",
"metadata": {},
"outputs": [],
"source": [
"import os, pathlib, glob\n",
"os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3'\n",
"\n",
"import itertools\n",
"import cv2 as cv\n",
"import numpy as np\n",
"import pandas as pd\n",
"import tensorflow as tf\n",
"from mtcnn.mtcnn import MTCNN\n",
"from PIL import Image, ImageOps\n",
"from matplotlib import pyplot as plt\n",
"from sklearn.utils import class_weight \n",
"from sklearn.metrics import confusion_matrix\n",
"\n",
"from tensorflow.keras import regularizers\n",
"from tensorflow.keras.optimizers import Adam\n",
"from tensorflow.keras.activations import relu\n",
"from tensorflow.keras.layers import GaussianNoise\n",
"from tensorflow.keras.models import Model, load_model, Sequential\n",
"\n",
"from tensorflow.keras.layers import GlobalAveragePooling2D, Dense, Dropout, Flatten, Input, Conv2D, multiply \\\n",
" , LocallyConnected2D, Lambda, BatchNormalization\n",
"############################################################################################\n",
"detector = MTCNN()\n",
"\n",
"def extract_face(\n",
"\t\t\t\tfilename,\n",
" detector = detector, \n",
"\t\t\t\trequired_size = (160, 160)\n",
"\t\t\t\t):\n",
"\timage = Image.open(filename)\n",
"\timage = ImageOps.exif_transpose(image)\n",
"\t\n",
"\timage = image.convert('RGB')\n",
"\tpixels = np.asarray(image)\n",
"\n",
"\tresults = detector.detect_faces(pixels)\n",
"\tx1, y1, width, height = results[0]['box']\n",
"\n",
"\tx1, y1 = abs(x1), abs(y1)\n",
"\tx2, y2 = x1 + width, y1 + height\n",
"\tface = pixels[y1:y2, x1:x2]\n",
"\n",
"\timage = Image.fromarray(face)\n",
"\timage = image.resize(required_size)\n",
"\tface_array = np.asarray(image)\n",
"\treturn face_array\n",
"\n",
"def preprocessing_function(img):\n",
" img = tf.keras.applications.xception.preprocess_input(img)\n",
" return img\n",
"\n",
"def image_data_generator():\n",
" train_datagen = tf.keras.preprocessing.image.ImageDataGenerator(\n",
" rotation_range = rotation_range,\n",
" shear_range = shear_range,\n",
" zoom_range = zoom_range,\n",
" width_shift_range=shift_range,\n",
" height_shift_range=shift_range,\n",
" horizontal_flip = True,\n",
" preprocessing_function=preprocessing_function\n",
" )\n",
" test_datagen = tf.keras.preprocessing.image.ImageDataGenerator(\n",
" preprocessing_function=preprocessing_function\n",
" )\n",
"\n",
" train_generator = train_datagen.flow_from_directory(\n",
" train_path,\n",
" target_size = target_size,\n",
" color_mode = color_mode,\n",
" batch_size = batch_size,\n",
" class_mode = 'binary',\n",
" shuffle = True\n",
" )\n",
"\n",
" test_generator = test_datagen.flow_from_directory(\n",
" test_path,\n",
" target_size = target_size,\n",
" color_mode = color_mode,\n",
" batch_size = batch_size,\n",
" class_mode = 'binary',\n",
" shuffle = False\n",
" )\n",
"\n",
" return train_generator, test_generator"
]
},
{
"cell_type": "markdown",
"id": "99c89e1e",
"metadata": {},
"source": []
},
{
"cell_type": "code",
"execution_count": 13,
"id": "b89d539f-4ae0-414d-a65f-84babe7f728a",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Found 23839 images belonging to 2 classes.\n",
"Found 5850 images belonging to 2 classes.\n"
]
}
],
"source": [
"train_generator, test_generator = image_data_generator()"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "4459b46b",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{0: 1.1495322596200213, 1: 0.8848923533778768}"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Calculate Class Weights \n",
"\n",
"class_weights = class_weight.compute_class_weight(\n",
" 'balanced',\n",
" classes = np.unique(train_generator.classes), \n",
" y = train_generator.classes\n",
" )\n",
"\n",
"train_class_weights = dict(enumerate(class_weights))\n",
"train_class_weights"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "4cea9b09-f98c-4ddc-bf10-90405a5c3f9f",
"metadata": {},
"outputs": [],
"source": [
"class EmotionDetection(object):\n",
" def __init__(self):\n",
" self.train_generator = train_generator\n",
" self.test_generator = test_generator\n",
"\n",
" self.train_step = self.train_generator.samples // batch_size\n",
" self.test_step = self.test_generator.samples // batch_size\n",
"\n",
" self.accuracy = tf.keras.metrics.BinaryAccuracy(name='accuracy')\n",
" self.recall = tf.keras.metrics.Recall()\n",
" self.precision = tf.keras.metrics.Precision()\n",
"\n",
" def classifier(self, x):\n",
" if not self.trainable:\n",
" x = Dense(dense_1, activation='relu')(x)\n",
" x = Dense(dense_1)(x)\n",
" x = BatchNormalization()(x)\n",
" x = relu(x)\n",
" x = Dropout(rate)(x)\n",
"\n",
" x = Dense(dense_2, activation='relu')(x)\n",
" x = Dense(dense_2)(x)\n",
" x = BatchNormalization()(x)\n",
" x = relu(x)\n",
" x = Dropout(rate)(x)\n",
"\n",
" x = Dense(dense_3, activation='relu')(x)\n",
" x = Dense(dense_3)(x)\n",
" x = BatchNormalization()(x)\n",
" x = relu(x)\n",
" x = Dropout(rate)(x)\n",
" return x\n",
"\n",
" def model_conversion(self, trainable):\n",
" # functional_model = tf.keras.applications.Xception(weights=\"imagenet\")\n",
" functional_model = tf.keras.applications.Xception(\n",
" weights=\"imagenet\",\n",
" include_top=False,\n",
" input_shape=input_shape\n",
" )\n",
" functional_model.trainable = trainable\n",
" \n",
" self.trainable = trainable\n",
"\n",
" inputs = functional_model.input\n",
"\n",
" x = functional_model.layers[-2].output\n",
" x = Flatten()(x)\n",
" \n",
" x = self.classifier(x)\n",
"\n",
" outputs = Dense(1, activation='sigmoid')(x)\n",
"\n",
" model = Model(\n",
" inputs=inputs,\n",
" outputs=outputs\n",
" )\n",
" self.model = model\n",
" self.model.summary()\n",
"\n",
" def train(self):\n",
" callback = tf.keras.callbacks.EarlyStopping(\n",
" monitor='val_loss', \n",
" patience=8\n",
" )\n",
" adam = tf.keras.optimizers.Adam(\n",
" learning_rate=learning_rate, \n",
" beta_1=0.9, \n",
" beta_2=0.999, \n",
" epsilon=1e-08, \n",
" decay=0.0\n",
" )\n",
" \n",
" METRICS = [\n",
" tf.keras.metrics.BinaryAccuracy(name='accuracy'),\n",
" tf.keras.metrics.AUC(name='auc')\n",
" ]\n",
"\n",
" self.model.compile(\n",
" optimizer=Adam(learning_rate),\n",
" loss='binary_crossentropy',\n",
" metrics=METRICS\n",
" )\n",
"\n",
" \n",
" histroy = self.model.fit(\n",
" self.train_generator,\n",
" steps_per_epoch = self.train_step,\n",
" validation_data = self.test_generator,\n",
" validation_steps = self.test_step,\n",
" epochs=epochs,\n",
" verbose=verbose,\n",
" # class_weight=train_class_weights\n",
" )\n",
"\n",
" #Visualize Metrics\n",
" plt.figure(figsize=(12,12))\n",
" plt.subplot(311)\n",
" plt.plot(histroy.history['loss'], label='train')\n",
" plt.plot(histroy.history['val_loss'], label='valid')\n",
" plt.legend()\n",
" plt.title('Loss')\n",
"\n",
" plt.subplot(312)\n",
" plt.plot(histroy.history['accuracy'], label='train')\n",
" plt.plot(histroy.history['val_accuracy'], label='valid')\n",
" plt.legend()\n",
" plt.title('Accuracy')\n",
" \n",
" plt.subplot(313)\n",
" plt.plot(histroy.history['auc'], label='train')\n",
" plt.plot(histroy.history['val_auc'], label='valid')\n",
" plt.legend()\n",
" plt.title('AUC')\n",
" plt.show()\n",
" \n",
" \n",
" def save_model(self):\n",
" self.model.save(model_weights)\n",
"\n",
" def load_model(self):\n",
" self.model = load_model(model_weights)\n",
" self.model.compile(\n",
" optimizer=Adam(learning_rate),\n",
" loss='binary_crossentropy',\n",
" metrics=[\n",
" tf.keras.metrics.BinaryAccuracy(name='accuracy'),\n",
" tf.keras.metrics.AUC(name='auc')\n",
" ]\n",
" )\n",
"\n",
" def TFconverter(self): # For deployment in the mobile devices quantization of the model using tensorflow lite\n",
" converter = tf.compat.v1.lite.TFLiteConverter.from_keras_model_file(model_weights)\n",
" converter.target_spec.supported_ops = [\n",
" tf.lite.OpsSet.TFLITE_BUILTINS, # Handling unsupported tensorflow Ops \n",
" tf.lite.OpsSet.SELECT_TF_OPS \n",
" ]\n",
" converter.optimizations = [tf.lite.Optimize.DEFAULT] # Set optimization default and it configure between latency, accuracy and model size\n",
" tflite_model = converter.convert()\n",
"\n",
" model_converter_file = pathlib.Path(model_converter) \n",
" model_converter_file.write_bytes(tflite_model) # save the tflite model in byte format\n",
" \n",
" def TFinterpreter(self):\n",
" self.interpreter = tf.lite.Interpreter(model_path=model_converter) # Load tflite model\n",
" self.interpreter.allocate_tensors()\n",
"\n",
" self.input_details = self.interpreter.get_input_details() # Get input details of the model\n",
" self.output_details = self.interpreter.get_output_details() # Get output details of the model\n",
"\n",
" def Inference(self, image):\n",
" image = cv.resize(image, target_size)\n",
" image = preprocessing_function(image)\n",
" image = np.expand_dims(image, axis=0)\n",
"\n",
" input_shape = self.input_details[0]['shape']\n",
" assert np.array_equal(input_shape, image.shape), \"Expected Tensor Shape : {} but recieved {}\".format(input_shape, image.shape)\n",
" \n",
" self.interpreter.set_tensor(self.input_details[0]['index'], image)\n",
" self.interpreter.invoke() # set the inference\n",
"\n",
" pred = self.interpreter.get_tensor(self.output_details[0]['index']) # Get predictions\n",
" pred = pred.squeeze()\n",
" Label = np.argmax(pred) # Get the label\n",
" class_ = class_dict_reverse[Label]\n",
" return class_\n",
"\n",
" def plot_confusion_matrix(self, cmap=None, normalize=True):\n",
" \n",
" P = self.model.predict(self.test_generator)\n",
" P = np.round(P).astype(int).flatten()\n",
" \n",
" Y = self.test_generator.classes\n",
" cm = confusion_matrix(Y, P)\n",
"\n",
" if cmap is None:\n",
" cmap = plt.get_cmap('Blues')\n",
"\n",
" plt.figure(figsize=(12, 12))\n",
" plt.imshow(cm, interpolation='nearest', cmap=cmap)\n",
" plt.title('Confusion Matrix - Emotion Detection')\n",
" plt.colorbar()\n",
"\n",
" class_names = list(class_dict.keys())\n",
"\n",
" if class_names is not None:\n",
" tick_marks = np.arange(len(class_names))\n",
" plt.xticks(tick_marks, class_names, rotation=0)\n",
" plt.yticks(tick_marks, class_names)\n",
"\n",
" if normalize:\n",
" cm = cm.astype('float') / cm.sum(axis=1)[:, np.newaxis]\n",
"\n",
" thresh = cm.max() / 1.5 if normalize else cm.max() / 2\n",
" for i, j in itertools.product(range(cm.shape[0]), range(cm.shape[1])):\n",
" if normalize:\n",
" plt.text(j, i, \"{:0.4f}\".format(cm[i, j]),\n",
" horizontalalignment=\"center\",\n",
" color=\"white\" if cm[i, j] > thresh else \"black\")\n",
" else:\n",
" plt.text(j, i, \"{:,}\".format(cm[i, j]),\n",
" horizontalalignment=\"center\",\n",
" color=\"white\" if cm[i, j] > thresh else \"black\")\n",
"\n",
"\n",
" plt.tight_layout()\n",
" plt.ylabel('True labels')\n",
" plt.xlabel('Predicted labels')\n",
" plt.savefig('results/cm.png')\n",
" \n",
" def process(self):\n",
" if not os.path.exists(model_converter):\n",
" if not os.path.exists(model_weights):\n",
" self.model_conversion(True)\n",
" self.train()\n",
" self.save_model()\n",
" else:\n",
" self.load_model()\n",
" self.plot_confusion_matrix()\n",
" self.TFconverter()\n",
" self.TFinterpreter()\n",
" "
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "f93b8e9e",
"metadata": {},
"outputs": [],
"source": [
"# mkvirtualenv --python=/usr/bin/python3.7 EMO\n",
"# pip install tensorflow==2.6.0\n",
"# pip install keras==2.6.0\n",
"# pip install opencv-python\n",
"# pip install flask\n",
"# pip install flask-cors\n",
"# pip install Pillow\n",
"# pip install mtcnn"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "79e77c9d-c22a-4301-a06a-4ab7a87868ee",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:Assets written to: C:\\Users\\Legion\\AppData\\Local\\Temp\\tmp59sgmc1u\\assets\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:Assets written to: C:\\Users\\Legion\\AppData\\Local\\Temp\\tmp59sgmc1u\\assets\n",
"c:\\Users\\Legion\\.conda\\envs\\tf26\\lib\\site-packages\\keras\\utils\\generic_utils.py:494: CustomMaskWarning: Custom mask layers require a config and must override get_config. When loading, the custom mask layer must be passed to the custom_objects argument.\n",
" warnings.warn('Custom mask layers require a config and must override '\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:Restoring parameters from C:\\Users\\Legion\\AppData\\Local\\Temp\\tmp59sgmc1u\\variables\\variables\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:Restoring parameters from C:\\Users\\Legion\\AppData\\Local\\Temp\\tmp59sgmc1u\\variables\\variables\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:The given SavedModel MetaGraphDef contains SignatureDefs with the following keys: {'__saved_model_init_op', 'serving_default'}\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:The given SavedModel MetaGraphDef contains SignatureDefs with the following keys: {'__saved_model_init_op', 'serving_default'}\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:input tensors info: \n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:input tensors info: \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:Tensor's key in saved_model's tensor_map: input_4\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:Tensor's key in saved_model's tensor_map: input_4\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow: tensor name: serving_default_input_4:0, shape: (-1, 71, 71, 3), type: DT_FLOAT\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow: tensor name: serving_default_input_4:0, shape: (-1, 71, 71, 3), type: DT_FLOAT\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:output tensors info: \n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:output tensors info: \n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:Tensor's key in saved_model's tensor_map: dense_9\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:Tensor's key in saved_model's tensor_map: dense_9\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow: tensor name: StatefulPartitionedCall:0, shape: (-1, 1), type: DT_FLOAT\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow: tensor name: StatefulPartitionedCall:0, shape: (-1, 1), type: DT_FLOAT\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"INFO:tensorflow:Restoring parameters from C:\\Users\\Legion\\AppData\\Local\\Temp\\tmp59sgmc1u\\variables\\variables\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"INFO:tensorflow:Restoring parameters from C:\\Users\\Legion\\AppData\\Local\\Temp\\tmp59sgmc1u\\variables\\variables\n"
]
},
{
"data": {
"image/png": "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",
"text/plain": [
"<Figure size 864x864 with 2 Axes>"
]
},
"metadata": {
"needs_background": "light"
},
"output_type": "display_data"
}
],
"source": [
"model = EmotionDetection()\n",
"model.process()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "cc62695d",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3.8.13 ('tf26')",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.8.13"
},
"vscode": {
"interpreter": {
"hash": "d4b521e29a846470c96e928a1c4aafac58a12234cdaa98f9ca60bc431873fee6"
}
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment