{"cells":[{"metadata":{},"cell_type":"markdown","source":"### Version Updates:\n 1. Xception network | lr=0.001, acc=~0.62\n 2. Same network with expon_scheduler_lr | acc=~0.61 (stayed at around 0.61 during whole training)\n 3. Adding a BatchNorm layer after between input and Xception layer | acc=0.83, val_acc=0.78 | lr=0.001 | some overfitting | 35 Epochs\n 4. Adding exponential_scheduler to batchnormNet | acc=~0.61\n 5. same network with lowest_loss_lr=0.000016 | acc=~0.76 | 35 epochs\n 6. Adding Dropout=0.2 after Xception layer | 50 epochs | acc=0.69 val_acc=0.684 \n 7. Adding second BatchNorm after Xception layer | 35 epochs | acc=0.86 val_acc=0.77 | Dropout=0.005 | severe overfitting\n 8. Same architecture | lr=0.001 | 50 epochs | Dropout=0.2 | acc=0.8297 val_acc=0.7847\n 9. Same architecture | lr=0.000123 | 50 epochs | Dropout=0.2 | acc=0.8036 val_acc=0.78\n 10. Same architecture | 50 epochs | lr=0.123,decay=1e-4 | acc=0.60 | doesnt improve\n 11. Same architecture | 50 epochs | LRcosinedecay=0.1, 5 steps | acc=0.65 | doesnt improve\n 12. Same architecture | 10 epochs | lr=0.1 | acc=0.61 | doesnt improve\n 13. Same archtiecture | 10 epochs | lr=2,decay=1e-4 | acc=0.60 | doesnt improve\n 14. Same architecture | lr=0.001 | 50 epochs | Dropout=0.1 | acc=0.838567 val_acc=0.7847\n 15. Same architecture | lr=0.001, with warmup-exponentailscheduling | 50 epochs | Dropout=0.2 | acc=0.838567 val_acc=0.7847\n 16. Same architecture | lr=0.001,reduce_lr_on_plateau | added random_crop | 50 epochs | Dropout=0.2 | acc= 0.8244 val_acc=0.77\n 17. Same architecture | changed image augmentation function to not make random choice but to do all augentations | acc=0.824444 val_acc=0.776336\n 18. Same architecture | Dropout=0.5 | lr=0.001 | 30 epochs | acc=0.8015 val_acc=0.7860"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install tensorflow==2.3","execution_count":null,"outputs":[]},{"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)\nimport matplotlib.pyplot as plt\nfrom tensorflow import keras\nimport tensorflow as tf\nimport glob\nfrom kaggle_datasets import KaggleDatasets\nfrom tensorflow.keras.applications import EfficientNetB3\nprint(tf.__version__)\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":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true},"cell_type":"code","source":"train_images_dir = \"/kaggle/input/cassava-leaf-disease-classification/train_images/*.jpg\"\ntrain_labels = \"/kaggle/input/cassava-leaf-disease-classification/train.csv\"\ntest_images_dir = \"/kaggle/input/cassava-leaf-disease-classification/test_images/*.jpg\"\nGCS_PATH = \"/kaggle/input/cassava-leaf-disease-classification\"\n#GCS_PATH = KaggleDatasets().get_gcs_path()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"GCS_PATH","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"byte_img = plt.imread(\"/kaggle/input/cassava-leaf-disease-classification/train_images/329174765.jpg\")\nplt.imshow(byte_img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"byte_img.shape","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_labels_df = pd.read_csv(train_labels)\ntrain_labels_df[\"label\"].hist()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Lets get our data in the correct form by reading it in chunks using tf.data bc this dataset is too big to fit in memory\n### Things to consider since our dataset doesn't fit in memory:\n* using sklearns partial_fit()\n* maybe loading data into hdf5 files\n* loading the data into different chunks\n* use prefetch and tf.data and decode_jpeg"},{"metadata":{"trusted":true},"cell_type":"code","source":"GCS_PATH = KaggleDatasets().get_gcs_path()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from sklearn.model_selection import train_test_split\nTRAINING_FILENAMES, VALID_FILENAMES = train_test_split(\n    tf.io.gfile.glob(GCS_PATH + '/train_tfrecords/ld_train*.tfrec'),\n    test_size=0.35, random_state=5\n)\n\nTEST_FILENAMES = tf.io.gfile.glob(GCS_PATH + '/test_tfrecords/ld_test*.tfrec')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TRAINING_FILENAMES, TEST_FILENAMES","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Initiating the tpu"},{"metadata":{"trusted":true},"cell_type":"code","source":"!pip install cloud-tpu-client\n\nimport tensorflow as tf\nfrom cloud_tpu_client import Client\nprint(tf.__version__)\n\nClient().configure_tpu_version(tf.__version__, restart_type='ifNeeded')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# detect and init the TPU\ntry:\n    tpu = tf.distribute.cluster_resolver.TPUClusterResolver()\n    print(f\"running on: {tpu.master()}\")\nexcept ValueError:\n    tpu = None\n    print(\"Couldnt connect to tpu\")\n    \nif tpu:    \n    tf.config.experimental_connect_to_cluster(tpu)\n    tf.tpu.experimental.initialize_tpu_system(tpu)\n    strategy = tf.distribute.experimental.TPUStrategy(tpu)\nelse:\n    strategy = tf.distribute.get_strategy()\n    \nprint(f\"activated cores: {strategy.num_replicas_in_sync}\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"AUTOTUNE = tf.data.experimental.AUTOTUNE\nIMAGE_SIZE = (512,512)\n\nBATCH_SIZE = 16 * strategy.num_replicas_in_sync\nBATCH_SIZE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def decode_image(image):\n    image = tf.image.decode_jpeg(image, channels=3)\n    image = tf.cast(image, tf.float32) / 255.0\n    image = tf.reshape(image, [*IMAGE_SIZE, 3])\n    return image\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def read_tfrecord(example, labeled):\n    tfrecord_format = {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"target\": tf.io.FixedLenFeature([], tf.int64)\n    } if labeled else {\n        \"image\": tf.io.FixedLenFeature([], tf.string),\n        \"image_name\": tf.io.FixedLenFeature([], tf.string)\n    }\n    example = tf.io.parse_single_example(example, tfrecord_format)\n    image = decode_image(example['image'])\n    if labeled:\n        label = tf.cast(example['target'], tf.int32)\n        return image, label\n    idnum = example['image_name']\n    return image, idnum","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from functools import partial\n\ndef load_dataset(filenames, labeled=True, ordered=False):\n    ignore_order = tf.data.Options()\n    if not ordered:\n        ignore_order.experimental_deterministic = False # disable order, increase speed\n    dataset = tf.data.TFRecordDataset(filenames, num_parallel_reads=AUTOTUNE) # automatically interleaves reads from multiple files\n    dataset = dataset.with_options(ignore_order) # uses data as soon as it streams in, rather than in its original order\n    dataset = dataset.map(partial(read_tfrecord, labeled=labeled), num_parallel_calls=AUTOTUNE)\n    return dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import random\n\ndef data_augmentation(image, label):\n    image = tf.image.random_flip_left_right(image)\n    image = tf.image.adjust_brightness(image,delta=0.1)\n    image = tf.image.random_crop(image,size=[512,512,3])\n    \n    return image, label","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def get_dataset(filenames):\n    dataset = load_dataset(filenames, labeled=True)  \n    dataset = dataset.map(data_augmentation, num_parallel_calls=AUTOTUNE)\n    dataset = dataset.repeat()\n    dataset = dataset.shuffle(2048)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset\n\ndef get_test_dataset(ordered=False):\n    dataset = load_dataset(TEST_FILENAMES, labeled=False, ordered=ordered)\n    dataset = dataset.batch(BATCH_SIZE)\n    dataset = dataset.prefetch(AUTOTUNE)\n    return dataset","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"TEST_FILENAMES","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data = get_dataset(TRAINING_FILENAMES)\nvalidation_data = get_dataset(VALID_FILENAMES)\ntest_data = get_test_dataset(ordered=True)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for ((val_image,val_label),(train_image,label)) in zip(validation_data.take(1),train_data.take(1)):\n    print(val_label,label)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Consider using a pretrained cnn like Xception or SesNet"},{"metadata":{"trusted":true},"cell_type":"code","source":"classes = pd.read_csv(train_labels)[\"label\"].unique()\nclasses","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"import re\n\ndef count_data_items(filenames):\n    n = [int(re.compile(r\"-([0-9]*)\\.\").search(filename).group(1)) for filename in filenames]\n    return np.sum(n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_size = count_data_items(TRAINING_FILENAMES)\nvalidation_size = count_data_items(VALID_FILENAMES)\ntest_size = count_data_items(TEST_FILENAMES)\n\nprint(f\"train_size: {train_size}\\nvalidation_size: {validation_size}\\ntest_size: {test_size}\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### We have to build our model inside of the strategy scope in order to take advantage of our tpu"},{"metadata":{"trusted":true},"cell_type":"code","source":"early_stopping = keras.callbacks.EarlyStopping(patience=8)\nbest_model_checkpoint = keras.callbacks.ModelCheckpoint(\"best_xception_model.h5\",\n                                                         monitor= 'val_loss', \n                                                         verbose=1, \n                                                         save_best_only=True, \n                                                         mode= 'min', \n                                                         save_weights_only = False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"from keras.callbacks import LearningRateScheduler\n\n\ndef exponential_decay(lr0, s):\n    def exponential_decay_fn(epoch):\n        return lr0 * 0.1**(epoch / s)\n    return exponential_decay_fn\n\n\nlr_scheduler = LearningRateScheduler(exponential_decay(lr0=3,s=5))","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"#### Lets find the best *learning* rate"},{"metadata":{"trusted":true},"cell_type":"code","source":"K = keras.backend\n\nclass LearningRateIncreaser(keras.callbacks.Callback):\n    def __init__(self,factor):\n        self.factor = factor\n        self.losses = []\n        self.lrs = []\n    def on_batch_end(self,batch,logs):\n        self.lrs.append(K.get_value(self.model.optimizer.lr))\n        self.losses.append(logs[\"loss\"])\n        K.set_value(self.model.optimizer.lr, self.model.optimizer.lr * self.factor) #Here we change the lr value after every batch\nkeras.backend.clear_session()\n\nincreasing_lr = LearningRateIncreaser(factor=1.05)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_steps = train_size // BATCH_SIZE\nvalidation_steps = validation_size // BATCH_SIZE","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():\n    input_adjust_layer = tf.keras.layers.Lambda(\n    tf.keras.applications.xception.preprocess_input,input_shape=[*IMAGE_SIZE,3])\n\n    Xception = keras.applications.Xception(\n        weights=\"imagenet\",include_top=False)\n    Xception.trainable = False\n\n\n    model = keras.Sequential([\n        input_adjust_layer,\n        keras.layers.BatchNormalization(),\n        Xception,\n        keras.layers.BatchNormalization(),\n        keras.layers.GlobalAveragePooling2D(),\n        keras.layers.Dropout(0.5),\n        keras.layers.Dense(8,activation=\"relu\"),\n        keras.layers.Dense(len(classes),\"softmax\")\n    ])\n    \n    model.compile(loss=\"sparse_categorical_crossentropy\",\n      optimizer=keras.optimizers.Adam(lr=0.001),\n         metrics=[\"sparse_categorical_accuracy\"])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.fit(train_data,\n         epochs=1,validation_data=validation_data,\n          steps_per_epoch=train_steps,validation_steps=validation_steps,\n         callbacks=[increasing_lr])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.plot(increasing_lr.lrs,increasing_lr.losses)\nplt.title(\"How different learning rates effect the loss\",fontsize=16)\nplt.gca().set_xscale('log')\nplt.xlabel(\"Learning Rate\")\nplt.ylabel(\"Loss\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"pd.DataFrame({\"Learning Rates\":increasing_lr.lrs, \"loss\":increasing_lr.losses}).sort_values(by=\"loss\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Lets train the real model now:"},{"metadata":{},"cell_type":"markdown","source":"Lets declare some callbacks. I tried to implement an exponential scheduling with warmup but unfortunately that didn't do much"},{"metadata":{"trusted":true},"cell_type":"code","source":"K = keras.backend\n\nclass WarmupScheduler(keras.callbacks.Callback):\n    def __init__(self,start_lr=0.001,max_lr=1,min_lr=0.0001,factor=0.005):\n        self.max_lr = max_lr\n        self.factor = factor\n        self.losses = []\n        self.lrs = []\n        self.smaller = False\n        self.epoch = 1\n    \n    def on_epoch_end(self,epoch,logs):\n        self.epoch += 1\n        \n    def on_batch_end(self,batch,logs):\n        lr = K.get_value(self.model.optimizer.lr)\n        if lr < self.max_lr and self.smaller == False:\n            K.set_value(self.model.optimizer.lr, lr + self.factor)\n        else:\n            self.smaller = True\n            K.set_value(self.model.optimizer.lr, lr * 0.1**(self.epoch/20000))\n        self.losses.append(logs[\"loss\"])\n        self.lrs.append(lr)\n        \n        \nkeras.backend.clear_session()\n        \nreduce_lr_on_plateau = keras.callbacks.ReduceLROnPlateau(monitor=\"val_loss\",\n                                                        patience=3,\n                                                        factor=0.001,\n                                                        min_lr=0.000001)\nwarmup = WarmupScheduler()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"with strategy.scope():\n    input_adjust_layer = tf.keras.layers.Lambda(\n    tf.keras.applications.xception.preprocess_input,input_shape=[*IMAGE_SIZE,3])\n\n    Xception = keras.applications.Xception(\n        weights=\"imagenet\",include_top=False)\n    Xception.trainable = False\n\n\n    model = keras.Sequential([\n        input_adjust_layer,\n        keras.layers.BatchNormalization(),\n        Xception,\n        keras.layers.BatchNormalization(),\n        keras.layers.GlobalAveragePooling2D(),\n        keras.layers.Dropout(0.2),\n        keras.layers.Dense(8,activation=\"relu\"),\n        keras.layers.Dense(len(classes),\"softmax\")\n    ])\n    \n    \n    model.compile(loss=\"sparse_categorical_crossentropy\",\n              optimizer=keras.optimizers.Adam(lr=0.001),\n                 metrics=[\"sparse_categorical_accuracy\"])\n    \nmodel.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(train_data,\n            epochs=50,\n            steps_per_epoch=train_steps,\n            validation_data=validation_data, \n            validation_steps=validation_steps,\n            callbacks=[reduce_lr_on_plateau,early_stopping,best_model_checkpoint])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_df = pd.DataFrame(history.history)\nmodel_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_df[\"lr\"].plot()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_df[[\"loss\",\"val_loss\"]].plot()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model_df[[\"sparse_categorical_accuracy\",\"val_sparse_categorical_accuracy\"]].plot()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Lets try out an EfficientNet"},{"metadata":{"trusted":true},"cell_type":"code","source":"#for i in train_data.take(1):\n#    print(i[0].shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#cosine_decay = keras.experimental.CosineDecay(initial_learning_rate=1e-4, decay_steps=train_size*5, alpha=0.3)\n\n'''\ndata_augmentation_layer = keras.Sequential([\n    keras.layers.experimental.preprocessing.RandomCrop(height=512, width=512),\n        keras.layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n        keras.layers.experimental.preprocessing.RandomRotation(0.25),\n        keras.layers.experimental.preprocessing.RandomZoom((-0.2, 0)),\n        keras.layers.experimental.preprocessing.RandomContrast((0.2,0.2))   \n    \n])'''","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''\nwith strategy.scope():\n    EffNet = EfficientNetB3(include_top=False,weights=\"imagenet\",input_shape=[512,512,3])\n    EffNet.trainable = False\n\n    effmodel = keras.Sequential([\n        keras.layers.Input(shape=[512,512,3]),\n        EffNet,\n        keras.layers.GlobalAveragePooling2D(),\n        keras.layers.Dropout(0.2),\n        #keras.layers.Dense(8,\"relu\"),\n        keras.layers.Dense(len(classes),\"softmax\")\n    ])\n\n    effmodel.compile(loss=\"sparse_categorical_crossentropy\",\n              optimizer=keras.optimizers.Adam(cosine_decay),\n                 metrics=[\"sparse_categorical_accuracy\"])\n\n\neffmodel.summary()'''","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#effmodel.fit(train_data,epochs=1,callbacks=[increasing_lr],steps_per_epoch=train_steps)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#plt.plot(increasing_lr.lrs,increasing_lr.losses)\n#plt.title(\"How different learning rates effect the loss\",fontsize=16)\n#plt.gca().set_xscale('log')\n#plt.xlabel(\"Learning Rate\")\n#plt.ylabel(\"Loss\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#pd.DataFrame({\"Learning Rates\":increasing_lr.lrs, \"loss\":increasing_lr.losses}).sort_values(by=\"loss\")","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"'''\neff_history = effmodel.fit(train_data,\n                          epochs=6,\n                            steps_per_epoch=train_steps,\n                            validation_data=validation_data, \n                            validation_steps=validation_steps,\n                            callbacks=[early_stopping])'''","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"#pd.DataFrame(eff_history.history)[[\"val_sparse_categorical_accuracy\",\"sparse_categorical_accuracy\"]].plot()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Lets make our first predictions and submit them"},{"metadata":{"trusted":true},"cell_type":"code","source":"def convert_float(image,label):\n    return tf.cast(image,tf.float32),label\n\ntest_data = test_data.map(convert_float)\ntest_images_data = test_data.map(lambda image,label: image)\n\ntest_pred_prob = model.predict(test_images_data)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for image in test_images_data.take(100):\n    image = np.array(image).reshape(512,512,3)\n    plt.imshow(image)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_pred = np.argmax(test_pred_prob)\ntest_pred","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for image,label in test_data.take(1):\n    print(image,label)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"test_data_ids = [ids.numpy()[0].decode() for (image,ids) in test_data]\ntest_data_ids","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df = pd.DataFrame({\"id\":test_data_ids,\"label\":test_pred})\nsubmission_df","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"submission_df.to_csv(\"submission.csv\",index=False)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"!cat submission.csv","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"tf.__version__","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}