{"cells":[{"metadata":{"_uuid":"7059ac6497e085e0c13bfebbf34cddc08f31950e"},"cell_type":"markdown","source":"In this notbook I will use Nearest Neighber Classification using Triplet loss training (FaceNet: A Unified Embedding for Face Recognition and Clustering), in five steps:\n1. Loading first sub-set of data-base (Whales having at least 29 images per whale) and train cross entropy CNN\n2. Optimize the first sub-set using triplet loss to extract 128 features\n3. Load secound sub-set (Whales with 18-28  images per whale) , split it to train and test.\n4. Predict the train set features\n5. Classification of test set using Nearest neighbor features to the train set.\n"},{"metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"scrolled":true},"cell_type":"code","source":"# This Python 3 environment comes with many helpful analytics libraries installed\n# It is defined by the kaggle/python docker image: https://github.com/kaggle/docker-python\n# For example, here's several helpful packages to load in \n\nimport numpy as np # linear algebra\nimport pandas as pd # data processing, CSV file I/O (e.g. pd.read_csv)\nfrom glob import glob\nfrom PIL import Image\nimport matplotlib.pyplot as plt\nfrom sklearn.model_selection import train_test_split\n# Input data files are available in the \"../input/\" directory.\n# For example, running this (by clicking run or pressing Shift+Enter) will list the files in the input directory\n\n\nimport keras.backend as K\nfrom keras.layers import Input,Conv2D,MaxPool2D,Dense,Dropout,Flatten,BatchNormalization\nfrom keras.models import Model,Sequential\nfrom keras.optimizers import Adam\nfrom keras.utils import to_categorical\nfrom keras.losses import categorical_crossentropy\nfrom keras.preprocessing import image\nimport os\nprint(os.listdir(\"../input\"))\n\n# Any results you write to the current directory are saved as output.","execution_count":null,"outputs":[]},{"metadata":{"_cell_guid":"79c7e3d0-c299-4dcb-8224-4455121ee9b0","_uuid":"d629ff2d2480ee46fbb7e2d37f6b5fab8052498a","trusted":true},"cell_type":"code","source":"Labels = pd.read_csv('../input/train.csv')\nLabels.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"213a88d28fa27207c07ae90f5fead37aae84ff9d"},"cell_type":"code","source":"Pics = os.listdir('../input/train/')\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"20b88b5682c2e01f4ccf8f86f3ff7c40831695ec"},"cell_type":"code","source":"SIZE = 128\ndef ImportImage( filename):\n    img = Image.open('../input/train/'+filename).convert(\"LA\").resize( (SIZE,SIZE))\n    return np.array(img)[:,:,0]","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"a09b79ddbf22d2804aaff8f2ac99d37e4190536e"},"cell_type":"code","source":"# load pictares and label them , \"MinPicsPerUser\" is the number of pictures requers for class to load.\n# At the begginig for easy training and convargse I will load only those with high number of pics\n\ndef LoadImage_And_MatchLabels(Pics,Labels,Unique_Labels,MinPicsPerUser,MaxPicPerUser = 1000,SIZE= 128):\n    ManyImageIndex = np.array(Unique_Labels['Count']\n                              [Unique_Labels.index[ Unique_Labels['Count'] > MinPicsPerUser ]].tolist())\n    ManyImageIndex_Sum = np.sum(ManyImageIndex[ManyImageIndex<MaxPicPerUser]) \n\n    Train_img_Array = np.zeros((ManyImageIndex_Sum,SIZE,SIZE))\n    PicInd = 0 \n    ImageLabel =  [] \n    for Pic in  Pics : \n        #print(Pic,PicInd)\n        ID = Labels['Id'][Labels.index[Labels['Image']==Pic ].tolist()].tolist()[0]\n        NumImages = Unique_Labels['Count'][Unique_Labels.index[Unique_Labels['Id']== ID].tolist()].tolist()\n        if (NumImages[0] > MinPicsPerUser and NumImages[0] < MaxPicPerUser) : \n            Train_img_Array[PicInd,:,:] = ImportImage(Pic)\n            PicInd += 1\n            ImageLabel.append(ID)\n    return Train_img_Array,ImageLabel","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"42cc4f9e916173213e4a5d638a3ce41ab2315342"},"cell_type":"code","source":"# load only clasess with >29 images per class \n\nMinPic = 29 \nUnique_Labels = Labels.drop_duplicates(subset='Id').reset_index()\nUnique_Labels['Count'] = 0\nSumImages = np.zeros(Unique_Labels.shape[0])\nfor i in range(Unique_Labels.shape[0]):\n    SumImages[i] = np.sum(Labels['Id']== Unique_Labels['Id'][i])\n    Unique_Labels['Count'][i] = SumImages[i]\nSumImages_Sort = np.sort(SumImages)\nTrain_Phase_1_Array,ImageLabel = LoadImage_And_MatchLabels(Pics,Labels,Unique_Labels,MinPic,SIZE=SIZE)             \n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"6bd7be769f16aac1b6ed2a17a3ea343d8148b6e6"},"cell_type":"code","source":"# Helper function to Categorical classes\ndef List_To_Categorical(Label_List):\n    LabelsArray = np.zeros(len(Label_List))\n    for j,label in enumerate(set(Label_List)):\n        inds = [i for i,e in enumerate(Label_List) if (e == label)]\n        for i in inds: \n            LabelsArray[i] = j\n    LabelsCategorical = to_categorical(LabelsArray)\n    print(LabelsCategorical.shape)\n    return LabelsCategorical,LabelsArray","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"2bd7e5ee06006edd35a78d6a8fdaaaa64c54dee9"},"cell_type":"code","source":"CtegoricalLabel,LabelsArray = List_To_Categorical(ImageLabel)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"fe92cf1ce552f606b74366db1eaab5ac1ede4738"},"cell_type":"code","source":"\ninputs1 = Input((SIZE,SIZE,1))\nC1 = Conv2D(32,kernel_size=(3,3),activation='relu')(inputs1)\nC1 = BatchNormalization()(C1)\nC1 = MaxPool2D(pool_size=(2,2))(C1)\nC2 = Conv2D(32,kernel_size=(3,3),activation='relu')(C1)\nC2 = BatchNormalization()(C2)\nC2 = MaxPool2D(pool_size=(2,2))(C2)\nC3 = Conv2D(64,kernel_size=(3,3),activation='relu')(C2)\nC3 = MaxPool2D(pool_size=(2,2))(C3)\nC4 = Conv2D(64,kernel_size=(3,3),activation='relu')(C3)\nC4 = MaxPool2D(pool_size=(2,2))(C4)\nC5 = Flatten()(C4)\nDanse1 = Dense(128,activation='relu')(C5)\nDanse1 = Dropout(0.5)(Danse1)\nDanse2 = Dense(128)(Danse1)\nDanse2d = Dropout(0.5)(Danse2)\nDense3 = Dense(CtegoricalLabel.shape[1],activation='softmax')(Danse2d)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1d69ff56321981b495143c9caf9ce2c4ad2eb529"},"cell_type":"code","source":"model = Model(inputs1,Dense3)\nmodel.compile(loss=categorical_crossentropy, optimizer=Adam(),metrics=['accuracy'])\nmodel.summary()\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"e0021d740d8924adca91d6891890b77fee586364"},"cell_type":"code","source":"model.fit(x=Train_Phase_1_Array.reshape([-1,SIZE,SIZE,1]),y=CtegoricalLabel,batch_size=32,epochs=50,verbose=1,\n          validation_split=0.15)","execution_count":null,"outputs":[]},{"metadata":{"_uuid":"0ad4ba20bd6c303e960aa61f5aa7b2a6c74d2373"},"cell_type":"markdown","source":"Define a Triplet network, with the wightes from previse CNN model:"},{"metadata":{"trusted":true,"_uuid":"050c1acf1147c74e3e2068ab41d36159c08b80ad"},"cell_type":"code","source":"\nTriplet_model = Sequential()\nfor layer in model.layers[:-2]:\n    Triplet_model.add(layer)\n    Triplet_model\nTriplet_model.summary()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"b304c19c77dbd5884970a5f69d7d7287a0eb0a5b"},"cell_type":"code","source":"# My Triplet loss, and sorting the data according to the loss definition\ndef TripletLoss_3(yTrue,y_pred):\n    #y_pred_norm = K.l2_normalize(y_pred,axis=0)\n    PosDiff = K.sqrt(K.mean(K.square(y_pred-yTrue[:,:128])))\n    NegDiff = K.sqrt(K.mean(K.square(y_pred-yTrue[:,128:256])))\n    Dist_Pos_Neg = .6  - (NegDiff) + (PosDiff)\n    #loss = K.maximum(0.0,Dist_Pos_Neg)\n    loss = K.log(1 + K.exp(Dist_Pos_Neg))\n    return loss\n\n\ndef SortFeatures(ImArray,Features,LabelsArray):\n    FeaturesOut = np.zeros((Features.shape[0],Features.shape[1]*2))\n    for j in range(ImArray.shape[0]) :\n        Ind_Same_Whale = np.array([i for i,e in enumerate(LabelsArray) if (e == LabelsArray[j]) & (j != i)])\n        Ind_different_Whale = np.array([i for i,e in enumerate(LabelsArray) if (e != LabelsArray[j]) & (j != i)])\n        PosInd = np.random.choice(Ind_Same_Whale)\n        NegInd = np.random.choice(Ind_different_Whale)\n\n        FeaturesOut[j,:128] = Features[PosInd,:]\n        FeaturesOut[j,128:256] = Features[NegInd,:]\n    return FeaturesOut","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"1b14919fbc325fbb7707fcb06051f9ac0a123de5"},"cell_type":"code","source":"# Optmize the Nearest neighber classification using the Triplet loss: \nCtegoricalLabel = List_To_Categorical(ImageLabel)\nTriplet_model.compile(optimizer='adam',loss=TripletLoss_3)\nPred = Triplet_model.predict(Train_Phase_1_Array.reshape([-1,SIZE,SIZE,1]))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5cf6d11c51dd25a21ae46b521e7cf17bf82462c1"},"cell_type":"code","source":"\nSortedPred = SortFeatures(Train_Phase_1_Array,Pred,ImageLabel)\nTriplet_model.fit(x=Train_Phase_1_Array.reshape([-1,SIZE,SIZE,1]),y=SortedPred,batch_size=32,epochs=5,verbose=1)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"926e9e4bf32aa85717931650e6ec422f1de35756"},"cell_type":"code","source":"# load other set of Classes for test , those with Number of Pics >16 <28\nMinPicsPerUser = 16\nMaxPic = 28\nTrain_Phase_2_Array,ImageLabel_2 = LoadImage_And_MatchLabels(Pics,Labels,Unique_Labels,MinPicsPerUser,MaxPicPerUser=MaxPic,SIZE=SIZE)             \n\n# Extract features\nPred_Features = Triplet_model.predict(Train_Phase_2_Array.reshape([-1,SIZE,SIZE,1]))\nLabelsCategorical_2,LabelsArray_2 = List_To_Categorical(ImageLabel_2)\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"3d90efd38f1d0a8ae4ebb07025f8c3293bb13b75"},"cell_type":"code","source":"#  train and test split\nX_train, X_test, y_train, y_test = train_test_split(Pred_Features, LabelsArray_2, test_size=0.25, random_state=42)\n# Nearest neighbor distance calculation\nDist = np.zeros((len(y_test),len(np.unique(y_train))))\nfor i in range(X_test.shape[0]):\n    DiffMat = np.sum(np.square(np.subtract(X_train,X_test[i,:])),axis=1)\n    for j in range(len(np.unique(y_train))):\n        Dist[i,j] = np.sum(DiffMat[np.where(j==y_train)])\n        ","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"891776136db36942aeeb71fe80cba27ee65bcabb"},"cell_type":"code","source":"# top 5 nearest neighbor classification \nSumTop5 = 0 \nfor k  in range(len(Dist)):\n    if np.sum(np.argsort(Dist[k,:])[:5]== y_test[k]) == 1 : \n        SumTop5 += 1\n        \nPercentCorrect_top5 = SumTop5/len(Dist) \nprint(PercentCorrect_top5)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"5c017a317a682fcd423035899ea32dd8e61489aa"},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"10d21a7f4ecd538c3d85070a1383cb7e4c71ba28"},"cell_type":"code","source":"","execution_count":null,"outputs":[]},{"metadata":{"trusted":true,"_uuid":"7c7e65257a968b188226a9bbd871e911ce14e971"},"cell_type":"code","source":"","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}