{"cells":[{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"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)\nfrom sklearn.preprocessing import LabelEncoder\n\nimport os\nprint(os.listdir(\"../input\"))","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"#define PATH and Constants\nTRAIN_PATH=\"/kaggle/input/train\"\nTEST_PATH=\"/kaggle/input/test\"\n\n\nprint(\"Files in Folder\", TRAIN_PATH, len([name for name in os.listdir(TRAIN_PATH)]))\nprint(\"Files in Folder\", TEST_PATH, len([name for name in os.listdir(TEST_PATH)]))\n\ncsv = pd.read_csv('../input/train.csv')\n#Take new_whale out of training set\ntrain_csv = csv[~(csv.Id == \"new_whale\")]\nprint(train_csv.head())\nfiles = \"../input/train/\"+train_csv.Image.values\nlabels = train_csv.Id.values\nprint(\"files=\",files[:5])\nprint(\"labels=\",labels[:5])\nle = LabelEncoder()\ntrue_labels = le.fit_transform(train_csv[\"Id\"]).astype(np.int32)\nmax_label_val = le.transform(le.classes_)[-1]\n\n#print(\"Files with labels=\\n\",train_label_data[:5])\n#le.inverse_transform([1])[0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e69d85872787b978f1d33dce1d8ef02a0f87b2c1"},"cell_type":"code","source":"#Split data set to test and train.\nfrom sklearn.model_selection import train_test_split\n\nx_train, x_test, y_train, y_test = train_test_split(files, true_labels, test_size=0.3)\nprint (x_train, y_train)\nprint (x_test, y_test)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"d542926dabe109c12c789f1739a752cf3b8b7ec6"},"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport skimage\nfrom tqdm import tnrange, tqdm_notebook\nfrom skimage.color import rgb2gray\n\ndef img_read_fn(file):\n    image = skimage.io.imread(file,1)\n    image = skimage.transform.resize(image, (128,128,1), anti_aliasing=True)\n    #image = skimage.color.rgb2gray(image)\n    return image\n\nimg = img_read_fn(x_train[6])\nplt.imshow(img.reshape(128,128), cmap='gray')\n\nprint(\"Done!\")","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"b8d644af49fae2dbaf3fbc2db976d9bcf30705db"},"cell_type":"markdown","source":"## Create Siamese net\n\nSave sample from: https://github.com/keras-team/keras/issues/8612\n\n    #Save model\n    model_json = model.to_json()\n    with open(\"model.json\", \"w\") as json_file:\n        json_file.write(model_json)\n    model.save_weights(\"color_tensorflow_real_mode_2_imgs.h5\")\n\n    ##### Load the model\n\n    with open('model.json','r') as f:\n        json = f.read()\n    model = model_from_json(json)\n    model.load_weights(\"color_tensorflow_real_mode.h5\")"},{"metadata":{"trusted":true,"_uuid":"d7b96208cde44793f432b4341fac664703b7ded2"},"cell_type":"code","source":"from keras.layers import Input, Conv2D, Lambda, merge, Dense, Flatten,MaxPooling2D, Conv1D, MaxPooling1D\nfrom keras.models import Model, Sequential\nfrom keras.regularizers import l2\nfrom keras import backend as K\nfrom keras.optimizers import SGD,Adam\nfrom keras.losses import binary_crossentropy\nimport numpy.random as rng\nimport numpy as np\nimport os\nimport pickle\nimport matplotlib.pyplot as plt\nimport seaborn as sns\nfrom sklearn.utils import shuffle\n%matplotlib inline\n\ndef W_init(shape,name=None):\n    \"\"\"Initialize weights as in paper\"\"\"\n    values = rng.normal(loc=0,scale=1e-2,size=shape)\n    return K.variable(values,name=name)\n#//TODO: figure out how to initialize layer biases in keras.\ndef b_init(shape,name=None):\n    \"\"\"Initialize bias as in paper\"\"\"\n    values=rng.normal(loc=0.5,scale=1e-2,size=shape)\n    return K.variable(values,name=name)\n\ninput_shape = (128, 128, 1)\nleft_input = Input(input_shape)\nright_input = Input(input_shape)\n#build convnet to use in each siamese 'leg'\nconvnet = Sequential()\nconvnet.add(Conv2D(64,(10,10),activation='relu',input_shape=input_shape,\n                   kernel_initializer=W_init,kernel_regularizer=l2(2e-4)))\nconvnet.add(MaxPooling2D())\nconvnet.add(Conv2D(128,(7,7),activation='relu',\n                   kernel_regularizer=l2(2e-4),kernel_initializer=W_init,bias_initializer=b_init))\nconvnet.add(MaxPooling2D())\nconvnet.add(Conv2D(128,(4,4),activation='relu',kernel_initializer=W_init,kernel_regularizer=l2(2e-4),bias_initializer=b_init))\nconvnet.add(MaxPooling2D())\nconvnet.add(Conv2D(256,(4,4),activation='relu',kernel_initializer=W_init,kernel_regularizer=l2(2e-4),bias_initializer=b_init))\nconvnet.add(Flatten())\nconvnet.add(Dense(4096,activation=\"sigmoid\",kernel_regularizer=l2(1e-3),kernel_initializer=W_init,bias_initializer=b_init))\n\n#call the convnet Sequential model on each of the input tensors so params will be shared\nencoded_l = convnet(left_input)\nencoded_r = convnet(right_input)\n#layer to merge two encoded inputs with the l1 distance between them\nL1_layer = Lambda(lambda tensors:K.abs(tensors[0] - tensors[1]))\n#call this layer on list of two input tensors.\nL1_distance = L1_layer([encoded_l, encoded_r])\nprediction = Dense(1,activation='sigmoid',bias_initializer=b_init)(L1_distance)\nsiamese_net = Model(inputs=[left_input,right_input],outputs=prediction)\n\noptimizer = Adam(0.00006)\n#//TODO: get layerwise learning rates and momentum annealing scheme described in paperworking\nsiamese_net.compile(loss=\"binary_crossentropy\",optimizer=optimizer)\n\nsiamese_net.count_params()\n\n# Save model\nmodel_json = siamese_net.to_json()\nwith open(\"/kaggle/working/model.json\", \"w\") as json_file:\n    json_file.write(model_json)\n","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"03aebffde4bde080b7ac352f2600baa304e3ef3a"},"cell_type":"markdown","source":"## Crease Siamese Loader"},{"metadata":{"trusted":true,"_uuid":"f37802cc64607db3af745fd896ab7b1da7626472"},"cell_type":"code","source":"## Test pad\nimport random\n\nh = 128\nw = 128\nbatch_size = 32\nN = 20\nlabels = random.sample(np.unique(y_train).tolist(), (N*2)-1)\n\ntest_image = np.empty([N,128,128,1])\nsupport_set = np.empty([N,128,128,1])\n\n# Make first entry to of same label in test_image and support_set\nsameLabel = labels.pop(0)\nt1 = np.where(y_train==sameLabel)[0]\nif len(t1) == 1:\n    #Only one image available for this label.\n    #Duplicate the entry so it picks up same entry for left and right.\n    np.append(t1, t1[0])\nleft = random.sample(t1.tolist(),1)[0]\nright = random.sample(t1.tolist(),1)[0]       \ntest_image[0] = img_read_fn(x_train[left])\nsupport_set[0] = img_read_fn(x_train[right])\n\n#Make rest of the entries to be different for test_image and support_set\nloop = 1\nwhile loop < N:\n    t1 = np.where(y_train==labels.pop(0))[0]\n    index = random.sample(t1.tolist(),1)[0]\n    test_image[loop] = img_read_fn(x_train[index])\n    t1 = np.where(y_train==labels.pop(0))[0]\n    index = random.sample(t1.tolist(),1)[0]\n    support_set[loop] = img_read_fn(x_train[index])\n    loop += 1\ntargets = np.zeros((N,))\ntargets[0] = 1\n\nprint(test_image.shape)\nprint(support_set.shape)\nprint(targets.shape)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1563c733acfa51c6e8b4b04759bfc0e1cdb31227"},"cell_type":"code","source":"from datetime import datetime\n\nclass Siamese_Loader:\n    \"\"\"For loading batches and testing tasks to a siamese net\"\"\"\n    def __init__(self, x_train, y_train, x_val, y_val):\n        self.x_train = x_train\n        self.y_train = y_train\n        self.x_val = x_val\n        self.y_val = y_val\n        \n    def get_batch(self,batch_size,s=\"train\"):\n        \"\"\"Create batch of n pairs, half same class, half different class\"\"\"\n        if s == \"train\":\n            X=self.x_train\n            y=self.y_train\n        else:\n            X=self.x_val\n            y=self.y_val\n            \n        random.seed(datetime.now())\n        \n        #n_classes, n_examples, w, h = X.shape\n        h = 128\n        w = 128\n        pairs=[np.zeros((batch_size, h, w, 1)) for i in range(2)]\n        targets=np.zeros((batch_size,))\n        targets[:batch_size//2] = 1 #1 - Means same class\n        \n        #First half of image are of same class and next half is of different class\n        count = 0\n        for label in random.sample(np.unique(y).tolist(), int(batch_size/2)):\n            t1 = np.where(y==label)[0]\n            if len(t1) == 1:\n                #Only one image available for this label. Duplicate the entry so it picks up same entry for left and right.\n                np.append(t1, t1[0])\n            left = random.sample(t1.tolist(),1)[0]\n            right = random.sample(t1.tolist(),1)[0]       \n            pairs[0][count,:,:,:] = img_read_fn(X[left])\n            pairs[1][count,:,:,:] = img_read_fn(X[right])\n            count += 1\n    \n        while (count < batch_size):\n            label1  = random.sample(np.unique(y).tolist(), 1)\n            label2 = random.sample(np.unique(y).tolist(), 1)\n            #Makesure two labels are different\n            while (label2 == label1):\n                label2 = random.sample(np.unique(y).tolist(), 1)\n            \n            t1 = np.where(y==label1)[0]\n            left = random.sample(t1.tolist(),1)[0]\n            \n            t2 = np.where(y==label2)[0]\n            right = random.sample(t2.tolist(),1)[0]       \n            pairs[0][count,:,:,:] = img_read_fn(X[left])\n            pairs[1][count,:,:,:] = img_read_fn(X[right])\n            count += 1\n    \n        return pairs, targets\n    \n    def generate(self, batch_size, s=\"train\"):\n        \"\"\"a generator for batches, so model.fit_generator can be used. \"\"\"\n        while True:\n            pairs, targets = self.get_batch(batch_size,s)\n            yield (pairs, targets)    \n\n    def make_oneshot_task(self,N,s=\"val\"):\n        \"\"\"Create pairs of test image, support set for testing N way one-shot learning. \"\"\"\n        \n        if s == \"train\":\n            X=self.x_train\n            y=self.y_train\n        else:\n            X=self.x_val\n            y=self.y_val\n            \n        random.seed(datetime.now())\n        \n        labels = random.sample(np.unique(y).tolist(), (N*2)-1)\n\n        test_image = np.empty([N,128,128,1])\n        support_set = np.empty([N,128,128,1])\n\n        # Make first entry to of same label in test_image and support_set\n        sameLabel = labels.pop(0)\n        t1 = np.where(y==sameLabel)[0]\n        if len(t1) == 1:\n            #Only one image available for this label.\n            #Duplicate the entry so it picks up same entry for left and right.\n            np.append(t1, t1[0])\n        left = random.sample(t1.tolist(),1)[0]\n        right = random.sample(t1.tolist(),1)[0]       \n        test_image[0] = img_read_fn(X[left])\n        support_set[0] = img_read_fn(X[right])\n\n        #Make rest of the entries to be different for test_image and support_set\n        loop = 1\n        while loop < N:\n            t1 = np.where(y==labels.pop(0))[0]\n            index = random.sample(t1.tolist(),1)[0]\n            test_image[loop] = img_read_fn(X[index])\n            t1 = np.where(y==labels.pop(0))[0]\n            index = random.sample(t1.tolist(),1)[0]\n            support_set[loop] = img_read_fn(X[index])\n            loop += 1\n        targets = np.zeros((N,))\n        targets[0] = 1\n        \n        targets, test_image, support_set = shuffle(targets, test_image, support_set)\n        pairs = [test_image,support_set]\n        return pairs, targets\n    \n    def test_oneshot(self,model,N,k,s=\"val\",verbose=0):\n        \"\"\"Test average N way oneshot learning accuracy of a siamese neural net over k one-shot tasks\"\"\"\n        n_correct = 0\n        if verbose:\n            print(\"Evaluating model on {} random {} way one-shot learning tasks ...\".format(k,N))\n        for i in range(k):\n            inputs, targets = self.make_oneshot_task(N,s)\n            probs = model.predict(inputs)\n            if np.argmax(probs) == np.argmax(targets):\n                n_correct+=1\n        percent_correct = (100.0*n_correct / k)\n        if verbose:\n            print(\"Got an average of {}% {} way one-shot learning accuracy\".format(percent_correct,N))\n        return percent_correct\n    \n    def train(self, model, epochs, verbosity):\n        model.fit_generator(self.generate(batch_size),\n                            \n                             )\n    \n#Instantiate the class\nloader = Siamese_Loader(x_train, y_train, x_test, y_test)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"87fecad1910628653eba6f5ee8c105d2062521b9"},"cell_type":"markdown","source":"Few Utility Functions"},{"metadata":{"trusted":true,"_uuid":"341e03fdf320546b407c442dc810c9997a4d5845"},"cell_type":"code","source":"def concat_images(X):\n    \"\"\"Concatenates a bunch of images into a big matrix for plotting purposes.\"\"\"\n    nc,h,w,_ = X.shape\n    X = X.reshape(nc,h,w)\n    n = np.ceil(np.sqrt(nc)).astype(\"int8\")\n    img = np.zeros((n*w,n*h))\n    x = 0\n    y = 0\n    for example in range(nc):\n        img[x*w:(x+1)*w,y*h:(y+1)*h] = X[example]\n        y += 1\n        if y >= n:\n            y = 0\n            x += 1\n    return img\n\ndef plot_oneshot_task(pairs):\n    \"\"\"Takes a one-shot task given to a siamese net and  \"\"\"\n    fig,(ax1,ax2) = plt.subplots(2)\n    ax1.matshow(pairs[0][0].reshape(128,128),cmap='gray')\n    img = concat_images(pairs[1])\n    ax1.get_yaxis().set_visible(False)\n    ax1.get_xaxis().set_visible(False)\n    ax2.matshow(img,cmap='gray')\n    plt.xticks([])\n    plt.yticks([])\n    plt.show()\n#example of a one-shot learning task\npairs, targets = loader.make_oneshot_task(20,\"train\")\nplot_oneshot_task(pairs)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"299d0e1eb8b2642a6ab8441085c74b741aa8c8ef"},"cell_type":"markdown","source":"## Training Loop"},{"metadata":{"trusted":true,"_uuid":"c1d2c0223dab5c79f81281e1ad56ed3c65875f8b"},"cell_type":"code","source":"#Training loop\nprint(\"!\")\nevaluate_every = 10 # interval for evaluating on one-shot tasks\nloss_every=1 # interval for printing loss (iterations)\nbatch_size = 32\nn_iter = 1000#90000\nN_way = 20 # how many classes for testing one-shot tasks>\nn_val = 100 #250 #how mahy one-shot tasks to validate on?\nbest = -1\nPATH='/kaggle/working'\nweights_path = os.path.join(PATH, \"weights.h5\")\neval_value_path = os.path.join(PATH, \"eval.value\")\nprint(\"training\")\nfor i in range(1, n_iter):\n    (inputs,targets)=loader.get_batch(batch_size)\n    loss=siamese_net.train_on_batch(inputs,targets)\n    #print(loss)\n    if i % loss_every == 0:\n        print(\"iteration {}, training loss: {:.2f},\".format(i,loss))\n\n    if i % evaluate_every == 0:\n        print(\"evaluating\")\n        val_acc = loader.test_oneshot(siamese_net,N_way,n_val,verbose=True)\n        if val_acc >= best:\n            print(\"saving\")\n            #siamese_net.save(weights_path)\n            siamese_net.save_weights(weights_path)\n            best=val_acc\n            f = open(eval_value_path,'w')\n            f.write(str(best))\n            f.close()\n","execution_count":null,"outputs":[]}],"metadata":{"kernelspec":{"display_name":"Python 3","language":"python","name":"python3"},"language_info":{"name":"python","version":"3.6.6","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"}},"nbformat":4,"nbformat_minor":1}