{"cells":[{"metadata":{},"cell_type":"markdown","source":"### Goal: identify any relationship between Levenshtein distance and the distance between image vectors\n\n* I wanted to see if a shorter distance between two molecules' image vectors correlates with a lower Levenshtein distance between the molecules InChI and vice versa.\n* I've checked several metrics used to measure distance between vectors: Euclidean, cosine, minkowski, dice, etc.\n\nSee results on a small set below\n"},{"metadata":{"execution":{"iopub.execute_input":"2021-03-03T23:33:59.117177Z","iopub.status.busy":"2021-03-03T23:33:59.116374Z","iopub.status.idle":"2021-03-03T23:34:00.461702Z","shell.execute_reply":"2021-03-03T23:34:00.460746Z"},"papermill":{"duration":1.368822,"end_time":"2021-03-03T23:34:00.461947","exception":false,"start_time":"2021-03-03T23:33:59.093125","status":"completed"},"tags":[],"trusted":true},"cell_type":"code","source":"from __future__ import absolute_import\nfrom __future__ import print_function\n\nimport os\nimport numpy as np\nimport pandas as pd\nfrom tqdm.auto import tqdm\ntqdm.pandas()\nimport Levenshtein\nimport cv2\nfrom PIL import Image\nfrom matplotlib import pyplot as plt\nimport seaborn as sns\nfrom numpy import save, load\nimport warnings\nwarnings.filterwarnings('ignore')\n\nfrom scipy.spatial import distance\nfrom sklearn.decomposition import PCA\n\nimport tensorflow as tf\nfrom tensorflow.python.keras.preprocessing.image import load_img, img_to_array\nfrom tensorflow.keras.applications.vgg16 import VGG16\nfrom tensorflow.keras.preprocessing import image\nfrom tensorflow.keras.applications.vgg16 import preprocess_input\n\nfrom scipy import spatial\n\nprint(os.listdir(\"../input\"))","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"# Levenshtein scoring function\n\ndef get_score(y_true, y_pred):\n    scores = []\n    for true, pred in zip(y_true, y_pred):\n        score = Levenshtein.distance(true, pred)\n        scores.append(score)\n    avg_score = np.mean(scores)\n    return avg_score\n\nprint(get_score('alex', 'axel'))\nprint(Levenshtein.distance('alex', 'axel'))","execution_count":null,"outputs":[]},{"metadata":{"papermill":{"duration":0.017229,"end_time":"2021-03-03T23:34:00.495481","exception":false,"start_time":"2021-03-03T23:34:00.478252","status":"completed"},"tags":[]},"cell_type":"markdown","source":"# Data"},{"metadata":{"trusted":true},"cell_type":"code","source":"nrows = 1100\n\nrawDF = pd.read_csv('../input/bms-arranged-label/arranged_bms_train_labels.csv', index_col=[0], nrows=nrows)\nplay = rawDF[['image_path','InChI']][:1000]\nplayTest = rawDF[['image_path','InChI']][1000:]\nprint(play.shape)\nplay.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"playTest.reset_index(drop=True, inplace=True)\nprint(playTest.shape)\nplayTest.head()","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Features extraction ... from images to vectors"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\n# Model predict = feat extract from images going thru VGG16\n# 10k images = 1Gb features.npy ... 7:30 mins prep feat ... 9:50 mins predict ... average on test Lev = 78.4\n\nmodel = VGG16(weights='imagenet', include_top=False)\n\nfeatures = []\n\nfor row in tqdm(range(play.shape[0])):\n    img_path = play.image_path[row]\n    img = image.load_img(img_path, target_size=(224, 224))\n    x = image.img_to_array(img)\n    x = np.expand_dims(x, axis=0)\n    x = preprocess_input(x)\n    \n    #features.append(model.predict(x).squeeze())\n    features.append(model.predict(x).ravel())\n\nfeatures = np.array(features)\nfeatures.shape","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"### Save / Load features npy"},{"metadata":{"trusted":true},"cell_type":"markdown","source":"#SAVE\nsave('features.npy', features)"},{"metadata":{"trusted":true},"cell_type":"markdown","source":"%%time\n\n#LOAD\nfeatures = load('../input/bms-euclidean-to-levenshtein/features.npy')\nfeatures.shape"},{"metadata":{},"cell_type":"markdown","source":"# Predict = find the nearest image vector"},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\n# Predict ONE img w Euclidean np.linalg.norm\n\nimgNum = 23\n\nmodel = VGG16(weights='imagenet', include_top=False)\n\nimg_path = playTest.image_path[imgNum]\nimg = image.load_img(img_path, target_size=(224, 224))\nx = image.img_to_array(img)\nx = np.expand_dims(x, axis=0)\nx = preprocess_input(x)\n\n#featOne = model.predict(x)\nfeatOne = model.predict(x).ravel()\n\n# Find the min Euclidean distance to one image vector\n\nMyDist = []\n\nfor v in range(features.shape[0]):\n    #Dist = np.linalg.norm(features[v]-featOne)\n    Dist = spatial.distance.euclidean(features[v],featOne)   \n    MyDist.append(Dist)\n    \nMyDist=np.array(MyDist)  \n\n# When the image we're searching with is in the features already - take the second smallest dist\n# as 0 is the first arg = same vector we search and dist = 0\n#nearVec = MyDist.argsort()[1] \n#vecDis = np.amin(MyDist[MyDist != np.amin(MyDist)]) # for picking the second smallest\n\nnearVec = MyDist.argmin()\nvecDis = MyDist.min()\nlevDis = Levenshtein.distance(playTest.InChI[imgNum], play.InChI[nearVec])\n\n\nprint('get_score ', get_score(playTest.InChI[imgNum], play.InChI[nearVec]))\nprint('levDis ',levDis)\nprint('vecDis ',vecDis)","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"print(playTest.shape)\nplayTest.head()","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"%%time\n\n# Predict on many img\nmodel = VGG16(weights='imagenet', include_top=False)\nrealInchi = []\npredInchi = []\n\nLevDist = []\nVecDist = []\n\nfor imgNum in tqdm(range(playTest.shape[0])):\n\n    img_path = playTest.image_path[imgNum]\n    img = image.load_img(img_path, target_size=(224, 224))\n    x = image.img_to_array(img)\n    x = np.expand_dims(x, axis=0)\n    x = preprocess_input(x)\n    #featOne = model.predict(x)\n    featOne = model.predict(x).ravel()\n\n    # Find the min Euclidean distance to one image vector\n    MyDist = []\n    for v in range(features.shape[0]):\n        \n        #Dist = np.linalg.norm(features[v]-featOne)\n        #Dist = spatial.distance.cosine(features[v],featOne) \n        #Dist = spatial.distance.braycurtis(features[v],featOne) \n        #Dist = spatial.distance.canberra(features[v],featOne) \n        #Dist = spatial.distance.chebyshev(features[v],featOne) # Takes 10 mins for 100 samples...\n        #Dist = spatial.distance.cityblock(features[v],featOne)\n        #Dist = spatial.distance.correlation(features[v],featOne)\n        #Dist = spatial.distance.jensenshannon(features[v],featOne)\n        #Dist = spatial.distance.minkowski(features[v],featOne)\n        #Dist = spatial.distance.dice(features[v],featOne)\n        #Dist = spatial.distance.hamming(features[v],featOne)\n        #Dist = spatial.distance.jaccard(features[v],featOne)\n        Dist = spatial.distance.kulsinski(features[v],featOne)\n        #Dist = spatial.distance.rogerstanimoto(features[v],featOne)\n        #Dist = spatial.distance.russellrao(features[v],featOne)\n        #Dist = spatial.distance.sokalmichener(features[v],featOne)\n        #Dist = spatial.distance.sokalsneath(features[v],featOne)\n        #Dist = spatial.distance.yule(features[v],featOne)\n        \n        MyDist.append(Dist)\n    MyDist=np.array(MyDist)  \n    nearVec = MyDist.argmin()\n    vecDis = MyDist.min()\n\n    realInchi.append(playTest.InChI[imgNum])\n    predInchi.append(play.InChI[nearVec])\n\n    levDis = Levenshtein.distance(playTest.InChI[imgNum], play.InChI[nearVec])\n    LevDist.append(levDis)\n    VecDist.append(vecDis)\n\n\nprint('Average Lev dist ',get_score(realInchi, predInchi))\n","execution_count":null,"outputs":[]},{"metadata":{"trusted":true},"cell_type":"code","source":"plt.scatter(VecDist,LevDist)\nplt.xlabel(\"Dist between Vectors \")\nplt.ylabel(\"Dist between InChI - Levenshtein \")\nplt.title(\"Relationship between Levenshtein and Vector distances\")","execution_count":null,"outputs":[]},{"metadata":{},"cell_type":"markdown","source":"# Results of Levenshtein average on the test w the metrics:\n\n* vectors (7,7,512) with np.linalg.norm Lev = 83.1\n* vectors flattened with np.linalg.norm Lev = 83.1\n* scipy spatial distance euclidean Lev = 83.1\n* cosine Lev = 79.4\n* braycurtis ... 79.2\n* canberra ... 81.9\n* cityblock ... 84.5\n* correlation ... 79.4\n* jensenshannon ... 78.5 \n* minkowski ... 83.1\n* dice ... 77.4\n* hamming ... 92.4\n* jaccard ... 78.7\n* **kulsinski ... 75.7**\n* rogerstanimoto ... 76.8\n* russellrao ... 76.7\n* sokalmichener ... 76.8\n* sokalsneath ... 77.4\n* yule ... 170\n"}],"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}