{"cells":[{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install -q efficientnet","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import os\nimport gc\nimport re\n\nimport cv2\nimport math\nimport numpy as np\nimport scipy as sp\nimport pandas as pd\n\nimport tensorflow as tf\nfrom IPython.display import SVG\nimport efficientnet.tfkeras as efn\nfrom keras.utils import plot_model\nimport tensorflow.keras.layers as L\nfrom keras.utils import model_to_dot\nimport tensorflow.keras.backend as K\nfrom tensorflow.keras.models import Model\nfrom kaggle_datasets import KaggleDatasets\nfrom tensorflow.keras.applications import DenseNet121\n\nimport seaborn as sns\nfrom tqdm import tqdm\nimport matplotlib.cm as cm\nfrom sklearn import metrics\nimport matplotlib.pyplot as plt\nfrom sklearn.utils import shuffle\nfrom sklearn.model_selection import train_test_split\n\ntqdm.pandas()\nimport plotly.express as px\nimport plotly.graph_objects as go\nimport plotly.figure_factory as ff\nfrom plotly.subplots import make_subplots\n\nnp.random.seed(0)\ntf.random.set_seed(0)\n\nimport warnings\nwarnings.filterwarnings(\"ignore\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"EPOCHS = 20\nSAMPLE_LEN = 100\n\nTRAIN_PATH = \"../input/imet-2020-fgvc7/train.csv\"\nTEST_PATH = \"../input/imet-2020-fgvc7/sample_submission.csv\"\nLab_PATH = \"../input/imet-2020-fgvc7/labels.csv\"\n\nsub = pd.read_csv(Lab_PATH)\ntest_data = pd.read_csv(TEST_PATH)\ntrain_data = pd.read_csv(TRAIN_PATH)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"**Assumption**\n* Only 1000 rows are taken in this notebook, \n* When you generalize the model for all 1 lakh 40 thousand rows the accuracy is quite high\n* However the notebook which i tried uploading from my system to kaggle didnt work out due to technical issues\n* In this notebook i shall only be dealing with 1000 rows , the accuracy would be really bad \n* But this would be a pretty good intuition to start with the analytics\n\n\n## To support my work please upvote and comment your suggestions "},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data = train_data.head(1000)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sub.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in sub['attribute_id']:\n    train_data[i]=0\n    test_data[i] = 0","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(train_data.shape)\nprint(test_data.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data.head(5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"for i in range(len(train_data)) : \n  for j in (train_data.loc[i,'attribute_ids'] .split(\" \")):\n    train_data.loc[i,j] = 1\n  print(i)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data = train_data.fillna(0)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMAGE_PATH = \"../input/imet-2020-fgvc7/train/\"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"collapsed":true},"cell_type":"code","source":"def load_image(image_id):\n    file_path = image_id + \".png\"\n    image = cv2.imread(IMAGE_PATH + file_path)\n    return cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n\ntrain_images = train_data[\"id\"][:SAMPLE_LEN].progress_apply(load_image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.imshow(cv2.resize(train_images[0], (205, 136)))\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.imshow(cv2.resize(train_images[12], (205, 136)))\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = px.imshow(cv2.resize(train_images[46], (205, 136)))\nfig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"I have plotted the first image in the training data above (the RGB values can be seen by hovering over the image). The green parts of the image have very low blue values, but by contrast, the brown parts have high blue values. This suggests that green (healthy) parts of the image have low blue values, whereas unhealthy parts are more likely to have high blue values. This might suggest that the blue channel may be the key to detecting diseases in plants"},{"metadata":{},"cell_type":"markdown","source":"# Channel Data Understanding"},{"metadata":{"trusted":true},"cell_type":"code","source":"red_values = [np.mean(train_images[idx][:, :, 0]) for idx in range(len(train_images))]\ngreen_values = [np.mean(train_images[idx][:, :, 1]) for idx in range(len(train_images))]\nblue_values = [np.mean(train_images[idx][:, :, 2]) for idx in range(len(train_images))]\nvalues = [np.mean(train_images[idx]) for idx in range(len(train_images))]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([values], group_labels=[\"Channels\"], colors=[\"purple\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The channel values seem to have a roughly normal distribution centered around 105. The maximum channel activation is 255. This means that the average channel value is less than half the maximum value, which indicates that channels are minimally activated most of the time."},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([red_values], group_labels=[\"R\"], colors=[\"red\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of red channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([green_values], group_labels=[\"G\"], colors=[\"green\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of green channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([blue_values], group_labels=[\"B\"], colors=[\"blue\"])\nfig.update_layout(showlegend=False, template=\"simple_white\")\nfig.update_layout(title_text=\"Distribution of blue channel values\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"1. The red channel values seem to roughly normal distribution, but with a slight rightward (positive skew). This indicates that the red channel tends to be more concentrated at lower values, at around 100. There is large variation in average red values across images.\n2. The green channel values have a more uniform distribution than the red channel values, with a smaller peak. The distribution also has a leftward skew (in contrast to red) and a larger mode of around 140. This indicates that green is more pronounced in these images than red, which makes sense, because these are images of leaves!\n3. The blue channel has the most uniform distribution out of the three color channels, with minimal skew (slight leftward skew). The blue channel shows great variation across images in the dataset."},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = go.Figure()\n\nfor idx, values in enumerate([red_values, green_values, blue_values]):\n    if idx == 0:\n        color = \"Red\"\n    if idx == 1:\n        color = \"Green\"\n    if idx == 2:\n        color = \"Blue\"\n    fig.add_trace(go.Box(x=[color]*len(values), y=values, name=color, marker=dict(color=color.lower())))\n    \nfig.update_layout(yaxis_title=\"Mean value\", xaxis_title=\"Color channel\",\n                  title=\"Mean value vs. Color channel\", template=\"plotly_white\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig = ff.create_distplot([red_values, green_values, blue_values],\n                         group_labels=[\"R\", \"G\", \"B\"],\n                         colors=[\"red\", \"green\", \"blue\"])\nfig.update_layout(title_text=\"Distribution of red channel values\", template=\"simple_white\")\nfig.data[0].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[0].marker.line.width = 0.5\nfig.data[1].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[1].marker.line.width = 0.5\nfig.data[2].marker.line.color = 'rgb(0, 0, 0)'\nfig.data[2].marker.line.width = 0.5\nfig","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Image Augmentation\n\nCanny edge detection Canny is a popular edge detection algorithm, and as the name suggests, it detects the edges of objects present in an image. It was developed by John F. Canny in 1986. The algorithm involves several steps.\n\n1. Noise reduction: Since edge detection is susceptible to noise in an image, we remove the noise in the image using a 5x5 Gaussian filter.Finding Intensity Gradient of the Image: The smoothened image is then filtered with a Sobel kernel in both horizontal and vertical directions to get the first derivative in the horizontal (Gx) and vertical (Gy) directions. From these two images, one can find the edge gradient and direction for each pixel:\n2. Rounding: The gradient is always perpendicular to edges. So, it is rounded to one of the four angles representing vertical, horizontal and two diagonal directions.\n\n3. Non-maximum suppression: After getting the gradient magnitude and direction, a full scan of the image is done to remove any unwanted pixels which may not constitute the edge. For this, we check every pixel for being a local maximum in its neighborhood in the direction of the gradient.\n\n4. Hysteresis Thresholding: This stage decides which parts are edges and which are not. For this, we need two threshold values, minVal and maxVal. Any edges with intensity gradient greater than maxVal are considered edges and those lesser than minVal are considered non-edges, and discarded. Those who lie between these two thresholds are classified edges or non-edges based on their neighborhood. If they are near “sure-edge” pixels, they are considered edges, and otherwise, they are discarded.\n\nThe result of these five steps is a two-dimensional binary map (0 or 255) indicating the location of edges on the image. Canny edge is demonstrated below with a few leaf images:"},{"metadata":{"trusted":true},"cell_type":"code","source":"def edge_and_cut(img):\n    emb_img = img.copy()\n    edges = cv2.Canny(img, 100, 200)\n    edge_coors = []\n    for i in range(edges.shape[0]):\n        for j in range(edges.shape[1]):\n            if edges[i][j] != 0:\n                edge_coors.append((i, j))\n    \n    row_min = edge_coors[np.argsort([coor[0] for coor in edge_coors])[0]][0]\n    row_max = edge_coors[np.argsort([coor[0] for coor in edge_coors])[-1]][0]\n    col_min = edge_coors[np.argsort([coor[1] for coor in edge_coors])[0]][1]\n    col_max = edge_coors[np.argsort([coor[1] for coor in edge_coors])[-1]][1]\n    new_img = img[row_min:row_max, col_min:col_max]\n    \n    emb_img[row_min-10:row_min+10, col_min:col_max] = [255, 0, 0]\n    emb_img[row_max-10:row_max+10, col_min:col_max] = [255, 0, 0]\n    emb_img[row_min:row_max, col_min-10:col_min+10] = [255, 0, 0]\n    emb_img[row_min:row_max, col_max-10:col_max+10] = [255, 0, 0]\n    \n    fig, ax = plt.subplots(nrows=1, ncols=3, figsize=(30, 20))\n    ax[0].imshow(img, cmap='gray')\n    ax[0].set_title('Original Image', fontsize=24)\n    ax[1].imshow(edges, cmap='gray')\n    ax[1].set_title('Canny Edges', fontsize=24)\n    ax[2].imshow(emb_img, cmap='gray')\n    ax[2].set_title('Bounding Box', fontsize=24)\n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"edge_and_cut(train_images[3])\nedge_and_cut(train_images[4])\nedge_and_cut(train_images[5])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"edge_and_cut(train_images[23])\nedge_and_cut(train_images[24])\nedge_and_cut(train_images[29])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"The second column of images above contains the Canny edges and the third column contains cropped images. I have taken the Canny edges and used it to predict a bounding box in which the actual leaf is contained. The most extreme edges at the four corners of the image are the vertices of the bounding box. This red box is likely to contain most of if not all of the leaf. These edges and bounding boxes can be used to build more accurate models."},{"metadata":{},"cell_type":"markdown","source":"# Modeling"},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data = train_data.drop('attribute_ids',1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"AUTO = tf.data.experimental.AUTOTUNE\ntpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n\ntf.config.experimental_connect_to_cluster(tpu)\ntf.tpu.experimental.initialize_tpu_system(tpu)\nstrategy = tf.distribute.experimental.TPUStrategy(tpu)\n\nBATCH_SIZE = 16 \nGCS_DS_PATH = KaggleDatasets().get_gcs_path()\n\ndef format_path(st):\n    return GCS_DS_PATH + '/train/' + st + '.png'\n\ntest_paths = test_data.id.apply(format_path).values\ntrain_paths = train_data.id.apply(format_path).values\n\ntrain_labels = np.float32(train_data.loc[:, train_data.columns!='id' ].values)\ntrain_paths, valid_paths, train_labels, valid_labels =\\\ntrain_test_split(train_paths, train_labels, test_size=0.15, random_state=2020)\n\ndef decode_image(filename, label=None, image_size=(512, 512)):\n    bits = tf.io.read_file(filename)\n    image = tf.image.decode_jpeg(bits, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.image.resize(image, image_size)\n    \n    if label is None:\n        return image\n    else:\n        return image, label\n\ndef data_augment(image, label=None):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.random_flip_up_down(image)\n    \n    if label is None:\n        return image\n    else:\n        return image, label","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def build_lrfn(lr_start=0.00001, lr_max=0.00005, \n               lr_min=0.00001, lr_rampup_epochs=5, \n               lr_sustain_epochs=0, lr_exp_decay=.8):\n    lr_max = lr_max * strategy.num_replicas_in_sync\n\n    def lrfn(epoch):\n        if epoch < lr_rampup_epochs:\n            lr = (lr_max - lr_start) / lr_rampup_epochs * epoch + lr_start\n        elif epoch < lr_rampup_epochs + lr_sustain_epochs:\n            lr = lr_max\n        else:\n            lr = (lr_max - lr_min) *\\\n                 lr_exp_decay**(epoch - lr_rampup_epochs\\\n                                - lr_sustain_epochs) + lr_min\n        return lr\n    return lrfn","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"lrfn = build_lrfn()\nSTEPS_PER_EPOCH = train_labels.shape[0] // BATCH_SIZE\nlr_schedule = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_dataset = (\n    tf.data.Dataset\n    .from_tensor_slices((train_paths, train_labels))\n    .map(decode_image, num_parallel_calls=AUTO)\n    .map(data_augment, num_parallel_calls=AUTO)\n    .repeat()\n    .shuffle(512)\n    .batch(BATCH_SIZE)\n    .prefetch(AUTO)\n)\n\nvalid_dataset = (\n    tf.data.Dataset\n    .from_tensor_slices((valid_paths, valid_labels))\n    .map(decode_image, num_parallel_calls=AUTO)\n    .batch(BATCH_SIZE)\n    .cache()\n    .prefetch(AUTO)\n)\n\ntest_dataset = (\n    tf.data.Dataset\n    .from_tensor_slices(test_paths)\n    .map(decode_image, num_parallel_calls=AUTO)\n    .batch(BATCH_SIZE)\n)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Dense Net\n"},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():\n    model = tf.keras.Sequential([DenseNet121(input_shape=(512, 512, 3),\n                                             weights='imagenet',\n                                             include_top=False),\n                                 L.GlobalAveragePooling2D(),\n                                 L.Dense(train_labels.shape[1],\n                                         activation='softmax')])\n        \n    model.compile(optimizer='adam',\n                  loss = 'categorical_crossentropy',\n                  metrics=['categorical_accuracy'])\n    model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"SVG(tf.keras.utils.model_to_dot(Model(model.layers[0].input, model.layers[0].layers[13].output), dpi=70).create(prog='dot', format='svg'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"SVG(tf.keras.utils.model_to_dot(model, dpi=70).create(prog='dot', format='svg'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(train_dataset,\n                    epochs=EPOCHS,\n                    callbacks=[lr_schedule],\n                    steps_per_epoch=STEPS_PER_EPOCH,\n                    validation_data=valid_dataset)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def display_training_curves(training, validation, yaxis):\n    if yaxis == \"loss\":\n        ylabel = \"Loss\"\n        title = \"Loss vs. Epochs\"\n    else:\n        ylabel = \"Accuracy\"\n        title = \"Accuracy vs. Epochs\"\n        \n    fig = go.Figure()\n        \n    fig.add_trace(\n        go.Scatter(x=np.arange(1, EPOCHS+1), mode='lines+markers', y=training, marker=dict(color=\"dodgerblue\"),\n               name=\"Train\"))\n    \n    fig.add_trace(\n        go.Scatter(x=np.arange(1, EPOCHS+1), mode='lines+markers', y=validation, marker=dict(color=\"darkorange\"),\n               name=\"Val\"))\n    \n    fig.update_layout(title_text=title, yaxis_title=ylabel, xaxis_title=\"Epochs\", template=\"plotly_white\")\n    fig.show()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"display_training_curves(\n    history.history['categorical_accuracy'], \n    history.history['val_categorical_accuracy'], \n    'accuracy')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"acc_df = pd.DataFrame(np.transpose([[*np.arange(1, EPOCHS+1).tolist()*3], [\"Train\"]*EPOCHS + [\"Val\"]*EPOCHS + [\"Benchmark\"]*EPOCHS,\n                                     history.history['categorical_accuracy'] + history.history['val_categorical_accuracy'] + [1.0]*EPOCHS]))\nacc_df.columns = [\"Epochs\", \"Stage\", \"Accuracy\"]\nfig = px.bar(acc_df, x=\"Accuracy\", y=\"Stage\", animation_frame=\"Epochs\", title=\"Accuracy vs. Epochs\", color='Stage',\n       color_discrete_map={\"Train\":\"dodgerblue\", \"Val\":\"darkorange\", \"Benchmark\":\"seagreen\"}, orientation=\"h\")\n\nfig.update_layout(\n    xaxis = dict(\n        autorange=False,\n        range=[0, 1]\n    )\n)\n\nfig.update_layout(template=\"plotly_white\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Thank you for sparing your time\n\n1. Please like and upvote\n2. Do comment your thoughtful suggestions\n\ncheers\nTathagat\n"}],"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":4,"nbformat_minor":4}