{"id":24839,"date":"2022-08-01T06:00:00","date_gmt":"2022-08-01T00:30:00","guid":{"rendered":"https:\/\/debuggercafe.com\/?p=24839"},"modified":"2024-09-15T20:42:53","modified_gmt":"2024-09-15T15:12:53","slug":"concrete-crack-classification-using-deep-learning","status":"publish","type":"post","link":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/","title":{"rendered":"Concrete Crack Classification using Deep Learning"},"content":{"rendered":"\n<p>Computer Vision and Deep Learning are finding their way into almost every field which requires vision and learning. Starting from medical imaging to real-life industrial applications, the possibilities are endless. Analyzing and detecting structural defects in buildings is also an upcoming application of deep learning and computer vision. We can use object detection, image segmentation, and image classification depending on the problem. In this post, we will start with a simple classification problem and cover more advanced use-cases in future posts. Here, we will carry out <strong><em>concrete crack classification using deep learning<\/em><\/strong>.  <\/p>\n\n\n\n<div class=\"wp-block-buttons is-horizontal is-content-justification-center is-layout-flex wp-container-core-buttons-is-layout-499968f5 wp-block-buttons-is-layout-flex\">\n<div class=\"wp-block-button is-style-outline is-style-outline--1\"><a class=\"wp-block-button__link has-black-color has-luminous-vivid-orange-background-color has-text-color has-background wp-element-button\" href=\"#download-code\"><strong>Jump to Download Code<\/strong><\/a><\/div>\n<\/div>\n\n\n\n<p><\/p>\n\n\n\n<p>This post will focus highly on the practical aspects of the problem which include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The dataset.<\/li>\n\n\n\n<li>Python code.<\/li>\n\n\n\n<li>Analysis of results.<\/li>\n\n\n\n<li>Test and inference.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-sample-inference.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"479\" height=\"247\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-sample-inference.png\" alt=\"Sample test result for concrete crack classification using deep learning.\" class=\"wp-image-24939\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-sample-inference.png 479w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-sample-inference-300x155.png 300w\" sizes=\"auto, (max-width: 479px) 100vw, 479px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 1. A sample test result using the trained model for the problem that we will solve here.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n<p><strong><em>Let&#8217;s check out the topics that we will cover here.<\/em><\/strong><\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><em>We will start with a discussion of the dataset. In this post, we will use the <strong><a href=\"https:\/\/digitalcommons.usu.edu\/all_datasets\/48\/\" target=\"_blank\" rel=\"noreferrer noopener\">Concrete Crack Image Dataset<\/a><\/strong>.<\/em><\/li>\n\n\n\n<li><em>Then we will move to the coding part where:<\/em>\n<ul class=\"wp-block-list\">\n<li><em>First, we will prepare the dataset and data loaders in the format that we need.<\/em><\/li>\n\n\n\n<li><em>Then write the model code.<\/em><\/li>\n\n\n\n<li><em>Next, we will prepare the training script.<\/em><\/li>\n\n\n\n<li><em>Then we will train the model.<\/em><\/li>\n\n\n\n<li><em>Finally, use the trained model for testing.<\/em><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>We will not cover the theoretical explanation of the deep learning concepts in much depth in this post.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">The Concrete Crack Image Dataset<\/h2>\n\n\n\n<p>We will use the <strong><a href=\"https:\/\/digitalcommons.usu.edu\/all_datasets\/48\/\" target=\"_blank\" rel=\"noreferrer noopener\">Concrete Crack Image Dataset (SDNET2018)<\/a><\/strong> in this post. <\/p>\n\n\n\n<p>This dataset contains more than 56000 images of cracked and non-cracked surfaces. The surfaces in the images belong to structures like bridge decks, walls, and pavements.<\/p>\n\n\n\n<p>The following are some of the cracked and non-cracked images from the dataset.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/cracked-and-uncracked-ground-truth-images.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"446\" height=\"414\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/cracked-and-uncracked-ground-truth-images.png\" alt=\"Ground truth images showing cracked and uncracked concrete structures.\" class=\"wp-image-24941\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/cracked-and-uncracked-ground-truth-images.png 446w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/cracked-and-uncracked-ground-truth-images-300x278.png 300w\" sizes=\"auto, (max-width: 446px) 100vw, 446px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 2. Ground truth images showing cracked and uncracked concrete structures.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n<p>Basically, there are images belonging to two classes in this dataset. One class of images contains cracks in them and the other does not.<\/p>\n\n\n\n<p>Right now, let&#8217;s take a look at the naming convention and directory structure of this dataset to get a better insight into it. The following is the final structure that we will be dealing with.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">SDNET2018\/\n\u251c\u2500\u2500 D\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 CD\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 UD\n\u251c\u2500\u2500 P\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 CP\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 UP\n\u2514\u2500\u2500 W\n    \u251c\u2500\u2500 CW\n    \u2514\u2500\u2500 UW<\/pre>\n\n\n\n<p>The directories <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">D<\/code>, <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">P<\/code>, and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">W<\/code> indicate the structure type, either bridge decks, pavements, or walls. The subdirectories contain either <em>C <\/em>or <em>U<\/em> prefixed to the structure type. Subdirectories starting with <em>C<\/em> contain images with cracks and those starting with <em>U<\/em> contain uncracked images.<\/p>\n\n\n\n<p>Right now, you need not worry about downloading or structuring the dataset. We will directly deal with these things in the coding section.<\/p>\n\n\n\n<p>But we need to keep in mind that this is a highly imbalanced dataset. Out of the around 56000 images, around 8400 images contain cracks in them and the rest do not. This will obviously affect the model, but to what extent, we will explore.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Directory Structure<\/h2>\n\n\n\n<p>The following block shows the directory structure for the project.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">\u251c\u2500\u2500 input\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 SDNET2018\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 D\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 P\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 W\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 test.csv\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 trainval.csv\n\u251c\u2500\u2500 outputs\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 test_results\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u251c\u2500\u2500 test_image_1089.png\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 ...\n\u2502\u00a0\u00a0 \u2502\u00a0\u00a0 \u2514\u2500\u2500 test_image_99.png\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 accuracy.png\n\u2502\u00a0\u00a0 \u251c\u2500\u2500 loss.png\n\u2502\u00a0\u00a0 \u2514\u2500\u2500 model.pth\n\u2514\u2500\u2500 src\n    \u251c\u2500\u2500 dataset.py\n    \u251c\u2500\u2500 download_and_extract.py\n    \u251c\u2500\u2500 model.py\n    \u251c\u2500\u2500 prepare_data.py\n    \u251c\u2500\u2500 test.py\n    \u251c\u2500\u2500 train.py\n    \u2514\u2500\u2500 utils.py<\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">input<\/code> directory contains the original <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">SDNET2018<\/code> dataset that we saw above. We will execute a simple script to download and extract this dataset. Apart from that, it contains <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">trainval.csv<\/code> and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">test.csv<\/code> that we will generate using a data preprocessing script in the coding section. <\/li>\n\n\n\n<li>The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">outputs<\/code> directory contains the output results from the test dataset as well as the accuracy &amp; loss graphs, and the trained model.<\/li>\n\n\n\n<li>Finally, the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">src<\/code> directory contains the Python code files, the details of which we will explore later in the coding section.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p><strong><em>When downloading the zip file for this tutorial, you will get access to all the Python scripts and the trained model<\/em><\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Concrete Crack Classification using Deep Learning<\/h2>\n\n\n\n<p>From here onward, we will start the coding section of the tutorial. We will cover all the necessary code in detail here.<\/p>\n\n\n\n<p>The following are the steps that we will follow:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Download the dataset and prepare the CSV files.<\/li>\n\n\n\n<li>Prepare the PyTorch datasets and data loaders.<\/li>\n\n\n\n<li>Prepare the training script.<\/li>\n\n\n\n<li>Train and test the model.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>Let&#8217;s start with downloading the dataset.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Download the SDNET2018 Dataset<\/h3>\n\n\n\n<p>Before we can start training a model for concrete crack classification using deep learning, we need the dataset. The Concrete Crack Classification dataset (SDNET2018) is directly available from <strong><a href=\"https:\/\/digitalcommons.usu.edu\/all_datasets\/48\/\" target=\"_blank\" rel=\"noreferrer noopener\">here<\/a><\/strong>. But instead of downloading it manually, let&#8217;s execute a simple script. <\/p>\n\n\n\n<p><strong><em>Note that to download the dataset using the script, you need to download the code zip files and extract them first. Please do that if you want to proceed this way. You can also download the dataset manually and structure it in the project directory in the required manner.<\/em><\/strong><\/p>\n\n\n\n<h3 class=\"wp-block-heading has-text-align-center\" id=\"download-code\">Download Code<\/h3>\n\n\n\n<div class=\"wp-block-button is-style-outline center\"><a data-sumome-listbuilder-id=\"0dcbdfbe-0bd0-4212-afc7-8d0e74d7d9d6\" class=\"wp-block-button__link has-black-color has-luminous-vivid-orange-background-color has-text-color has-background\"><b>Download the Source Code for this Tutorial<\/b><\/a><\/div>\n\n\n\n<p><strong><em>The required code is available in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">download_and_extract.py<\/code> file.<\/em><\/strong> It is a simple script and we need not go into the details of it here. Just execute it from the command line\/terminal in the project directory.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">python download_and_extract.py<\/pre>\n\n\n\n<p>You should get an output similar to the following.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">Downloading data.zip\n  1%|\u258e                                     | 5.07M\/528M [00:02&lt;02:52, 3.03MiB\/s]<\/pre>\n\n\n\n<p>Executing the script will do two things. It will download the dataset and extract it into the directory structure that we need.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Generating the Training and Testing CSV Files<\/h3>\n\n\n\n<p>To make the dataset easier to work with, we will create two CSV files. A <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">trainval.csv<\/code> and a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">test.csv<\/code> file. They will contain two columns. One for the path to each image in the dataset and one for the corresponding label. For the labels, we consider 0 as &#8216;Crack Detected&#8217; and 1 as &#8216;Crack Undetected&#8217;. 0 means image folder starting with &#8216;C&#8217;, as in &#8216;CW&#8217;, and 1 means image folder starting with &#8216;U&#8217; as in &#8216;UW&#8217;.<\/p>\n\n\n\n<p><strong><em>The code to create this CSV file is present in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">prepare_data.py<\/code> file<\/em><\/strong>. <\/p>\n\n\n\n<p>Although we will not go into the details of this code, the following block contains the entire code. Please go through it if you need to.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"prepare_data.py\" data-enlighter-group=\"prepare_data_1\">import pandas as pd\nimport os\nimport glob as glob\n\nfrom tqdm import tqdm\nfrom sklearn.preprocessing import LabelBinarizer\n\nROOT_DIR = os.path.join('..', 'input', 'SDNET2018')\n\n# Get all the image folder paths. Structure as such so that the files inside\n# the folders in `all_paths` should contain the images.\nall_paths = glob.glob(os.path.join(ROOT_DIR, '*', '*'), recursive=True)\nfolder_paths = [x for x in all_paths if os.path.isdir(x)]\nprint(f\"Folder paths: {folder_paths}\")\nprint(f\"Number of folders: {len(folder_paths)}\")\n\n# The class names. We will consider 0 as 'Crack Detected' \n# and 1 as 'Crack Undetected'. 0 means image folder starting with 'C', \n# as in 'CW' and 1 means image folder starting with 'U' as in 'UW'.\n\n# Create a DataFrame\ndata = pd.DataFrame()\n\n# Image formats to consider.\nimage_formats = ['jpg', 'JPG', 'PNG', 'png']\nlabels = []\ncounter = 0\nfor i, folder_path in tqdm(enumerate(folder_paths), total=len(folder_paths)):\n    image_paths = os.listdir(folder_path)\n    folder_name = folder_path.split(os.path.sep)[-1]\n    if folder_name.startswith('C'):\n        label = 0\n    if folder_name.startswith('U'):\n        label = 1\n    # Save image paths in the DataFrame.\n    for image_path in image_paths:\n        if image_path.split('.')[-1] in image_formats:\n            path_to_save = os.path.join(folder_path, image_path)\n            data.loc[counter, 'image_path'] = path_to_save\n            data.loc[counter, 'target'] = int(label)\n            labels.append(label)\n            counter += 1\n\n# Shuffle the dataset.\ndata = data.sample(frac=1).reset_index(drop=True)\n\n# Data to be used for training and validation.\ntrainval_split = 0.9\n\ntotal_instances = len(data)\ntrainval_instances = int(total_instances*trainval_split)\ntest_instances = total_instances - trainval_instances \n\nprint(f\"Training and validation instances: {trainval_instances}\")\nprint(f\"Test instances: {test_instances}\")\n# Save as CSV file\ndata.iloc[:trainval_instances].to_csv(os.path.join('..', 'input', 'trainval.csv'), index=False)\ndata.iloc[trainval_instances:].to_csv(os.path.join('..', 'input', 'test.csv'), index=False)<\/pre>\n\n\n\n<p>Use the following command to execute the script.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">python prepare_data.py<\/pre>\n\n\n\n<p>This will generate the two CSV files in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">input<\/code> directory. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/trainval-csv-concrete-crack-classification-using-deep-learning.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"314\" height=\"240\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/trainval-csv-concrete-crack-classification-using-deep-learning.png\" alt=\"Image showing a few rows from the trainval dataset.\" class=\"wp-image-24944\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/trainval-csv-concrete-crack-classification-using-deep-learning.png 314w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/trainval-csv-concrete-crack-classification-using-deep-learning-300x229.png 300w\" sizes=\"auto, (max-width: 314px) 100vw, 314px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 2. Image showing a few rows from the trainval dataset.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n<p>The above figure shows a few of the rows from the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">trainval.csv<\/code> file. The <strong>image_path<\/strong> column holds the image paths relative to the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">src<\/code> directory and the <strong>target<\/strong> column holds the class label numbers.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Utility and Helper Scripts<\/h3>\n\n\n\n<p>We need a few helper functions and a utility class to make our work easier throughout the project. <\/p>\n\n\n\n<p><strong><em>All of the code for this will be in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">utils.py<\/code> file<\/em><\/strong>.<\/p>\n\n\n\n<p>Let&#8217;s start with the import statements and a function to save the trained PyTorch model.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"utils.py\" data-enlighter-group=\"utils_1\">import torch\nimport matplotlib\nimport matplotlib.pyplot as plt\nimport os\n\nmatplotlib.style.use('ggplot')\n\ndef save_model(epochs, model, optimizer, criterion):\n    \"\"\"\n    Function to save the trained model to disk.\n    \"\"\"\n    torch.save({\n                'epoch': epochs,\n                'model_state_dict': model.state_dict(),\n                'optimizer_state_dict': optimizer.state_dict(),\n                'loss': criterion,\n                }, os.path.join('..', 'outputs', 'model.pth'))<\/pre>\n\n\n\n<p>The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">save_model<\/code> function saves the trained model weights along with the number of epochs trained for, the optimizer state dictionary, and the loss function. This will help us resume training in the future if we want to.<\/p>\n\n\n\n<p>Next, we have a simple function to save the accuracy and loss graphs to disk.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"18\" data-enlighter-title=\"utils.py\" data-enlighter-group=\"utils_2\">def save_plots(train_acc, valid_acc, train_loss, valid_loss):\n    \"\"\"\n    Function to save the loss and accuracy plots to disk.\n    \"\"\"\n    # Accuracy plots.\n    plt.figure(figsize=(10, 7))\n    plt.plot(\n        train_acc, color='tab:blue', linestyle='-', \n        label='train accuracy'\n    )\n    plt.plot(\n        valid_acc, color='tab:red', linestyle='-', \n        label='validataion accuracy'\n    )\n    plt.xlabel('Epochs')\n    plt.ylabel('Accuracy')\n    plt.legend()\n    plt.savefig(os.path.join('..', 'outputs', 'accuracy.png'))\n    \n    # Loss plots.\n    plt.figure(figsize=(10, 7))\n    plt.plot(\n        train_loss, color='tab:blue', linestyle='-', \n        label='train loss'\n    )\n    plt.plot(\n        valid_loss, color='tab:red', linestyle='-', \n        label='validataion loss'\n    )\n    plt.xlabel('Epochs')\n    plt.ylabel('Loss')\n    plt.legend()\n    plt.savefig(os.path.join('..', 'outputs', 'loss.png'))<\/pre>\n\n\n\n<p>The above function accepts the training\/validation accuracy lists, and training\/validation loss lists and plots the respective graphs. These graphs are stored in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">outputs<\/code> folder.<\/p>\n\n\n\n<p>Finally, we have a class for the learning rate scheduler.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"51\" data-enlighter-title=\"utils.py\" data-enlighter-group=\"utils_3\">class LRScheduler():\n    \"\"\"\n    Learning rate scheduler. If the validation loss does not decrease for the \n    given number of `patience` epochs, then the learning rate will decrease by\n    by given `factor`.\n    \"\"\"\n    def __init__(\n        self, optimizer, patience=5, min_lr=1e-6, factor=0.1\n    ):\n        \"\"\"\n        new_lr = old_lr * factor\n\n        :param optimizer: the optimizer we are using\n        :param patience: how many epochs to wait before updating the lr\n        :param min_lr: least lr value to reduce to while updating\n        :param factor: factor by which the lr should be updated\n        \"\"\"\n        self.optimizer = optimizer\n        self.patience = patience\n        self.min_lr = min_lr\n        self.factor = factor\n\n        self.lr_scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( \n                self.optimizer,\n                mode='min',\n                patience=self.patience,\n                factor=self.factor,\n                min_lr=self.min_lr,\n                verbose=True\n            )\n\n    def __call__(self, val_loss):\n        self.lr_scheduler.step(val_loss)<\/pre>\n\n\n\n<p>The LRScheduler class simply calls the ReduceLROnPlateau scheduler from <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">torch.optim.lr_scheduler<\/code>. The only reason we are again initializing this inside a custom class is to abstract it away from the training script. Basically, this will reduce the learning rate by a certain <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">factor<\/code>, when the validation loss does not improve for a certain number of epochs controlled by the patience <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">parameter<\/code>. This will help us prevent overfitting when the model starts to learn as the training progresses.<\/p>\n\n\n\n<p>The above are all the helper functions and utility classes that we need.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Code for Dataset and DataLoaders<\/h3>\n\n\n\n<p>Let&#8217;s move on to write the code that will create the datasets and data loaders for PyTorch. We will need a custom dataset class for this.<\/p>\n\n\n\n<p><strong><em>The code for preparing the dataset will go into the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">dataset.py<\/code> file<\/em><\/strong>.<\/p>\n\n\n\n<p>The following code block deals with all the import statements.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"dataset.py\" data-enlighter-group=\"dataset_1\">import torch\nimport numpy as np\nimport pandas as pd\nimport os\nimport torchvision.transforms as transforms\n\nfrom PIL import Image\nfrom torch.utils.data import DataLoader, Dataset\nfrom sklearn.model_selection import train_test_split<\/pre>\n\n\n\n<p>A few of the important modules that we import here are:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">transforms<\/code> to carry out image augmentation.<\/li>\n\n\n\n<li><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">Image<\/code> module from PIL to read images.<\/li>\n\n\n\n<li><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">train_test_split<\/code> so that we can divide the dataset between a train and validation test.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>Next, we have a custom dataset class.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"10\" data-enlighter-title=\"dataset.py\" data-enlighter-group=\"datasets_2\"># Custom dataset.\nclass ImageDataset(Dataset):\n    def __init__(self, images, labels=None, tfms=None):\n        self.X = images\n        self.y = labels\n\n        # Apply Augmentations if training.\n        if tfms == 0: # If validating.\n            self.aug = transforms.Compose([\n                transforms.Resize((256, 256)),\n                transforms.ToTensor(),\n                transforms.Normalize(\n                    mean=[0.485, 0.456, 0.406],\n                    std=[0.229, 0.224, 0.225]\n                    )\n            ])\n        else: # If training.\n            self.aug = transforms.Compose([\n                transforms.Resize((256, 256)),\n                transforms.RandomHorizontalFlip(p=0.5),\n                transforms.RandomVerticalFlip(p=0.5),\n                transforms.RandomAdjustSharpness(sharpness_factor=2, p=0.5),\n                transforms.RandomAutocontrast(p=0.5),\n                transforms.RandomGrayscale(p=0.5),\n                transforms.RandomRotation(45),\n                transforms.ToTensor(),\n                transforms.Normalize(\n                    mean=[0.485, 0.456, 0.406],\n                    std=[0.229, 0.224, 0.225]\n                    )\n            ])\n         \n    def __len__(self):\n        return (len(self.X))\n    \n    def __getitem__(self, i):\n        image = Image.open(self.X[i])\n        image = image.convert('RGB')\n        image = self.aug(image)\n        label = self.y[i].astype(np.int32)\n        return (\n            image,\n            torch.tensor(label, dtype=torch.long)\n        )<\/pre>\n\n\n\n<p>It is a very simple class that first initializes the image paths (<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">self.X<\/code>) and labels (<code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">self.y<\/code>). We have a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">tfms<\/code> parameter controlling the application of augmentations depending on whether we are preparing the training or validation set. As we can see, we are applying a lot of augmentations for the training data. This is mainly to prevent overfitting by introducing more variability into the dataset.<\/p>\n\n\n\n<p><strong><em>We will be using an ImageNet pretrained model. For that reason, we are using the ImageNet normalization stats in both the training and validation transforms.<\/em><\/strong><\/p>\n\n\n\n<p>In the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">__getitem__<\/code> method, we simply read the image in RGB format, load the label and return both of them as tensors.<\/p>\n\n\n\n<p>For the final part, we have the functions that will prepare the datasets and data loaders.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"54\" data-enlighter-title=\"dataset.py\" data-enlighter-group=\"dataset_3\">def get_datasets():\n    # Read the data.csv file and get the image paths and labels.\n    df = pd.read_csv(os.path.join('..', 'input', 'trainval.csv'))\n    X = df.image_path.values # Image paths.\n    y = df.target.values # Targets\n    (xtrain, xtest, ytrain, ytest) = train_test_split(\n        X, y,\n        test_size=0.20, random_state=42\n    )\n    dataset_train = ImageDataset(xtrain, ytrain, tfms=1)\n    dataset_valid = ImageDataset(xtest, ytest, tfms=0)\n    return dataset_train, dataset_valid\n\ndef get_data_loaders(dataset_train, dataset_valid, batch_size, num_workers=0):\n    \"\"\"\n    Prepares the training and validation data loaders.\n\n    :param dataset_train: The training dataset.\n    :param dataset_valid: The validation dataset.\n\n    Returns the training and validation data loaders.\n    \"\"\"\n    train_loader = DataLoader(\n        dataset_train, batch_size=batch_size, \n        shuffle=True, num_workers=num_workers\n    )\n    valid_loader = DataLoader(\n        dataset_valid, batch_size=batch_size, \n        shuffle=False, num_workers=num_workers\n    )\n    return train_loader, valid_loader <\/pre>\n\n\n\n<p>The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">getdatasets<\/code> function reads the CSV file and stores the image paths and labels in <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">X<\/code> and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">y<\/code> variables respectively. Then we split the dataset into an 80% training set and a 20% validation set. We get the PyTorch datasets by calling the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">ImageDataset<\/code> class on <strong>lines 63 and 64<\/strong>.<\/p>\n\n\n\n<p>The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">get_data_loaders<\/code> function accepts the training &amp; validation datasets, the batch size, and the number of workers. It creates the respective training and validation data loaders and simply returns them.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Deep Learning Model<\/h3>\n\n\n\n<p>We will use the MobileNetV3 Large model for concrete classification using deep learning. We will use the ImageNet pretrained weights to leverage the advantage of faster convergence.<\/p>\n\n\n\n<p><strong><em>The code for model preparation is available in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">model.py<\/code> file<\/em><\/strong>.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"model.py\" data-enlighter-group=\"model_1\">import torchvision.models as models\nimport torch.nn as nn\n\ndef build_model(pretrained=True, fine_tune=False, num_classes=10):\n    if pretrained:\n        print('[INFO]: Loading pre-trained weights')\n    else:\n        print('[INFO]: Not loading pre-trained weights')\n    model = models.mobilenet_v3_large(pretrained=pretrained)\n\n    if fine_tune:\n        print('[INFO]: Fine-tuning all layers...')\n        for params in model.parameters():\n            params.requires_grad = True\n    elif not fine_tune:\n        print('[INFO]: Freezing hidden layers...')\n        for params in model.parameters():\n            params.requires_grad = False\n\n    # Change the final classification head.\n    model.classifier[3] = nn.Linear(in_features=1280, out_features=num_classes)\n    return model<\/pre>\n\n\n\n<p>It is a pretty simple code where we load the pretrained weights based on the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">pretrained<\/code> parameter and also decide whether to fine-tune or not based on the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">fine_tune<\/code> parameter.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">The Training Script<\/h3>\n\n\n\n<p>Now, we have reached the point to write the code for the training script.<\/p>\n\n\n\n<p><strong><em>We will write this code in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">train.py<\/code> file<\/em><\/strong>.<\/p>\n\n\n\n<p>First, we have the import statements and the argument parsers.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"train.py\" data-enlighter-group=\"train_1\">import torch\nimport argparse\nimport torch.nn as nn\nimport torch.optim as optim\nimport time\n\nfrom tqdm.auto import tqdm\n\nfrom model import build_model\nfrom dataset import get_datasets, get_data_loaders\nfrom utils import save_model, save_plots, LRScheduler\n\n# Construct the argument parser.\nparser = argparse.ArgumentParser()\nparser.add_argument(\n    '-e', '--epochs', type=int, default=20,\n    help='number of epochs to train our network for'\n)\nparser.add_argument(\n    '-b', '--batch-size', dest='batch_size', type=int, default=32,\n    help='batch size for data loaders'\n)\nparser.add_argument(\n    '-lr', '--learning-rate', type=float,\n    dest='learning_rate', default=0.001,\n    help='learning rate for training the model'\n)\nargs = vars(parser.parse_args())<\/pre>\n\n\n\n<p>We load all the custom modules that we need. For the argument parser we have the following flags:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">--epochs<\/code>: To control the number of epochs to train for.<\/li>\n\n\n\n<li><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">--batch-size<\/code>: To provide the batch size directly while executing the script.<\/li>\n\n\n\n<li><code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">--learning-rate<\/code>: We can also control the initial learning rate using this flag.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The Training and Validation Functions<\/h4>\n\n\n\n<p>We will use pretty standard PyTorch image classification functions for training and validation. The following block contains both functions.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"29\" data-enlighter-title=\"train.py\" data-enlighter-group=\"train_2\"># Training function.\ndef train(model, trainloader, optimizer, criterion):\n    model.train()\n    print('Training')\n    train_running_loss = 0.0\n    train_running_correct = 0\n    counter = 0\n    for i, data in tqdm(enumerate(trainloader), total=len(trainloader)):\n        counter += 1\n        image, labels = data\n        image = image.to(device)\n        labels = labels.to(device)\n        optimizer.zero_grad()\n        # Forward pass.\n        outputs = model(image)\n        # Calculate the loss.\n        loss = criterion(outputs, labels)\n        train_running_loss += loss.item()\n        # Calculate the accuracy.\n        _, preds = torch.max(outputs.data, 1)\n        train_running_correct += (preds == labels).sum().item()\n        # Backpropagation\n        loss.backward()\n        # Update the weights.\n        optimizer.step()\n    \n    # Loss and accuracy for the complete epoch.\n    epoch_loss = train_running_loss \/ counter\n    # epoch_acc = 100. * (train_running_correct \/ len(trainloader.dataset))\n    epoch_acc = 100. * (train_running_correct \/ len(trainloader.dataset))\n    return epoch_loss, epoch_acc\n\n# Validation function.\ndef validate(model, testloader, criterion):\n    model.eval()\n    print('Validation')\n    valid_running_loss = 0.0\n    valid_running_correct = 0\n    counter = 0\n\n    with torch.no_grad():\n        for i, data in tqdm(enumerate(testloader), total=len(testloader)):\n            counter += 1\n            \n            image, labels = data\n            image = image.to(device)\n            labels = labels.to(device)\n            # Forward pass.\n            outputs = model(image)\n            # Calculate the loss.\n            loss = criterion(outputs, labels)\n            valid_running_loss += loss.item()\n            # Calculate the accuracy.\n            _, preds = torch.max(outputs.data, 1)\n            valid_running_correct += (preds == labels).sum().item()\n        \n    # Loss and accuracy for the complete epoch.\n    epoch_loss = valid_running_loss \/ counter\n    epoch_acc = 100. * (valid_running_correct \/ len(testloader.dataset))\n    return epoch_loss, epoch_acc<\/pre>\n\n\n\n<p>For both the functions, we return the loss and accuracy value for each epoch at the end.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">The Main Code Block<\/h4>\n\n\n\n<p>Finally, the rest of the code is inside the main block.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"74\" data-enlighter-title=\"train.py\" data-enlighter-group=\"train_3\">if __name__ == '__main__':\n    # Load the training and validation datasets.\n    dataset_train, dataset_valid = get_datasets()\n    print(f\"[INFO]: Number of training images: {len(dataset_train)}\")\n    print(f\"[INFO]: Number of validation images: {len(dataset_valid)}\")\n    # Load the training and validation data loaders.\n    train_loader, valid_loader = get_data_loaders(\n        dataset_train=dataset_train, \n        dataset_valid=dataset_valid,\n        batch_size=args['batch_size'],\n        num_workers=4\n    )\n\n    # Learning_parameters. \n    lr = args['learning_rate']\n    epochs = args['epochs']\n    device = ('cuda' if torch.cuda.is_available() else 'cpu')\n    print(f\"Computation device: {device}\")\n    print(f\"Learning rate: {lr}\")\n    print(f\"Epochs to train for: {epochs}\\n\")\n\n    # Load the model.\n    model = build_model(\n        pretrained=True,\n        fine_tune=True, \n        num_classes=2\n    ).to(device)\n    \n    # Total parameters and trainable parameters.\n    total_params = sum(p.numel() for p in model.parameters())\n    print(f\"{total_params:,} total parameters.\")\n    total_trainable_params = sum(\n        p.numel() for p in model.parameters() if p.requires_grad)\n    print(f\"{total_trainable_params:,} training parameters.\")\n\n    # Optimizer.\n    optimizer = optim.Adam(model.parameters(), lr=lr)\n    # Loss function.\n    criterion = nn.CrossEntropyLoss()\n    # Initialize LR scheduler.\n    lr_scheduler = LRScheduler(optimizer, patience=1, factor=0.5)\n\n    # Lists to keep track of losses and accuracies.\n    train_loss, valid_loss = [], []\n    train_acc, valid_acc = [], []\n    # Start the training.\n    for epoch in range(epochs):\n        print(f\"[INFO]: Epoch {epoch+1} of {epochs}\")\n        train_epoch_loss, train_epoch_acc = train(\n            model, \n            train_loader, \n            optimizer, \n            criterion\n        )\n        valid_epoch_loss, valid_epoch_acc = validate(\n            model, \n            valid_loader, \n            criterion\n        )\n        train_loss.append(train_epoch_loss)\n        valid_loss.append(valid_epoch_loss)\n        train_acc.append(train_epoch_acc)\n        valid_acc.append(valid_epoch_acc)\n        print(f\"Training loss: {train_epoch_loss:.3f}, training acc: {train_epoch_acc:.3f}\")\n        print(f\"Validation loss: {valid_epoch_loss:.3f}, validation acc: {valid_epoch_acc:.3f}\")\n        lr_scheduler(valid_epoch_loss)\n        print('-'*50)\n        time.sleep(5)\n        \n    # Save the trained model weights.\n    save_model(epochs, model, optimizer, criterion)\n    # Save the loss and accuracy plots.\n    save_plots(train_acc, valid_acc, train_loss, valid_loss)\n    print('TRAINING COMPLETE')\n<\/pre>\n\n\n\n<p>Let&#8217;s go over some of the important parts:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>For the model initialization, we are using the pretrained weights and fine-tuning all the layers as well (<strong>line 96<\/strong>).<\/li>\n\n\n\n<li>We are initializing the learning rate scheduler on <strong>line 114<\/strong>. The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">patience<\/code> is 1 and <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">factor<\/code> is 0.5. So, the learning rate will be reduced by half if the validation loss does not improve even for 1 epoch. <\/li>\n\n\n\n<li>After that, the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">for<\/code> loop (from <strong>line 120<\/strong>) carries out the training and validation for the specified number of epochs.<\/li>\n<\/ul>\n\n\n\n<p><\/p>\n\n\n\n<p>In the end, we save the trained model and the accuracy &amp; loss graphs to disk.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Execute train.py for Concrete Crack Classification using Deep Learning<\/h3>\n\n\n\n<p>Open the command line\/terminal in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">src<\/code> directory and execute the following command.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">python train.py --epochs 30 --batch-size 128<\/pre>\n\n\n\n<p>We are training for 30 epochs with a batch size of 128. If you face an <strong><em>Out Of Memory <\/em><\/strong>error, then please reduce the batch size to the maximum amount that you can fit in the memory.<\/p>\n\n\n\n<p>The following block shows the truncated outputs.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">python train.py --epochs 30 --batch-size 128\n[INFO]: Number of training images: 40385\n[INFO]: Number of validation images: 10097\nComputation device: cuda\nLearning rate: 0.001\nEpochs to train for: 30\n\n[INFO]: Loading pre-trained weights\n[INFO]: Fine-tuning all layers...\n4,204,594 total parameters.\n4,204,594 training parameters.\n[INFO]: Epoch 1 of 30\nTraining\n100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 316\/316 [01:01&lt;00:00,  5.14it\/s]\nValidation\n100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 79\/79 [00:05&lt;00:00, 13.67it\/s]\nTraining loss: 0.257, training acc: 91.274\nValidation loss: 0.211, validation acc: 92.612\n--------------------------------------------------\n.\n.\n.\n[INFO]: Epoch 29 of 30\nTraining\n100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 316\/316 [01:00&lt;00:00,  5.22it\/s]\nValidation\n100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 79\/79 [00:05&lt;00:00, 14.04it\/s]\nTraining loss: 0.130, training acc: 95.615\nValidation loss: 0.160, validation acc: 94.850\nEpoch 00029: reducing learning rate of group 0 to 3.9063e-06.\n--------------------------------------------------\n[INFO]: Epoch 30 of 30\nTraining\n100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 316\/316 [01:00&lt;00:00,  5.20it\/s]\nValidation\n100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 79\/79 [00:05&lt;00:00, 14.46it\/s]\nTraining loss: 0.127, training acc: 95.694\nValidation loss: 0.160, validation acc: 94.880\n--------------------------------------------------\nTRAINING COMPLETE<\/pre>\n\n\n\n<p>After the final epoch, the validation loss is 0.160 and the validation accuracy is 94.880%. Looks pretty good. Let&#8217;s take a look at the graphs to get some more insights.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-accuracy.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"700\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-accuracy.png\" alt=\"Concrete crack classification using deep learning accuracy graph.\" class=\"wp-image-24947\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-accuracy.png 1000w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-accuracy-300x210.png 300w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-accuracy-768x538.png 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 4. Accuracy graph after training the MobileNetv3 model on the concrete crack classification dataset.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-loss.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"1000\" height=\"700\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-loss.png\" alt=\"Loss graphs for concrete crack classification using deep learning.\" class=\"wp-image-24949\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-loss.png 1000w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-loss-300x210.png 300w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/concrete-crack-classification-deep-learning-loss-768x538.png 768w\" sizes=\"auto, (max-width: 1000px) 100vw, 1000px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 5. The loss graph after training the neural network model.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n<p>From the validation loss plot, it looks like the model is slightly overfitting in the final few epochs. <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Testing the Trained Model for Concrete Crack Classification using Deep Learning<\/h3>\n\n\n\n<p>As we have the trained model with us now, let&#8217;s test it on all the images whose paths are available in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">test.csv<\/code> file. The test code is going to be pretty straightforward.<\/p>\n\n\n\n<p><strong><em>The code for testing the model is available in the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">test.py<\/code> script<\/em><\/strong>.<\/p>\n\n\n\n<p>First, we need to import all the required modules and define a few constants.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"test.py\" data-enlighter-group=\"test_1\">from selectors import EpollSelector\nimport torch\nimport numpy as np\nimport cv2\nimport pandas as pd\nimport os\nimport torch.nn.functional as F\n\nfrom torch.utils.data import DataLoader\nfrom tqdm.auto import tqdm\nfrom model import build_model\nfrom dataset import ImageDataset\n\n# Constants and other configurations.\nBATCH_SIZE = 1\nDEVICE = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')\nIMAGE_RESIZE = 256\nNUM_WORKERS = 4\nCLASS_NAMES = ['Crack Detected', 'Crack Undetected']<\/pre>\n\n\n\n<p>To create the test data loaders we will use a batch size of 1. We use the same image size as in the training, that is, 256&#215;256. We have a <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">CLASS_NAMES<\/code> list to map the results to the class names. Index 0 will represent that a crack is detected, while index 1 will represent that a crack is not detected.<\/p>\n\n\n\n<p>Next, we have three functions.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"20\" data-enlighter-title=\"test.py\" data-enlighter-group=\"test_2\">def denormalize(\n    x, \n    mean=[0.485, 0.456, 0.406], \n    std=[0.229, 0.224, 0.225]\n):\n    for t, m, s in zip(x, mean, std):\n        t.mul_(s).add_(m)\n    return torch.clamp(x, 0, 1)\n\ndef save_test_results(tensor, target, output_class, counter):\n    \"\"\"\n    This function will save a few test images along with the \n    ground truth label and predicted label annotated on the image.\n\n    :param tensor: The image tensor.\n    :param target: The ground truth class number.\n    :param output_class: The predicted class number.\n    :param counter: The test image number.\n    \"\"\"\n    image = denormalize(tensor).cpu()\n    image = image.squeeze(0).permute((1, 2, 0)).numpy()\n    image = np.ascontiguousarray(image, dtype=np.float32)\n    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)\n    gt = target.cpu().numpy()\n    cv2.putText(\n        image, f\"GT: {CLASS_NAMES[int(gt)]}\", \n        (5, 25), cv2.FONT_HERSHEY_SIMPLEX, \n        0.5, (0, 255, 0), 2, cv2.LINE_AA\n    )\n    if output_class == gt:\n        color = (0, 255, 0)\n    else:\n        color = (0, 0, 255)\n    cv2.putText(\n        image, f\"Pred: {CLASS_NAMES[int(output_class)]}\", \n        (5, 55), cv2.FONT_HERSHEY_SIMPLEX, \n        0.5, color, 2, cv2.LINE_AA\n    )\n    cv2.imwrite(\n        os.path.join('..', 'outputs', 'test_results', 'test_image_'+str(counter)+'.png'), \n        image*255.\n    )\n\ndef test(model, testloader, DEVICE):\n    \"\"\"\n    Function to test the trained model on the test dataset.\n\n    :param model: The trained model.\n    :param testloader: The test data loader.\n    :param DEVICE: The computation device.\n\n    Returns:\n        predictions_list: List containing all the predicted class numbers.\n        ground_truth_list: List containing all the ground truth class numbers.\n        acc: The test accuracy.\n    \"\"\"\n    model.eval()\n    print('Testing model')\n    predictions_list = []\n    ground_truth_list = []\n    test_running_correct = 0\n    counter = 0\n    with torch.no_grad():\n        for i, data in tqdm(enumerate(testloader), total=len(testloader)):\n            counter += 1\n            image, labels = data\n            image = image.to(DEVICE)\n            labels = labels.to(DEVICE)\n\n            # Forward pass.\n            outputs = model(image)\n            # Softmax probabilities.\n            predictions = F.softmax(outputs).cpu().numpy()\n            # Predicted class number.\n            output_class = np.argmax(predictions)\n            # Append the GT and predictions to the respective lists.\n            predictions_list.append(output_class)\n            ground_truth_list.append(labels.cpu().numpy())\n            # Calculate the accuracy.\n            _, preds = torch.max(outputs.data, 1)\n            test_running_correct += (preds == labels).sum().item()\n\n            # Save a few test images.\n            if counter % 99 == 0:\n                save_test_results(image, labels, output_class, counter)\n\n    acc = 100. * (test_running_correct \/ len(testloader.dataset))\n    return predictions_list, ground_truth_list, acc<\/pre>\n\n\n\n<p>As we will be using ImageNet normalization stats while creating the datasets, so, we will also need to denormalize them. We need this for saving the image properly to disk again with the predicted and ground-truth annotations. The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">denormalize<\/code> function does that for us.<\/p>\n\n\n\n<p>The <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">save_test_results<\/code> function saves an image to disk after annotating it with the ground truth and predicted label to disk. <\/p>\n\n\n\n<p>And the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">test<\/code> function runs the test on the entire test dataset. It returns the accuracy in the end. <\/p>\n\n\n\n<p><em>Note that instead of saving all the images to disk, we save only every 99th image <\/em>(<strong>line 103<\/strong>).<\/p>\n\n\n\n<p>Finally, we have the main code block.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"python\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"108\" data-enlighter-title=\"test.py\" data-enlighter-group=\"test_3\">if __name__ == '__main__':\n    df = pd.read_csv(os.path.join('..', 'input', 'test.csv'))\n    X = df.image_path.values # Image paths.\n    y = df.target.values # Targets\n    dataset_test = ImageDataset(X, y, tfms=0)\n\n    test_loader = DataLoader(\n        dataset_test, batch_size=BATCH_SIZE, \n        shuffle=False, num_workers=NUM_WORKERS\n    )\n    checkpoint = torch.load(os.path.join('..', 'outputs', 'model.pth'))\n    # Load the model.\n    model = build_model(\n        pretrained=False,\n        fine_tune=False, \n        num_classes=2\n    ).to(DEVICE)\n    model.load_state_dict(checkpoint['model_state_dict'])\n    predictions_list, ground_truth_list, acc = test(\n        model, test_loader, DEVICE\n    )\n    print(f\"Test accuracy: {acc:.3f}%\")<\/pre>\n\n\n\n<p>We create the test data loader, load the trained model weights and call the <code data-enlighter-language=\"generic\" class=\"EnlighterJSRAW\">test<\/code> function.<\/p>\n\n\n\n<p>Execute the test script using the following command.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"generic\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">python test.py<\/pre>\n\n\n\n<p>The following is the output.<\/p>\n\n\n\n<pre class=\"EnlighterJSRAW\" data-enlighter-language=\"raw\" data-enlighter-theme=\"\" data-enlighter-highlight=\"\" data-enlighter-linenumbers=\"\" data-enlighter-lineoffset=\"\" data-enlighter-title=\"\" data-enlighter-group=\"\">[INFO]: Not loading pre-trained weights\n[INFO]: Freezing hidden layers...\nTesting model\n100%|\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588| 5610\/5610 [00:35&lt;00:00, 157.98it\/s]\nTest accuracy: 94.973%<\/pre>\n\n\n\n<p>We get almost 95% accuracy, which is pretty nice to be fair.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Analyzing the Test Results<\/h3>\n\n\n\n<p>A few of the test results are saved to disk. Let&#8217;s check out a few of them.<\/p>\n\n\n\n<p>The following image shows some results where the original structure did not contain any cracks and the model also predicted so.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-no-crack-predictions.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"197\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-no-crack-predictions.png\" alt=\"Correct predictions where no cracks were present.\" class=\"wp-image-24951\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-no-crack-predictions.png 600w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-no-crack-predictions-300x99.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 6. Correct predictions where no cracks were present.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n<p>The next figure shows where the image contains cracks and the model also predicted that correctly.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-crack-detected-predictions.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"195\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-crack-detected-predictions.png\" alt=\"Correct predictions where cracks were present.\" class=\"wp-image-24953\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-crack-detected-predictions.png 600w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/correct-crack-detected-predictions-300x98.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 7. Correct predictions where cracks were present.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n<p>Finally, a few images where the model made mistakes.<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full is-resized\"><a href=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/wrong-predictions.png\" target=\"_blank\" rel=\"noopener\"><img loading=\"lazy\" decoding=\"async\" width=\"600\" height=\"192\" src=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/wrong-predictions.png\" alt=\"Wrong predictions for concrete crack classification using deep learning.\" class=\"wp-image-24955\" style=\"width:600px;height:192px\" srcset=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/wrong-predictions.png 600w, https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/wrong-predictions-300x96.png 300w\" sizes=\"auto, (max-width: 600px) 100vw, 600px\" \/><\/a><figcaption class=\"wp-element-caption\"><strong>Figure 8. Wrong predictions for concrete crack classification using deep learning.<\/strong><\/figcaption><\/figure>\n<\/div>\n\n\n<p>To be fair, it is pretty difficult for even a human eye to find the crack in the above images. No doubt, the model predicted them wrongly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Further Improvements<\/h2>\n\n\n\n<p>We saw the cases where the model is making mistakes. Maybe using a bigger model like ResNet34 or ResNet50 will solve that. If you try with a larger model, try to share your results in the comment section.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Summary and Conclusion<\/h2>\n\n\n\n<p>In this post, we carried out a simple yet interesting project of concrete crack classification using deep learning. Starting from preparing the data in the appropriate format till the testing of the trained model, we followed each step. Hopefully, you found this post useful.<\/p>\n\n\n\n<p>If you have any doubts, thoughts, or suggestions, please leave them in the comment section. I will surely address them.<\/p>\n\n\n\n<p>You can contact me using the <strong><a aria-label=\"Contact (opens in a new tab)\" href=\"https:\/\/debuggercafe.com\/contact-us\/\" target=\"_blank\" rel=\"noreferrer noopener\">Contact<\/a><\/strong> section. You can also find me on <strong><a aria-label=\"LinkedIn (opens in a new tab)\" href=\"https:\/\/www.linkedin.com\/in\/sovit-rath\/\" target=\"_blank\" rel=\"noreferrer noopener\">LinkedIn<\/a><\/strong>, and <strong><a href=\"https:\/\/x.com\/SovitRath5\" target=\"_blank\" rel=\"noreferrer noopener\">X<\/a><\/strong>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this post, we solve the problem of concrete crack classification using deep learning with the MobileNetv3 model.<\/p>\n","protected":false},"author":1,"featured_media":24958,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[76,113,59,119,57,90],"tags":[77,112,61,123,183,91,163],"class_list":["post-24839","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-computer-vision","category-convolutional-neural-networks","category-deep-learning","category-image-classification","category-neural-networks","category-pytorch","tag-computer-vision","tag-convolutional-neural-networks","tag-deep-learning","tag-image-classification","tag-mobilenetv3","tag-pytorch","tag-torchvision"],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v26.9 - https:\/\/yoast.com\/product\/yoast-seo-wordpress\/ -->\n<title>Concrete Crack Classification using Deep Learning<\/title>\n<meta name=\"description\" content=\"In this post, we solve the problem of concrete crack classification using deep learning with the MobileNetv3 model.\" \/>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/\" \/>\n<meta property=\"og:locale\" content=\"en_US\" \/>\n<meta property=\"og:type\" content=\"article\" \/>\n<meta property=\"og:title\" content=\"Concrete Crack Classification using Deep Learning\" \/>\n<meta property=\"og:description\" content=\"In this post, we solve the problem of concrete crack classification using deep learning with the MobileNetv3 model.\" \/>\n<meta property=\"og:url\" content=\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/\" \/>\n<meta property=\"og:site_name\" content=\"DebuggerCafe\" \/>\n<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/profile.php?id=100013731104496\" \/>\n<meta property=\"article:published_time\" content=\"2022-08-01T00:30:00+00:00\" \/>\n<meta property=\"article:modified_time\" content=\"2024-09-15T15:12:53+00:00\" \/>\n<meta property=\"og:image\" content=\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png\" \/>\n\t<meta property=\"og:image:width\" content=\"1000\" \/>\n\t<meta property=\"og:image:height\" content=\"563\" \/>\n\t<meta property=\"og:image:type\" content=\"image\/png\" \/>\n<meta name=\"author\" content=\"Sovit Ranjan Rath\" \/>\n<meta name=\"twitter:card\" content=\"summary_large_image\" \/>\n<meta name=\"twitter:creator\" content=\"@SovitRath5\" \/>\n<meta name=\"twitter:site\" content=\"@SovitRath5\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Sovit Ranjan Rath\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"1 minute\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"Article\",\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#article\",\"isPartOf\":{\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/\"},\"author\":{\"name\":\"Sovit Ranjan Rath\",\"@id\":\"https:\/\/debuggercafe.com\/#\/schema\/person\/27719b14d930bd4a88ade40d18b0a752\"},\"headline\":\"Concrete Crack Classification using Deep Learning\",\"datePublished\":\"2022-08-01T00:30:00+00:00\",\"dateModified\":\"2024-09-15T15:12:53+00:00\",\"mainEntityOfPage\":{\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/\"},\"wordCount\":2453,\"commentCount\":0,\"image\":{\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png\",\"keywords\":[\"Computer Vision\",\"Convolutional Neural Networks\",\"Deep Learning\",\"Image Classification\",\"MobileNetV3\",\"PyTorch\",\"torchvision\"],\"articleSection\":[\"Computer Vision\",\"Convolutional Neural Networks\",\"Deep Learning\",\"Image Classification\",\"Neural Networks\",\"PyTorch\"],\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"CommentAction\",\"name\":\"Comment\",\"target\":[\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#respond\"]}]},{\"@type\":\"WebPage\",\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/\",\"url\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/\",\"name\":\"Concrete Crack Classification using Deep Learning\",\"isPartOf\":{\"@id\":\"https:\/\/debuggercafe.com\/#website\"},\"primaryImageOfPage\":{\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage\"},\"image\":{\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage\"},\"thumbnailUrl\":\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png\",\"datePublished\":\"2022-08-01T00:30:00+00:00\",\"dateModified\":\"2024-09-15T15:12:53+00:00\",\"author\":{\"@id\":\"https:\/\/debuggercafe.com\/#\/schema\/person\/27719b14d930bd4a88ade40d18b0a752\"},\"description\":\"In this post, we solve the problem of concrete crack classification using deep learning with the MobileNetv3 model.\",\"breadcrumb\":{\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/\"]}]},{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage\",\"url\":\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png\",\"contentUrl\":\"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png\",\"width\":1000,\"height\":563,\"caption\":\"Concrete Crack Classification using Deep Learning\"},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/debuggercafe.com\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"Concrete Crack Classification using Deep Learning\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/debuggercafe.com\/#website\",\"url\":\"https:\/\/debuggercafe.com\/\",\"name\":\"DebuggerCafe\",\"description\":\"Machine Learning and Deep Learning\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/debuggercafe.com\/?s={search_term_string}\"},\"query-input\":{\"@type\":\"PropertyValueSpecification\",\"valueRequired\":true,\"valueName\":\"search_term_string\"}}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/debuggercafe.com\/#\/schema\/person\/27719b14d930bd4a88ade40d18b0a752\",\"name\":\"Sovit Ranjan Rath\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/debuggercafe.com\/#\/schema\/person\/image\/\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/f71ca13ec56d630e7d8045e8b846396068791aa204936c3d74d721c6dd2b4d3c?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/f71ca13ec56d630e7d8045e8b846396068791aa204936c3d74d721c6dd2b4d3c?s=96&d=mm&r=g\",\"caption\":\"Sovit Ranjan Rath\"}}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"Concrete Crack Classification using Deep Learning","description":"In this post, we solve the problem of concrete crack classification using deep learning with the MobileNetv3 model.","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/","og_locale":"en_US","og_type":"article","og_title":"Concrete Crack Classification using Deep Learning","og_description":"In this post, we solve the problem of concrete crack classification using deep learning with the MobileNetv3 model.","og_url":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/","og_site_name":"DebuggerCafe","article_publisher":"https:\/\/www.facebook.com\/profile.php?id=100013731104496","article_published_time":"2022-08-01T00:30:00+00:00","article_modified_time":"2024-09-15T15:12:53+00:00","og_image":[{"width":1000,"height":563,"url":"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png","type":"image\/png"}],"author":"Sovit Ranjan Rath","twitter_card":"summary_large_image","twitter_creator":"@SovitRath5","twitter_site":"@SovitRath5","twitter_misc":{"Written by":"Sovit Ranjan Rath","Est. reading time":"1 minute"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"Article","@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#article","isPartOf":{"@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/"},"author":{"name":"Sovit Ranjan Rath","@id":"https:\/\/debuggercafe.com\/#\/schema\/person\/27719b14d930bd4a88ade40d18b0a752"},"headline":"Concrete Crack Classification using Deep Learning","datePublished":"2022-08-01T00:30:00+00:00","dateModified":"2024-09-15T15:12:53+00:00","mainEntityOfPage":{"@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/"},"wordCount":2453,"commentCount":0,"image":{"@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage"},"thumbnailUrl":"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png","keywords":["Computer Vision","Convolutional Neural Networks","Deep Learning","Image Classification","MobileNetV3","PyTorch","torchvision"],"articleSection":["Computer Vision","Convolutional Neural Networks","Deep Learning","Image Classification","Neural Networks","PyTorch"],"inLanguage":"en-US","potentialAction":[{"@type":"CommentAction","name":"Comment","target":["https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#respond"]}]},{"@type":"WebPage","@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/","url":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/","name":"Concrete Crack Classification using Deep Learning","isPartOf":{"@id":"https:\/\/debuggercafe.com\/#website"},"primaryImageOfPage":{"@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage"},"image":{"@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage"},"thumbnailUrl":"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png","datePublished":"2022-08-01T00:30:00+00:00","dateModified":"2024-09-15T15:12:53+00:00","author":{"@id":"https:\/\/debuggercafe.com\/#\/schema\/person\/27719b14d930bd4a88ade40d18b0a752"},"description":"In this post, we solve the problem of concrete crack classification using deep learning with the MobileNetv3 model.","breadcrumb":{"@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/"]}]},{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#primaryimage","url":"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png","contentUrl":"https:\/\/debuggercafe.com\/wp-content\/uploads\/2022\/07\/Concrete-Crack-Classification-using-Deep-Learning-e1657019694776.png","width":1000,"height":563,"caption":"Concrete Crack Classification using Deep Learning"},{"@type":"BreadcrumbList","@id":"https:\/\/debuggercafe.com\/concrete-crack-classification-using-deep-learning\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/debuggercafe.com\/"},{"@type":"ListItem","position":2,"name":"Concrete Crack Classification using Deep Learning"}]},{"@type":"WebSite","@id":"https:\/\/debuggercafe.com\/#website","url":"https:\/\/debuggercafe.com\/","name":"DebuggerCafe","description":"Machine Learning and Deep Learning","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/debuggercafe.com\/?s={search_term_string}"},"query-input":{"@type":"PropertyValueSpecification","valueRequired":true,"valueName":"search_term_string"}}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/debuggercafe.com\/#\/schema\/person\/27719b14d930bd4a88ade40d18b0a752","name":"Sovit Ranjan Rath","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/debuggercafe.com\/#\/schema\/person\/image\/","url":"https:\/\/secure.gravatar.com\/avatar\/f71ca13ec56d630e7d8045e8b846396068791aa204936c3d74d721c6dd2b4d3c?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/f71ca13ec56d630e7d8045e8b846396068791aa204936c3d74d721c6dd2b4d3c?s=96&d=mm&r=g","caption":"Sovit Ranjan Rath"}}]}},"_links":{"self":[{"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/posts\/24839","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/comments?post=24839"}],"version-history":[{"count":124,"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/posts\/24839\/revisions"}],"predecessor-version":[{"id":38061,"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/posts\/24839\/revisions\/38061"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/media\/24958"}],"wp:attachment":[{"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/media?parent=24839"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/categories?post=24839"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/debuggercafe.com\/wp-json\/wp\/v2\/tags?post=24839"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}