{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"markdown","source":"# **YOLOv5 Simplified: Great-Barrier-Reef Train (Adapted by Orkatz, Awsaf, and Cèsar Olivares)** \n![](https://storage.googleapis.com/kaggle-competitions/kaggle/31703/logos/header.png?t=2021-10-29-00-30-04)","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19"}},{"cell_type":"markdown","source":"## Introduction\nNow we have completed finding the COTS in the EDA version, let's take things seriously. This time, we are going to detect COTS with YOLOv5. But first, we have to train detecting COTS with YOLOv5, and then submit predictions using inference over YOLOv5, which I'll demonstrate on the next notebook.","metadata":{}},{"cell_type":"markdown","source":"## Install Libraries\nThe first part is different. You need to install libaries with bash commands. First of all, we need to install WandB via pip. Next, add the apt repository, the ppa:ubuntu-toolchain-r/test. Lastly, update and upgrade libstdc++6. The reason why we have to install all four libraries because these four libraries are the most important keys to train YOLOv5.","metadata":{}},{"cell_type":"code","source":"!pip install -qU wandb\n!add-apt-repository ppa:ubuntu-toolchain-r/test -y\n!apt-get update\n!apt-get upgrade libstdc++6 -y","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:19:45.766116Z","iopub.execute_input":"2022-01-06T20:19:45.766387Z","iopub.status.idle":"2022-01-06T20:24:26.046574Z","shell.execute_reply.started":"2022-01-06T20:19:45.766342Z","shell.execute_reply":"2022-01-06T20:24:26.045406Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Import Libraries\nAfter downloading libraries with the bash command lines, we are going to import libraries with Python. In order to train YOLOv5, we need to import groupby from itertools, np from numpy (for linear algebra), tqdm from tqdm.notebook (since we need to setup the pandas function to tqdm), pd from pandas (for CSV processing), os, pickle, cv2 (aka OpenCV), Pool from multiprocessing, ast and glob. We also need to import shutil and sys since we will append the path of the competition dataset, along with Parellel, delayed from joblib, and display, HTML from IPython.display, and lastly but mostly important, the animation and rc from the popular plotting matplotlib module, although we have to set up the rc function with animation and jshtml. We even install and import imagesize too.","metadata":{}},{"cell_type":"code","source":"from itertools import groupby\nimport numpy as np\nfrom tqdm.notebook import tqdm\ntqdm.pandas()\nimport pandas as pd\nimport os\nimport pickle\nimport cv2\nfrom multiprocessing import Pool\nimport ast\nimport glob\n\nimport shutil\nimport sys\nsys.path.append('../input/tensorflow-great-barrier-reef')\n\nfrom joblib import Parallel, delayed\nfrom IPython.display import display, HTML\nfrom matplotlib import animation, rc\nrc('animation', html='jshtml')","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:24:26.048729Z","iopub.execute_input":"2022-01-06T20:24:26.048951Z","iopub.status.idle":"2022-01-06T20:24:26.443727Z","shell.execute_reply.started":"2022-01-06T20:24:26.048922Z","shell.execute_reply":"2022-01-06T20:24:26.443002Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Some Key-Points About Training YOLOv5\nHere are the key-points about what to train Yolov5:\n* One have to submit prediction(s) using the provided **python time-series API**, meaning that this makes this competition very different from previous Object Detection Competitions, like the global wheat detection.\n* Every prediction row needs to include all of the bounding boxes for the image. The submission format seems also **COCO (Common Objects in Context dataset)**, which formats in `[x_min, y_min, width, height]`. \n* The competition metric, F2, tolerates some of the false positives (aka FP) in order to ensure that very few starfish weren't detected. In conclusion, the **false negatives (aka FN)** is way more useful than false positives (aka FP).\n\n### **If you were satisfied and helpful about the \"YOLOv5 Simplified: Train\" notebook, upvote it since that will support me.**","metadata":{}},{"cell_type":"markdown","source":"## WandB\n![](https://camo.githubusercontent.com/dd842f7b0be57140e68b2ab9cb007992acd131c48284eaf6b1aca758bfea358b/68747470733a2f2f692e696d6775722e636f6d2f52557469567a482e706e67)\n\nIf you haven't heard about the first line of the code cell of the **Install Libraries** section, then you didn't know that this is Weights & Biases. Weights & Biases (aka W&B) is a MLOps platform known for tracking our experiments on everything in computer training. We can use W&B to build a lot of better models faster experiment tracking, dataset vision, and bug-fix discussion, and even demonstrating progress towards milestones. There are some of the awesome features speaking of W&B:\n* Known for tracking, comparing, and visualizing ML experiments\n* Known for obtaining live metrics, terminal logs, and system stats that were streamed to the centralized dashboard\n* Known to explain how their model works, which meant showing graphs of how model versions improved, discussion of bug-fixing, and demonstating progress towards milestones.\n\nMore info:\nhttps://docs.wandb.ai/","metadata":{}},{"cell_type":"markdown","source":"## The Meta Data\nHere are the meta data in this notebook:\n* `train_images/` - The folder that contains training set of photos of the form at this format of `video_{video_id}/{video_frame}.jpg`.\n* `[train/test].csv` - This is known of the the metadata for all of the images. As along with the other test files, most of the test metadata data is only available to their notebook upon submission over predictions. Only the first few rows are available to download.\n* `video_id` - This specifies the ID number of the video image that was part of. \n* `video_frame` - This specifies the frame number of the image within the video. It is expected to see occasional gaps in the frame number from when the diver surfaced.\n* `sequence` - This is an ID of a gap-free subset of a given video. All of the sequence ids are not meaningfully ordered.\n* `sequence_frame` - This specifies the frame number within a given sequence.\n* `image_id` - This specifies an ID code for the images, within a format of `{video_id}-{video_frame}`.\n* `annotations` - And finally, this is what we are talking about. These are the bounding boxes of any starfish detections in a string format that can be evaluated directly with Python. It doesn't use the same format as the predictions you will submit thus not available in test.csv. A bounding box is described by the pixel coordinate as `(x_min, y_min)` of its lower left corner within the image together with the `width` and `height` in pixels (This is in COCO format).\n\nAfter the metadata mentioned in this notebook in a list, let's set up the variable needed for Yolov5 training! First of all, we have to set the fold variable to 4, which fold to train, the remove_nobbox to true because we have to remove images that contains none of the bboxes, the root directory to the competition dataset, the image and label directory variables. Notice that these are the directories to save images and labels.","metadata":{}},{"cell_type":"code","source":"FOLD = 4 # this meant that the fold to train or maybe crease...\nREMOVE_NOBBOX = True # this meant to exterminate images that has no bbox.\nROOT_DIR = '/kaggle/input/tensorflow-great-barrier-reef'\nIMAGE_DIR = '/kaggle/images' # this is the directory to save images\nLABEL_DIR = '/kaggle/labels' # this is the directory to save labels","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:24:26.445142Z","iopub.execute_input":"2022-01-06T20:24:26.445411Z","iopub.status.idle":"2022-01-06T20:24:26.450248Z","shell.execute_reply.started":"2022-01-06T20:24:26.445357Z","shell.execute_reply":"2022-01-06T20:24:26.449436Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create Directories\nAfter creating the variables, let's now create directories in order to train YOLOv5. All we have to do is to run the mkdir command in bash, adding the image and label dir variables, so that way, we can use these directories to save images and labels seperately.","metadata":{}},{"cell_type":"code","source":"!mkdir -p {IMAGE_DIR}\n!mkdir -p {LABEL_DIR}","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:24:26.452718Z","iopub.execute_input":"2022-01-06T20:24:26.452985Z","iopub.status.idle":"2022-01-06T20:24:27.79012Z","shell.execute_reply.started":"2022-01-06T20:24:26.45295Z","shell.execute_reply":"2022-01-06T20:24:27.789188Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Obtain Paths\nAfter creating the image and label directories, we are now obtaining paths to the images. First of all, we are going to define the get_path function along with the row variable inside. Inside the get_path function, the row with the key, old_image_path, defines the path to the root_dir images. On the other hand, the row variable that has the keys, image/label path, defines the path to the image/label images and txt files. And alas, we finally return the row function.","metadata":{}},{"cell_type":"code","source":"def get_path(row):\n    row['old_image_path'] = f'{ROOT_DIR}/train_images/video_{row.video_id}/{row.video_frame}.jpg'\n    row['image_path'] = f'{IMAGE_DIR}/video_{row.video_id}_{row.video_frame}.jpg'\n    row['label_path'] = f'{LABEL_DIR}/video_{row.video_id}_{row.video_frame}.txt'\n    return row","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:24:27.792759Z","iopub.execute_input":"2022-01-06T20:24:27.793033Z","iopub.status.idle":"2022-01-06T20:24:27.799182Z","shell.execute_reply.started":"2022-01-06T20:24:27.792995Z","shell.execute_reply":"2022-01-06T20:24:27.798326Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now after defining the get_path function, lets train some train data! First of all, create a variable called df, and set it to read csv of train.csv from the root directory, then set the df variable (or a DataFrame) again to itself to apply progress containing the get_path function in order to train it. Next, the df variable (DataFrame) with the annotations key can be setup'd as a variable to the df variable (DataFrame) with annotations key again but applying progress containing a lambda, x, to ast.literal_eval function with the lambda x inside it to train it again. Finally, display the first two rows of df using the head function being set up with 2.","metadata":{}},{"cell_type":"code","source":"# Here's the train data!\ndf = pd.read_csv(f'{ROOT_DIR}/train.csv')\ndf = df.progress_apply(get_path, axis=1)\ndf['annotations'] = df['annotations'].progress_apply(lambda x: ast.literal_eval(x))\ndisplay(df.head(2))","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:24:27.800864Z","iopub.execute_input":"2022-01-06T20:24:27.801496Z","iopub.status.idle":"2022-01-06T20:25:08.116489Z","shell.execute_reply.started":"2022-01-06T20:24:27.801452Z","shell.execute_reply":"2022-01-06T20:25:08.115687Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"After running the train data code cell, you may see that the progress bar for the df variables (DataFrames) and and the df variable (DataFrames) with the annotations key had the estimated progress running and after that, displays something that is similar to train.csv but with the old_image_path, image_path, and label_path keys.","metadata":{}},{"cell_type":"markdown","source":"## Counting the Number of BBoxes\nIn order to check the number of bboxes, we setup the df variable (DataFrames) with the df variable with the annotations key and again, applying the progress containing the get_path function with lambda x to find the len of it. Next, we will define another variable, data and set it to the df variable (DataFrames) with the attribute num_bbox greater than 0 with value counts containing normalize to True times 100. ","metadata":{}},{"cell_type":"code","source":"df['num_bbox'] = df['annotations'].progress_apply(lambda x: len(x))\ndata = (df.num_bbox>0).value_counts(normalize=True)*100\nprint(f\"Number of None BBoxes: {data[0]:0.2f}% | Number of BBoxes: {data[1]:0.2f}%\")","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:08.117945Z","iopub.execute_input":"2022-01-06T20:25:08.118217Z","iopub.status.idle":"2022-01-06T20:25:08.216293Z","shell.execute_reply.started":"2022-01-06T20:25:08.118183Z","shell.execute_reply":"2022-01-06T20:25:08.215489Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As we observed, about 79% (or 80%) of images contained no bboxes. But on the other hand, about 20% (or approximately 21%) of images contained bboxes. And we see that the competition dataset does have mostly lots of images that contained none of the bounding boxes.","metadata":{}},{"cell_type":"markdown","source":"## Data Cleanup\nIn this simplified notebook, we use just **bboxed-images** only (`~5k`). Thus, we can use all of the `~23K` images for training but most images don't have any labels. In conclusion, it would be easier to carry out experiments with **bboxed images** only. So, in order to clean data up, we will make an if statement about the REMOVE_NOBBOX type, then the df variable (DataFrame) would be assigned to the df variable (DataFrame) with the query function over the num_bbox greater than 0 stringed inside.","metadata":{}},{"cell_type":"code","source":"if REMOVE_NOBBOX:\n    df = df.query(\"num_bbox>0\")","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:08.217772Z","iopub.execute_input":"2022-01-06T20:25:08.218031Z","iopub.status.idle":"2022-01-06T20:25:08.244267Z","shell.execute_reply.started":"2022-01-06T20:25:08.217996Z","shell.execute_reply":"2022-01-06T20:25:08.24355Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Writing Images\nIn order to write images, we need to copy all of the images to the Current Directory (which is `/kaggle/working`) as it doesn't have **access to write**, which is why it is needed for **YOLOv5**. But at least we can make this process go faster by using **Joblib**, because it used **Parallel** computing. To start writing images, we first define a function called make_a_copy with a variable, path, inside. Then, the variable, data is assigned to the other variable, path with the function, split over the strings of '/'. Next, we have to specify filename variable by getting the fragment of data with -1, which the same goes to video_id, but with -2. Then, define new_path into joining path with IMAGE_DIR variable and the format of `{video_id}_{filename}'.` Lastly, use shutil and copy it to the new_path variable from the path variable, although this return statement remained blank...","metadata":{}},{"cell_type":"code","source":"def make_a_copy(path):\n    data = path.split('/')\n    filename = data[-1]\n    video_id = data[-2]\n    new_path = os.path.join(IMAGE_DIR, f'{video_id}_{filename}')\n    shutil.copy(path, new_path)\n    return # what?!","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:08.245608Z","iopub.execute_input":"2022-01-06T20:25:08.245871Z","iopub.status.idle":"2022-01-06T20:25:08.250989Z","shell.execute_reply.started":"2022-01-06T20:25:08.245835Z","shell.execute_reply":"2022-01-06T20:25:08.250128Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"After running the make_a_copy function, we assign a variable called image_paths to the df variable (DataFrame) with the old_image_path attribute followed by a tolist function to transform the df variable (DataFrame) to a list. Lastly, assign the underscore character to the Parallel function, which contains the n_jobs being set to -1, and threading being set in backend attribute, thus having another parentheses containing the function delayed that holds the function call, make_a_copy along with the inner parentheses with the path variable included, since it was defined in the for loop over the tqdm function containing the variable, image_paths inside. ","metadata":{}},{"cell_type":"code","source":"image_paths = df.old_image_path.tolist()\n_ = Parallel(n_jobs=-1, backend='threading')(delayed(make_a_copy)(path) for path in tqdm(image_paths))","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:08.254533Z","iopub.execute_input":"2022-01-06T20:25:08.254841Z","iopub.status.idle":"2022-01-06T20:25:39.573505Z","shell.execute_reply.started":"2022-01-06T20:25:08.25478Z","shell.execute_reply":"2022-01-06T20:25:39.572784Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Helper Part for Creating a BBox.\nIf you didn't know what that part is, then you will see that this is the supporters to create a bounding boxes to detect starfish. But note that this code cell remained hidden, which meant that you'll have to reveal the code cell. So, here's how this code cell work in a code cell (see my comments!).","metadata":{}},{"cell_type":"code","source":"# First of all, define a function called voc2yolo containing the variables, image_height, image_width, and bboxes.\ndef voc2yolo(image_height, image_width, bboxes):\n    \"\"\"\n    So, here's how it broke down:\n    voc  => [x1, y1, x2, y1]\n    yolo => [xmid, ymid, w, h] (normalized)\n    \"\"\"\n    \n    # Inside of the voc2yolo function, we first setup a variable called bboxes and set it to the inner voc2yolo variable bboxes with the copy function and the astype function containing the float type.\n    bboxes = bboxes.copy().astype(float) # if we didn't setup the astype as float then all of the values will be 0 as the voc_pascal dtype is now np.int.\n    \n    # Next, assign the bboxes with the slices of ... and the other slices from 0 to 2 to the same of them divided by image_width. This applies to the image_height line, but the inner slices is now 1, 3.\n    bboxes[..., [0, 2]] = bboxes[..., [0, 2]] / image_width\n    bboxes[..., [1, 3]] = bboxes[..., [1, 3]] / image_height\n    \n    # Then specify the two variables, w and h, to the bboxes variable with the slices of ... and 2 (and 0 for h) minus the bboxes variable with slices of ... and 0 (and 1 for h).\n    w = bboxes[..., 2] - bboxes[..., 0]\n    h = bboxes[..., 3] - bboxes[..., 1]\n    \n    # Now, assign bboxes with slices of ... and 0 (and 1) to the variable bboxes with slices of ... and 0 (and 1) plus image_width (and image_height). Thus, assigning bboxes with slices ... and 2 (and 3) to the variables w (and h).\n    bboxes[..., 0] = bboxes[..., 0] + w/2\n    bboxes[..., 1] = bboxes[..., 1] + h/2\n    bboxes[..., 2] = w\n    bboxes[..., 3] = h\n    \n    # Finally, return the bboxes variable.\n    return bboxes\n\n# Next, we then create another function called yolo2voc containing the same variables as the voc2yolo function.\ndef yolo2voc(image_height, image_width, bboxes):\n    \"\"\"\n    Again, here's how it broke down:\n    yolo => [xmid, ymid, w, h] (normalized)\n    voc  => [x1, y1, x2, y1]\n    \"\"\"\n    \n    # Like the voc2yolo function, we again setup the variable called bboxes and assign it to themself with the copy function along with the astype function containing the float type.\n    bboxes = bboxes.copy().astype(float) # again, if we didn't setup the astype as float then tall of the values will be 0 as the voc_pascal dtype is now np.int.\n    \n    # This time, assign the bboxes that has the slices of ... and another inner slices of 0, 2 (1, 3) to itself times the image_width (image_height) variable\n    bboxes[..., [0, 2]] = bboxes[..., [0, 2]] * image_width\n    bboxes[..., [1, 3]] = bboxes[..., [1, 3]] * image_height\n    \n    # And now, assign the bboxes variable with the slices of ... and another inner slices of 0, 1 (and 2, 3) to itself (from the first one for the bboxes variable that has the inner slices of 2 and 3) plus (or minus) the variable with the slices of ... and another slices of 2 and 3 (divided by 2 for the bboxes with the slices of ... and another inner slices of 0, 1).\n    bboxes[..., [0, 1]] = bboxes[..., [0, 1]] - bboxes[..., [2, 3]] / 2\n    bboxes[..., [2, 3]] = bboxes[..., [0, 1]] + bboxes[..., [2, 3]]\n    \n    # Again finally, return the bboxes variable.\n    return bboxes\n\n# Finally, create a new function that converts yolo to coco by naming it yolo2coco, containing the variables image_height, image_width, and bboxes.\ndef yolo2coco(image_height, image_width, bboxes):\n    \"\"\"\n    Finally, here's how it broke down (but this time, it's coco):\n    yolo (again for the last time) => [xmid, ymid, w, h] (normalized)\n    coco (for the debut)           => [xmin, ymin, w, h]\n    \"\"\"\n    \n    # Again for the last time, we setup the variable called bboxes and assign it to themself with the copy function along with the astype function containing the float type.\n    bboxes = bboxes.copy().astype(float) # For the last time, if we didn't setup the astype as float then tall of the values will be 0 as the voc_pascal dtype is now np.int.\n    \n    # Now, let's denormalize! We assign the bboxes with the slices: ... and another inner slices of zero, two (or 1, 3) to itself times image_width (and image_height).\n    bboxes[..., [0, 2]] = bboxes[..., [0, 2]] * image_width\n    bboxes[..., [1, 3]] = bboxes[..., [1, 3]] * image_height\n    \n    # Next, we are now converting the (xmid, ymid) to (xmin, y_min) by assigning the bboxes variable with the slices of ... and the inner slices [0, 1] to itself minus the bboxes variable with the slices of ... and the inner slices, [2, 3] divided by 2.\n    bboxes[..., [0, 1]] = bboxes[..., [0, 1]] - bboxes[..., [2, 3]] / 2\n    \n    # Finally for the last time, return the bboxes variable\n    return bboxes\n\n# But we need one more function, we are creating a new function that converts coco to yolo (coco2yolo) with the same three variables just like the yolo2coco function.\ndef coco2yolo(image_height, image_width, bboxes):\n    \"\"\"\n    Here's how it break down for the very last time (this to the yolo...):\n    yolo => [xmid, ymid, w, h] (again pretty normalized)\n    \"\"\"\n    \n    # We setup the variable bboxes just like other three functions to itself to copy with (the copy function) followed by an astype function for a specific dtype of a float.\n    bboxes = bboxes.copy().astype(float) # Else all value will be 0 as voc_pascal dtype is np.int.\n    \n    # Now, lets normalize for the last time! We assign the bboxes with the slices: ... and another inner slices of zero, two (or 1, 3) to itself divided by the image_width (and image_height).\n    bboxes[..., [0, 2]] = bboxes[..., [0, 2]] / image_width\n    bboxes[..., [1, 3]] = bboxes[..., [1, 3]] / image_height\n    \n    # Next, we are now converting the (xmid, ymid) to (xmin, y_min) by assigning the bboxes variable with the slices of ... and the inner slices [0, 1] to itself minus the bboxes variable with the slices of ... and the inner slices, [2, 3] divided by 2.\n    bboxes[..., [0, 1]] = bboxes[..., [0, 1]] - bboxes[..., [2, 3]] / 2\n    \n    # Finally for the very last time, return the bboxes variable\n    return bboxes\n    \n# Now, let's create the load_image function (along with the image_path variable inside)!\ndef load_image(image_path):\n    # This function contains one line of code: returning a cv2 module along with the cvtColor function containing cv2 module with the imread function with image_path variable inside and the cv2 module with the COLOR_BGR2RGB attribute function.\n    return cv2.cvtColor(cv2.imread(image_path), cv2.COLOR_BGR2RGB)\n\n# And now, we are now plotting one box (or more) by making a function called plot_one_box. Inside of this function contains 2 variables and 3 hybrids (both variables and attributes): x, img, color being set to None, label set to None, and the line_thickness being set to None.\ndef plot_one_box(x, img, color=None, label=None, line_thickness=None):\n    # The first line of code plots one bounding box on image img. To do so, we are assigning tl to the line_thickness hybrid (variable or attribute) comparison to the round function containing the equation of 0.002 times the parentheses of img variable with the shape index of 0 plus the img variable with the shape index of 1 and the plus 1 outside of the round function.\n    tl = line_thickness or round(0.002 * (img.shape[0] + img.shape[1])) + 1 # Thus, this represents the line/font thickness.\n    # Then, we setup a variable called color and assign it to another variable/attribute that also named color comparing it random module with the randint function containing the numbers 0 and 255 followed by a for loop containing the underscore variable in the range function of 3.\n    color = color or [random.randint(0, 255) for _ in range(3)]\n    # Next, we created two variables, c1 and c2, and assign them to each 'x' variables with indexes from 0 to 3 that is each locked inside the int function, which is sealed inside the two parentheses.\n    c1, c2 = (int(x[0]), int(x[1])), (int(x[2]), int(x[3]))\n    # Now, we create a rectangle by using the cv2 module with the rectangle function containing the 4 variables: img, c1, c2, and color, thus the 2 attributes: thickness being set to the tl variable and the lineType being set to the cv2 module with the LINE_AA attribute.\n    cv2.rectangle(img, c1, c2, color, thickness=tl, lineType=cv2.LINE_AA)\n    \n    # We then make an if statement saying that if there's an label variable, then...\n    if label:\n        # the variable called tf is assigned to the max function containing two items the tl variable minus 1, and 1.\n        tf = max(tl - 1, 1) # Note: This is for font thickness\n        # the variable called t_size is assigned to the cv2 module with the getTextSize function containing 4 items: the label variable, a 0, a fontScale attribute being set to the tl variable divided by 3, and the thickness attribute being set to the tf variable along with the index of 0 on the outside of the getTextSize function.\n        t_size = cv2.getTextSize(label, 0, fontScale=tl / 3, thickness=tf)[0]\n        # the c2 variable is assigned to the c1 variable with the slice index of 0 plus the t_size variable with 0 along with the c1 variable with the slice index of 1 minus the t_size variable with a slice index of 1 minus 3.\n        c2 = c1[0] + t_size[0], c1[1] - t_size[1] - 3\n        # the rectangle shape is filled when the cv2 module with the rectangle function that contains 6 objects: the variables img, c1, c2, color, the number -1, and the cv2 module with the LINE_AA attribute.\n        cv2.rectangle(img, c1, c2, color, -1, cv2.LINE_AA)\n        # the cv2 module with the putText function is being setup along with the inner objects: the variables img and label, c1 with the slice index of 0 and c1 with the slice index of 1 minus 2 inside parentheses, the number 0, the tl variable divided by 3, a list of four 225s, the thickness attribute setup'd to the tf variable, and finally, the lineType attribute setup'd to the cv2 module with the LINE_AA attribute.\n        cv2.putText(img, label, (c1[0], c1[1] - 2), 0, tl / 3, [225, 225, 225, 225], thickness=tf, lineType=cv2.LINE_AA)\n        \n# Now to the fun part, let's draw bounding boxes! In order to do so, we will create a function called draw_bboxes, containing the variables img, bboxes, classes, class_ids, and the attributes colors being set to None, show_classes being set to None, bbox_format being set to 'yolo', class_name set to False, and line_thickness being set to 2.\ndef draw_bboxes(img, bboxes, classes, class_ids, colors=None, show_classes=None, bbox_format='yolo', class_name=False, line_thickness=2):\n    # First of all, we define a variable called image and assign it to the variable that is inside the function, img with the copy function.\n    image = img.copy()\n    # Then, define a variable called show_classes which we will assign it to the classes variable inside the draw_bboxes function along with an if statement showing that if the variable show_classes is set to None, thus otherwise the show_classes variable is being set.\n    show_classes = classes if show_classes is None else show_classes\n    # Now, we will define a variable called colors and we will assign it to the green (or any) color (you'll like) of the RGB format if the colors variable inside the draw_bboxes function is set to None otherwise the colors variable inside the draw_bboxes function is declared.\n    colors = (0, 255, 0) if colors is None else colors\n    \n    # Now, let's classify what type of bbox format is valid for object detection inside of the bbox_format variable! If the bbox_format is equal to yolo, then...\n    if bbox_format == 'yolo':\n        # the for loop is looping the created idx variable to the len function of the bboxes variable nested by the range function.\n        for idx in range(len(bboxes)):\n            # Inside the for loop, the bbox, cls, cls_id, color variables were defined and assigned to bboxes (or classes, class_ids) variable with the slice index of the idx variable (for the bbox, cls, and cls_id variables) and the colors variable with the slice index of the cls_id variable if the colors variable encased inside the type function is a list otherwise the colors variable is defined.\n            bbox = bboxes[idx]\n            cls  = classes[idx]\n            cls_id = class_ids[idx]\n            color = colors[cls_id] if type(colors) is list else colors\n            # Another if statement is called whether the cls variable is in the show_classes variable then...\n            if cls in show_classes:\n                # the variables xl, y1, w, and h was created and defined to the float function containing the bbox variable with the slice index from 0 to 3 times the image variable with the shape attribute with the slice index of 0 and 1 (divided by 2 for the variables w and h) thus caked inside the round function.\n                x1 = round(float(bbox[0]) * image.shape[1])\n                y1 = round(float(bbox[1]) * image.shape[0])\n                w  = round(float(bbox[2]) * image.shape[1] / 2) # AKA w/2\n                h  = round(float(bbox[3]) * image.shape[0] / 2)\n                \n                # the variable called voc_bbox is defined and assigned to a tuple of the 4 items: the x1 variable minus the w variable, the y1 variable minus the h variable, the x1 variable plus the w variable, and the y1 variable minus the h variable.\n                voc_bbox = (x1-w, y1-h, x1+w, y1+h)\n                # the plot_one_box was called out with two variables: voc_bbox and image thus the 3 attributes: color set to the color variable, label set to the cls variable if class_name variable is present otherwise the get_label function with the cls is called along with the str caked in, and line_thickness set to the line_thickness variable.\n                plot_one_box(voc_bbox, image, color=color, label=cls if class_name else str(cls_id), line_thickness=line_thickness)\n                \n    # If the bbox_format is not yolo but is coco, then...\n    elif bbox_format == 'coco':\n        # another for loop is looping the idx variable through the len function holding the bboxes variable inside the range function.\n        for idx in range(len(bboxes)):\n            # Inside the for loop, the bbox, cls, cls_id, color variables were defined and assigned to bboxes (or classes, class_ids) variable with the slice index of the idx variable (for the bbox, cls, and cls_id variables) and the colors variable with the slice index of the cls_id variable if the colors variable encased inside the type function is a list otherwise the colors variable is defined.\n            bbox = bboxes[idx]\n            cls  = classes[idx]\n            cls_id = class_ids[idx]\n            color = colors[cls_id] if type(colors) is list else colors\n            \n            # Another if statement has appeared whether the cls variable is inside the show_classes variable. If this happen then...\n            if cls in show_classes:\n                # the variables x1, y1, w, and h is defined just like the yolo part in this code cell. This time, they were assigned to the round function holding the bbox variable with the slice index from 0 to 3 that is caked in the int function.\n                x1 = int(round(bbox[0]))\n                y1 = int(round(bbox[1]))\n                w  = int(round(bbox[2]))\n                h  = int(round(bbox[3]))\n                \n                # the variable, voc_bbox is assigned to a tuple consisting of two variables: x1, y1 and two same variables plus the w (and h) variables.\n                voc_bbox = (x1, y1, x1 + w, y1 + h)\n                # the plot_one_box is called out with two variables: voc_bbox and image thus the 3 attributes: color set to the color variable, label set to the cls variable if class_name variable is present otherwise the get_label function with the cls is called along with the str caked in, and line_thickness set to the line_thickness variable.\n                plot_one_box(voc_bbox, image, color=color, label=cls if class_name else str(cls_id), line_thickness=line_thickness)\n                \n    # If the bbox_format is not yolo and coco but is voc_pascal then...\n    elif bbox_format == 'voc_pascal':\n        # another for loop is made looping over idx in the len function along with the range function.\n        for idx in range(len(bboxes)):\n            # In the for loop, the bbox, cls, cls_id, color variables were defined and assigned to bboxes (or classes, class_ids) variable with the slice index of the idx variable (for the bbox, cls, and cls_id variables) and the colors variable with the slice index of the cls_id variable if the colors variable encased inside the type function is a list otherwise the colors variable is defined just like the yolo and coco part in the code cell.\n            bbox = bboxes[idx]\n            cls  = classes[idx]\n            cls_id = class_ids[idx]\n            color = colors[cls_id] if type(colors) is list else colors\n            \n            # Another if statement is created whether the cls variable is in the show_classes variable then...\n            if cls in show_classes:\n                # the variables x1 and y1 is defined but this time its different from yolo and coco. Two new variables called x2 and y2 are defined. But like coco, they were assigned to the round function holding the bbox variable with the slice index from 0 to 3 that is caked in the int function.\n                x1 = int(round(bbox[0]))\n                y1 = int(round(bbox[1]))\n                x2 = int(round(bbox[2]))\n                y2 = int(round(bbox[3]))\n                \n                # the voc_bbox variable assigns to a tuple of four variables: x1, y1, x2, y2.\n                voc_bbox = (x1, y1, x2, y2)\n                # the plot_one_box is called out with two variables: voc_bbox and image thus the 3 attributes: color set to the color variable, label set to the cls variable if class_name variable is present otherwise the get_label function with the cls is called along with the str caked in, and line_thickness set to the line_thickness variable.\n                plot_one_box(voc_bbox, image, color=color, label=cls if class_name else str(cls_id), line_thickness=line_thickness)\n    \n    # If the bbox_format is not all 3 mentioned then...\n    else:\n        # We will raise the ValueError Exception, alerting that the bbox_format variable has wrong bbox format.\n        raise ValueError('Sorry. Wrong BBOX format.')\n        \n    # Finally, return the image variable\n    return image\n\n# Let's obtain the BBoxes! we define a function called get_bbox with annots variable inside.\ndef get_bbox(annots):\n    # The bboxes variable is defined and assigned to a list function containing the annot variable with the values function, which contained a for loop outside of the list function of the annot variable in the annots variable inside this function surrounded by the brackets.\n    bboxes = [list(annot.values()) for annot in annots]\n    # Finally, return the bboxes variable.\n    return bboxes\n\n# We might see the image size, but how do we get it? Well, we will create a function called get_imgsize with a variable inside called row.\ndef get_imgsize(row):\n    # The row variables that is inside the get_imgsize function with two keys, 'width' and 'height' is assigned to the imagesize module linked with the get function that contains the row function with the 'image_path' key.\n    row['width'], row['height'] = imagesize.get(row['image_path'])  # We don't need to import imagesize...\n    # Finally, return the row variable.\n    return row\n\n# This one's getting familiar. We are creating animations of reef images. In order to do so, we can create a function called create_animation along with the ims variable inside. But for reference, check out my notebook: EDA Solution Simplfified (make sure to like it!) https://www.kaggle.com/dinowun/eda-solution-simplified\ndef create_animation(ims):\n    # We first set up our figure variable fig, to set up the plot with the plt module with the figure function which holds the figsize attribute to (16, 12).\n    fig = plt.figure(figsize=(16, 12))\n    # We can create a plot without axis by calling out the plt module with the axis function and set it to 'off'.\n    plt.axis('off')\n    # Now, we can create a variable called im and assign it to represent the data as an image by setting the plt module with linking the imshow function with the ims variable with the slice index of 0.\n    im = plt.imshow(ims[0])\n    \n    # Time to create an animation function! We create a function called animate_func containing the i variable\n    def animate_func(i):\n        # We inherit the im variable to the animate_func function and plug the set_array function containing the ims variable of the create_animation function with the slice index of the i variable in animate_func.\n        im.set_array(ims[0])\n        # Finally, return the im variable enclosed on brackets.\n        return [im]\n    \n    # Back inside the create_animation function, we finally return the animation module from matplotlib that is followed by the FuncAnimation module containing two variables: fig and animate_func (function call) and two attributes: frames set to the length (the len function) of the images variable, and the interval being set to 1000 divided by 12 (no remainders).\n    return animation.FuncAnimation(fig, animate_func, frames=len(images), interval=1000//12)\n\n# Now, after these functions, we set up the random seed by calling the np module with the random attribute along with the seed function and set it to 32.\nnp.random.seed(32)\n\n# Finally, define a variable called colors and assign it to a tuple of four np modules with random attributes linking the randint function containing 255 along with the idx looping through a range function of 1 in a for loop after the \\ function followed by a newline.\ncolors = [(np.random.randint(255), np.random.randint(255), np.random.randint(255), np.random.randint(255))\\\n          for idx in range(1)]","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-01-06T20:25:39.575053Z","iopub.execute_input":"2022-01-06T20:25:39.575485Z","iopub.status.idle":"2022-01-06T20:25:39.62794Z","shell.execute_reply.started":"2022-01-06T20:25:39.575437Z","shell.execute_reply":"2022-01-06T20:25:39.62721Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create BBox\nAfter getting through the Helper code build, let's now make bboxes! We first redefine a variable called df and stick it with the bboxes key and assign it to itself with the annotations attribute and the progress_apply function containing the get_bbox function call so that it will append the 'bboxes' key to the df DataFrame. Finally, display out the df variable (DataFrame) with the head function calling out the first two rows.","metadata":{}},{"cell_type":"code","source":"df['bboxes'] = df.annotations.progress_apply(get_bbox)\ndf.head(2)","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:39.629475Z","iopub.execute_input":"2022-01-06T20:25:39.629973Z","iopub.status.idle":"2022-01-06T20:25:39.700198Z","shell.execute_reply.started":"2022-01-06T20:25:39.629922Z","shell.execute_reply":"2022-01-06T20:25:39.69953Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Obtain Image-Size\nTime to measure the size of every image there! In order to get the image size of every image, we call out the df variable (or DataFrame) along with two keys, width and height and we'll assign the width key to 1280 and the height key to 720. Why? Because:\n> All images have same dimension, [Width, Height] = `[1280, 720]`\n\nAfter all, display the two head rows of the df DataFrame that has the annotations.","metadata":{}},{"cell_type":"code","source":"df['width'] = 1280\ndf['height'] = 720\ndisplay(df.head(2))","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:39.701627Z","iopub.execute_input":"2022-01-06T20:25:39.702087Z","iopub.status.idle":"2022-01-06T20:25:39.721452Z","shell.execute_reply.started":"2022-01-06T20:25:39.702049Z","shell.execute_reply":"2022-01-06T20:25:39.720531Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Create Labels\nIn order to create labels, We need to export our labels to convert it to **YOLO format**, with one `*.txt` file per every image (when if there are no object in image, there is no `*.txt` is required). The *.txt specifications are:\n* One row every object\n* Every row is in class of `[x_center, y_center, width, height]` format.\n* Box coordinates must be in **normalized** `xywh` format (from 0 to 1). If our boxes are in pixels, we can divide `x_center` and `width` by `image width`, `y-center`, and `height` by `image_height`.\n\n> The competition bbox format is exactly **COCO** hence the format of `[x_min, y_min, width, height]`. Without of doubt, we need to convert from **COCO** to **YOLO** format. \n\nWith all of that let's demonstrate how to create labels with descriptions in the code cell below! (Unhide the code plz...)","metadata":{}},{"cell_type":"code","source":"# First, we need to define a variable called cnt and set it to the value of 0.\ncnt = 0\n# We also define a variable called all_boxes and assign it to an empty list.\nall_bboxes = []\n# Now, we are going to create a for loop, defining a variable called row_idx and looping it to the tqdm function over the range (function) of df DataFrame with the shape attribute over a slice index of 0.\nfor row_idx in tqdm(range(df.shape[0])):\n    # Inside this for loop, the row variable is assigned to the df DataFrame with the iloc to index through the row_idx variable.\n    row = df.iloc[row_idx]\n    # We define the image_height and the image_width and assign them to the row variable with the height and width attribute\n    image_height = row.height\n    image_width = row.width\n    # Another variable we define is the bboxes_coco. We assign it to np module with the array module that represents an array of basic values to the row variable with the bboxes attribute followed by an astype function for a specific dtype of a np module with the float32 type and copy it with the copy function. \n    bboxes_coco = np.array(row.bboxes).astype(np.float32).copy()\n    # We also define a variable called num_bbox and assign it to the length (len function) of the bboxes_coco variable.\n    num_bbox = len(bboxes_coco)\n    # Lastly for setup inside the for loop, we setup two variables: names and labels and assign them to the slice index of 'cots' (0 for labels) times the num_bbox variable.\n    names = ['cots'] * num_bbox\n    labels = [0] * num_bbox\n    # Now, we are creating annotations (YOLO)! First of all, we are opening the file which is the the row variable with the label_path attribute and write it as the f variable.\n    with open(row.label_path, 'w') as f:\n        # After row.label_path file was opened, there was an if statement saying that the num_bbox variable is greater than 1. If the num_bbox greater than 1, then...\n        if num_bbox < 1:\n            # the annot variable was assigned to just an empty string.\n            annot = ''\n            # The file function, f, writes the annot string.\n            f.write(annot)\n            # Lastly, the cnt value variable will add 1 value.\n            cnt += 1\n            # And we have to continue outside the if statement.\n            continue\n        # Outside the for loop, the bboxes_yolo is defined as the coco2yolo function call with the height (image_height variable), width (image_width), and the bounding boxes in coco format (bboxes_coco). And it also defines the np module with the function, clip, that limits the values in an array withstanding three values, the bounding boxes in yolo format (bboxes_yolo variable), the number 0 and 1.\n        bboxes_yolo = coco2yolo(image_height, image_width, bboxes_coco)\n        bboxes_yolo = np.clip(bboxes_yolo, 0, 1)\n        # We also call out the all_bboxes variable that has the empty array and extend it (with the extend function) containing the bounding boxes in yolo format variable, bboxes_yolo.\n        all_bboxes.extend(bboxes_yolo)\n        # And another for loop is made looping out the bbox_idx variable in the range (function) of the length (len function) of the bboxes_yolo (bounding boxes in yolo format) variable.\n        for bbox_idx in range(len(bboxes_yolo)):\n            # Inside the for loop, the annot variable is defined again, but this time, it is assigned to an array that contains the string (str function) of the labels variable with the slice index of bbox_idx variable plus the list (function) of bboxes_yolo (bbox format of yolo) variable with the slice index of bbox_idx variable followed by an astype function for a specific dtype of a str type plus the key of '\\n' along with an if statement saying that if the num_bbox variable is not equal (!=) to the bbox_idx value variable plus 1 in parentheses thus otherwise its just an empty string key. It also redefined again to annexing (join function) itself to the empty space string and again to itself that stripped off the empty space of a string.\n            annot = [str(labels[bbox_idx])] + list(bboxes_yolo[bbox_idx].astype(str)) + (['\\n'] if num_bbox != (bbox_idx + 1) else [''])\n            annot = ' '.join(annot)\n            annot = annot.strip(' ')\n            # Lastly, make the f variable write the annot string.\n            f.write(annot)\n            \n# Now, let's print out how many missing bboxes are there!\nprint(\"BBoxes Missing:\", cnt)","metadata":{"_kg_hide-input":true,"execution":{"iopub.status.busy":"2022-01-06T20:25:39.72307Z","iopub.execute_input":"2022-01-06T20:25:39.723331Z","iopub.status.idle":"2022-01-06T20:25:42.064139Z","shell.execute_reply.started":"2022-01-06T20:25:39.723295Z","shell.execute_reply":"2022-01-06T20:25:42.063332Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Cool!** There are no bboxes missing after we created (and checked) annotation labels! After all, we are now proceeding to fold creation!","metadata":{}},{"cell_type":"markdown","source":"## Fold Creation\nBefore we create some folds, we know that the\n> Number of samples aren't alike in every fold that can create huge variance in **Cross-Validation**.\n\nWell, first of all, in order to create folds, we need to import the scikit-learn (sklearn) module with the model_selection section and import the GroupKFold function iterator variant from it. Then, we are defining variable kf to the GroupKFold iterator function variant, setting the n_splits attribute value to 5. Then we return new DataFrame by redefining the df DataFrame to itself with the reset_index with the drop attribute set to True. We then create a new key that is added to the df DataFrame that is called the fold key and we will set their value to -1. Next, we are creating a for loop, looping the fold variable and the tuple of train_idx and val_idx variable in add a counter with the enumerate function containing the kf GroupKFold iterator spliting out the df DataFrame, setting the y attribute to the df DataFrame with the video_id data converting to a list (tolist function), and setting the groups to the sequence of the df DataFrame. Inside the for loop, the df DataFrame with the loc attribute that accessed a group of rows and columns by labels of the val_idx variable and the 'fold' key is assigned to the fold variable. Lastly, display the value counts of the df DataFrame with the Fold key.","metadata":{}},{"cell_type":"code","source":"from sklearn.model_selection import GroupKFold\n\nkf = GroupKFold(n_splits=5)\ndf = df.reset_index(drop=True)\ndf['fold'] = -1\n\nfor fold, (train_idx, val_idx) in enumerate(kf.split(df, y=df.video_id.tolist(), groups=df.sequence)):\n    df.loc[val_idx, 'fold'] = fold\ndisplay(df.fold.value_counts())","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:42.065758Z","iopub.execute_input":"2022-01-06T20:25:42.066275Z","iopub.status.idle":"2022-01-06T20:25:42.773992Z","shell.execute_reply.started":"2022-01-06T20:25:42.066234Z","shell.execute_reply":"2022-01-06T20:25:42.773277Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"After running the results, we see that the name of the key in the df DataFrame is fold and the dtype of it is int64. Now let's make a dataset from the df!","metadata":{}},{"cell_type":"markdown","source":"## Dataset\nIn order to config a dataset, let's define two variables, train_files and val_files and assign it to an empty array. Then, we will define another two variables, train_df and valid_df and assign it to query the columns of a df DataFrame with a boolean expression of an if statement regarding whether the fold variable is (not for train_df variable) equal (for valid_df variable) to @FOLD thing. Now, the train_files and val_files variable added a value of a list of the train_df (valid_df) variable of image_path key into finding the unique elements of the image_path of the two variables mentioned (unique). Finally, print out the number of files in train_files and val_files variable.","metadata":{}},{"cell_type":"code","source":"train_files = []\nval_files = []\n\ntrain_df = df.query(\"fold!=@FOLD\")\nvalid_df = df.query(\"fold==@FOLD\")\n\ntrain_files += list(train_df.image_path.unique())\nval_files += list(valid_df.image_path.unique())\nlen(train_files), len(val_files)","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:42.775156Z","iopub.execute_input":"2022-01-06T20:25:42.777557Z","iopub.status.idle":"2022-01-06T20:25:42.794628Z","shell.execute_reply.started":"2022-01-06T20:25:42.777514Z","shell.execute_reply":"2022-01-06T20:25:42.793836Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"As you can see, there are 3974 files in the train_files section, and there are 945 files in the val_files section. With all being set, let's now config the dataset config file!","metadata":{}},{"cell_type":"markdown","source":"## Configuring the Dataset Config File\nIn order to config the dataset config file, it requires:\n1. The dataset root directory path and the other relative paths leading to `train/val/test` image directories (or rather, *.txt files that contain image paths...)\n2. The no. of classes `nc` and...\n3. A list of class `names`: `['cots']`\n\nFirst of all, We have to import the yaml module so that we will save the config file in a yolo format. Then, set the cwd variable to the path in a string, kaggle/working. Then, open the file joined by the os module followed by the path attribute with a join function along with the train(val).txt file and write it as the f variable. When writing the train.txt or val.txt file, there is a for loop looping the path variable in the the valid_df DataFrame with the image_path key that is converted to list by the tolist function. Then, define another variable, data to the dictionary creation using the dict function, so that it would be easy to assign 5 variables inside: set path to kaggle/working, in strings, train (or val) to joining the path of cwd variable and a train(val).txt using os (the module with the path attribute followed by the join function), nc set to 1, and names set to the cots key. We now open the file path that the os module combined (with the path attribute followed by a join function) of the cwd variable path and the bgr.yaml file thus writing it as the outfile variable. When writing the path provided by cwd variable to the bgr.yaml file, the yaml module serialized a Python object into a YAML stream containing the data dictionary variable, the outfile variable that is writing the bgr.yaml, and set the default_flow_style that the HTTP URL to send the request to the yaml file has set to False. Finally, set the f variable that writes the train and val txt file to open the full file between the cwd variable path and the bgr.yaml supported by the os module that has the path attribute with the join function). We then print out the newline character with the yaml in a string and the f variable file being read.","metadata":{}},{"cell_type":"code","source":"import yaml\n\ncwd = '/kaggle/working/'\n\nwith open(os.path.join(cwd, 'train.txt'), 'w') as f:\n    for path in train_df.image_path.tolist():\n        f.write(path + '\\n')\n        \nwith open(os.path.join(cwd, 'val.txt'), 'w') as f:\n    for path in train_df.image_path.tolist():\n        f.write(path + '\\n')\n        \ndata = dict(\n    path = '/kaggle/working',\n    train = os.path.join(cwd, 'train.txt'),\n    val = os.path.join(cwd, 'val.txt'),\n    nc = 1,\n    names = ['cots'],\n)\n\nwith open(os.path.join(cwd, 'bgr.yaml'), 'w') as outfile:\n    yaml.dump(data, outfile, default_flow_style = False)\n    \nf = open(os.path.join(cwd, 'bgr.yaml'), 'r')\nprint(\"yaml:\")\nprint(f.read())","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:42.79605Z","iopub.execute_input":"2022-01-06T20:25:42.796298Z","iopub.status.idle":"2022-01-06T20:25:42.838881Z","shell.execute_reply.started":"2022-01-06T20:25:42.796264Z","shell.execute_reply":"2022-01-06T20:25:42.838195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"When reading the bgr.yaml file provided by the f variable file, then the names would be just cots key, the nc is set to 1, the path was set to kaggle working, the train has set to the path leading to train.txt, and the val has set to the the same thing as train but with the val.txt file.","metadata":{}},{"cell_type":"markdown","source":"## [YOLOv5](https://github.com/ultralytics/yolov5/)\n![Here we go! YOLOv5!](https://github.com/ultralytics/yolov5/releases/download/v1.0/splash.jpg)\nThis is the part we all been waiting for, the YOLOv5! It is the most valuable object detection algorithm that can detect the infamous cots without any flaws. So Let's start the fun of the YOLO(v5)!","metadata":{}},{"cell_type":"markdown","source":"### Installation\nFirst before going to YOLOv5, we change the directory path (cd) to /kaggle/working/, then remove the yolov5 folder in the kaggle/working directory (if possible). Later on, we clone out the yolov5 repository via git clone command (https://github.com/ultralytics/yolov5). Next, we copy the yolov5 lib ds (the dataset you may get in kaggle) to the yolov5 directory. Finally, change directory to yolov5 and install the requirements in the txt file via pip. To get started on YOLOv5, we first import utils from their module and then check YOLOv5 by assigning the display variable to the utils module with the notebook_init function.","metadata":{}},{"cell_type":"code","source":"%cd /kaggle/working\n!rm -r /kaggle/working/yolov5 # if possible\n!git clone https://github.com/ultralytics/yolov5 # clone it!\n!cp -r /kaggle/input/yolov5-lib-ds /kaggle/working/yolov5\n%cd yolov5\n%pip install -qr requirements.txt # install it!\n\nfrom yolov5 import utils\ndisplay = utils.notebook_init() # check it!","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:42.840063Z","iopub.execute_input":"2022-01-06T20:25:42.840298Z","iopub.status.idle":"2022-01-06T20:25:59.48618Z","shell.execute_reply.started":"2022-01-06T20:25:42.840265Z","shell.execute_reply":"2022-01-06T20:25:59.485329Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"And we have done started setting up YOLOv5 without failure! Without further ado, let's train YOLOv5 correctly by setting up the custom hyperparameters!","metadata":{}},{"cell_type":"markdown","source":"### Custom Hyperparameters (Fine-Tuning)\nNow, let's get some training over detecting COTS! In order to fine-tune Yolov5 well, we reimport the os and yaml module again, then setup the cwd variable to the file path leading to custom.yaml file. We then set up the data variable followed by a hyperparams variable to the dictionary containing lr0, lrf, momentum, weight_decay, warmup_epochs, warmup_momentum, warmup_bias_lr, box, cls, obj, obj_pw, iou_t, anchor_t, fl_gamma, hsv_h, hsv_s, hsv_v, degrees, translate, scale, shear, perspective, flipud, fliplr, mosaic, mixup, and copy_paste to any value that can make our model detect cots better. Like configuring the dataset config file, we open the file provided by the cwd variable and write it as the outfile variable. When writing the cwd variable file, the yaml module serialized a Python object into a YAML stream containing the hyperparams variable with the dictionary in the data variable, the outfile variable that is writing the bgr.yaml, and set the default_flow_style that the HTTP URL to send the request to the yaml file has set to False. Finally, set the f variable to open the cwd file variable and set it to read mode and print out the yaml file being read by the f variable (although you have to print out \"yaml:\" in strings).","metadata":{}},{"cell_type":"code","source":"import os\nimport yaml\n\ncwd = '/kaggle/working/custom.yaml'\n\n# Change it whatever you want!\ndata = hyperparams = {\n    'lr0': 0.01,\n    'lrf': 0.1,\n    'momentum': 0.937,\n    'weight_decay': 0.0005,\n    'warmup_epochs': 5.0,\n    'warmup_momentum': 0.8,\n    'warmup_bias_lr': 0.1,\n    'box': 0.05,\n    'cls': 0.5,\n    'cls_pw': 1.0,\n    'obj': 1.0,\n    'obj_pw': 1.0,\n    'iou_t': 0.2,\n    'anchor_t': 4.0,\n    'fl_gamma': 0.0,\n    'hsv_h': 0.015,\n    'hsv_s': 0.7,\n    'hsv_v': 0.3,\n    'degrees': 0.0,\n    'translate': 0.1,\n    'scale': 0.7,\n    'shear': 0.0,\n    'perspective': 0.0,\n    'flipud': 0.0,\n    'fliplr': 0.5,\n    'mosaic': 0.5, #0.0 or 1.0 would be better and recommended\n    'mixup': 0.5,\n    'copy_paste': 0.0\n}\n\nwith open(os.path.join(cwd), 'w') as outfile:\n    yaml.dump(data, outfile, default_flow_style=False)\n    \nf = open(os.path.join(cwd), 'r')\nprint('yaml:')\nprint(f.read())","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:59.488229Z","iopub.execute_input":"2022-01-06T20:25:59.488526Z","iopub.status.idle":"2022-01-06T20:25:59.504025Z","shell.execute_reply.started":"2022-01-06T20:25:59.488485Z","shell.execute_reply":"2022-01-06T20:25:59.502901Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Weights and Biases (Optional)\nLike mentioned, Weights & Biases is useful for detecting performance of object detection. So, you'll mind that this is optional. All you have to do is to import wandb and then login to it by anonymous.","metadata":{}},{"cell_type":"code","source":"import wandb\nwandb.login(anonymous='must')","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:25:59.505166Z","iopub.execute_input":"2022-01-06T20:25:59.50599Z","iopub.status.idle":"2022-01-06T20:26:01.057185Z","shell.execute_reply.started":"2022-01-06T20:25:59.505952Z","shell.execute_reply":"2022-01-06T20:26:01.056314Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Training\nNow've setup our object-detection model with YOLOv5, let's now train it! We run the train.py file using python3 command and set the hyperparameters to the path leading towards custom.yaml file, set the batch to 12 (set it whatever you want...), epochs to 18, data to the path leading toward the bgr.yaml file, weights set to yolov5m.pt file, and set no workers.","metadata":{}},{"cell_type":"code","source":"# We should clean our memory first...\nimport gc\ngc.collect()\n\n!python3 train.py --img 1280\\\n--hyp /kaggle/working/custom.yaml\\\n--batch 12\\\n--epochs 18\\\n--data /kaggle/working/bgr.yaml\\\n--weights yolov5m.pt --workers 0","metadata":{"execution":{"iopub.status.busy":"2022-01-06T20:26:01.058989Z","iopub.execute_input":"2022-01-06T20:26:01.059197Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Output Files\nAfter running through all of the painful epoch training, let's take a peek at the output files! We run the ls command to take a peek at the runs/train/exp directory.","metadata":{}},{"cell_type":"code","source":"!ls runs/train/exp","metadata":{"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Class Distribution\nSince we took a peek at the runs/train, let's take some peek at the class distribution! We look at the cots labels in a correlogram graph image file. First we import the matplotlib with pyplot module again as plt, set the graph figure size width and height to 10 units. Then, disable the axis using plt module with the axis function and set the string 'off'. Finally, display the graph of the correlogram of labels being read out from the imread function over the path to the labels_correlogram.jpg file.","metadata":{}},{"cell_type":"code","source":"import matplotlib.pyplot as plt\n\nplt.figure(figsize=(10,10))\nplt.axis('off')\nplt.imshow(plt.imread('runs/train/exp/labels_correlogram.jpg'))\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"Now we will see our labels analyzed seperately in a graph. Its just like what we have looked in the labels in a correlogram but this time, it displayed the graph of the four seperate label graphs being read out from the imread function over the path to the labels.jpg file.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(10,10))\nplt.axis('off')\nplt.imshow(plt.imread('runs/train/exp/labels.jpg'))\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Batch Image\nTo get the batch images plotted, we create a figure whose width and height size is 10 three times, and show the image read out by the plt module with the imread function to the path leading to train_batch0 to train_batch2.jpg file.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(10, 10))\nplt.imshow(plt.imread('runs/train/exp/train_batch0.jpg'))\n\nplt.figure(figsize=(10, 10))\nplt.imshow(plt.imread('runs/train/exp/train_batch1.jpg'))\n\nplt.figure(figsize=(10, 10))\nplt.imshow(plt.imread('runs/train/exp/train_batch2.jpg'))","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## GT vs Predictions\nAfter running the batch images, let's now compare GT and our predictions! In order to compare our predictions with the GT, we assign two variables: fig and ax to the subplots function that creates a figure with a set of subplots already made with the numbers 3, 2, setting the width and height to 18 units and 15 units, and the constrained_layout attribute set to True for every seperate image graphs. We later then set a for loop looping the row variable to the range of 3. Inside there, we call out the ax variable with the row variable in the index along with the 0 and 1 index seperate to show the path towards the val_batch with values from 0 to 2 in the row variable over the labels(pred).jpg file, then setting the x and y ticks, and finally set title over the file path leading to val_batch with values from 0 to 2 in the row vairable over the labels(pred).jpg file, although we have to set the font size to 12. Now, we show the image graph.","metadata":{}},{"cell_type":"code","source":"fig, ax = plt.subplots(3, 2, figsize=(2 * 9 ,3 * 5), constrained_layout=True)\n\nfor row in range(3):\n    ax[row][0].imshow(plt.imread(f'runs/train/exp/val_batch{row}_labels.jpg'))\n    ax[row][0].set_xticks([])\n    ax[row][0].set_yticks([])\n    ax[row][0].set_title(f'runs/train/exp/val_batch{row}_labels.jpg', fontsize=12)\n    \n    ax[row][1].imshow(plt.imread(f'runs/train/exp/val_batch{row}_pred.jpg'))\n    ax[row][1].set_xticks([])\n    ax[row][1].set_yticks([])\n    ax[row][1].set_title(f'runs/train/exp/val_batch{row}_pred.jpg', fontsize=12)\n\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## The Results\nAfter training every epoch in our object detection model, Let's get started over graphing over the results with every epoch and score in matplot style!","metadata":{}},{"cell_type":"markdown","source":"### Score vs Epoch\nIn order to see the score being compared to an epoch, we set the figure width and height to 30 and 15 units, turn off the axis using the axis function, and then show the path to results.png file being read out. Finally, show the image graph.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(30,15))\nplt.axis('off')\nplt.imshow(plt.imread('runs/train/exp/results.png'))\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Confusion Matrix\nNow we create a confusion matrix based on our predicted labels over COTS! We first set up the width and height figure size to 12 and 10 units, turn off the axis using the axis function again, then show the path to results.png file being read out. Finally, show the image graph using show function.","metadata":{}},{"cell_type":"code","source":"plt.figure(figsize=(12,10))\nplt.axis('off')\nplt.imshow(plt.imread('runs/train/exp/confusion_matrix.png'))\nplt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"### Metrics\nNow, lets finally display more results, in metrics! We first set up a for loop, looping the metric variable to an array of four strings, 'F1', 'PR', 'P', and 'R'. Inside this for loop, the metric type will be printed out, then the figure's width and height has been set up to 12 and 10 units, thus turning off the axis using the axis function. Next, the image graph is shown by the file path to any metric looped by the metric variable over the curve.png file being read out. Finally, show the image file using the show function.","metadata":{}},{"cell_type":"code","source":"for metric in ['F1', 'PR', 'P', 'R']:\n    print(f\"Metric: {metric}\")\n    plt.figure(figsize=(12, 10))\n    plt.axis('off')\n    plt.imshow(plt.imread(f'runs/train/exp/{metric}_curve.png'));\n    plt.show()","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Conclusion\nAfter running through all of the training over object detection, we are pretty sure that we have done the training part for our YOLOv5 Simplified Series! But for sure, we are going to create another notebook about infering YOLOv5, so stay tuned!","metadata":{}},{"cell_type":"markdown","source":"## Acknoledgements and References\nhttps://www.kaggle.com/csarolivares/own-great-barrier-reef-yolov5-train/notebook\nhttps://www.kaggle.com/awsaf49/great-barrier-reef-yolov5-train","metadata":{}}]}