{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:22:02.247205Z",
     "start_time": "2019-11-17T06:22:02.110589Z"
    }
   },
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "d = 64                           # dimension\n",
    "nb = 100000                      # database size\n",
    "nq = 10000                       # nb of queries\n",
    "np.random.seed(1234)             # make reproducible\n",
    "xb = np.random.random((nb, d)).astype('float32')\n",
    "xb[:, 0] += np.arange(nb) / 1000.\n",
    "xq = np.random.random((nq, d)).astype('float32')\n",
    "xq[:, 0] += np.arange(nq) / 1000."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:22:08.519574Z",
     "start_time": "2019-11-17T06:22:08.489942Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "True\n",
      "100000\n"
     ]
    }
   ],
   "source": [
    "import faiss                   # make faiss available\n",
    "index = faiss.IndexFlatL2(d)   # build the index\n",
    "print(index.is_trained)\n",
    "index.add(xb)                  # add vectors to the index\n",
    "print(index.ntotal)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:25:25.006581Z",
     "start_time": "2019-11-17T06:25:24.334080Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>QUOTE</th>\n",
       "      <th>AUTHOR</th>\n",
       "      <th>GENRE</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>Age is an issue of mind over matter. If you do...</td>\n",
       "      <td>Mark Twain</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>Anyone who stops learning is old, whether at t...</td>\n",
       "      <td>Henry Ford</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>Wrinkles should merely indicate where smiles h...</td>\n",
       "      <td>Mark Twain</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>True terror is to wake up one morning and disc...</td>\n",
       "      <td>Kurt Vonnegut</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>A diplomat is a man who always remembers a wom...</td>\n",
       "      <td>Robert Frost</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>...</th>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "      <td>...</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>216</th>\n",
       "      <td>In the information age, you don't teach philos...</td>\n",
       "      <td>Timothy Leary</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>217</th>\n",
       "      <td>Once I planned to write a book of poems entire...</td>\n",
       "      <td>Gilbert K. Chesterton</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>218</th>\n",
       "      <td>The harvest of old age is the recollection and...</td>\n",
       "      <td>Marcus Tullius Cicero</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>219</th>\n",
       "      <td>It is the spirit of the age to believe that an...</td>\n",
       "      <td>Gore Vidal</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>220</th>\n",
       "      <td>The arrogance of age must submit to be taught ...</td>\n",
       "      <td>Edmund Burke</td>\n",
       "      <td>age</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "<p>221 rows × 3 columns</p>\n",
       "</div>"
      ],
      "text/plain": [
       "                                                 QUOTE                 AUTHOR  \\\n",
       "0    Age is an issue of mind over matter. If you do...             Mark Twain   \n",
       "1    Anyone who stops learning is old, whether at t...             Henry Ford   \n",
       "2    Wrinkles should merely indicate where smiles h...             Mark Twain   \n",
       "3    True terror is to wake up one morning and disc...          Kurt Vonnegut   \n",
       "4    A diplomat is a man who always remembers a wom...           Robert Frost   \n",
       "..                                                 ...                    ...   \n",
       "216  In the information age, you don't teach philos...          Timothy Leary   \n",
       "217  Once I planned to write a book of poems entire...  Gilbert K. Chesterton   \n",
       "218  The harvest of old age is the recollection and...  Marcus Tullius Cicero   \n",
       "219  It is the spirit of the age to believe that an...             Gore Vidal   \n",
       "220  The arrogance of age must submit to be taught ...           Edmund Burke   \n",
       "\n",
       "    GENRE  \n",
       "0     age  \n",
       "1     age  \n",
       "2     age  \n",
       "3     age  \n",
       "4     age  \n",
       "..    ...  \n",
       "216   age  \n",
       "217   age  \n",
       "218   age  \n",
       "219   age  \n",
       "220   age  \n",
       "\n",
       "[221 rows x 3 columns]"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import pandas as pd\n",
    "from bert_serving.client import BertClient\n",
    "\n",
    "# Bert Client must be running locally\n",
    "bc = BertClient()\n",
    "\n",
    "# Read quote data\n",
    "quotes = pd.read_csv('quotes.csv', sep=';', skiprows=1)\n",
    "quotes"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:26:17.548948Z",
     "start_time": "2019-11-17T06:25:57.320190Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "/Users/xiaofeiwu/miniconda3/envs/cv/lib/python3.6/site-packages/bert_serving/client/__init__.py:299: UserWarning: some of your sentences have more tokens than \"max_seq_len=50\" set on the server, as consequence you may get less-accurate or truncated embeddings.\n",
      "here is what you can do:\n",
      "- disable the length-check by create a new \"BertClient(check_length=False)\" when you do not want to display this warning\n",
      "- or, start a new server with a larger \"max_seq_len\"\n",
      "  '- or, start a new server with a larger \"max_seq_len\"' % self.length_limit)\n"
     ]
    }
   ],
   "source": [
    "embeddings = bc.encode(quotes.QUOTE.to_list())\n",
    "quotes['EMBEDDINGS'] = embeddings.tolist()\n",
    "\n",
    "# Persist to pickle\n",
    "quotes.to_pickle('data/embedded_quotes.pkl')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:29:50.577768Z",
     "start_time": "2019-11-17T06:29:50.530143Z"
    }
   },
   "outputs": [],
   "source": [
    "def load_quotes_and_embeddings():\n",
    "    quotes = pd.read_pickle('data/embedded_quotes.pkl')\n",
    "\n",
    "    # change dtype in place for memory efficiency\n",
    "    quotes['EMBEDDINGS'] = quotes['EMBEDDINGS'].apply(\n",
    "        lambda arr: np.array(arr, dtype='float32')\n",
    "    )\n",
    "\n",
    "    quote_embeddings = np.stack(quotes.EMBEDDINGS.values)\n",
    "\n",
    "    # reduce memory footprint by dropping column\n",
    "    quotes.drop('EMBEDDINGS', axis='columns')\n",
    "\n",
    "    # normalize embeddings for cosine distance\n",
    "    embedding_sums = quote_embeddings.sum(axis=1)\n",
    "    normed_embeddings = quote_embeddings / embedding_sums[:, np.newaxis]\n",
    "    return quotes, normed_embeddings\n",
    "\n",
    "quotes, embeddings = load_quotes_and_embeddings()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:30:05.099026Z",
     "start_time": "2019-11-17T06:30:05.084499Z"
    }
   },
   "outputs": [],
   "source": [
    "def create_index(embeddings):\n",
    "    \"\"\"\n",
    "    Create an index over the quote embeddings for fast similarity search.\n",
    "    \"\"\"\n",
    "    dim = embeddings.shape[1]\n",
    "    index = faiss.IndexFlatL2(dim)\n",
    "    index.add(embeddings)\n",
    "    return index\n",
    "\n",
    "index = create_index(embeddings)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:30:50.004329Z",
     "start_time": "2019-11-17T06:30:49.703153Z"
    }
   },
   "outputs": [],
   "source": [
    "bc = BertClient()\n",
    "text=\"I dreamed a dream.\"\n",
    "\n",
    "text_embedding = bc.encode([text])\n",
    "normalized_text_embedding = text_embedding / text_embedding.sum()\n",
    "\n",
    "_, idx = index.search(normalized_text_embedding, 5)\n",
    "\n",
    "relevant_quotes = quotes.iloc[idx.flatten()].QUOTE.values\n",
    "relevant_authors = quotes.iloc[idx.flatten()].AUTHOR.values"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "ExecuteTime": {
     "end_time": "2019-11-17T06:31:23.426588Z",
     "start_time": "2019-11-17T06:31:23.416886Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      ">I don't feel old. I don't feel anything till noon. That's when it's time for my nap.\n",
      "Bob Hope\n",
      ">I think the biggest disease the world suffers from in this day and age is the disease of people feeling unloved. I know that I can give love for a minute, for half an hour, for a day, for a month, but I can give. I am very happy to do that, I want to do that.\n",
      "Princess Diana\n",
      ">Once I planned to write a book of poems entirely about the things in my pocket. But I found it would be too long and the age of the great epics is past.\n",
      "Gilbert K. Chesterton\n",
      ">I don't think of myself as a poor deprived ghetto girl who made good. I think of myself as somebody who from an early age knew I was responsible for myself, and I had to make good.\n",
      "Oprah Winfrey\n",
      ">Dressing up is a bore. At a certain age, you decorate yourself to attract the opposite sex, and at a certain age, I did that. But I'm past that age.\n",
      "Katharine Hepburn\n"
     ]
    }
   ],
   "source": [
    "for q in range(5):\n",
    "    print('>'+relevant_quotes[q])\n",
    "    print(relevant_authors[q])"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.9"
  },
  "varInspector": {
   "cols": {
    "lenName": 16,
    "lenType": 16,
    "lenVar": 40
   },
   "kernels_config": {
    "python": {
     "delete_cmd_postfix": "",
     "delete_cmd_prefix": "del ",
     "library": "var_list.py",
     "varRefreshCmd": "print(var_dic_list())"
    },
    "r": {
     "delete_cmd_postfix": ") ",
     "delete_cmd_prefix": "rm(",
     "library": "var_list.r",
     "varRefreshCmd": "cat(var_dic_list()) "
    }
   },
   "types_to_exclude": [
    "module",
    "function",
    "builtin_function_or_method",
    "instance",
    "_Feature"
   ],
   "window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
