{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport matplotlib.pyplot as plt\nimport cv2\nimport tensorflow as tf\nfrom tqdm import tqdm\n\nfrom sklearn.model_selection import train_test_split\n\n%matplotlib inline\n#With this backend, the output of plotting commands is displayed inline within frontends like the Jupyter notebook, \n#directly below the code cell that produced it. The resulting plots will then also be stored in the notebook document.\n\n","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"train_df = pd.read_csv('../input/aptos2019-blindness-detection/train.csv')\ntest_df = pd.read_csv('../input/aptos2019-blindness-detection/test.csv')\n#read CSV file","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"IMG_SIZE = 224\n#set image size","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def load_img(path):\n    image = cv2.imread(path)\n    return image\n\n\"\"\"\nArgument: path to image\nOutput: Image (cv2)\n\"\"\"","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let us take a look at some of the images.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(3,3,figsize=(10,10))\nselection = np.random.choice(train_df.index, size=9, replace=False)#select 9 random images\nimages = '../input/aptos2019-blindness-detection/train_images/'+train_df.loc[selection]['id_code']+'.png'\nfor image, axis in zip(images, axes.ravel()): #array.ravel() ;return flattened array\n    img = load_img(image)\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)\n    axis.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"As we can see, there are extra black areas surrounding the eyes which can be removed. The lighting conditions also differ significantly from image to image.","execution_count":null},{"metadata":{},"cell_type":"markdown","source":"Let's crop images so that the extra spaces surrounding eyes are removed.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def remove_unwanted_space(image, threshold=7):# remove black area\n    gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n    mask = gray_image > threshold\n    return image[np.ix_(mask.any(1), mask.any(0))]","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Ben Graham's preprocessing method (last competition) is used for fixing lighting conditions. Please refer to this kernel for more: https://www.kaggle.com/ratthachat/aptos-updatedv14-preprocessing-ben-s-cropping","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def preprocess_img(path):\n    image = load_img(path)\n    image = remove_unwanted_space(image, 5)\n    image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)\n    image = cv2.resize(image, (IMG_SIZE, IMG_SIZE))\n    image = cv2.addWeighted(image,4, cv2.GaussianBlur(image, (0,0), 30), -4, 128)# doan nay kho, no lam tien xu li cho anh sang hon\n    #https://www.kaggle.com/ratthachat/aptos-eye-preprocessing-in-diabetic-retinopathy\n    #https://docs.opencv.org/master/d0/d86/tutorial_py_image_arithmetics.html \n    return image","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(3,3,figsize=(10,10))\nselection = np.random.choice(train_df.index, size=9, replace=False)\nimages = '../input/aptos2019-blindness-detection/train_images/'+train_df.loc[selection]['id_code']+'.png'\nfor image, axis in zip(images, axes.ravel()):\n    img = preprocess_img(image)\n    axis.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"There seems a correlation between the way images are cropped in the training data and the target variables. In order to avoid the model predicting targets based on the way images are cropped, we can circle crop the eyes ourself in preprocessing. Refer to this kernel for more info: https://www.kaggle.com/taindow/be-careful-what-you-train-on ","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"def circle_crop(img):\n    circle_img = np.zeros((IMG_SIZE, IMG_SIZE), np.uint8) # create matrix of 0\n    cv2.circle(circle_img, ((int)(IMG_SIZE/2),(int)(IMG_SIZE/2)), int(IMG_SIZE/2), 1, thickness=-1)\n    #image = cv2.circle(image, center_coordinates, radius, color, thickness) \n    img = cv2.bitwise_and(img, img, mask=circle_img)\n    return img","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"fig, axes = plt.subplots(3,3,figsize=(10,10))\nselection = np.random.choice(train_df.index, size=9, replace=False)\nimages = '../input/aptos2019-blindness-detection/train_images/'+train_df.loc[selection]['id_code']+'.png'\nfor image, axis in zip(images, axes.ravel()):\n    img = circle_crop(preprocess_img(image))\n    axis.imshow(img)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N = train_df.shape[0]\ntrain = np.empty((N, IMG_SIZE, IMG_SIZE, 3), dtype=np.uint8)\nfor i, image_id in enumerate(tqdm(train_df['id_code'])):\n    train[i,:,:,:] = circle_crop(preprocess_img('../input/aptos2019-blindness-detection/train_images/'+image_id+'.png'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"N = test_df.shape[0]\ntest = np.empty((N, IMG_SIZE, IMG_SIZE, 3), dtype=np.uint8)\nfor i, image_id in enumerate(tqdm(test_df['id_code'])):\n    test[i,:,:,:] = circle_crop(preprocess_img('../input/aptos2019-blindness-detection/test_images/'+image_id+'.png'))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X_train, X_val, y_train, y_val = train_test_split(train, train_df['diagnosis'], test_size=0.15, random_state=42)# function to automatically split train/test","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"BATCH_SIZE = 32 #batch zsize","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_data_generator = tf.keras.preprocessing.image.ImageDataGenerator(\n        zoom_range=[0.9, 1.0],\n        fill_mode='constant',\n        cval=0.,  # value used for fill_mode = \"constant\"\n        horizontal_flip=True,  # randomly flip images\n        vertical_flip=True,  # randomly flip images\n        rotation_range=120\n    )#data augumentation","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"val_data_generator = tf.keras.preprocessing.image.ImageDataGenerator()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"train_gen = train_data_generator.flow(X_train, y_train, batch_size=BATCH_SIZE)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"val_gen = val_data_generator.flow(X_val, y_val, batch_size=BATCH_SIZE)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"We can consider this problem as a regression problem instead of classification. Here targets 0,1,2,3,4 are different stages of diabetic retinopathy and are not just independent classes. Their magnitude is representative of the severity of the disease. So this is a regression problem.","execution_count":null},{"metadata":{"trusted":true},"cell_type":"code","source":"resnet = tf.keras.applications.ResNet50(include_top=False, weights='../input/weight/resnet50_weights_tf_dim_ordering_tf_kernels_notop.h5', input_shape=(IMG_SIZE, IMG_SIZE, 3))\n#include top: include the 3 fully-connected layers at the top of the network.","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = tf.keras.Sequential([\n    resnet,\n    tf.keras.layers.GlobalAveragePooling2D(),\n    #pooling layer (được dùng giữa các convolutional layer,)\n    tf.keras.layers.BatchNormalization(),\n    #Batch Normalize đã normalize dữ liệu về zero mean, và chuẩn hoá variance về dạng unit. Bằng cách này đã giải quyết được rất tốt các vấn đề kể trên. Những ưu điểm của batch norm như sau:\n    #Cho phép learning rate lớn [vấn đề 1]\n    #Giảm sự phụ thuộc vào việc initialize [vấn đề 2]\n    tf.keras.layers.Dropout(0.5),\n    #skip connection \n    #Lý giải cho việc này, bạn có thể đọc bài báo http://papers.nips.cc/paper/4878-understanding-dropout.pdf.\n    tf.keras.layers.Dense(256, activation='relu'),\n    #Just your regular densely-connected NN layer.\n    tf.keras.layers.BatchNormalization(),\n    tf.keras.layers.Dropout(0.5),\n    tf.keras.layers.Dense(1, activation='relu')\n    #Just your regular densely-connected NN layer.\n])","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"checkpoint = tf.keras.callbacks.ModelCheckpoint('model_weights.hdf5', monitor='val_loss', verbose=1, \n                             save_best_only=True, mode='min')\n#load check point\n#verbose=1 animate progress bar","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"optimizer = tf.keras.optimizers.Adam(lr=0.00005)\n# just a regular optimizer","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.compile(optimizer=optimizer, loss='mse')\n# MSE : Mean square error ( a regression loss function)\n#https://heartbeat.fritz.ai/5-regression-loss-functions-all-machine-learners-should-know-4fb140e9d4b0?gi=8cbfdfe4e200\n#Once the model is created, you can config the model with losses and metrics with model.compile(), train the model with model.fit(), or use the model to do prediction with model.predict().","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"steps_per_epoch = int(np.ceil(X_train.shape[0]/BATCH_SIZE))\nval_steps_per_epoch = int(np.ceil(X_val.shape[0]/BATCH_SIZE))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"history = model.fit(train_gen, validation_data=val_gen, steps_per_epoch=steps_per_epoch, \n                              validation_steps=val_steps_per_epoch, callbacks=[checkpoint], epochs=25)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model.load_weights('model_weights.hdf5')","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction = model.predict(test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"for i, pred in enumerate(prediction):\n    if pred < 0.5:\n        prediction[i] = 0\n    elif pred < 1.5:\n        prediction[i] = 1\n    elif pred < 2.5:\n        prediction[i] = 2\n    elif pred < 3.5:\n        prediction[i] = 3\n    else:\n        prediction[i] = 4","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"prediction = np.squeeze(prediction.astype(np.int8))\n#https://www.tutorialspoint.com/numpy/numpy_squeeze.htm\n# removes one-dimensional entry from the shape of the given array","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"sample = pd.read_csv(\"../input/aptos2019-blindness-detection/sample_submission.csv\")\nsample.diagnosis = prediction\nsample.to_csv(\"submission.csv\", index=False)","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}