{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python Docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load\n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\n\n# Input data files are available in the read-only \"../input/\" directory\n# For example, running this (by clicking run or pressing Shift+Enter) will list all files under the input directory\n\nimport os\n#for dirname, _, filenames in os.walk('/kaggle/input'):\n#    for filename in filenames:\n#        print(os.path.join(dirname, filename))\n\n# You can write up to 20GB to the current directory (/kaggle/working/) that gets preserved as output when you create a version using \"Save & Run All\" \n# You can also write temporary files to /kaggle/temp/, but they won't be saved outside of the current session","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Read data"},{"metadata":{"trusted":true},"cell_type":"code","source":"#import\nimport matplotlib.pyplot as plt\nimport seaborn as sn\nimport missingno as msno\nimport tensorflow as tf\nfrom tensorflow.keras.preprocessing.image import ImageDataGenerator\nfrom tensorflow.keras.callbacks import ReduceLROnPlateau\nfrom PIL import Image\nbasepath = '/kaggle/input/cassava-leaf-disease-classification'\npd.options.mode.chained_assignment = None\nTARGET_SZ=300 #Global variable for image size","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data=pd.read_csv(\"/kaggle/input/cassava-leaf-disease-classification/train.csv\")\ndata.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# How the image looks?"},{"metadata":{"trusted":true},"cell_type":"code","source":"img = Image.open(\"../input/cassava-leaf-disease-classification/train_images/1000723321.jpg\")\nplt.imshow(img)\nplt.show()\nprint(img.size)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Learn about data"},{"metadata":{"trusted":true},"cell_type":"code","source":"#How is the data distribution?\nprint(data.groupby('label').nunique())\nsn.countplot(x='label',data=data)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# It is an Imbalanced training data set\n\nRemember the disease map\n\"root\": { 5 items\n\n\"0\":string\"Cassava Bacterial Blight (CBB)\"\n\n\"1\":string\"Cassava Brown Streak Disease (CBSD)\"\n\n\"2\":string\"Cassava Green Mottle (CGM)\"\n\n\"3\":string\"Cassava Mosaic Disease (CMD)\"\n\n\"4\":string\"Healthy\"\n}\n\nSo we have imbalanced data. \nCategory 3 - Mosaic Disease has large number of samples. Does this imbalance matters? Yes. We can try to fix it.\n\nBut wondering why Healthy is not as high as this - it seems  easy to get photos of healthy leaves.Shouldn't it?"},{"metadata":{},"cell_type":"markdown","source":"# Now to remove the imbalance\nThere are different techniques. We go for a simple method"},{"metadata":{"trusted":true},"cell_type":"code","source":"\n# To make the data set balanced, we select only 3000 samples of CMD (3) type.\n\n#balanced_data = data.loc[data['label'].isin([0,1,2,4])] \n#data=data.loc[data['label']==3]\n#data=data.sample(n=1000,random_state=1)\n#data=data.append(balanced_data)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Now let's see how the data looks"},{"metadata":{"trusted":true},"cell_type":"code","source":"print(data.groupby('label').nunique())\nsn.countplot(x='label',data=data)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Check for null values"},{"metadata":{"trusted":true},"cell_type":"code","source":"#let's check the  data for missing values\n#msno.bar(data)\ndata.isnull().sum()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Good. No null values."},{"metadata":{"trusted":true},"cell_type":"code","source":"# For experimenting take only 9000 records in total\n\n#data=data.sample(n=300,random_state=1)\n# and again see the data distribution\n#sn.countplot(x='label',data=data)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Split data to training and validation"},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\n\ntrain, val = train_test_split(data, test_size=0.2)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Now define a CNN model"},{"metadata":{"trusted":true},"cell_type":"code","source":"classes_to_prdict=train.label.unique()\n\nmodel = tf.keras.models.Sequential([\n    # input shape is the desired size of the image 300x300 with 3 bytes color\n    tf.keras.layers.Conv2D(16, (3,3), activation='relu', input_shape=(TARGET_SZ, TARGET_SZ, 3)),# convolution -1\n    tf.keras.layers.MaxPooling2D(2, 2), \n    tf.keras.layers.Conv2D(32, (3,3), activation='relu'), #Convolution-2\n    tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Conv2D(64, (3,3), activation='relu'), #Convolution-3\n    tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Conv2D(64, (3,3), activation='relu'), #Convolution-4\n    tf.keras.layers.MaxPooling2D(2,2), \n    tf.keras.layers.Conv2D(64, (3,3), activation='relu'), #Convolution-5\n    tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Conv2D(64, (3,3), activation='relu'), #Convolution-6 sha\n    tf.keras.layers.MaxPooling2D(2,2),\n    #tf.keras.layers.Conv2D(64, (3,3), activation='relu'), #Convolution-7 sha - delete next also\n    #tf.keras.layers.MaxPooling2D(2,2),\n    tf.keras.layers.Flatten(), # Flatten before giving to NN\n    tf.keras.layers.Dense(512, activation='relu'),  # 512 neuron hidden layer\n    tf.keras.layers.Dense(256, activation='relu'),  # 256 neuron hidden layer - new\n    tf.keras.layers.Dense(128, activation='relu'),  # 128 neuron hidden layer - new\n    tf.keras.layers.Dense(64, activation='relu'),  # 64 neuron hidden layer - new\n    tf.keras.layers.Dense(32, activation='relu'),  # 32 neuron hidden layer - sha\n    #tf.keras.layers.Dense(16, activation='relu'),  # 16 neuron hidden layer - sha\n    tf.keras.layers.Dense(len(classes_to_prdict), activation='softmax') #Multi-class output\n])\nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#We use Adam optimizer\n\nfrom tensorflow.keras.optimizers import Adam\n\n#model.compile(loss='binary_crossentropy',\nmodel.compile(loss='categorical_crossentropy',\n#model.compile(loss='sparse_categorical_crossentropy',\n              #optimizer=Adam(lr=0.001),\n              optimizer=Adam(lr=0.001),\n              metrics=['accuracy'])\n#optimizer=tf.keras.optimizers.Adam(learning_rate=1e-4)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# All images will be rescaled by 1./255\ntrain_datagen = ImageDataGenerator(rescale=1./255)\nvalidation_datagen = ImageDataGenerator(rescale=1./255)\n#targetSz=300\ntargetSz=TARGET_SZ\n#batchSz=128\n#batchSz=100\nbatchSz=333\n\ntrain['label'] = train['label'].astype(str)\nval['label'] = val['label'].astype(str)\n\n# Flow training images in batches of 128 using train_datagen generator\ntrain_generator = train_datagen.flow_from_dataframe(train, \n                                                    directory = os.path.join(basepath, 'train_images'),\n                                                    x_col = 'image_id',\n                                                    y_col = 'label',\n                                                    target_size = (TARGET_SZ, TARGET_SZ),\n                                                    batch_size = batchSz,\n                                                    class_mode = 'categorical')\n\n# Flow training images in batches of 128 using train_datagen generator\nvalidation_generator = train_datagen.flow_from_dataframe(val, \n                                                    directory = os.path.join(basepath, 'train_images'),\n                                                    x_col = 'image_id',\n                                                    y_col = 'label',\n                                                    target_size = (TARGET_SZ, TARGET_SZ),\n                                                    batch_size = batchSz,\n                                                    class_mode = 'categorical')","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Train the model"},{"metadata":{"trusted":true},"cell_type":"code","source":"#The parameters \"steps_per_epoch\" and \"validation_steps\" have to be equal to the\n#length of the dataset divided by the batch size. Otherwise within the first epoch itself it comes out. As\n#Then I found out from stack overflow the above rule. Not sure why?\n\ncallbacks = ReduceLROnPlateau(monitor='val_acc', \n                              #factor=0.5, \n                              factor=0.2,\n                              patience=5, \n                              verbose=1, \n                              #min_lr=0.0001)\n                              min_lr=0.001)\n\nhistory = model.fit_generator(\n            train_generator,\n            #steps_per_epoch = 3,\n            steps_per_epoch = 27,\n            #epochs = 3,\n            #epochs = 25,\n            epochs = 25,\n            verbose = 1,\n            validation_data = validation_generator,\n            #validation_steps = 3,\n            validation_steps = 27,\n            callbacks = [callbacks])","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plot loss progression "},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(history.history['loss'])\nplt.plot(history.history['val_loss'])\nplt.title('Loss over epochs')\nplt.ylabel('Loss')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Validation'], loc='best')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Plot accuracy "},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(history.history['accuracy'])\nplt.plot(history.history['val_accuracy'])\nplt.title('Accuracy over epochs')\nplt.ylabel('Accuracy')\nplt.xlabel('Epoch')\nplt.legend(['Train', 'Validation'], loc='best')\nplt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# OK Model is ready. Now apply on test"},{"metadata":{"trusted":true},"cell_type":"code","source":"#Now use the model on test images\ntest_folder = os.path.join(basepath,  \"test_images\")\n\n#test_images = os.listdir(os.path.join(basepath,  \"test_images\"))\ntest_images = os.listdir(test_folder)\npredictions=[]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i in test_images:\n    #image = Image.open(f'/kaggle/input/cassava-leaf-disease-classification/test_images/{i}')\n    print(i) \n    tmp_image=os.path.join(test_folder,i)\n    print(tmp_image) \n    image = Image.open(tmp_image)\n    image = image.resize((targetSz, targetSz))\n       \n    \n    image = np.expand_dims(image, axis = 0)\n    image = image/255.0\n    predictions.append(np.argmax(model.predict(image)))\n                       \nsubmission = pd.DataFrame({'image_id': test_images, 'label': predictions})\nsubmission\n","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Finally submit"},{"metadata":{"trusted":true},"cell_type":"code","source":"submission.to_csv('/kaggle/working/submission.csv', index = False)","execution_count":null,"outputs":[]}],"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}