{"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":"# All the Required Import statements\nimport gc\nimport tensorflow\nfrom tensorflow.keras.layers import Dense, Flatten, Conv2D, MaxPool2D, Dropout, AveragePooling2D, BatchNormalization, add\nfrom tensorflow.keras import Input\nfrom tensorflow.keras.applications.resnet50 import ResNet50\nfrom tensorflow.keras.applications.xception import Xception\nfrom tensorflow.keras.regularizers import l2 as L2\nfrom tensorflow.keras.callbacks import EarlyStopping, ReduceLROnPlateau\nfrom tensorflow.keras.models import Model\nfrom PIL import Image\nimport pandas\nimport numpy\n\n# Collect Unrefereced Memory and Free them using gc.collect()\ngc.collect()\n\n# Hyperparameters\nurl = '/kaggle/input/aptos2019-blindness-detection/'\n\nshape = (224, 224)\nrotateAngle = 15\n\ntrain_data = pandas.read_csv(url + '/train.csv')\ntest_data = pandas.read_csv(url + '/test.csv')\n\ntrain_y = train_data['diagnosis'].astype('category', [0, 1, 2, 3, 4])\ntrain_y = pandas.get_dummies(train_y).values\n\n# print(train_y)\n\ntrain_y[:,3] = numpy.where(train_y[:,4] > train_y[:,3], train_y[:,4], train_y[:,3])\ntrain_y[:,2] = numpy.where(train_y[:,3] > train_y[:,2], train_y[:,3], train_y[:,2])\ntrain_y[:,1] = numpy.where(train_y[:,2] > train_y[:,1], train_y[:,2], train_y[:,1])\ntrain_y[:,0] = numpy.where(train_y[:,1] > train_y[:,0], train_y[:,1], train_y[:,0])\n\n# print(train_y[:5])\n# print(train_data['diagnosis'].head())\n\n\n# This Method Splits the data depending on which set you want to choose from (i.e, train or test)\n# and giving a start and end point will give you the data required only upto that amount\ndef split_data(name, start=0, end=-1, rotation=0):\n\n    if name == 'train':\n        data = train_data\n\n    elif name == 'test':\n        data = test_data\n\n    else:\n        print('wrong split name')\n        return None\n\n    if end > data.shape[0] or end == -1:\n        end = data.shape[0]\n\n    if rotation >= 360:\n        rotation %= 360\n\n    x = []\n\n    for i in range(start, end):\n        image = Image.open(url + '/' + name + '_images/' +\n                           data['id_code'][i] + '.png').convert('L')\n        image = image.resize(shape)\n        image = image.rotate(rotation)\n        x.append(numpy.asarray(image).reshape(shape + (1,)))\n    x = numpy.array(x)\n\n    if name == 'train':\n        return x, train_y[start:end]\n\n    elif name == 'test':\n        return x\n\ndef xception_model():\n    \n    model = Xception(include_top = False, weights = None, input_shape = shape + (1,), pooling = 'avg', classes = False)\n    x = model.output\n    \n    x = Dense(1024, activation = 'relu')(x)\n    x = Dense(256, activation = 'relu')(x)\n    outputs = Dense(5, activation = 'sigmoid')(x)\n    \n    model = Model(model.input, outputs)\n    model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])\n    \n    print(model.summary())\n    \n    return model\n\n# Make methods to create new model\n# This method creates a basic model\ndef custom_model():\n\n    inputs = Input(shape + (1,))\n\n    x = BatchNormalization()(inputs)\n\n    x = Conv2D(32, (5, 5), 2, data_format = 'channels_last',activation = 'relu')(x)\n    x = MaxPool2D(strides = 2)(x)\n    \n    x = Conv2D(32, (3, 3), activation = 'relu')(x)\n    y = Dropout(0.25)(x)\n\n    for _ in range(2):\n        x = Conv2D(32, (3,3), padding = 'same', activation = 'relu')(y)\n        x = Conv2D(32, (3,3), padding = 'same', activation = 'relu')(x)\n        x = Dropout(0.1)(x)\n\n        y = add([y, x])\n        \n    y = Conv2D(64, (3, 3), activation = 'relu')(x)\n\n    for _ in range(2):\n        x = Conv2D(64, (3,3), padding = 'same', activation = 'relu')(y)\n        x = Conv2D(64, (3,3), padding = 'same', activation = 'relu')(x)\n        x = Dropout(0.1)(x)\n\n        y = add([y, x])\n        \n    y = Conv2D(128, (3, 3), activation = 'relu')(x)\n\n    for _ in range(3):\n        x = Conv2D(128, (3,3), padding = 'same', activation = 'relu')(y)\n        x = Conv2D(128, (3,3), padding = 'same', activation = 'relu')(x)\n        x = Conv2D(128, (3,3), padding = 'same', activation = 'relu')(x)\n        x = Dropout(0.1)(x)\n\n        y = add([y, x])\n    \n    x = AveragePooling2D()(y)\n\n    x = Flatten()(x)\n    x = Dense(1000, activation = 'relu')(x)\n    x = Dense(500, activation = 'relu')(x)\n    x = Dense(20, activation = 'relu')(x)\n    outputs = Dense(5, activation = 'sigmoid')(x)\n\n    model = Model(inputs,outputs)\n    model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])\n\n    print(model.summary())\n    \n    return model\n\ndef resnet_model():\n    model = ResNet50(include_top = False, weights = None, input_shape = (shape + (1,)), pooling = 'avg')\n    x = model.output\n    \n    x = Flatten()(x)\n    x = Dense(1024, activation = 'relu', kernel_regularizer = L2(0.01), bias_regularizer = L2(0.01))(x)\n    x = Dense(256, activation = 'relu', kernel_regularizer = L2(0.01), bias_regularizer = L2(0.01))(x)\n    outputs = Dense(5, activation = 'sigmoid')(x)\n    \n    model = Model(model.input, outputs)\n    model.compile(loss = 'binary_crossentropy', optimizer = 'adam', metrics = ['accuracy'])\n    \n    print(model.summary())\n\n    return model\n\ndef conv_model():\n\n    inputs = Input(shape + (1,))\n\n    x = BatchNormalization()(inputs)\n    x = Conv2D(512, (3, 3), data_format='channels_last',\n               activation='relu')(x)\n    x = MaxPool2D((3, 3))(x)\n\n    x = Flatten()(x)\n    x = Dense(64, activation='relu')(x)\n    x = Dense(32, activation='relu')(x)\n    x = Dense(32, activation='relu')(x)\n    outputs = Dense(5, activation='relu')(x)\n\n    model = Model(inputs, outputs)\n    model.compile(loss='binary_crossentropy',\n                  optimizer='adam', metrics=['accuracy'])\n\n    print(model.summary())\n\n    return model\n\n\n# Important\n# -> This Method fits the model calling the fit method multiple times\n# -> It uses manual batch system and manual epoch system rather than passing the arguments in fit method\n#    This is done due to large images (each image is around 3000x3000 on an average around 1800x1800)\n#    and the current hardware wouldn't be able to store this large amount of data.\n# -> We call the Fit method (batch_size * epochs) times.\n# -> We call the Fit method multiple times to meet the required Hardware Limitations\ndef model_fitting(model, batch_size=250, epochs=5, validation=False):\n\n    early_stop = EarlyStopping(monitor = 'val_loss', min_delta = 0.0001, patience=3, verbose=1, mode='auto')\n    reduce_lr = ReduceLROnPlateau(monitor='val_loss', min_delta=0.0004, patience=2, factor=0.1, min_lr=1e-6, mode='auto', verbose=1)\n    \n    end = int(train_data.shape[0] * 0.8) if validation else train_data.shape[0]\n\n#     for i in range(epochs):\n#         print(\"\\n\\nEPOCHS (\" + str(i + 1) + \"/\" + str(epochs) + \") : \")\n    with tensorflow.device('/device:GPU:0'):\n        for j in range(0, end, batch_size):\n\n            last = end if (j + batch_size) > end else (j + batch_size)\n\n            for k in range(0,360,rotateAngle):\n\n                print(\"Rotating Image by\",k,\"degrees.\")\n\n                train_x, train_y = split_data('train', j, last, k)\n                model.fit(train_x, train_y, batch_size=50, epochs=epochs, shuffle=True, callbacks = [early_stop, reduce_lr])\n\n                del train_x, train_y\n                gc.collect()\n\n            print(\"(\" + str(last) + \"/\" + str(end) + \") images have been Scanned!!\")\n\n        if validation:\n            validate_x, validate_y = split_data('train', end)\n            model.evaluate(validate_x, validate_y)\n\n            del validate_x, validate_y\n            gc.collect()\n\n        gc.collect()\n\n    return model\n\n# Submitting the predicted values can be done using this method\n# Since the test_y values are not given we have to submit the model to check for the accuracy achieved\n\n\ndef submit(model, batch_size=300):\n\n    gc.collect()\n\n    for i in range(0, test_data.shape[0], batch_size):\n\n        end = test_data.shape[0] if (i + batch_size) > test_data.shape[0] else (i + batch_size)\n\n        test_x = split_data('test', i, end)\n        test_y = model.predict(test_x)\n\n        test_y = numpy.sum((test_y > 0.5).astype(int), axis = 1) - 1\n\n        predicted_data = pandas.DataFrame({'id_code': test_data['id_code'][i:end].values, 'diagnosis': test_y})\n\n        if i == 0:\n            final_data = predicted_data\n\n        if i != 0:\n            final_data = final_data.append(predicted_data, ignore_index=True)\n\n        del test_x, test_y\n        gc.collect()\n\n    final_data[['id_code', 'diagnosis']].to_csv('submission.csv', index=False)\n    print('Data Converted to CSV Format')\n\n    return final_data[['id_code', 'diagnosis']]\n\n\nif __name__ == '__main__':\n\n    print('Images are resized to', shape, '\\n\\n')\n\n#     model = conv_model()\n#     model = custom_model()\n    model = resnet_model()\n#     model = xception_model()\n    model = model_fitting(model, batch_size=750, epochs=25, validation=False)\n    model.save('custom_model')\n    print(submit(model))","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"","metadata":{"trusted":true},"execution_count":null,"outputs":[]}]}