{"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":"code","source":"GAUSSIAN_NOISE = 0.1\nUPSAMPLE_MODE = 'SIMPLE'\n# number of validation images to use\nVALID_IMG_COUNT = 1000\n# maximum number of training images\nMAX_TRAIN_IMAGES = 15000 \nBASE_MODEL='DenseNet169' # ['VGG16', 'RESNET52', 'InceptionV3', 'Xception', 'DenseNet169', 'DenseNet121']\nIMG_SIZE = (299, 299) # [(224, 224), (384, 384), (512, 512), (640, 640)]\nBATCH_SIZE = 64 # [1, 8, 16, 24]\nDROPOUT = 0.5\nDENSE_COUNT = 128\nLEARN_RATE = 1e-4\nRGB_FLIP = 1 # should rgb be flipped when rendering images","metadata":{"_uuid":"301a5d939c566d1487a049bb2554d09b592b18b1","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import os\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom skimage.io import imread\nimport matplotlib.pyplot as plt\nfrom skimage.segmentation import mark_boundaries\nfrom skimage.util.montage import montage2d as montage\nmontage_rgb = lambda x: np.stack([montage(x[:, :, :, i]) for i in range(x.shape[3])], -1)\nship_dir = '../input'\ntrain_image_dir = os.path.join(ship_dir, 'train_v2')\ntest_image_dir = os.path.join(ship_dir, 'test_v2')\nimport gc; gc.enable() # memory is tight","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"masks = pd.read_csv(os.path.join('../input/',\n                                 'train_ship_segmentations_v2.csv'))\nprint(masks.shape[0], 'masks found')\nprint(masks['ImageId'].value_counts().shape[0])\nmasks['path'] = masks['ImageId'].map(lambda x: os.path.join(train_image_dir, x))\nmasks.head()","metadata":{"_uuid":"3ca7119188fbb4c6540d9df55f5833b55435287e","execution":{"iopub.status.busy":"2023-08-19T05:17:29.125963Z","iopub.execute_input":"2023-08-19T05:17:29.126306Z","iopub.status.idle":"2023-08-19T05:17:30.609638Z","shell.execute_reply.started":"2023-08-19T05:17:29.126239Z","shell.execute_reply":"2023-08-19T05:17:30.608487Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nmasks['ships'] = masks['EncodedPixels'].map(lambda c_row: 1 if isinstance(c_row, str) else 0)\nunique_img_ids = masks.groupby('ImageId').agg({'ships': 'sum'}).reset_index()\nunique_img_ids['has_ship'] = unique_img_ids['ships'].map(lambda x: 1.0 if x>0 else 0.0)\nunique_img_ids['has_ship_vec'] = unique_img_ids['has_ship'].map(lambda x: [x])\nmasks.drop(['ships'], axis=1, inplace=True)\ntrain_ids, valid_ids = train_test_split(unique_img_ids, \n                 test_size = 0.3, \n                 stratify = unique_img_ids['ships'])\ntrain_df = pd.merge(masks, train_ids)\nvalid_df = pd.merge(masks, valid_ids)\nprint(train_df.shape[0], 'training masks')\nprint(valid_df.shape[0], 'validation masks')","metadata":{"_uuid":"871720221ac25f7f9408bfe01aeb4ccb95edbd1f","execution":{"iopub.status.busy":"2023-08-19T05:17:34.606166Z","iopub.execute_input":"2023-08-19T05:17:34.606495Z","iopub.status.idle":"2023-08-19T05:17:35.854687Z","shell.execute_reply.started":"2023-08-19T05:17:34.606439Z","shell.execute_reply":"2023-08-19T05:17:35.853912Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df = train_df.sample(min(MAX_TRAIN_IMAGES, train_df.shape[0])) # limit size of training set (otherwise it takes too long)","metadata":{"_uuid":"fa2154d3621497e7497731e5ef6c6c72f27ad483","execution":{"iopub.status.busy":"2023-08-19T05:17:37.796227Z","iopub.execute_input":"2023-08-19T05:17:37.79659Z","iopub.status.idle":"2023-08-19T05:17:37.842077Z","shell.execute_reply.started":"2023-08-19T05:17:37.796529Z","shell.execute_reply":"2023-08-19T05:17:37.841172Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df[['ships', 'has_ship']].hist()","metadata":{"_uuid":"2612fa47c7e9fdcaa7aa720c4e15fc86fd65d69a","execution":{"iopub.status.busy":"2023-08-19T05:17:41.137771Z","iopub.execute_input":"2023-08-19T05:17:41.138096Z","iopub.status.idle":"2023-08-19T05:17:41.601386Z","shell.execute_reply.started":"2023-08-19T05:17:41.138034Z","shell.execute_reply":"2023-08-19T05:17:41.600232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\nif BASE_MODEL=='VGG16':\n    from keras.applications.vgg16 import VGG16 as PTModel, preprocess_input\nelif BASE_MODEL=='RESNET52':\n    from keras.applications.resnet50 import ResNet50 as PTModel, preprocess_input\nelif BASE_MODEL=='InceptionV3':\n    from keras.applications.inception_v3 import InceptionV3 as PTModel, preprocess_input\nelif BASE_MODEL=='Xception':\n    from keras.applications.xception import Xception as PTModel, preprocess_input\nelif BASE_MODEL=='DenseNet169': \n    from keras.applications.densenet import DenseNet169 as PTModel, preprocess_input\nelif BASE_MODEL=='DenseNet121':\n    from keras.applications.densenet import DenseNet121 as PTModel, preprocess_input\nelse:\n    raise ValueError('Unknown model: {}'.format(BASE_MODEL))","metadata":{"_uuid":"ccbc1747ffb0f2942cc99bc27e4afc3262ba9f94","execution":{"iopub.status.busy":"2023-08-19T05:17:44.330537Z","iopub.execute_input":"2023-08-19T05:17:44.330957Z","iopub.status.idle":"2023-08-19T05:17:44.344273Z","shell.execute_reply.started":"2023-08-19T05:17:44.3309Z","shell.execute_reply":"2023-08-19T05:17:44.343214Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.preprocessing.image import ImageDataGenerator\ndg_args = dict(featurewise_center = False, \n                  samplewise_center = False,\n                  rotation_range = 45, \n                  width_shift_range = 0.1, \n                  height_shift_range = 0.1, \n                  shear_range = 0.01,\n                  zoom_range = [0.9, 1.25],  \n                  brightness_range = [0.5, 1.5],\n                  horizontal_flip = True, \n                  vertical_flip = True,\n                  fill_mode = 'reflect',\n                   data_format = 'channels_last',\n              preprocessing_function = preprocess_input)\nvalid_args = dict(fill_mode = 'reflect',\n                   data_format = 'channels_last',\n                  preprocessing_function = preprocess_input)\n\ncore_idg = ImageDataGenerator(**dg_args)\nvalid_idg = ImageDataGenerator(**valid_args)","metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","execution":{"iopub.status.busy":"2023-08-19T05:17:47.346621Z","iopub.execute_input":"2023-08-19T05:17:47.346966Z","iopub.status.idle":"2023-08-19T05:17:47.356094Z","shell.execute_reply.started":"2023-08-19T05:17:47.346901Z","shell.execute_reply":"2023-08-19T05:17:47.355283Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def flow_from_dataframe(img_data_gen, in_df, path_col, y_col, **dflow_args):\n    base_dir = os.path.dirname(in_df[path_col].values[0])\n    print('## Ignore next message from keras, values are replaced anyways')\n    df_gen = img_data_gen.flow_from_directory(base_dir, \n                                     class_mode = 'sparse',\n                                    **dflow_args)\n    df_gen.filenames = in_df[path_col].values\n    df_gen.classes = np.stack(in_df[y_col].values)\n    df_gen.samples = in_df.shape[0]\n    df_gen.n = in_df.shape[0]\n    df_gen._set_index_array()\n    df_gen.directory = '' # since we have the full path\n    print('Reinserting dataframe: {} images'.format(in_df.shape[0]))\n    return df_gen","metadata":{"_uuid":"c5cc31e3117fdfa923b2acb1e6542a7007f4f955","execution":{"iopub.status.busy":"2023-08-19T05:17:56.144339Z","iopub.execute_input":"2023-08-19T05:17:56.144674Z","iopub.status.idle":"2023-08-19T05:17:56.153019Z","shell.execute_reply.started":"2023-08-19T05:17:56.144612Z","shell.execute_reply":"2023-08-19T05:17:56.152176Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_gen = flow_from_dataframe(core_idg, train_df, \n                             path_col = 'path',\n                            y_col = 'has_ship_vec', \n                            target_size = IMG_SIZE,\n                             color_mode = 'rgb',\n                            batch_size = BATCH_SIZE)\n\n# used a fixed dataset for evaluating the algorithm\nvalid_x, valid_y = next(flow_from_dataframe(valid_idg, \n                               valid_df, \n                             path_col = 'path',\n                            y_col = 'has_ship_vec', \n                            target_size = IMG_SIZE,\n                             color_mode = 'rgb',\n                            batch_size = VALID_IMG_COUNT)) # one big batch\nprint(valid_x.shape, valid_y.shape)","metadata":{"_uuid":"67136e1743dbbc4e07dba0d69f79231603f31d93","execution":{"iopub.status.busy":"2023-08-19T05:18:01.003696Z","iopub.execute_input":"2023-08-19T05:18:01.004017Z","iopub.status.idle":"2023-08-19T05:29:26.052007Z","shell.execute_reply.started":"2023-08-19T05:18:01.00396Z","shell.execute_reply":"2023-08-19T05:29:26.050909Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"t_x, t_y = next(train_gen)\nprint('x', t_x.shape, t_x.dtype, t_x.min(), t_x.max())\nprint('y', t_y.shape, t_y.dtype, t_y.min(), t_y.max())\nfig, (ax1, ax2) = plt.subplots(1, 2, figsize = (20, 10))\nax1.imshow(montage_rgb((t_x-t_x.min())/(t_x.max()-t_x.min()))[:, :, ::RGB_FLIP], cmap='gray')\nax1.set_title('images')\nax2.plot(t_y)\nax2.set_title('ships')","metadata":{"_uuid":"6122ccb9e58bfac6fa5e11c86121e78d9e5151b1","execution":{"iopub.status.busy":"2023-08-19T05:29:52.20331Z","iopub.execute_input":"2023-08-19T05:29:52.203647Z","iopub.status.idle":"2023-08-19T05:29:56.644762Z","shell.execute_reply.started":"2023-08-19T05:29:52.20359Z","shell.execute_reply":"2023-08-19T05:29:56.643886Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model\n","metadata":{"_uuid":"ba08494eb9736ec3556b7c879143cdcdea89febf"}},{"cell_type":"code","source":"base_pretrained_model = PTModel(input_shape =  t_x.shape[1:], \n                              include_top = False, \n                                weights = 'imagenet')\nbase_pretrained_model.trainable = False","metadata":{"_uuid":"9fb62d14ec059f7f92d82e07b35822169c77112d","execution":{"iopub.status.busy":"2023-08-19T05:31:09.01143Z","iopub.execute_input":"2023-08-19T05:31:09.011783Z","iopub.status.idle":"2023-08-19T05:36:56.671796Z","shell.execute_reply.started":"2023-08-19T05:31:09.011722Z","shell.execute_reply":"2023-08-19T05:36:56.670579Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Setup the Subsequent Layers\nHere we setup the rest of the model which we will actually be training","metadata":{"_uuid":"0bc7c0ee177697838d22aed4de046e7542027a10"}},{"cell_type":"code","source":"from keras import models, layers\nfrom keras.optimizers import Adam\nimg_in = layers.Input(t_x.shape[1:], name='Image_RGB_In')\nimg_noise = layers.GaussianNoise(GAUSSIAN_NOISE)(img_in)\npt_features = base_pretrained_model(img_noise)\npt_depth = base_pretrained_model.get_output_shape_at(0)[-1]\nbn_features = layers.BatchNormalization()(pt_features)\nfeature_dropout = layers.SpatialDropout2D(DROPOUT)(bn_features)\ngmp_dr = layers.GlobalMaxPooling2D()(feature_dropout)\ndr_steps = layers.Dropout(DROPOUT)(layers.Dense(DENSE_COUNT, activation = 'relu')(gmp_dr))\nout_layer = layers.Dense(1, activation = 'sigmoid')(dr_steps)\n\nship_model = models.Model(inputs = [img_in], outputs = [out_layer], name = 'full_model')\n\nship_model.compile(optimizer = Adam(lr=LEARN_RATE), \n                   loss = 'binary_crossentropy',\n                   metrics = ['binary_accuracy'])\n\nship_model.summary()","metadata":{"_uuid":"2687377309d3cbbab1197f4eccd2b50ab996f5a6","execution":{"iopub.status.busy":"2023-08-17T15:07:46.580531Z","iopub.execute_input":"2023-08-17T15:07:46.580871Z","iopub.status.idle":"2023-08-17T15:08:08.074211Z","shell.execute_reply.started":"2023-08-17T15:07:46.580804Z","shell.execute_reply":"2023-08-17T15:08:08.072559Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.callbacks import ModelCheckpoint, LearningRateScheduler, EarlyStopping, ReduceLROnPlateau\nweight_path=\"{}_weights.best.hdf5\".format('boat_detector')\n\ncheckpoint = ModelCheckpoint(weight_path, monitor='val_loss', verbose=1, \n                             save_best_only=True, mode='min', save_weights_only = True)\n\nreduceLROnPlat = ReduceLROnPlateau(monitor='val_loss', factor=0.8, patience=10, verbose=1, mode='auto', epsilon=0.0001, cooldown=5, min_lr=0.0001)\nearly = EarlyStopping(monitor=\"val_loss\", \n                      mode=\"min\", \n                      patience=10) # probably needs to be more patient, but kaggle time is limited\ncallbacks_list = [checkpoint, early, reduceLROnPlat]","metadata":{"_uuid":"7282d18de3aff1cee12ff89b7d511a391702814f","execution":{"iopub.status.busy":"2023-08-17T15:09:17.74173Z","iopub.execute_input":"2023-08-17T15:09:17.742228Z","iopub.status.idle":"2023-08-17T15:09:17.751352Z","shell.execute_reply.started":"2023-08-17T15:09:17.742157Z","shell.execute_reply":"2023-08-17T15:09:17.750397Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_gen.batch_size = BATCH_SIZE\nship_model.fit_generator(train_gen, \n                         steps_per_epoch=train_gen.n//BATCH_SIZE,\n                      validation_data=(valid_x, valid_y), \n                      epochs=30, \n                      callbacks=callbacks_list,\n                      workers=3)","metadata":{"_uuid":"5b67d808c0b8c7e28bff41e6d3858ff6f09dd626","execution":{"iopub.status.busy":"2023-08-17T15:09:21.533226Z","iopub.execute_input":"2023-08-17T15:09:21.533558Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"ship_model.load_weights(weight_path)\nship_model.save('full_ship_model.h5')","metadata":{"_uuid":"a168c8b1af446b800f6129104906003ededd61c4","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Run the test data\nWe use the sample_submission file as the basis for loading and running the images.","metadata":{"_uuid":"17edb177402ae51651692511827a7e9d60646533"}},{"cell_type":"code","source":"test_paths = os.listdir(test_image_dir)\nprint(len(test_paths), 'test images found')\nsubmission_df = pd.read_csv('../input/sample_submission_v2.csv')\nsubmission_df['path'] = submission_df['ImageId'].map(lambda x: os.path.join(test_image_dir, x))","metadata":{"_uuid":"4911811f267f9f3397a58902da9e75c6f261ad40","execution":{"iopub.status.busy":"2023-08-17T18:02:47.977889Z","iopub.execute_input":"2023-08-17T18:02:47.978242Z","iopub.status.idle":"2023-08-17T18:02:48.053023Z","shell.execute_reply.started":"2023-08-17T18:02:47.978175Z","shell.execute_reply":"2023-08-17T18:02:48.052187Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Setup Test Data Generator\nWe use the same generator as before to read and preprocess images","metadata":{"_uuid":"5b3eea954bd883d598c6ac80167dfd4353b0f558"}},{"cell_type":"code","source":"test_gen = flow_from_dataframe(valid_idg, \n                               submission_df, \n                             path_col = 'path',\n                            y_col = 'ImageId', \n                            target_size = IMG_SIZE,\n                             color_mode = 'rgb',\n                            batch_size = BATCH_SIZE, \n                              shuffle = False)","metadata":{"_uuid":"f75595679ba8606fd1ac15645b8612e117db0b30","execution":{"iopub.status.busy":"2023-08-17T18:02:50.529737Z","iopub.execute_input":"2023-08-17T18:02:50.530071Z","iopub.status.idle":"2023-08-17T18:03:47.102171Z","shell.execute_reply.started":"2023-08-17T18:02:50.530009Z","shell.execute_reply":"2023-08-17T18:03:47.101246Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"fig, m_axs = plt.subplots(3, 2, figsize = (20, 30))\nfor (ax1, ax2), (t_x, c_img_names) in zip(m_axs, test_gen):\n    t_y = ship_model.predict(t_x)\n    t_stack = ((t_x-t_x.min())/(t_x.max()-t_x.min()))[:, :, :, ::RGB_FLIP]\n    ax1.imshow(montage_rgb(t_stack))\n    ax1.set_title('images')\n    alpha_stack = np.tile(np.expand_dims(np.expand_dims(t_y, -1), -1), [1, t_stack.shape[1], t_stack.shape[2], 1])\n    rgba_stack = np.concatenate([t_stack, alpha_stack], -1)\n    ax2.imshow(montage_rgb(rgba_stack))\n    ax2.set_title('ships')\nfig.savefig('test_predictions.png')","metadata":{"_uuid":"73ef7b3b2a74bf64968c79b4005075d4f0e23143","execution":{"iopub.status.busy":"2023-08-17T18:04:00.698182Z","iopub.execute_input":"2023-08-17T18:04:00.698522Z","iopub.status.idle":"2023-08-17T18:04:03.788678Z","shell.execute_reply.started":"2023-08-17T18:04:00.698461Z","shell.execute_reply":"2023-08-17T18:04:03.787526Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Prepare Submission\nProcess all images (batchwise) and keep the score at the end","metadata":{"_uuid":"22f5f7e53cb82ff54bfb0e4ee818315d05c1e1af"}},{"cell_type":"code","source":"BATCH_SIZE = BATCH_SIZE*2 # we can use larger batches for inference\ntest_gen = flow_from_dataframe(valid_idg, \n                               submission_df, \n                             path_col = 'path',\n                            y_col = 'ImageId', \n                            target_size = IMG_SIZE,\n                             color_mode = 'rgb',\n                            batch_size = BATCH_SIZE, \n                              shuffle = False)","metadata":{"_uuid":"b103e0ce6ccae69dbf82a55067dc693efaeadf8f","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from tqdm import tqdm_notebook\nall_scores = dict()\nfor _, (t_x, t_names) in zip(tqdm_notebook(range(test_gen.n//BATCH_SIZE+1)),\n                            test_gen):\n    t_y = ship_model.predict(t_x)[:, 0]\n    for c_id, c_score in zip(t_names, t_y):\n        all_scores[c_id] = c_score","metadata":{"_uuid":"0a38d343b2654f87934a88524ebc14a5759e07cb","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# The Scores","metadata":{"_uuid":"4a942dbb4a939d73526dbb35745402b35785fdf4"}},{"cell_type":"code","source":"submission_df['score'] = submission_df['ImageId'].map(lambda x: all_scores.get(x, 0))\nsubmission_df['score'].hist()","metadata":{"_uuid":"41ab9326407f59c983f3991ff736da8dd822605c","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"true_labels_df = pd.read_csv('/kaggle/input/airbus-ship-detection/train_ship_segmentations_v2.csv')  # Replace with the actual path to your true labels CSV file\n\n# Merge the true labels with the submission_df based on ImageId\nsubmission_df = pd.merge(submission_df, true_labels_df, on='ImageId', how='left')\n\n# Define a threshold to determine whether a ship is detected\nthreshold = 0.5\n\n# Convert scores to binary predictions based on the threshold\nsubmission_df['predicted_ship'] = submission_df['score'].apply(lambda x: 1 if x >= threshold else 0)\n\n# Calculate the accuracy and F1 score\naccuracy = accuracy_score(submission_df['true_ship'], submission_df['predicted_ship'])\nf1 = f1_score(submission_df['true_ship'], submission_df['predicted_ship'])\n\n# Print the results\nprint('Accuracy:', accuracy)\nprint('F1 Score:', f1)","metadata":{},"execution_count":null,"outputs":[]}]}