{"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":"# 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","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","execution":{"iopub.status.busy":"2022-02-13T05:42:29.394165Z","iopub.execute_input":"2022-02-13T05:42:29.394916Z","iopub.status.idle":"2022-02-13T05:42:29.401319Z","shell.execute_reply.started":"2022-02-13T05:42:29.394878Z","shell.execute_reply":"2022-02-13T05:42:29.400014Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Upvotes are appreciated!\n\n## I'd be happy to answer any questions I can in the comments! or talk off kaggle if you prefer so feel free to comment or reach out via the contact this user on kaggle\n### I am also open to any tips or corrections on my work! Constructive criticism is always appreciated.\n","metadata":{}},{"cell_type":"markdown","source":"# Notebook Overview\n* this notebook is a step one in this competition\n* the goal is to predict weather an image is a whale or a dolphin\n    * this will allow for training seperate feature extraction networks later on\n* the notebook uses the cropped dataset as its images, theses are focused on the animal of interest\n* it also uses the splits data to reproducibly provide cross-validation splits when comparing networks\n* we will use the google pretrained efficient nets with fine tuning to see if we can learn better","metadata":{}},{"cell_type":"markdown","source":"# Imports","metadata":{}},{"cell_type":"code","source":"# DOWNLOADS\n# !pip install -q efficientnet\n# !pip install tensorflow_addons\n# !pip install wandb\n\n# NORMAL IMPORTS\nimport os\nimport gc\nimport random\nimport pickle\nimport json\nimport numpy as np\nimport pandas as pd\nfrom tqdm.auto import tqdm\nimport matplotlib\n%matplotlib inline\nimport matplotlib.pyplot as plt \nimport seaborn as sns\n\n# PREPROCESSING IMPORTS\nimport cv2\nfrom sklearn.model_selection import StratifiedKFold, train_test_split, StratifiedShuffleSplit\nfrom sklearn.metrics import accuracy_score, classification_report, confusion_matrix\n\n# MODEL BUILDING AND TRAINING IMPORTS\nimport tensorflow_hub as tfhub\nimport tensorflow as tf\nfrom tensorflow.keras import backend as K\n# import tensorflow_addons as tfa\n# import efficientnet.tfkeras as efn\n\nBASE_PATH = '/kaggle/input/'\nprint(os.listdir(BASE_PATH))\nMAIN_DATASET = os.path.join(BASE_PATH, 'happy-whale-and-dolphin')\nCV_SPLITS_DATASET = os.path.join(BASE_PATH, 'happywhale-splits')\nCROPPED_DATASET = os.path.join(BASE_PATH, 'happywhale-cropped-dataset-yolov5-ds')","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.403455Z","iopub.execute_input":"2022-02-13T05:42:29.404043Z","iopub.status.idle":"2022-02-13T05:42:29.419551Z","shell.execute_reply.started":"2022-02-13T05:42:29.403987Z","shell.execute_reply":"2022-02-13T05:42:29.418769Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# The Setup\n* we want to setup the training strategy incase we use a tpu\n* we want to define the model config class that holds parameters\n* we want to seed the environment for reporducibility\n* in the future I want to learn to use wandb and connect it in the setup","metadata":{}},{"cell_type":"code","source":"try:\n    # TPU detection. No parameters necessary if TPU_NAME environment variable is\n    # set: this is always the case on Kaggle.\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print('Running on TPU ', tpu.master())\nexcept ValueError:\n    tpu = None\n    \nif tpu:\n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.TPUStrategy(tpu)\nelse:\n    # Default distribution strategy in Tensorflow. Works on CPU and single GPU.\n    strategy = tf.distribute.get_strategy()\n\nAUTO = tf.data.experimental.AUTOTUNE\nprint(\"REPLICAS: \", strategy.num_replicas_in_sync)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.420810Z","iopub.execute_input":"2022-02-13T05:42:29.421580Z","iopub.status.idle":"2022-02-13T05:42:29.429786Z","shell.execute_reply.started":"2022-02-13T05:42:29.421542Z","shell.execute_reply":"2022-02-13T05:42:29.429092Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"## Config\n\n#### efficientnet is a python module we can use to download the pretrained efficient net model.\n\n#### The models were trained on a lot of image data, and according to the internet (and the fact a lot of other people are using them) they seem like a good option to extract information from our images\n\n#### We want to use a pretrained model that learned to extract meaningful features on a bunch of images because it is likely that it has learned some good general strategies. By starting with a model that generally preforms (or performs I dont know) well, we can leverage this feature extraction information on other images, and just fine tune the weights to learn to extract what we want from our images.","metadata":{}},{"cell_type":"code","source":"# this just translates the name of the model were using\n# to the function we need to import it\n# again this uses the efficient net module that\n# creates an efficient net model architecture and \n# preloads it with the corresponsding weights\n\n# EFF_NET_MODEL_DICT_V1 = {'EfficientNetB0':efn.EfficientNetB0, \n#                          'EfficientNetB1':efn.EfficientNetB1,\n#                          'EfficientNetB2':efn.EfficientNetB2,\n#                          'EfficientNetB3':efn.EfficientNetB3,\n#                          'EfficientNetB4':efn.EfficientNetB4,\n#                          'EfficientNetB5':efn.EfficientNetB5,\n#                          'EfficientNetB6':efn.EfficientNetB6,\n#                          'EfficientNetB7':efn.EfficientNetB7}","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.431377Z","iopub.execute_input":"2022-02-13T05:42:29.431968Z","iopub.status.idle":"2022-02-13T05:42:29.439673Z","shell.execute_reply.started":"2022-02-13T05:42:29.431926Z","shell.execute_reply":"2022-02-13T05:42:29.438494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"### In the future see if we can load the efficient net v2 as it looks like it does better on the \n# sets it was trained on over v1 (plus I mean 2 > 1 thus must be better)\n\n### These are for Efficient net version 1\n# python package makes it a lot easier to use\n# ill integrate the capability to test these models later\n# EFF_NET_WEIGHTS_OPTIONS = ['noisy-student', 'imagenet']\n# EFF_NET_MODEL_OPTIONS = ['EfficientNetB0', \n#                          'EfficientNetB1', \n#                          'EfficientNetB2', \n#                          'EfficientNetB3', \n#                          'EfficientNetB4',\n#                          'EfficientNetB5',\n#                          'EfficientNetB6',\n#                          'EfficientNetB7']\n\n### These are for Efficient net version 2\nEFF_NET_V2_TASK_OPTIONS = ['classification',\n                           'feature_vector']\nMODEL_TYPE = \"imagenet21k_b1\"\nMODEL_TASK = 'feature_vector'\nEFF_NET_PATH = \"https://tfhub.dev/google/imagenet/efficientnet_v2_{model_type}/{task}/2\".format(model_type=MODEL_TYPE, task=MODEL_TASK)\nprint(EFF_NET_PATH)\n\n\nclass Config():\n    \"\"\"This class basically just holds information for us\n    \"\"\"\n    ### Hyper parameters\n    SEED = 123\n    BATCH_SIZE = 64 * strategy.num_replicas_in_sync\n    EPOCHS = 20\n    \n    ### Data params\n    IMAGE_SIZE = 240\n    IMAGE_CHANNELS = 3\n    ROTATION_FACTOR = 0.2\n    ZOOM_FACTOR=0.2\n    FOLDS=5\n    \n    ### Efficient net params\n    EFF_NET_MODEL = f\"efficientnet_v2_{MODEL_TYPE}\"\n    EFF_NET_MODEL_TASK = MODEL_TASK\n    \n    \nconfig = Config()\nprint(config.EFF_NET_MODEL)\nprint(f\"Batch size {config.BATCH_SIZE}\")","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.443071Z","iopub.execute_input":"2022-02-13T05:42:29.443490Z","iopub.status.idle":"2022-02-13T05:42:29.452938Z","shell.execute_reply.started":"2022-02-13T05:42:29.443454Z","shell.execute_reply":"2022-02-13T05:42:29.452195Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"OUTPUT_FOLDER = '/kaggle/working'","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.454307Z","iopub.execute_input":"2022-02-13T05:42:29.455056Z","iopub.status.idle":"2022-02-13T05:42:29.462321Z","shell.execute_reply.started":"2022-02-13T05:42:29.455005Z","shell.execute_reply":"2022-02-13T05:42:29.461563Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def seed_everything(seed):\n    \"\"\"We want to seed the env so that results are reproducible and more easily comparable.\n    \"\"\"\n    random.seed(seed)\n    np.random.seed(seed)\n    os.environ['PYTHONHASHSEED'] = str(seed)\n    tf.random.set_seed(seed)\n    \nseed_everything(config.SEED) ","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.463771Z","iopub.execute_input":"2022-02-13T05:42:29.464317Z","iopub.status.idle":"2022-02-13T05:42:29.474760Z","shell.execute_reply.started":"2022-02-13T05:42:29.464282Z","shell.execute_reply":"2022-02-13T05:42:29.474010Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Model Setup\n* here we want to define the model helper functions\n* we want to create the model itself","metadata":{}},{"cell_type":"code","source":"### COPIED FROM THIS NOTEBOOK\n# https://www.kaggle.com/manojprabhaakr/effnet-b6-whale-comp\n\n\"\"\"\nI'm not well versed in why this is helpful, but aparently actually ramping up the learning rate at the start of training can perform very well.\n\"\"\"\n\ndef get_lr_callback(plot=False):\n    lr_start   = 0.000001\n    lr_max     = 0.000005 * config.BATCH_SIZE  \n    lr_min     = 0.000001\n    lr_ramp_ep = 4\n    lr_sus_ep  = 0\n    lr_decay   = 0.9\n   \n    def lrfn(epoch):\n        if epoch < lr_ramp_ep:\n            lr = (lr_max - lr_start) / lr_ramp_ep * epoch + lr_start\n        elif epoch < lr_ramp_ep + lr_sus_ep:\n            lr = lr_max\n        else:\n            lr = (lr_max - lr_min) * lr_decay**(epoch - lr_ramp_ep - lr_sus_ep) + lr_min\n        return lr\n    if(plot):\n        epochs = list(range(config.EPOCHS))\n        learning_rates = [lrfn(x) for x in epochs]\n        plt.scatter(epochs,learning_rates)\n        ax = plt.gca()\n        ax.get_yaxis().get_major_formatter().set_scientific(False)\n        plt.show()\n\n    lr_callback = tf.keras.callbacks.LearningRateScheduler(lrfn, verbose=False)\n    return lr_callback\n\nget_lr_callback(plot=True)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.476499Z","iopub.execute_input":"2022-02-13T05:42:29.477018Z","iopub.status.idle":"2022-02-13T05:42:29.665541Z","shell.execute_reply.started":"2022-02-13T05:42:29.476982Z","shell.execute_reply":"2022-02-13T05:42:29.664890Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"\"\"\"\nAgain I learned this on kaggle but apparently freezing the batch normalization layers from a pretrained model\ncan help prevent our model from overfitting. I learned this from other people in the competition\n\nTLDR from stack overflow\nthe new data we train on will likely have very different batch normalization parameters (on average the images are different from the ones the model was trained on)\nbatch norm is calculated over the train set after training is complete (during training it can be the mean and std of that batch or a rolling average).\nkeeping this frozen means that the ranges and values from the data will stay the same, requiring the model to keep the general pretrained learnings, and \nnot require it to relearn a ton of new information (in our case the parameter to data ratio is high so it could just memorize the training set)\nFrozen batch norm keeps the gradients smaller and less drastic so we can fine tune more without worrying about overfitting and losing the generalization capabilities\nwe wanted this pretrained model for to begin with\n(please correct me in comments if my understanding is bad, I'll update this section)\n\n\nhttps://www.kaggle.com/manojprabhaakr/effnet-b6-whale-comp\nhttps://stackoverflow.com/questions/63016740/why-its-necessary-to-frozen-all-inner-state-of-a-batch-normalization-layer-when#:~:text=This%20can%20help%20to%20minimize,in%20batch%20statistics%2C%20decreasing%20regularization.\nhttps://towardsdatascience.com/batch-norm-explained-visually-why-does-it-work-90b98bcc58a0\nhttps://towardsdatascience.com/batch-norm-explained-visually-how-it-works-and-why-neural-networks-need-it-b18919692739\n\"\"\"\n\ndef freeze_BN(model):\n    # Unfreeze layers while leaving BatchNorm layers frozen\n    for layer in model.layers:\n        if not isinstance(layer, tf.keras.layers.BatchNormalization):\n            layer.trainable = True\n        else:\n            layer.trainable = False","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.666976Z","iopub.execute_input":"2022-02-13T05:42:29.667249Z","iopub.status.idle":"2022-02-13T05:42:29.674110Z","shell.execute_reply.started":"2022-02-13T05:42:29.667214Z","shell.execute_reply":"2022-02-13T05:42:29.672667Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_resizing_and_rescaling():\n    with strategy.scope():\n        resize_and_rescale = tf.keras.Sequential([\n            tf.keras.layers.Resizing(config.IMAGE_SIZE, config.IMAGE_SIZE),\n            tf.keras.layers.Rescaling(scale=1./127.5, offset=-1)\n        ])\n    return resize_and_rescale\n\nresizer = get_resizing_and_rescaling()\n\ndef get_augmentation():\n    with strategy.scope():\n        augmentation = tf.keras.Sequential([\n            tf.keras.layers.RandomFlip(mode=\"horizontal\", seed=config.SEED),\n            tf.keras.layers.RandomZoom(config.ZOOM_FACTOR),\n            tf.keras.layers.RandomRotation(config.ROTATION_FACTOR)  \n        ])\n    return augmentation\n\naugmenter = get_augmentation()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.675223Z","iopub.execute_input":"2022-02-13T05:42:29.675978Z","iopub.status.idle":"2022-02-13T05:42:29.700536Z","shell.execute_reply.started":"2022-02-13T05:42:29.675929Z","shell.execute_reply":"2022-02-13T05:42:29.699870Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"def get_model():\n    \"\"\"This function returns the classification model. This inputs the images of whatever shape\n        we pick shape based on the expected image size of the different efficient net models\n        it then sends the images through efficient net and then uses a single node dense layer \n        with the sigmoid activation to binary classify an image as a whale or a dolphin\n    \"\"\"\n    # use the strategy scope becuase might be in tpu \n    # we want to make the model inside the scope of our strategy\n    # efficient net reads in an image of any size and spits out a 1000 dimension feature vector\n    with strategy.scope():\n        # input shape is (batch_size, image_size, image_size, image_channels)\n        inputs = tf.keras.layers.Input(shape=(None, None, config.IMAGE_CHANNELS), name=\"images\")\n        \n        x = resizer(inputs)\n        x = augmenter(x)\n        \n        # input shape is (batch_size, any, any, 3)\n        x = tfhub.KerasLayer(EFF_NET_PATH, trainable=True)(x)\n        \n        # takes the feature vector and condences it down for binary classification\n        # kernel regularization provides loss based on the layers weights\n        # this promotes the model to maintain smaller weights\n        # this is important to stop overfitting as with this number of parameters to data the \n        # network could just memorize the data easily, this means the weights will be more evenly distributed\n        # and more likely to generalize\n        # l2 regilarization adds weight * sum of the squared coefficients\n        outputs = tf.keras.layers.Dense(1, activation='sigmoid', kernel_regularizer=tf.keras.regularizers.l2(0.0001))(x)\n   \n        # make the model\n        model = tf.keras.models.Model(inputs=inputs, outputs=outputs)\n        \n        optimizer = tf.keras.optimizers.Adam()\n        loss = tf.keras.losses.BinaryCrossentropy(from_logits=False)\n        metrics = tf.keras.metrics.BinaryAccuracy()\n        model.compile(loss=loss, optimizer=optimizer, metrics=[metrics])\n    return model\n\nmodel = get_model()\nmodel.summary()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.701677Z","iopub.execute_input":"2022-02-13T05:42:29.701927Z","iopub.status.idle":"2022-02-13T05:42:29.947945Z","shell.execute_reply.started":"2022-02-13T05:42:29.701891Z","shell.execute_reply":"2022-02-13T05:42:29.947232Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"del model\ngc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:29.949258Z","iopub.execute_input":"2022-02-13T05:42:29.949500Z","iopub.status.idle":"2022-02-13T05:42:30.178256Z","shell.execute_reply.started":"2022-02-13T05:42:29.949464Z","shell.execute_reply":"2022-02-13T05:42:30.177422Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Data\n* here we want to load the data","metadata":{}},{"cell_type":"code","source":"# load the main data file\ntrain_df = pd.read_csv(os.path.join(MAIN_DATASET, 'train.csv'))\ntrain_df.species.replace(\"bottlenose_dolpin\", \"bottlenose_dolphin\", inplace=True)\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:30.180221Z","iopub.execute_input":"2022-02-13T05:42:30.180547Z","iopub.status.idle":"2022-02-13T05:42:30.244812Z","shell.execute_reply.started":"2022-02-13T05:42:30.180509Z","shell.execute_reply":"2022-02-13T05:42:30.244088Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# determine the different species in the dataframe\nDOLPHIN_REPRESENTATION = 0\nWHALE_REPRESENTATION = 1\nCLASSES = ['dolphin', 'whale']\n\nspecies = train_df.species.unique()\nprint(\"Species not with dolphin or whale in name: \", [s for s in species if not np.any([x in s for x in ['whale', 'dolphin']])])\n\ndef get_sup_species(species):\n    \"\"\"This function uses the species name to extract the sup species, either whale or dolphin for classification.\n    \"\"\"\n    if('whale' in species):\n        return CLASSES[WHALE_REPRESENTATION]\n    elif('dolphin' in species):\n        return CLASSES[DOLPHIN_REPRESENTATION]\n    elif('beluga' == species):\n        return CLASSES[WHALE_REPRESENTATION]\n    elif('globis' == species):\n        return CLASSES[WHALE_REPRESENTATION]\n    else:\n        raise Exception(f'Unk species: {species} was found in dataframe')\n        \ntrain_df['target'] = train_df.species.apply(get_sup_species)\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:30.248288Z","iopub.execute_input":"2022-02-13T05:42:30.248504Z","iopub.status.idle":"2022-02-13T05:42:30.285336Z","shell.execute_reply.started":"2022-02-13T05:42:30.248478Z","shell.execute_reply":"2022-02-13T05:42:30.284395Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"## We can see we might have a class imbalance issue between the \n# dolphins and the whales, could sample dolphins more\n# could also add class weights.\nplot = sns.catplot(data=train_df, x=\"target\", kind=\"count\", order=CLASSES);\n# plot.set_xticklabels([\"Dolphin\", \"Whale\"]);","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:30.286933Z","iopub.execute_input":"2022-02-13T05:42:30.287232Z","iopub.status.idle":"2022-02-13T05:42:30.749174Z","shell.execute_reply.started":"2022-02-13T05:42:30.287194Z","shell.execute_reply":"2022-02-13T05:42:30.748477Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#     image_count = len(train_df.image.unique())\n#     test_size = int(0.15 * image_count)\n#     val_size = int(0.15 * image_count)\n#     test_spliter = StratifiedShuffleSplit(n_splits=1, test_size=test_size, random_state=config.SEED)\n#     for train_idx, test_idx in test_spliter.split(train_df.image.values, train_df.target.values):\n#         test_images = train_df.iloc[test_idx, :].image\n#         for im in test_images:\n#             image_to_fold[im] = TEST_FOLD\n            \n#     fold_spliter = StratifiedShuffleSplit(n_splits=5, test_size=val_size, random_state=config.SEED)\n\nTEST_FOLD = 0\ndef generate_splits(train_df):\n    image_to_fold = {}\n    \n    fold_spliter = StratifiedKFold(n_splits=config.FOLDS+1, shuffle=True, random_state=config.SEED)\n    for fold, (train_idx, val_idx) in enumerate(fold_spliter.split(train_df.image.values, train_df.target.values)):\n        val_images = train_df.iloc[val_idx, :].image\n        for im in val_images:\n            image_to_fold[im] = fold\n               \n    return image_to_fold\n\ndef return_folds(fold_df_path=None):\n    if(fold_df_path is None):\n        image_to_fold = generate_splits(train_df)\n    else:\n        image_to_fold = None\n    return image_to_fold\n    \n        ","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:30.753425Z","iopub.execute_input":"2022-02-13T05:42:30.755394Z","iopub.status.idle":"2022-02-13T05:42:30.765994Z","shell.execute_reply.started":"2022-02-13T05:42:30.755340Z","shell.execute_reply":"2022-02-13T05:42:30.765087Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load the split data or generate our own\nimage_to_fold = return_folds()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:30.771127Z","iopub.execute_input":"2022-02-13T05:42:30.773932Z","iopub.status.idle":"2022-02-13T05:42:30.918602Z","shell.execute_reply.started":"2022-02-13T05:42:30.773830Z","shell.execute_reply":"2022-02-13T05:42:30.917830Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_df[\"fold\"] = train_df.image.map(image_to_fold)\ntrain_df.fold.fillna(-1, inplace=True)\ntrain_df.fold = train_df.fold.astype(np.int16)\ntrain_df.head()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:30.923078Z","iopub.execute_input":"2022-02-13T05:42:30.925008Z","iopub.status.idle":"2022-02-13T05:42:30.992454Z","shell.execute_reply.started":"2022-02-13T05:42:30.924968Z","shell.execute_reply":"2022-02-13T05:42:30.991494Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"FOLD_DF_PATH = os.path.join(OUTPUT_FOLDER, 'folds.csv')\ntrain_df.to_csv(FOLD_DF_PATH, index=False)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:30.996526Z","iopub.execute_input":"2022-02-13T05:42:30.999248Z","iopub.status.idle":"2022-02-13T05:42:31.325663Z","shell.execute_reply.started":"2022-02-13T05:42:30.999168Z","shell.execute_reply":"2022-02-13T05:42:31.324772Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# make sure the fold data distributions look good\nfor fold in np.sort(train_df.fold.unique()):\n    print(f'Fold {fold}')\n    plot = sns.catplot(data=train_df.loc[train_df.fold == fold], x=\"target\", kind=\"count\", order=CLASSES);\n#     plot.set_xticklabels([\"Dolphin\", \"Whale\"]);\n    plt.show()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:31.327307Z","iopub.execute_input":"2022-02-13T05:42:31.327901Z","iopub.status.idle":"2022-02-13T05:42:32.881016Z","shell.execute_reply.started":"2022-02-13T05:42:31.327854Z","shell.execute_reply":"2022-02-13T05:42:32.879914Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"test_df = train_df.loc[train_df.fold == TEST_FOLD]\ntrain_df = train_df.loc[train_df.fold != TEST_FOLD]","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:32.882669Z","iopub.execute_input":"2022-02-13T05:42:32.883140Z","iopub.status.idle":"2022-02-13T05:42:32.895681Z","shell.execute_reply.started":"2022-02-13T05:42:32.883097Z","shell.execute_reply":"2022-02-13T05:42:32.894565Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# setup the image pipeline function\ntrain_datagen = tf.keras.preprocessing.image.ImageDataGenerator()#preprocessing_function=resizer)\nval_datagen = tf.keras.preprocessing.image.ImageDataGenerator()#preprocessing_function=resizer)\ntest_datagen = tf.keras.preprocessing.image.ImageDataGenerator()#preprocessing_function=resizer)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:42:32.897870Z","iopub.execute_input":"2022-02-13T05:42:32.898505Z","iopub.status.idle":"2022-02-13T05:42:32.904711Z","shell.execute_reply.started":"2022-02-13T05:42:32.898441Z","shell.execute_reply":"2022-02-13T05:42:32.903890Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Train the model","metadata":{}},{"cell_type":"code","source":"def get_generator(generator, wells):\n    gen =  generator.flow_from_dataframe(wells, \n                                         directory=os.path.join(CROPPED_DATASET,'train_images', 'train_images'),\n                                         x_col='image',\n                                         y_col='target',\n                                         color_mode='rgb',\n                                         subset='training',\n                                         batch_size=config.BATCH_SIZE,\n                                         shuffle=True,\n                                         classes=CLASSES,\n                                         class_mode='binary',\n                                         seed=config.SEED)\n    return gen\n\ndef train_model(fold):\n    assert(TEST_FOLD < fold and fold in train_df.fold.unique())\n    fold_train_data = train_df.loc[train_df.fold!=fold]\n    fold_val_data = train_df.loc[train_df.fold==fold]\n    print(fold_train_data.shape[0], fold_val_data.shape[0], test_df.shape[0])\n    stopper = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=3, mode='min', restore_best_weights=True)\n    model = get_model()\n    freeze_BN(model)\n    train_ds = get_generator(train_datagen, fold_train_data)\n    val_ds = get_generator(val_datagen, fold_val_data)\n    hist = model.fit(train_ds,\n                     validation_data=val_ds,\n                     epochs=config.EPOCHS,\n                     callbacks=[stopper])\n    return model, hist\n\ndef test_model(model, test_ds):\n    ev = model.evaluate(test_ds)\n    return ev\n\ndef graph_training(hist):\n    plt.figure(figsize=(8,8), tight_layout=True)\n    plt.subplot(211)\n    plt.plot(hist.history['loss'])\n    plt.plot(hist.history['val_loss'])\n    plt.title('MSE Loss')\n    plt.ylabel('Error')\n    plt.xlabel('Epoch')\n    plt.legend(['train', 'val'], loc='upper right')\n\n    plt.subplot(212)\n    plt.plot(hist.history['binary_accuracy'])\n    plt.plot(hist.history['val_binary_accuracy'])\n    plt.title('Binary Accuracy Metric')\n    plt.ylabel('Error')\n    plt.xlabel('Epoch')\n    plt.legend(['train', 'val'], loc='upper left')\n    plt.show()\n\ndef conf_matrix(y_true, preds):\n    cf = confusion_matrix(y_true, preds)\n    plt.figure(figsize=(12,12))\n    sns.heatmap(cf, annot=True, xticklabels=class_names, yticklabels=class_names, cmap='Blues', robust=True)\n    plt.show()\n\ndef train_loop(fold):\n    print(f\"FOLD {fold}\")\n    print(\"===================================================\")\n    model, hist = train_model(fold)\n    graph_training(hist)\n    test_ds = get_generator(test_datagen, test_df)\n    ev = test_model(model, test_ds)\n    print(\"Model EV\", ev)\n    model.save(os.path.join(OUTPUT_FOLDER, f\"Model_{fold}\"))\n    del model\n    del test_ds\n    del ev\n    plt.clf()\n    gc.collect()","metadata":{"execution":{"iopub.status.busy":"2022-02-13T06:06:08.640991Z","iopub.execute_input":"2022-02-13T06:06:08.641313Z","iopub.status.idle":"2022-02-13T06:06:08.662816Z","shell.execute_reply.started":"2022-02-13T06:06:08.641277Z","shell.execute_reply":"2022-02-13T06:06:08.659730Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loop(fold=1)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T06:06:09.206303Z","iopub.execute_input":"2022-02-13T06:06:09.206819Z","iopub.status.idle":"2022-02-13T06:06:29.688812Z","shell.execute_reply.started":"2022-02-13T06:06:09.206777Z","shell.execute_reply":"2022-02-13T06:06:29.688045Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loop(fold=2)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T06:06:29.690575Z","iopub.execute_input":"2022-02-13T06:06:29.691019Z","iopub.status.idle":"2022-02-13T06:07:03.454651Z","shell.execute_reply.started":"2022-02-13T06:06:29.690978Z","shell.execute_reply":"2022-02-13T06:07:03.453916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loop(fold=3)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T06:04:01.092681Z","iopub.status.idle":"2022-02-13T06:04:01.093155Z","shell.execute_reply.started":"2022-02-13T06:04:01.092891Z","shell.execute_reply":"2022-02-13T06:04:01.092916Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loop(fold=4)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:54:20.376452Z","iopub.status.idle":"2022-02-13T05:54:20.377414Z","shell.execute_reply.started":"2022-02-13T05:54:20.377148Z","shell.execute_reply":"2022-02-13T05:54:20.377174Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"train_loop(fold=5)","metadata":{"execution":{"iopub.status.busy":"2022-02-13T05:54:20.378477Z","iopub.status.idle":"2022-02-13T05:54:20.379415Z","shell.execute_reply.started":"2022-02-13T05:54:20.379139Z","shell.execute_reply":"2022-02-13T05:54:20.379164Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{},"execution_count":null,"outputs":[]}]}