{"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"codemirror_mode":{"name":"ipython","version":3},"file_extension":".py","mimetype":"text/x-python","name":"python","nbconvert_exporter":"python","pygments_lexer":"ipython3","version":"3.11.5"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":13333,"databundleVersionId":862146,"sourceType":"competition"}],"dockerImageVersionId":30588,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from copy import deepcopy\nimport numpy as np\nimport cv2\nfrom keras.utils import Sequence\nimport os\n\nclass DataGenerator(Sequence):\n    \"\"\"\n    Class to generate images of a certain batch size\n    \n    Args:\n        - images_list: list of images to be used\n        - label_vector: list of corresponding labels\n        - dir_imgs: directory containing the images\n        - batch_size: batch size for the generator\n        - shuffle: boolean, shuffles dataset if True\n        - augmentation: data augmentation from albumentations package\n        - resized_dims: dimensions to which images will be resized to\n    Returns:\n        - X: image tensor of the format (batch_size, width, height, channels)\n        - y: corresponding labels\n    \"\"\"\n    def __init__(self, images_list=None, label_vector=None, dir_imgs='./train_images',\n                 batch_size=64, shuffle=True, augmentation=None,\n                 resized_dims=(480,480,3)):\n\n        self.batch_size = batch_size\n        self.shuffle = shuffle\n        self.augmentation = augmentation\n        if images_list is None:\n            self.images_list = os.listdir(dir_imgs)\n        else:\n            self.images_list = deepcopy(images_list)\n        #print(self.images_list)\n        self.dir_imgs = dir_imgs\n        self.len = len(self.images_list) // self.batch_size\n        self.resized_dims = resized_dims\n        self.num_classes = 4\n        self.label_vector = label_vector\n        #self.mode = mode\n        #self.is_test = not 'train' in dir_imgs\n        #if 'train' in self.mode:\n        #self.labels = [self.label_vector[img] for img in self.images_list[:self.len*self.batch_size]]\n\n    def __len__(self):\n        return self.len\n\n    def __getitem__(self, idx):\n        current_batch = self.images_list[idx*self.batch_size:(idx+1)*self.batch_size]\n        X = np.empty((self.batch_size, self.resized_dims[0], self.resized_dims[1], self.resized_dims[2]))\n        y = np.empty((self.batch_size, self.num_classes))\n\n        for i, image_name in enumerate(current_batch):\n            path = os.path.join(self.dir_imgs, image_name)\n            img = cv2.resize(cv2.imread(path), (self.resized_dims[0], self.resized_dims[1])).astype(np.float32)\n            img = (img - np.min(img))/(np.max(img)-np.min(img))\n            if not self.augmentation is None:\n                augmented = self.augmentation(image=img)\n                img = augmented['image']\n            X[i, :, :, :] = img\n            #if 'train' in self.mode:\n            #self.labels = [self.label_vector[img] for img in self.images_list[:self.len*self.batch_size]]\n            if self.label_vector is not None:\n                y[i, :] = self.label_vector[image_name]\n        return X, y","metadata":{"execution":{"iopub.status.busy":"2023-12-05T23:29:32.696159Z","iopub.execute_input":"2023-12-05T23:29:32.696995Z","iopub.status.idle":"2023-12-05T23:29:44.259169Z","shell.execute_reply.started":"2023-12-05T23:29:32.696962Z","shell.execute_reply":"2023-12-05T23:29:44.257926Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import pandas as pd\nimport numpy as np\nimport keras\nimport os\nimport random\nfrom keras.callbacks import ModelCheckpoint\nfrom keras.applications.inception_v3 import InceptionV3\nfrom keras.optimizers import Adam\nfrom keras.losses import binary_crossentropy\nfrom keras.layers import Dense, GlobalMaxPool2D, Dropout, Flatten\nfrom keras.models import Model\n\ndef generate_data(train_imgs, val_imgs, labels_df, albumentations_train, batch_size, train_dir, resized_dims):\n    \"\"\"\n    Args:\n        - train_imgs: list of images to be used for training\n        - val_imgs: list of images to be used for validation\n        - albumentations_train: data augmentation from albumentations package\n        - batch_size: batch size for the generator\n        - train_dir: directory containing training images\n        - resized_dims: dimensions to which images will be resized to\n    Returns:\n        - data_generator_train: generator object for training images\n        - data_generator_val: generator object for validation images\n    \"\"\"\n\n    data_generator_train = DataGenerator(train_imgs, label_vector=labels_df, \n                                         dir_imgs=train_dir,\n                                         resized_dims=resized_dims,\n                                         batch_size=batch_size,\n                                         augmentation=albumentations_train,\n                                         shuffle=True)\n\n    data_generator_val = DataGenerator(val_imgs, label_vector=labels_df,\n                                       dir_imgs=train_dir,\n                                       batch_size=batch_size,\n                                       resized_dims=resized_dims,\n                                       shuffle=False)\n    \n    return (data_generator_train, data_generator_val)\n\ndef dice_coef(y_true, y_pred, smooth=1):\n    \"\"\"\n    Dice = (2*|X & Y|)/ (|X|+ |Y|)\n         =  2*sum(|A*B|)/(sum(A^2)+sum(B^2))\n    ref: https://arxiv.org/pdf/1606.04797v1.pdf\n    \"\"\"\n    intersection = K.sum(K.abs(y_true * y_pred), axis=-1)\n    return (2. * intersection + smooth) / (K.sum(K.square(y_true),-1) + K.sum(K.square(y_pred),-1) + smooth)\n\ndef dice_coef_loss(y_true, y_pred):\n    return 1-dice_coef(y_true, y_pred)\n\ndef bce_dice_loss(y_true, y_pred):\n    return binary_crossentropy(y_true, y_pred) + dice_coef_loss(y_true, y_pred)\n\n\ndef build_model(classes=4, learning_rate=0.001,\n                resized_dims=(299,299,3),\n                dropout_probability=0.5,\n                train_status = True,\n                loss_function='binary_crossentropy',\n                accuracy_function='accuracy'):\n    \"\"\"\n    Args:\n        - classes: number of classes in output\n        - learning_rate: learning rate for optimizer\n        - resized_dims: dimensions to which image will be resized to\n        - dropout_probability: probability value between 0 and 1 for dropout layer\n        - train_status: Boolean, set to True if weights need to be updated during training, set to \n                        False if weights need to be frozen\n        - loss_function: loss function for model\n        - accuracy_function: accuracy function for model\n    Returns:\n        - model: Keras model with compiled architectural layers\n    \"\"\"\n    \n    backbone_model = InceptionV3(include_top=False, weights=None, input_shape=resized_dims)\n\n    CLASSES = classes\n    x = backbone_model.output\n    #x = GlobalMaxPool2D()(x)\n    # Add a dropout layer with dropout probability of 0.5 by default\n    x = Dropout(dropout_probability)(x)\n    \n    # Add a Flatten layer to make dimensions compatible\n    x = Flatten()(x)\n    \n    # Add Dense (Fully-Connected Layer)\n    predictions = Dense(CLASSES, activation='softmax')(x)\n    model = Model(inputs=backbone_model.input, outputs=predictions)\n    \n    # trainable status - whether to freeze weights or not. Setting trainable=True will mean weights are updated\n    for layer in backbone_model.layers:\n        layer.trainable = train_status\n\n    model.compile(optimizer=Adam(learning_rate=learning_rate,clipnorm=1.,clipvalue=0.5),\n                  loss=bce_dice_loss,\n                  metrics=[dice_coef])\n    \n    return model\n\ndef train_model(model, filepath, data_generator_train, data_generator_val, epochs=25, steps_per_epoch=50):\n    \"\"\"\n    Args:\n        - model: Keras model which has already been compiled\n        - filepath: path where weights need to be saved\n        - data_generator_train: generator object for training images\n        - data_generator_val: generator object for validation images\n        - epochs: number of epochs for training\n        - steps_per_epoch: number of steps per epoch\n    Returns:\n        void\n    \"\"\"    \n    checkpoint = ModelCheckpoint(filepath, save_best_only=True, verbose=1, period=1)\n    print('Model will be saved in directory: {} as {}\\n'.format(os.path.split(filepath)[0],os.path.split(filepath)[1]))\n    model.fit_generator(data_generator_train,\n                        validation_data=data_generator_val,\n                        callbacks=[checkpoint],\n                        epochs=epochs,verbose=1,\n                        steps_per_epoch=steps_per_epoch)\n    print('Finished training model. Exiting function ...\\n')\n","metadata":{"execution":{"iopub.status.busy":"2023-12-05T23:57:25.22393Z","iopub.execute_input":"2023-12-05T23:57:25.224866Z","iopub.status.idle":"2023-12-05T23:57:25.242742Z","shell.execute_reply.started":"2023-12-05T23:57:25.224829Z","shell.execute_reply":"2023-12-05T23:57:25.241761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport pandas as pd\nimport numpy as np\nimport tensorflow as tf\nimport keras\nimport os,sys\nimport random\nimport argparse\nfrom sklearn.model_selection import train_test_split\nfrom albumentations import Compose, VerticalFlip, HorizontalFlip, Rotate, GridDistortion\nfrom keras.models import load_model\nfrom keras import backend as K\nK.clear_session()\n#tf.reset_default_graph()\n#tf.logging.set_verbosity(tf.logging.ERROR)\n\n# Read in the csv file\ntrain_df = pd.read_csv(\"/kaggle/input/understanding_cloud_organization/train.csv\")\ntrain_df = train_df[~train_df['EncodedPixels'].isnull()]\ntrain_df['image_name'] = train_df['Image_Label'].map(lambda x: x.split('_')[0])\ntrain_df['labels'] = train_df['Image_Label'].map(lambda x: x.split('_')[1])\nclasses = train_df['labels'].unique()\ntrain_df = train_df.groupby('image_name')['labels'].agg(set).reset_index()\nfor class_name in classes:\n    train_df[class_name] = train_df['labels'].map(lambda x: 1 if class_name in x else 0)\n\n# Create a pandas dataframe with one-hot encoded labels\nlabels_df = {img:vec for img, vec in zip(train_df['image_name'], train_df.iloc[:, 2:].values)}\nstratify_split = train_df['labels'].map(lambda x: str(sorted(list(x))))\n\n# Hyperparameters\nBATCH_SIZE = 64\nRESIZED_DIMS = (299,299,3)\n#print('resize dims=',RESIZED_DIMS)\nrandom_state= 43\ntest_size = 0.25\n\n# Data augmentation\nalbumentations_train = Compose([VerticalFlip(), HorizontalFlip(), Rotate(limit=10), GridDistortion()], p=1)\n\n# Split the dataset into training and validation images\ntrain_imgs, val_imgs = train_test_split(train_df['image_name'].values, \n                                        test_size=test_size, \n                                        stratify=stratify_split,\n                                        random_state=random_state)\n\n# Create the generator objects for training and validation sets\ntrain_data, val_data = generate_data(train_imgs, val_imgs, labels_df, \n                                     albumentations_train, \n                                     batch_size=BATCH_SIZE,\n                                     train_dir=\"/kaggle/input/understanding_cloud_organization/train_images\",\n                                     resized_dims=RESIZED_DIMS)\n\n# Compile a Keras model\nmodel = build_model(resized_dims=RESIZED_DIMS,train_status=True)\n\nfilepath = os.path.join(\"/kaggle/working\",\"model.h5\")\n\n# Train the model on the training and validation images\ntrain_model(model, filepath,\n            data_generator_train=train_data,\n            data_generator_val=val_data,\n            epochs=25,\n            steps_per_epoch=60)\n\n# Use the trained model to make predictions\ny_predictions = test_model(path_to_model=filepath, batch_size=BATCH_SIZE, test_dir=args.test_dir)\n\nclass_names = ['Fish', 'Flower', 'Sugar', 'Gravel']\nimage_names = []\nlabels = []\nfor i, (image_name, predictions) in enumerate(zip(os.listdir(\"/kaggle/input/understanding_cloud_organization/test_images\"), y_predictions)):\n    for class_i, class_name in enumerate(class_names):\n        image_names.append(image_name)\n        labels.append(class_name)\n\n# Save labels predicted as a csv file\npd.DataFrame(list(zip(image_names,predicted_labels)),columns=['image_name','label']).to_csv(\"/kaggle/working/output.csv\")\n\nprint('Saved predicted labels as {}'.format(\"/kaggle/working/output.csv\"))\n","metadata":{"execution":{"iopub.status.busy":"2023-12-05T23:57:36.894289Z","iopub.execute_input":"2023-12-05T23:57:36.894761Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import numpy as np\nimport keras\nfrom keras.models import load_model\n\n\ndef test_model(path_to_model, test_dir, batch_size, resized_dims=(299,299,3)):\n    \"\"\"\n    Args:\n        - path_to_model: path to the model (h5 file) to be loaded\n        - test_dir: directory containing the test images\n        - batch_size: batch size for the generator\n        - resized_dims: dimensions to which image will be resized to.\n    Returns:\n        - y_pred: numpy array containing label probabilities\n    \"\"\"\n    model = load_model(path_to_model)\n    data_generator_test = DataGenerator(dir_imgs=test_dir,\n                                        shuffle=False, \n                                        batch_size=batch_size,\n                                        resized_dims=resized_dims)\n    \n    y_pred = model.predict_generator(data_generator_test, verbose=1)\n    print('Finished predictions successfully, exiting testing...\\n')\n    \n    return y_pred\n","metadata":{"execution":{"iopub.status.busy":"2023-12-05T23:56:41.234079Z","iopub.execute_input":"2023-12-05T23:56:41.234477Z","iopub.status.idle":"2023-12-05T23:56:41.241168Z","shell.execute_reply.started":"2023-12-05T23:56:41.234446Z","shell.execute_reply":"2023-12-05T23:56:41.240059Z"},"trusted":true},"execution_count":null,"outputs":[]}]}