{"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":"markdown","source":"## PHOTOMETRIC LSST ASTRONOMICAL TIME-SERIES CLASSIFICATION CHALLENGE (PLASTICC)\nhttps://plasticc.org/\n\n## CNN\nhttps://notionpress.com/read/cnn-kernel","metadata":{"_uuid":"f6ae395b51319ea53b4689ab2e00bb9056987e44"}},{"cell_type":"code","source":"import numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nimport cv2 #deal with images\nimport matplotlib.pyplot as plt\n%matplotlib inline\nfrom tqdm import tqdm\nnp.random.seed(42)\n\ntraining = pd.read_csv('../input/training_set.csv')\nmeta_training = pd.read_csv(\"../input/training_set_metadata.csv\")\nmerged = training.merge(meta_training, on = \"object_id\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"###recurrent plot\n\ndef sigmoid(x):\n    '''\n    Returns the sigmoid of a value\n    '''\n    return 1/(1+np.exp(-x))\n\ndef R_matrix(signal, eps):\n    '''\n    Given a time series (signal) and an epsilon,\n    return the Recurrent Plot matrix\n    '''\n    R = np.zeros((signal.shape[0], signal.shape[0]))\n    for i in range(R.shape[0]):\n        for j in range(R.shape[1]):\n            R[i][j] = np.heaviside((eps - abs(signal[i] - signal[j])),1)\n    return R\n\n#using sigmoid rather than heaviside\n#because in this dataset the epsilon parameter needs to\n#change from object to object and therefore should be learned as well\ndef R_matrix_modified(signal):\n    '''\n    Given a time series (signal) and an epsilon,\n    return the modified Recurrent Plot matrix\n    using sigmoid rather than heaviside\n    '''\n    R = np.zeros((signal.shape[0], signal.shape[0]))\n    for i in range(R.shape[0]):\n        for j in range(R.shape[1]):\n            R[i][j] = sigmoid((abs(signal[i] - signal[j])))\n    return R\n\ndef create_objects_dict(merged_dataset):\n    '''\n    Input: dataset containing both training data and metadata\n    Creates a dictionary using each object as keys and\n    one R matrix for each passband in that object\n    '''\n    objects = {}\n    for obj in tqdm(np.unique(merged_dataset.object_id)):\n        R_passbands = []\n        for passband in np.unique(merged_dataset.passband):\n            obj_flux = merged_dataset[(merged_dataset.object_id == obj) & (merged_dataset.passband == passband)].flux.values\n            R_passbands.append(R_matrix_modified(obj_flux))\n        objects[obj] = (np.asarray(R_passbands), max(merged_dataset[merged_dataset.object_id == obj].target))\n    return objects\n\ndef get_minmax_shapes(obj_R_matrices):\n    '''\n    Given an R matrix, get the min and max width \n    to be used to crop and let all images from a given\n    object be of the same size so they can be concatenated\n    '''\n    min_length = 0\n    max_length = 0\n    for passband in np.unique(merged.passband):\n        if passband == 0:\n            length = len(obj_R_matrices[passband])\n            min_length = length\n            max_length = length\n        else:\n            length = len(obj_R_matrices[passband])\n            min_length = min(min_length, length)\n            max_length = max(max_length, length)\n    return (min_length, max_length)\n\ndef crop_obj_plots(objects):\n    '''\n    Accepts a dictionary where each key is a different object\n    and each value is a tuple - one slot with a list of R matrices and \n    the other with the target value (object class)\n    '''\n    for obj in tqdm(objects.keys()):\n        min_len, max_len = get_minmax_shapes(objects[obj][0])\n        for passband in np.unique(merged.passband):\n            objects[obj][0][passband] = objects[obj][0][passband][:min_len, :min_len]\n    return objects\n\nobjects = create_objects_dict(merged)\ncropped_objects = crop_obj_plots(objects)","metadata":{"_uuid":"9275ed05e8576b5362be46c4425c1f252e392769","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"cropped_objects[730][0][3].shape\nplt.imshow(cropped_objects[730][0][0])","metadata":{"_uuid":"6c23ed7e28a7a2211e202c8d39b9f5f52e465376","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from collections import Counter\n\nshapes = []\nfor key in tqdm(cropped_objects.keys()):\n    shapes.append(cropped_objects[key][0][0].shape[0])\nplt.hist(shapes, bins = 50)\nCounter(shapes)","metadata":{"_uuid":"523cf3426033520cd9256f5e890098a113157d60","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import math\ncropped_2 = np.copy(cropped_objects).item()\nfor key in tqdm(cropped_2.keys()):\n    shape = cropped_2[key][0][0].shape[0]\n    if shape < 11:\n        for passband in np.unique(merged.passband):\n            #how much we will increase the border\n            increaseBorder = abs(shape-11)/2\n            cropped_2[key][0][passband] = cv2.copyMakeBorder(src = cropped_2[key][0][passband],\n                                                             top = math.ceil(increaseBorder), \n                                                             left = math.ceil(increaseBorder),\n                                                             bottom = round(increaseBorder),\n                                                             right = round(increaseBorder),\n                                                             borderType = cv2.BORDER_REFLECT)\n    elif shape>11 and shape < 25:\n        for passband in np.unique(merged.passband):\n            cropped_2[key][0][passband] = cropped_2[key][0][passband][:-(shape-11), :-(shape-11)]\n            \n    \n    elif shape >= 50 and shape < 57:\n        for passband in np.unique(merged.passband):\n            increaseBorder57 = abs(shape-57)/2\n            cropped_2[key][0][passband] = cv2.copyMakeBorder(src = cropped_2[key][0][passband],\n                                                             top = math.ceil(increaseBorder57), \n                                                             left = math.ceil(increaseBorder57),\n                                                             bottom = round(increaseBorder57),\n                                                             right = round(increaseBorder57),\n                                                             borderType = cv2.BORDER_REFLECT)\n    else:\n        continue","metadata":{"_uuid":"22aab92ed0c8d11faa24b664b894e4715655dacd","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"shapes = []\nfor key in tqdm(cropped_2.keys()):\n    shapes.append(cropped_2[key][0][0].shape[0])\nplt.hist(shapes, bins = 50)\nCounter(shapes)","metadata":{"_uuid":"e069117d6f3bb7363bd2c1534a413b5913aad388","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"objects = list(cropped_2.keys())\ninput_images = list()\nlabels = list()\nfor key in tqdm(objects):\n    if cropped_2[key][0][0].shape[0] == 57:\n        img = np.stack((cropped_2[key][0][0],\n                        cropped_2[key][0][1],\n                        cropped_2[key][0][2],\n                        cropped_2[key][0][3],\n                        cropped_2[key][0][4]), axis = -1)  \n                                           \n        input_images.append(np.expand_dims(img, axis = 0))\n        labels.append(cropped_2[key][1])                                                        \ninput_images = np.vstack(input_images)     \ninput_images.shape","metadata":{"_uuid":"7e51c39a5426602ea2bff88332f94c6699999945","scrolled":true,"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#LabelBinarizer and train-test split\nfrom sklearn.model_selection import train_test_split\nfrom sklearn.preprocessing import LabelBinarizer\nfrom keras.utils import np_utils\n\n\ntrain_fraction = 0.8\n\nencoder = LabelBinarizer()\ny = encoder.fit_transform(labels)\nx = input_images\n\ntrain_tensors, test_tensors, train_targets, test_targets =\\\n    train_test_split(x, y, train_size = train_fraction, random_state = 42)\n\nval_size = int(0.5*len(test_tensors))\n\nval_tensors = test_tensors[:val_size]\nval_targets = test_targets[:val_size]\ntest_tensors = test_tensors[val_size:]\ntest_targets = test_targets[val_size:]\n","metadata":{"_uuid":"aaf22c66ead98a0f7a20c51c4e5c79651102eb6b","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"from keras.layers import Conv2D, MaxPooling2D, GlobalAveragePooling2D, GlobalMaxPooling2D\nfrom keras.layers import Dropout, Flatten, Dense, LeakyReLU\nfrom keras.callbacks import EarlyStopping, ModelCheckpoint\nfrom keras.models import Sequential\nfrom tensorflow import set_random_seed\n\nset_random_seed(42)\n\nearly_stopping = EarlyStopping(monitor = 'val_loss', patience = 10)\ncheckpointer = ModelCheckpoint(filepath='weights.hdf5', \n                               verbose=1, save_best_only=True)\n\n\nmodel = Sequential()\nmodel.add(Conv2D(filters = 128, kernel_size = (3,3), padding = 'same', activation = 'elu', input_shape = (None, None,5)))\nmodel.add(Conv2D(filters = 128, kernel_size = (3,3), padding = 'same', activation = 'elu'))\nmodel.add(Dropout(0.3))\nmodel.add(MaxPooling2D(pool_size = 2)) \n\nmodel.add(Dense(256, activation = 'relu'))\nmodel.add(Dense(5, activation = 'softmax'))\n\n\nmodel.compile(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])\nepochs = 10\nmodel.fit(train_tensors, train_targets, \n          validation_data=(val_tensors, val_targets),\n          epochs=epochs, batch_size=128, verbose=1, callbacks = [early_stopping, checkpointer])","metadata":{"_uuid":"9b5bfe7796eadd4e97dc51b9d1ab86d687de421b","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"model.load_weights('weights.hdf5')\n\ncell_predictions =  [np.argmax(model.predict(np.expand_dims(tensor, axis=0))) for tensor in test_tensors]\n\ntest_accuracy = 100*np.sum(np.array(cell_predictions)==np.argmax(test_targets, axis=1))/len(cell_predictions)\nprint('Test accuracy: %.4f%%' % test_accuracy)","metadata":{"_uuid":"a366577a1c8de9820517a7d3c67e94c264f267d6","trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"#manipulations to get our images into the proper dimensions (done before with shape 57)\nobjects = list(cropped_2.keys())\nsmall_labels = list()\nsmall_input_images = list()\nfor key in tqdm(objects):\n    if cropped_2[key][0][0].shape[0] == 11:\n        img = np.stack((cropped_2[key][0][0],\n                        cropped_2[key][0][1],\n                        cropped_2[key][0][2],\n                        cropped_2[key][0][3],\n                        cropped_2[key][0][4]), axis = -1)  \n                                           \n        small_input_images.append(np.expand_dims(img, axis = 0))\n        small_labels.append(cropped_2[key][1])                                                        \nsmall_input_images = np.vstack(small_input_images)     \nsmall_input_images.shape\n\n#predictions\ncell_predictions =  [np.argmax(model.predict(np.expand_dims(small_image, axis=0))) for small_image in small_input_images]\n\nsmall_encoder = LabelBinarizer()\nsmall_labels_encoded = encoder.fit_transform(small_labels)\ntest_accuracy = 100*np.sum(np.array(cell_predictions)==np.argmax(small_labels_encoded, axis=1))/len(cell_predictions)\nprint('Accuracy in small images: %.4f%%' % test_accuracy)","metadata":{"scrolled":true,"_uuid":"cfdbe5f0e0bd51ad1884e00f13d57323d05f0de8","trusted":true},"execution_count":null,"outputs":[]}]}