{"cells":[{"metadata":{},"cell_type":"markdown","source":"<h1><center>Monet masterpieces. EDA and image classification</center></h1>\n\n<center><img src=\"https://painting-planet.com/images/1/image178.jpg\"></center>"},{"metadata":{},"cell_type":"markdown","source":"<a id=\"top\"></a>\n\n<div class=\"list-group\" id=\"list-tab\" role=\"tablist\">\n<h3 class=\"list-group-item list-group-item-action active\" data-toggle=\"list\" style='background:Caribbean green; border:0' role=\"tab\" aria-controls=\"home\"><center>Quick navigation</center></h3>\n\n* [1. Data overview](#1)\n* [2. Classifier training](#2)"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"_kg_hide-input":true,"_kg_hide-output":true},"cell_type":"code","source":"import numpy as np\nimport pandas as pd\nimport os\nimport math\nimport matplotlib.pyplot as plt\nimport cv2\nimport random\n\nfrom plotly.subplots import make_subplots\nimport plotly.graph_objs as go\n\nfrom skimage import data\n\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.metrics import confusion_matrix, f1_score, accuracy_score\n\nimport tensorflow as tf\nfrom tensorflow.keras import layers\nfrom tensorflow.keras.layers import Conv2D, MaxPooling2D, Dropout, Flatten, Dense, BatchNormalization, Input\nfrom tensorflow.keras import backend as K\nfrom tensorflow.keras.utils import to_categorical\nfrom tensorflow.keras.callbacks import EarlyStopping","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id=\"1\"></a>\n<h2 style='background:Caribbean green; border:0; color:white'><center>1. Data overview<center><h2>"},{"metadata":{},"cell_type":"markdown","source":"Let's see number of images for every directory. We wiill work here only with .jpg images."},{"metadata":{"_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","trusted":true,"_kg_hide-input":true},"cell_type":"code","source":"MONET_JPG_PATH = '../input/gan-getting-started/monet_jpg/'\nPHOTO_JPG_PATH = '../input/gan-getting-started/photo_jpg/'\n\nprint('Number of images in Monet directory: ', len(os.listdir(MONET_JPG_PATH)))\nprint('Number of images in Photo directory: ', len(os.listdir(PHOTO_JPG_PATH)))","execution_count":null,"outputs":[]},{"metadata":{"_kg_hide-input":true},"cell_type":"markdown","source":"Now it is time to check shapes of images."},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"shapes_set = set()\nimage_names = os.listdir(MONET_JPG_PATH)\nfor img_name in image_names:\n    img = cv2.imread(os.path.join(MONET_JPG_PATH, img_name))\n    shapes_set.add(img.shape)\n\nprint('Number of unique image shapes inside Monet directory: ', len(shapes_set))\nprint('Image shape sizes: ', shapes_set.pop())\n\nshapes_set = set()\nimage_names = os.listdir(PHOTO_JPG_PATH)\nfor img_name in image_names:\n    img = cv2.imread(os.path.join(PHOTO_JPG_PATH, img_name))\n    shapes_set.add(img.shape)\n    \nprint('Number of unique image shapes inside Photo directory: ', len(shapes_set))\nprint('Image shape sizes: ', shapes_set.pop())","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"So all images in our dataser have the same shapes."},{"metadata":{},"cell_type":"markdown","source":"Let's visualize some images from both sets. Code for this is taken from https://www.kaggle.com/ihelon/monet-eda."},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"def visualize_images(path, n_images, is_random=True, figsize=(16, 16)):\n    plt.figure(figsize=figsize)\n    w = int(n_images ** .5)\n    h = math.ceil(n_images / w)\n    \n    all_names = os.listdir(path)\n    image_names = all_names[:n_images]   \n    if is_random:\n        image_names = random.sample(all_names, n_images)\n            \n    for ind, image_name in enumerate(image_names):\n        img = cv2.imread(os.path.join(path, image_name))\n        img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) \n        plt.subplot(h, w, ind + 1)\n        plt.imshow(img)\n        plt.xticks([])\n        plt.yticks([])\n    \n    plt.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Monet pictures"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"visualize_images(MONET_JPG_PATH, 9)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Photos"},{"metadata":{"_kg_hide-input":true,"trusted":true},"cell_type":"code","source":"visualize_images(PHOTO_JPG_PATH, 9)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Let's see image woth corresponded color's histogram"},{"metadata":{"_kg_hide-input":true,"_kg_hide-output":true,"trusted":true},"cell_type":"code","source":"def show_color_histogram(path):\n    image_names = os.listdir(path)\n    image_name = random.choice(image_names)\n    img = cv2.imread(os.path.join(path, image_name))\n    img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) \n    fig = make_subplots(1, 2)\n\n    fig.add_trace(go.Image(z=img), 1, 1)\n    for channel, color in enumerate(['red', 'green', 'blue']):\n        fig.add_trace(\n            go.Histogram(\n                x=img[..., channel].ravel(), \n                opacity=0.5,\n                marker_color=color, \n                name='%s channel' %color), \n            1, \n            2\n        )\n    fig.update_layout(height=400)\n    fig.show()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Monet picture"},{"metadata":{"trusted":true},"cell_type":"code","source":"show_color_histogram(MONET_JPG_PATH)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Photos"},{"metadata":{"trusted":true},"cell_type":"code","source":"show_color_histogram(PHOTO_JPG_PATH)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"<a id=\"2\"></a>\n<h2 style='background:Caribbean green; border:0; color:white'><center>Classifier training<center><h2>"},{"metadata":{},"cell_type":"markdown","source":"Let's build simple image classifier and see how difficult to detect Monet's pictures."},{"metadata":{"trusted":true},"cell_type":"code","source":"X = list()\ny = list()\n\nimage_names = os.listdir(MONET_JPG_PATH)\nfor img_name in image_names:\n    img = cv2.imread(os.path.join(MONET_JPG_PATH, img_name))\n    X.append(img)\n    y.append(1)\n\nimage_names = os.listdir(PHOTO_JPG_PATH)\nfor img_name in image_names:\n    img = cv2.imread(os.path.join(PHOTO_JPG_PATH, img_name))\n    X.append(img)\n    y.append(0)\n    \nX = np.stack(X)\ny = np.stack(y)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"X = X.astype('float32') / 255.\ny = to_categorical(y)\nX, X_test, y, y_test = train_test_split(X, y, random_state=666, test_size=0.2, shuffle=True)","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"Here we drfine metrics for imbalanced data"},{"metadata":{"trusted":true},"cell_type":"code","source":"def recall_score(y_true, y_pred):\n    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n    possible_positives = K.sum(K.round(K.clip(y_true, 0, 1)))\n    recall = true_positives / (possible_positives + K.epsilon())\n    return recall\n\ndef precision_score(y_true, y_pred):\n    true_positives = K.sum(K.round(K.clip(y_true * y_pred, 0, 1)))\n    predicted_positives = K.sum(K.round(K.clip(y_pred, 0, 1)))\n    precision = true_positives / (predicted_positives + K.epsilon())\n    return precision\n\ndef keras_f1_score(y_true, y_pred):\n    precision = precision_score(y_true, y_pred)\n    recall = recall_score(y_true, y_pred)\n    return 2*((precision*recall)/(precision+recall+K.epsilon()))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"data_augmentation = tf.keras.Sequential(\n    [\n        layers.experimental.preprocessing.RandomFlip(\"horizontal_and_vertical\"),\n        layers.experimental.preprocessing.RandomRotation(0.2),\n        layers.experimental.preprocessing.RandomRotation(0.5),\n    ]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"def create_model():\n    input_img = Input(shape=(256, 256, 3))\n    x = data_augmentation (input_img)\n    x = Conv2D(16, kernel_size=(3, 3), activation='elu')(x)\n    x = MaxPooling2D(pool_size=(2,2))(x)\n    x = BatchNormalization()(x)\n\n    x = Conv2D(32, kernel_size=(3, 3), activation='elu')(x)\n    x = MaxPooling2D(pool_size=(2,2))(x)\n    x = BatchNormalization()(x)\n    \n    x = Conv2D(16, kernel_size=(3, 3), activation='elu')(x)\n    x = MaxPooling2D(pool_size=(2,2))(x)\n    x = BatchNormalization()(x)\n    x = Dropout(0.2)(x)\n    \n    x = Flatten()(x)\n    x = Dense(128, activation='sigmoid')(x)\n    out = Dense(2, activation = 'softmax')(x)\n\n    model = tf.keras.Model(input_img, out)\n    model.compile(\n        loss=tf.keras.losses.binary_crossentropy, \n        optimizer=tf.keras.optimizers.Adam(learning_rate=0.0005),\n        metrics=[keras_f1_score]\n    )\n    \n    return model","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"To fight with imbalanced data let's try to use class_weights."},{"metadata":{"trusted":true},"cell_type":"code","source":"class_weight = {\n    0: 1.,\n    1: 20.\n}","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"model = create_model()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"early_stopping = EarlyStopping(\n    patience=4, \n    verbose=1\n)\n\nmodel.fit(\n    X, \n    y, \n    validation_split=0.2, \n    batch_size=18, \n    epochs=100,\n    verbose=1, \n    class_weight=class_weight,  \n    callbacks=[early_stopping]\n)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"preds = model.predict(X_test)\npreds = np.argmax(preds, axis=1)\ny_test = np.argmax(y_test, axis=1)\nconfusion_matrix(y_test, preds)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print('Accuracy: ', accuracy_score(y_test, preds))\nprint('F1-score: ', f1_score(y_test, preds))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"","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}