mirror of
https://github.com/qdrant/fastembed.git
synced 2026-07-25 20:21:10 -05:00
* chore: formatting * chore: formatting * chore: remove other hooks * Update poetry lock --------- Co-authored-by: Nirant Kasliwal <nirant.bits@gmail.com>
329 lines
42 KiB
Plaintext
329 lines
42 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"# 🤗 Huggingface vs ⚡ FastEmbed️\n",
|
||
"\n",
|
||
"Comparing the performance of Huggingface's 🤗 Transformers and ⚡ FastEmbed️ on a simple task on the following machine: Apple M2 Max, 32 GB RAM\n",
|
||
"\n",
|
||
"## 📦 Imports\n",
|
||
"\n",
|
||
"Importing the necessary libraries for this comparison."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import time\n",
|
||
"from typing import Callable, List, Tuple\n",
|
||
"\n",
|
||
"import torch.nn.functional as F\n",
|
||
"from fastembed.embedding import DefaultEmbedding\n",
|
||
"import matplotlib.pyplot as plt\n",
|
||
"from torch import Tensor\n",
|
||
"from transformers import AutoModel, AutoTokenizer"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 📖 Data\n",
|
||
"\n",
|
||
"data is a list of strings, each string is a document."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"12"
|
||
]
|
||
},
|
||
"execution_count": 2,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"documents: List[str] = [\n",
|
||
" \"Chandrayaan-3 is India's third lunar mission\",\n",
|
||
" \"It aimed to land a rover on the Moon's surface - joining the US, China and Russia\",\n",
|
||
" \"The mission is a follow-up to Chandrayaan-2, which had partial success\",\n",
|
||
" \"Chandrayaan-3 will be launched by the Indian Space Research Organisation (ISRO)\",\n",
|
||
" \"The estimated cost of the mission is around $35 million\",\n",
|
||
" \"It will carry instruments to study the lunar surface and atmosphere\",\n",
|
||
" \"Chandrayaan-3 landed on the Moon's surface on 23rd August 2023\",\n",
|
||
" \"It consists of a lander named Vikram and a rover named Pragyan similar to Chandrayaan-2. Its propulsion module would act like an orbiter.\",\n",
|
||
" \"The propulsion module carries the lander and rover configuration until the spacecraft is in a 100-kilometre (62 mi) lunar orbit\",\n",
|
||
" \"The mission used GSLV Mk III rocket for its launch\",\n",
|
||
" \"Chandrayaan-3 was launched from the Satish Dhawan Space Centre in Sriharikota\",\n",
|
||
" \"Chandrayaan-3 was launched earlier in the year 2023\",\n",
|
||
"]\n",
|
||
"len(documents)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Setting up 🤗 Huggingface\n",
|
||
"\n",
|
||
"We'll be using the [Huggingface Transformers](https://huggingface.co/transformers/) with PyTorch library to generate embeddings. We'll be using the same model across both libraries for a fair(er?) comparison."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"torch.Size([12, 384])"
|
||
]
|
||
},
|
||
"execution_count": 3,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"class HF:\n",
|
||
" \"\"\"\n",
|
||
" HuggingFace Transformer implementation of FlagEmbedding\n",
|
||
" \"\"\"\n",
|
||
"\n",
|
||
" def __init__(self, model_id: str):\n",
|
||
" self.model = AutoModel.from_pretrained(model_id)\n",
|
||
" self.tokenizer = AutoTokenizer.from_pretrained(model_id)\n",
|
||
"\n",
|
||
" def embed(self, texts: List[str]):\n",
|
||
" encoded_input = self.tokenizer(texts, max_length=512, padding=True, truncation=True, return_tensors=\"pt\")\n",
|
||
" model_output = self.model(**encoded_input)\n",
|
||
" sentence_embeddings = model_output[0][:, 0]\n",
|
||
" sentence_embeddings = F.normalize(sentence_embeddings)\n",
|
||
" return sentence_embeddings\n",
|
||
"\n",
|
||
"\n",
|
||
"hf = HF(model_id=\"BAAI/bge-small-en-v1.5\")\n",
|
||
"hf.embed(documents).shape"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Setting up ⚡️FastEmbed\n",
|
||
"\n",
|
||
"Sorry, don't have a lot to set up here. We'll be using the default model, which is Flag Embedding, same as the Huggingface model."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 4,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"embedding_model = DefaultEmbedding()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 📊 Comparison\n",
|
||
"\n",
|
||
"We'll be comparing the following metrics: Minimum, Maximum, Mean, across k runs. Let's write a function to do that:\n",
|
||
"\n",
|
||
"### 🚀 Calculating Stats"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 5,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Huggingface Transformers (Average, Max, Min): (0.0635751485824585, 0.06534004211425781, 0.06181025505065918)\n",
|
||
"FastEmbed (Average, Max, Min): (0.03929698467254639, 0.039344072341918945, 0.03924989700317383)\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"import types\n",
|
||
"\n",
|
||
"\n",
|
||
"def calculate_time_stats(embed_func: Callable, documents: list, k: int) -> Tuple[float, float, float]:\n",
|
||
" times = []\n",
|
||
" for _ in range(k):\n",
|
||
" # Timing the embed_func call\n",
|
||
" start_time = time.time()\n",
|
||
" embeddings = embed_func(documents)\n",
|
||
" # Force computation if embed_func returns a generator\n",
|
||
" if isinstance(embeddings, types.GeneratorType):\n",
|
||
" embeddings = list(embeddings)\n",
|
||
"\n",
|
||
" end_time = time.time()\n",
|
||
" times.append(end_time - start_time)\n",
|
||
"\n",
|
||
" # Returning mean, max, and min time for the call\n",
|
||
" return (sum(times) / k, max(times), min(times))\n",
|
||
"\n",
|
||
"\n",
|
||
"hf_stats = calculate_time_stats(hf.embed, documents, k=2)\n",
|
||
"print(f\"Huggingface Transformers (Average, Max, Min): {hf_stats}\")\n",
|
||
"fst_stats = calculate_time_stats(embedding_model.embed, documents, k=2)\n",
|
||
"print(f\"FastEmbed (Average, Max, Min): {fst_stats}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 📈 Results\n",
|
||
"\n",
|
||
"Let's run the comparison and see the results."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAk0AAAGzCAYAAAAyiiOsAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8pXeV/AAAACXBIWXMAAA9hAAAPYQGoP6dpAABdnElEQVR4nO3dd1QU198G8GcB6SxNiogCoiIo2Av2ghR7iT0RrIm9RE1MImqMIbaoMcYSEzWW2GOs2HsXxY4FRWwUpQkoCNz3D1/m57qgs7rIJnk+53CSvXPnzneW3eVx5s6sQgghQERERERvpFfcBRARERH9EzA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNFGRUigUGDp0aHGXQQTg5etx0qRJxV0GAXB1dUVISEhxl/GPEBISAldX1+Iug8DQRO8oOjoan376KcqVKwdjY2MolUo0aNAAc+fOxbNnz4q7vPf28OFDTJo0CZGRkcVdiopJkyZBoVBIP6ampvDy8sI333yDtLS04i6PtCgmJgZ9+vSBu7s7jI2N4ejoiMaNG2PixInFXdoH9/z5c8yePRt169aFpaUljI2NUbFiRQwdOhQ3btwo7vLoP8SguAugf57t27ejS5cuMDIyQu/evVGlShVkZ2fj6NGjGDt2LK5cuYLFixcXd5nv5eHDh5g8eTJcXV1RrVq14i5HzYIFC2Bubo709HTs3r0bU6dOxf79+3Hs2DEoFIriLo/e061bt1C7dm2YmJigb9++cHV1xaNHj3Du3DlMmzYNkydPLu4SP5jHjx8jMDAQERERaNOmDXr27Alzc3Ncv34da9asweLFi5GdnV3cZRapX3/9FXl5ecVdBoGhiTR0584ddO/eHS4uLti/fz9KlSolLRsyZAhu3bqF7du3f9CaMjIyYGZm9kG3+a60VetHH32EkiVLAgA+++wzdO7cGZs2bcLJkyfh6+tb4DqZmZkwNTV9722TdrzptTB79mykp6cjMjISLi4uKssSEhI+RHk6IyQkBOfPn8eGDRvQuXNnlWVTpkzB119/XUyVFb3810iJEiWKuxT6fzw9RxqZPn060tPT8dtvv6kEpnzly5fHiBEj1No3b96MKlWqwMjICJUrV0Z4eLjK8rt372Lw4MHw8PCAiYkJbG1t0aVLF8TExKj0W7ZsGRQKBQ4dOoTBgwfD3t4ezs7OGo0BACkpKRg1ahRcXV1hZGQEZ2dn9O7dG48fP8bBgwdRu3ZtAECfPn2kU2HLli2T1j916hQCAwNhaWkJU1NTNGnSBMeOHVPZRv6ptKtXr6Jnz56wtrZGw4YNAQBxcXHo06cPnJ2dYWRkhFKlSqF9+/YF1ipH8+bNAbwMtQDQtGlTVKlSBREREWjcuDFMTU3x1VdfAXj5R7dfv35wcHCAsbExqlatiuXLl6uNmZeXh7lz58Lb2xvGxsaws7NDYGAgzp49q9Jv5cqVqFmzJkxMTGBjY4Pu3bvj3r17Kn1u3ryJzp07w9HREcbGxnB2dkb37t2Rmpoq9dmzZw8aNmwIKysrmJubw8PDQ6o5X1ZWFiZOnIjy5cvDyMgIZcqUwbhx45CVlaXWb9SoUbCzs4OFhQXatWuH+/fvy3ouDx48CIVCgbVr1+Krr76Co6MjzMzM0K5dO7X9At7/tVCQ6OhoODs7qwUmALC3t1dr27lzJxo1agQzMzNYWFigdevWuHLlilq/qKgodO3aFXZ2djAxMYGHh4da6Dh//jyCgoKgVCphbm6OFi1a4OTJkyp98t+Hx44dw+jRo2FnZwczMzN07NgRiYmJKn2FEPjuu+/g7OwMU1NTNGvWrMDaCnLq1Cls374d/fr1UwtMAGBkZISZM2eqtO3fv196LqysrNC+fXtcu3ZNpU/+7+PGjRv4+OOPYWlpCTs7O0yYMAFCCNy7dw/t27eHUqmEo6MjZs2apbK+Jq+RI0eOoEuXLihbtqz0mh01apTaNIaQkBCYm5sjOjoarVq1goWFBXr16iUte31O05o1a1CzZk1YWFhAqVTC29sbc+fOVelz+/ZtdOnSBTY2NjA1NUW9evXU/lGbvy/r1q3D1KlT4ezsDGNjY7Ro0QK3bt0q5Dfz38UjTaSRrVu3oly5cqhfv77sdY4ePYpNmzZh8ODBsLCwwE8//YTOnTsjNjYWtra2AIAzZ87g+PHj6N69O5ydnRETE4MFCxagadOmuHr1qtoRksGDB8POzg6hoaHIyMjQaIz09HQ0atQI165dQ9++fVGjRg08fvwYW7Zswf379+Hp6Ylvv/0WoaGhGDhwIBo1agQA0j7v378fQUFBqFmzJiZOnAg9PT0sXboUzZs3x5EjR1CnTh2VWrt06YIKFSrg+++/hxACANC5c2dcuXIFw4YNg6urKxISErBnzx7Exsa+04TP6OhoAJCeTwB48uQJgoKC0L17d3z88cdwcHDAs2fP0LRpU9y6dQtDhw6Fm5sb1q9fj5CQEKSkpKgE3n79+mHZsmUICgpC//79kZOTgyNHjuDkyZOoVasWAGDq1KmYMGECunbtiv79+yMxMRHz5s1D48aNcf78eVhZWSE7OxsBAQHIysrCsGHD4OjoiAcPHmDbtm1ISUmBpaUlrly5gjZt2sDHxwfffvstjIyMcOvWLZXwkZeXh3bt2uHo0aMYOHAgPD09cenSJcyePRs3btzA5s2bpb79+/fHypUr0bNnT9SvXx/79+9H69atNXpOp06dCoVCgS+++AIJCQmYM2cO/Pz8EBkZCRMTEwDaeS0UxMXFBXv37sX+/fulQFyYFStWIDg4GAEBAZg2bRoyMzOxYMECNGzYEOfPn5deTxcvXkSjRo1QokQJDBw4EK6uroiOjsbWrVsxdepUAMCVK1fQqFEjKJVKjBs3DiVKlMCiRYvQtGlTHDp0CHXr1lXZ9rBhw2BtbY2JEyciJiYGc+bMwdChQ7F27VqpT2hoKL777ju0atUKrVq1wrlz5+Dv7y/rlNqWLVsAAJ988slb+wLA3r17ERQUhHLlymHSpEl49uwZ5s2bhwYNGuDcuXNq761u3brB09MTP/zwA7Zv347vvvsONjY2WLRoEZo3b45p06Zh1apVGDNmDGrXro3GjRurrC/nNbJ+/XpkZmZi0KBBsLW1xenTpzFv3jzcv38f69evVxkvJycHAQEBaNiwIWbOnFnokeE9e/agR48eaNGiBaZNmwYAuHbtGo4dOya9h+Pj41G/fn1kZmZi+PDhsLW1xfLly9GuXTts2LABHTt2VBnzhx9+gJ6eHsaMGYPU1FRMnz4dvXr1wqlTp2Q99/8Zgkim1NRUAUC0b99e9joAhKGhobh165bUduHCBQFAzJs3T2rLzMxUW/fEiRMCgPjjjz+ktqVLlwoAomHDhiInJ0elv9wxQkNDBQCxadMmtf55eXlCCCHOnDkjAIilS5eqLa9QoYIICAiQ+uZv283NTbRs2VJqmzhxogAgevTooTJGcnKyACBmzJihtv23yR/z+vXrIjExUdy5c0csWrRIGBkZCQcHB5GRkSGEEKJJkyYCgFi4cKHK+nPmzBEAxMqVK6W27Oxs4evrK8zNzUVaWpoQQoj9+/cLAGL48OGFPkcxMTFCX19fTJ06VWX5pUuXhIGBgdR+/vx5AUCsX7++0P2aPXu2ACASExML7bNixQqhp6cnjhw5otK+cOFCAUAcO3ZMCCFEZGSkACAGDx6s0q9nz54CgJg4cWKh2xBCiAMHDggAonTp0tLzIYQQ69atEwDE3LlzpefhfV8Lhbl8+bIwMTERAES1atXEiBEjxObNm6Xfb76nT58KKysrMWDAAJX2uLg4YWlpqdLeuHFjYWFhIe7evavS99XaO3ToIAwNDUV0dLTU9vDhQ2FhYSEaN24steW/D/38/FTWHzVqlNDX1xcpKSlCCCESEhKEoaGhaN26tUq/r776SgAQwcHBb3weOnbsKACI5OTkN/bLV61aNWFvby+ePHkitV24cEHo6emJ3r17S235v4+BAwdKbTk5OcLZ2VkoFArxww8/SO3JycnCxMREpVa5rxEhCv5cCgsLEwqFQuV3ERwcLACIL7/8Uq1/cHCwcHFxkR6PGDFCKJVKtc/AV40cOVIAUHm/PH36VLi5uQlXV1eRm5ursi+enp4iKytL6jt37lwBQFy6dKnQbfwX8fQcyZZ/dZaFhYVG6/n5+cHd3V167OPjA6VSidu3b0tt+f8qA4AXL17gyZMnKF++PKysrHDu3Dm1MQcMGAB9fX2VNrljbNy4EVWrVlX7lxaAt06ijoyMxM2bN9GzZ088efIEjx8/xuPHj5GRkYEWLVrg8OHDahM2P/vsM7U6DQ0NcfDgQSQnJ79xe4Xx8PCAnZ0d3Nzc8Omnn6J8+fLYvn27yr9MjYyM0KdPH5X1duzYAUdHR/To0UNqK1GiBIYPH4709HQcOnQIwMvnSKFQFHilVv5ztGnTJuTl5aFr167S8/D48WM4OjqiQoUKOHDgAADA0tISALBr1y5kZmYWuD9WVlYAgL///rvQCa/r16+Hp6cnKlWqpLK9/CMx+dvbsWMHAGD48OEq648cObLAcQvTu3dvldf6Rx99hFKlSknja+O1UJjKlSsjMjISH3/8MWJiYjB37lx06NABDg4O+PXXX6V+e/bsQUpKCnr06KHynOjr66Nu3brSc5KYmIjDhw+jb9++KFu2rMq28n+fubm52L17Nzp06IBy5cpJy0uVKoWePXvi6NGjaldoDhw4UOU906hRI+Tm5uLu3bsAXh75yc7OxrBhw1T6yf1daPKZ8+jRI0RGRiIkJAQ2NjZSu4+PD1q2bCn93l7Vv39/6f/19fVRq1YtCCHQr18/qd3KygoeHh4qn1f53vYaAVQ/lzIyMvD48WPUr18fQgicP39ebcxBgwa9dV+trKyQkZGBPXv2FNpnx44dqFOnjsppYHNzcwwcOBAxMTG4evWqSv8+ffrA0NBQepx/hL2g/f4v4+k5kk2pVAIAnj59qtF6r39IA4C1tbVKYHj27BnCwsKwdOlSPHjwQOXUxavzXvK5ubmptckdIzo6usD5EXLcvHkTABAcHFxon9TUVFhbWxdaq5GREaZNm4bPP/8cDg4OqFevHtq0aYPevXvD0dFRVh0bN26EUqlEiRIl4OzsrBJK85UuXVrlQxB4Oe+rQoUK0NNT/feSp6entBx4+Rw5OTmp/PF53c2bNyGEQIUKFQpcnj951c3NDaNHj8aPP/6IVatWoVGjRmjXrp00lwR4eZpkyZIl6N+/P7788ku0aNECnTp1wkcffSTVevPmTVy7dg12dnYFbi9/gvTdu3ehp6en9px4eHgUui8FeX2/FAoFypcvL80708Zr4U0qVqyIFStWIDc3F1evXsW2bdswffp0DBw4EG5ubvDz85NqKOwUXv57Nv8PX5UqVQrdXmJiIjIzMwt8njw9PZGXl4d79+6hcuXKUvvr7+38fc1/b+e/nl5/Lu3s7FSel8K8+pmTH6wLk7+twurftWuX2uT71+vPv51B/kUWr7Y/efJEbdy3vUYAIDY2FqGhodiyZYvaP5Je/2wzMDCQ5mi+yeDBg7Fu3ToEBQWhdOnS8Pf3R9euXREYGCj1uXv3rtrpVED1vf7q6+Ftv0t6iaGJZFMqlXBycsLly5c1Wu/1I0L5Xg01w4YNw9KlSzFy5Ej4+vrC0tISCoUC3bt3L/DIw6v/envXMd5F/jgzZswo9FYE5ubmb6115MiRaNu2LTZv3oxdu3ZhwoQJCAsLw/79+1G9evW31tG4cWO1D/bXFbRdbcrLy4NCocDOnTsL/B2/+jzMmjULISEh+Pvvv7F7924MHz4cYWFhOHnyJJydnWFiYoLDhw/jwIED2L59O8LDw7F27Vo0b94cu3fvhr6+PvLy8uDt7Y0ff/yxwHrKlClTZPtaEG29Ft5GX18f3t7e8Pb2hq+vL5o1a4ZVq1bBz89PqmHFihUFBm4Dg6L9iJfz3n4flSpVAgBcunRJOvKhTQXVr819ys3NRcuWLZGUlIQvvvgClSpVgpmZGR48eICQkBC1zyUjIyO1f9AUxN7eHpGRkdi1axd27tyJnTt3YunSpejdu3eBF3XIUdS/y38LhibSSJs2bbB48WKcOHGi0Evb38WGDRsQHByscpXK8+fPkZKSovUx3N3d3xr8CjtNl3/0QqlUws/PT3ZthY31+eef4/PPP8fNmzdRrVo1zJo1CytXrnyvcd/ExcUFFy9eRF5ensqHc1RUlLQ8v7Zdu3YhKSmp0KNN7u7uEELAzc0NFStWfOu28//wf/PNNzh+/DgaNGiAhQsX4rvvvgMA6OnpoUWLFmjRogV+/PFHfP/99/j6669x4MAB6RTvhQsX0KJFizeeRnVxcUFeXh6io6NVjjpcv3797U/QK/KP4uQTQuDWrVvw8fGR9h/QzmtBrvwJ+I8ePVKpwd7e/o015J9ue9Pr3s7ODqampgU+T1FRUdDT09M4mOa/nm7evKlyyi8xMVHWEYy2bdsiLCwMK1eufGtoyt9WYfWXLFlS67cmedtr5NKlS7hx4waWL1+O3r17S/3edFpNLkNDQ7Rt2xZt27ZFXl4eBg8ejEWLFmHChAkoX748XFxcCn0uABR4ZSa9Hec0kUbGjRsHMzMz9O/fH/Hx8WrLo6Oj1S57lUNfX1/tXzTz5s1Dbm6u1sfo3LkzLly4gL/++kttjPz18z9cXw9cNWvWhLu7O2bOnIn09HS19V+/3LogmZmZeP78uUqbu7s7LCws1C6d17ZWrVohLi5O5eqmnJwczJs3D+bm5mjSpAmAl8+REKLAmyjmP0edOnWCvr4+Jk+erPa8CyGk0xlpaWnIyclRWe7t7Q09PT1pf5OSktS2k3/0Jr9P165d8eDBA5U5PfmePXsmXUUZFBQEAPjpp59U+syZM6eAZ6Rwf/zxh8qp6A0bNuDRo0fS+Np4LRTmyJEjePHihVp7/lyZ/DAYEBAApVKJ77//vsD++TXY2dmhcePG+P333xEbG6vSJ/93p6+vD39/f/z9998qp5fi4+OxevVqNGzYUDpdJpefnx9KlCiBefPmqbxG5P4ufH19ERgYiCVLlqhcHZkvOzsbY8aMAfBy7lW1atWwfPlylfft5cuXsXv3brRq1Uqj2uV422sk/+jNq/suhHinz8hXvX6qUE9PTwpq+e+XVq1a4fTp0zhx4oTULyMjA4sXL4arqyu8vLzeq4b/Kh5pIo24u7tj9erV0qW6r94R/Pjx49Ll65pq06YNVqxYAUtLS3h5eeHEiRPYu3evyiX02hpj7Nix2LBhA7p06YK+ffuiZs2aSEpKwpYtW7Bw4UJUrVoV7u7usLKywsKFC2FhYQEzMzPUrVsXbm5uWLJkCYKCglC5cmX06dMHpUuXxoMHD3DgwAEolUps3br1jXXeuHEDLVq0QNeuXeHl5QUDAwP89ddfiI+PR/fu3TV+7jQxcOBALFq0CCEhIYiIiICrqys2bNiAY8eOYc6cOdKk1mbNmuGTTz7BTz/9hJs3byIwMBB5eXk4cuQImjVrhqFDh8Ld3R3fffcdxo8fj5iYGHTo0AEWFha4c+cO/vrrLwwcOBBjxozB/v37MXToUHTp0gUVK1ZETk4OVqxYAX19fWlu2bfffovDhw+jdevWcHFxQUJCAn755Rc4OztLE1k/+eQTrFu3Dp999hkOHDiABg0aIDc3F1FRUVi3bh127dqFWrVqoVq1aujRowd++eUXpKamon79+ti3b5/G95yxsbFBw4YN0adPH8THx2POnDkoX748BgwYAODlH6r3fS0UZtq0aYiIiECnTp2kP4bnzp3DH3/8ARsbG2kitVKpxIIFC/DJJ5+gRo0a6N69O+zs7BAbG4vt27ejQYMG+PnnnwG8DJENGzZEjRo1pHlRMTEx2L59u/R1Qd999510v6zBgwfDwMAAixYtQlZWFqZPn67xftjZ2WHMmDEICwtDmzZt0KpVK5w/fx47d+586+nlfH/88Qf8/f3RqVMntG3bFi1atICZmRlu3ryJNWvW4NGjR9K9mmbMmIGgoCD4+vqiX79+0i0HLC0ti+Q7B9/2GqlUqRLc3d0xZswYPHjwAEqlEhs3bnzveUL9+/dHUlISmjdvDmdnZ9y9exfz5s1DtWrVpDlLX375Jf78808EBQVh+PDhsLGxwfLly3Hnzh1s3LhR1mlAKsAHvFKP/kVu3LghBgwYIFxdXYWhoaGwsLAQDRo0EPPmzRPPnz+X+gEQQ4YMUVvfxcVF5RLe5ORk0adPH1GyZElhbm4uAgICRFRUlFq//Eudz5w5ozam3DGEEOLJkydi6NChonTp0sLQ0FA4OzuL4OBg8fjxY6nP33//Lby8vISBgYHa7QfOnz8vOnXqJGxtbYWRkZFwcXERXbt2Ffv27ZP65F/W/Ppl9I8fPxZDhgwRlSpVEmZmZsLS0lLUrVtXrFu37m1Pe6Fjvq5JkyaicuXKBS6Lj4+XnidDQ0Ph7e2tdmsFIV5egj1jxgxRqVIlYWhoKOzs7ERQUJCIiIhQ6bdx40bRsGFDYWZmJszMzESlSpXEkCFDxPXr14UQQty+fVv07dtXuLu7C2NjY2FjYyOaNWsm9u7dK42xb98+0b59e+Hk5CQMDQ2Fk5OT6NGjh7hx44bKtrKzs8W0adNE5cqVhZGRkbC2thY1a9YUkydPFqmpqVK/Z8+eieHDhwtbW1thZmYm2rZtK+7du6fRLQf+/PNPMX78eGFvby9MTExE69at1S7XF+L9XguFOXbsmBgyZIioUqWKsLS0FCVKlBBly5YVISEhKrcDeLXmgIAAYWlpKYyNjYW7u7sICQkRZ8+eVel3+fJl0bFjR2FlZSWMjY2Fh4eHmDBhgkqfc+fOiYCAAGFubi5MTU1Fs2bNxPHjx1X6FPY+zH/uDhw4ILXl5uaKyZMni1KlSgkTExPRtGlTcfny5QLfl4XJzMwUM2fOFLVr1xbm5ubC0NBQVKhQQQwbNkzldiZCCLF3717RoEEDYWJiIpRKpWjbtq24evWqSp/Cfh/BwcHCzMxMbfuvv580eY1cvXpV+Pn5CXNzc1GyZEkxYMAA6bYrr77vCtt2/rJXbzmwYcMG4e/vL+zt7YWhoaEoW7as+PTTT8WjR49U1ouOjhYfffSR9PuuU6eO2LZtm0qf/H15/ZYgd+7cKfC2K/91CiE4y4uIKN/BgwfRrFkzrF+/Hh999FFxl0M6iK+R/y4enyMiIiKSgaGJiIiISAaGJiIiIiIZOKeJiIiISAYeaSIiIiKSgaGJiIiISAbe3FJL8vLy8PDhQ1hYWLzxKx6IiIhIdwgh8PTpUzg5Ob31pp8MTVry8OHDD/6FoURERKQd9+7dg7Oz8xv7MDRpSf7XT9y7d0/j72ciIiJVl6PuY8ueczgTeQcP4pNhZWEKH88yGNq3JVydX34FS15eHrbuicTeo1cQdesRUp9morSjNYKa+iC4a0MYGZZQGfNJcjrmLNmFw6euIzMzC25l7dC/RxP4N/FW237841TM+GUHTkTcQp4QqF3VDeMGtYaz0/++wDouIQV/hUfgyKnruPvgCfT19FDe1QEDezVFvZrl37qPD+KSEfTxzAKXTfu6G4Ka+WjylNE7SktLQ5kyZaS/42/Cq+e0JC0tDZaWlkhNTWVoIiJ6T4O+/A1nL9xG6xbVUam8ExKfpGH5+sPIfJaFv37/HB7uTsjIzELlpmNQvYorWjSsAlsbc5y7FION20+hTvXy+POXYdJ0iafpz9A2eAYeJz1Fn25NYGerxLa953H6/C3M/TYY7QNrSdvOyMxCm0+m4WnGc/Tv2RwGBnr4/c+DEEJgx8ovYW318gu9l687hLB5f8O/iQ9qVS2HnNw8bNpxGpej7mH6hF7o2rbeG/fx3sMnaNRhEtr510SzBpVVltWu5g7nUjaFrEnapNHf7+L7Bpd/l9TUVAFA5fuviIjo3Zy9EC2ysl+otN2+Gy8qNBgpRkxYJoQQIiv7hTh7Qf27+Ob8ukO41B4qjpy6JrUt/GOPcKk9VBw7HSW15ebminbB00WtwK9UtrVg+cu+kVdipLabdx6JcvWGi2nz/5bart96KJ4kP1XZ9vOsbNG8yxRRr/U3b93H2AePhUvtoWLRir1v7UtFR5O/37x6joiIdE5Nn3IwLKE6g8StrD0qliuFWzHxAADDEgao6VNObd2AplUBALfuxEttZyKjYWttjvq1PaQ2PT09tPargcQnaTh17pbUvnP/eVT1KouqXi5SW3lXR9SvVRHb956X2iq6l4KNlbnKto0MS6BZfS88SkhBesZz2fub+SwL2S9yZPen4sHQRERE/whCCDxOegprS7M39kt8kgYA0mk0AMjKzoGxUQm1vibGL9suRcUCeDlP6tqth/D2LKvWt1plF9y9//itYSjxyVOYGBvCxNjwzTv0/+Yu2QmvJmPg0XA02gXPwOGT12StRx8eQxMREf0jbA4/i7iEFLRtWeON/Rat2AsLM2M0re8ltbm7OOBRQgruP0pS6Xs6MhoAEJ+QCgBISctEdnYO7Etaqo1rX/LlfJf4x6mFbjvmXiLCD15AULOq0Nd/859YPT0FGtWthPHDOmDJrIGYMKoTniQ/RcjIBdh/9PIb16XiwavniIhI592KiUPo9HWo4e2Gzq3rFtpv/tJdOHr6OqaM6wpLC1OpvVt7X6zadBRDvvodoaM6oaSNBbbtPY9dBy8CAJ5nvVD57+unBgFIV+Pl93nds+fZGDz+dxgblcAXQ9u/dZ9KO9pgxbwhKm2dgmrDr9tUfDf3LzRvWOWtY9CHxSNNRESk0xIep6HvqEWwMDfBgh/6FXoEZ+ueCMxcuB3d2vnik48aqSzzrFAac6cEI/b+Y3TuPxtNOn2LZWsPIXRUZwCAqakRAEin8AqaX5SV/UKlz6tyc/Mw7OuluHUnDgvC+sLBTv1IlRxWlmbo0rYebt9NwKP45Hcag4oOjzQREZHOSkt/hpCRC5D2NBPrF48sNIwcORWFzyetRPMGlTH1y24F9mnVojr8Gnvj2s0HyM3NQ5VKZXAy4iYAoFxZOwCAldIUhoYGSCjgFFzC45dzpRwKOHX35fd/Yt/RK5jzbW+VyebvwsnBCsDLU4WlHKzfayzSLoYmIiLSSc+zXqD/6EW4E5uAlT8PRYVypQrsd/5yDD4d9yu8Pctg/vd9YGCgX+iYhiUMVK6KO3b6OgCgQe1KAF5eUVfJ3QmXrsWqrRt55S7Kli4JczNjlfbvf9qM9VtPInR0Z7QPqKW2nqZiHzwBANham7+lJ31oPD1HREQ6Jzc3D0O/Xopzl+7gl7C+qOnjVmC/W3fi0HfUQjiXssXvP34GY5lXrAHAndgErPrrGFo0rIJyLvZSe1DzarhwNRYXr/4vOEXfjcfxszfQqkU1lTEWrdiLxSv3YUiIP/p2b1rottLSn+FWTBzS0p9JbU+Sn6r1i0tIwbqtJ1GpvFOBk9GpePFIExER6Zzv5v6FvYcvwa9RFaSkZeKvnWdUlncMqo30jOfoPfwXpD7NxMCPW2D/sSsqfcqWLqkStvy6TUWrFtVQ2sEG9x4+wcqNR2CpNFU7nffJR42w5u/j6Dt6IQb0ag4DA338tvoAStpYYECv5lK/8AMXEDbvb7iVsUN5N0e1GhvW8YCd7csr7nYdvICx367CjNBe6NLm5Z3Cw+b9jdj7j1G/dkU42Fni/sMkrP7rGJ49y8bEzz96/yeRtI6hiYiIdM7VG/cBAHuPXMbeI+qX33cMqo3k1Aw8/P/J0tPmb1Hr07l1HZXQ5FnBCRu2nnp5rycrM7T2q4FRA1uhpI3qd46ZmxljzYLhmDJ7E37+fRfyhEC9GhUwYVQn2Fr/r++1mw8AAHfuJWLUxD/Utv/nguFSaCpIo7qVsOrBMazYcASpaZlQWpiiTnV3DOsbiCqV+AXwuojfPacl/O45IiKifx5N/n5zThMRERGRDAxNRERERDJwThMRkY647dqmuEsg0mnlYrYV6/Z5pImIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikqFYQ1NYWBhq164NCwsL2Nvbo0OHDrh+/bpKn+fPn2PIkCGwtbWFubk5OnfujPj4eJU+sbGxaN26NUxNTWFvb4+xY8ciJydHpc/BgwdRo0YNGBkZoXz58li2bJlaPfPnz4erqyuMjY1Rt25dnD59Wuv7TERERP9MxRqaDh06hCFDhuDkyZPYs2cPXrx4AX9/f2RkZEh9Ro0aha1bt2L9+vU4dOgQHj58iE6dOknLc3Nz0bp1a2RnZ+P48eNYvnw5li1bhtDQUKnPnTt30Lp1azRr1gyRkZEYOXIk+vfvj127dkl91q5di9GjR2PixIk4d+4cqlatioCAACQkJHyYJ4OIiIh0mkIIIYq7iHyJiYmwt7fHoUOH0LhxY6SmpsLOzg6rV6/GRx99BACIioqCp6cnTpw4gXr16mHnzp1o06YNHj58CAcHBwDAwoUL8cUXXyAxMRGGhob44osvsH37dly+fFnaVvfu3ZGSkoLw8HAAQN26dVG7dm38/PPPAIC8vDyUKVMGw4YNw5dffvnW2tPS0mBpaYnU1FQolUptPzVE9B9w27VNcZdApNPKxWzT+pia/P3WqTlNqampAAAbGxsAQEREBF68eAE/Pz+pT6VKlVC2bFmcOHECAHDixAl4e3tLgQkAAgICkJaWhitXrkh9Xh0jv0/+GNnZ2YiIiFDpo6enBz8/P6nP67KyspCWlqbyQ0RERP9eOhOa8vLyMHLkSDRo0ABVqlQBAMTFxcHQ0BBWVlYqfR0cHBAXFyf1eTUw5S/PX/amPmlpaXj27BkeP36M3NzcAvvkj/G6sLAwWFpaSj9lypR5tx0nIiKifwSdCU1DhgzB5cuXsWbNmuIuRZbx48cjNTVV+rl3715xl0RERERFyKC4CwCAoUOHYtu2bTh8+DCcnZ2ldkdHR2RnZyMlJUXlaFN8fDwcHR2lPq9f5ZZ/dd2rfV6/4i4+Ph5KpRImJibQ19eHvr5+gX3yx3idkZERjIyM3m2HiYiI6B+nWI80CSEwdOhQ/PXXX9i/fz/c3NxUltesWRMlSpTAvn37pLbr168jNjYWvr6+AABfX19cunRJ5Sq3PXv2QKlUwsvLS+rz6hj5ffLHMDQ0RM2aNVX65OXlYd++fVIfIiIi+m8r1iNNQ4YMwerVq/H333/DwsJCmj9kaWkJExMTWFpaol+/fhg9ejRsbGygVCoxbNgw+Pr6ol69egAAf39/eHl54ZNPPsH06dMRFxeHb775BkOGDJGOBH322Wf4+eefMW7cOPTt2xf79+/HunXrsH37dqmW0aNHIzg4GLVq1UKdOnUwZ84cZGRkoE+fPh/+iSEiIiKdU6yhacGCBQCApk2bqrQvXboUISEhAIDZs2dDT08PnTt3RlZWFgICAvDLL79IffX19bFt2zYMGjQIvr6+MDMzQ3BwML799lupj5ubG7Zv345Ro0Zh7ty5cHZ2xpIlSxAQECD16datGxITExEaGoq4uDhUq1YN4eHhapPDiYiI6L9Jp+7T9E/G+zQR0fvifZqI3oz3aSIiIiL6B2BoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBgM5nUaPHi17wB9//PGdiyEiIiLSVbJC0/nz51Uenzt3Djk5OfDw8AAA3LhxA/r6+qhZs6b2KyQiIiLSAbJC04EDB6T///HHH2FhYYHly5fD2toaAJCcnIw+ffqgUaNGRVMlERERUTFTCCGEJiuULl0au3fvRuXKlVXaL1++DH9/fzx8+FCrBf5TpKWlwdLSEqmpqVAqlcVdDhH9A912bVPcJRDptHIx27Q+piZ/vzWeCJ6WlobExES19sTERDx9+lTT4YiIiIj+ETQOTR07dkSfPn2wadMm3L9/H/fv38fGjRvRr18/dOrUqShqJCIiIip2suY0vWrhwoUYM2YMevbsiRcvXrwcxMAA/fr1w4wZM7ReIBEREZEu0HhOU76MjAxER0cDANzd3WFmZqbVwv5pOKeJiN4X5zQRvVlxz2nS+EhTPjMzM/j4+Lzr6kRERET/KBqHpoyMDPzwww/Yt28fEhISkJeXp7L89u3bWiuOiIiISFdoHJr69++PQ4cO4ZNPPkGpUqWgUCiKoi4iIiIinaJxaNq5cye2b9+OBg0aFEU9RERERDpJ41sOWFtbw8bGpihqISIiItJZGoemKVOmIDQ0FJmZmUVRDxEREZFO0vj03KxZsxAdHQ0HBwe4urqiRIkSKsvPnTunteKIiIiIdIXGoalDhw5FUAYRERGRbtM4NE2cOLEo6iAiIiLSae98c8uIiAhcu3YNAFC5cmVUr15da0URERER6RqNQ1NCQgK6d++OgwcPwsrKCgCQkpKCZs2aYc2aNbCzs9N2jURERETFTuOr54YNG4anT5/iypUrSEpKQlJSEi5fvoy0tDQMHz68KGokIiIiKnYaH2kKDw/H3r174enpKbV5eXlh/vz58Pf312pxRERERLpC4yNNeXl5arcZAIASJUqofQ8dERER0b+FxqGpefPmGDFiBB4+fCi1PXjwAKNGjUKLFi20WhwRERGRrtA4NP38889IS0uDq6sr3N3d4e7uDjc3N6SlpWHevHlFUSMRERFRsdN4TlOZMmVw7tw57N27F1FRUQAAT09P+Pn5ab04IiIiIl3xTvdpUigUaNmyJVq2bKnteoiIiIh0ksan54YPH46ffvpJrf3nn3/GyJEjtVETERERkc7RODRt3LgRDRo0UGuvX78+NmzYoNFYhw8fRtu2beHk5ASFQoHNmzerLA8JCYFCoVD5CQwMVOmTlJSEXr16QalUwsrKCv369UN6erpKn4sXL6JRo0YwNjZGmTJlMH36dLVa1q9fj0qVKsHY2Bje3t7YsWOHRvtCH1ZGZhZ+XLwdvYf/gqp+X8C1zjCs33byjeu8yMmFX7epcK0zDItX7lNb/vPvu9D/80WoFfgVXOsMw+zFBb8GZi/eAdc6w9R+KjYcVWD/xCdpGB+2BnVbf4OKDUehQfuJGDdl1Vv38UTEzQK341pnGM5duvPW9YmISLs0Pj335MkTWFpaqrUrlUo8fvxYo7EyMjJQtWpV9O3bF506dSqwT2BgIJYuXSo9NjIyUlneq1cvPHr0CHv27MGLFy/Qp08fDBw4EKtXrwYApKWlwd/fH35+fli4cCEuXbqEvn37wsrKCgMHDgQAHD9+HD169EBYWBjatGmD1atXo0OHDjh37hyqVKmi0T7Rh5GUko6floSjtKM1PCuUxsmIm29dZ/naQ3gYl1To8pkLt8HOVgmvis44fPLaW8f77otuMDP93+tRT0+h1udhfDI+6j8bANCrU0M42lkiPjEVF67efev4+UK6NUFVLxeVNtcyvPM+EdGHpnFoKl++PMLDwzF06FCV9p07d6JcuXIajRUUFISgoKA39jEyMoKjo2OBy65du4bw8HCcOXMGtWrVAgDMmzcPrVq1wsyZM+Hk5IRVq1YhOzsbv//+OwwNDVG5cmVERkbixx9/lELT3LlzERgYiLFjxwIApkyZgj179uDnn3/GwoULNdon+jDsSypxesdU2JdU4uLVWLQLmfHG/o+TnmLub+H4rHdL/Lhoe4F9jmyehDJOtkhKSUcN//FvraFVi2qwsTJ/Y5+vwtZAX18PW5aNhbWV2VvHLEidau5o1YLf7UhEVNw0Pj03evRojBs3DhMnTsShQ4dw6NAhhIaG4ssvv8SoUQWfnngfBw8ehL29PTw8PDBo0CA8efJEWnbixAlYWVlJgQkA/Pz8oKenh1OnTkl9GjduDENDQ6lPQEAArl+/juTkZKnP61f/BQQE4MSJE4XWlZWVhbS0NJUf+nCMDEvAvqRSdv9p87egnIs9OgbWKrRPGSdbjWoQQuBp+jMIIQpcfismDgePX8XAj1vA2soMz7Ne4EVOrkbbyJee8Rw577guERFph8ZHmvr27YusrCxMnToVU6ZMAQC4urpiwYIF6N27t1aLCwwMRKdOneDm5obo6Gh89dVXCAoKwokTJ6Cvr4+4uDjY29urrGNgYAAbGxvExcUBAOLi4uDm5qbSx8HBQVpmbW2NuLg4qe3VPvljFCQsLAyTJ0/Wxm5SEYu8EoON209h/eJRgEL9FNq7atxxMjIys2BqYgj/Jj74ekRH2Nn+L8gdO30dAGBna4Geg+fh+Nkb0NfXQ8M6Hvjui26yQ9rYKauQkZkFfX091K7mjq+GdYCPV1mt7QcREcnzTrccGDRoEAYNGoTExESYmJjA3PzNpyjeVffu3aX/9/b2ho+PD9zd3XHw4MFiv/v4+PHjMXr0aOlxWloaypQpU4wVUUGEEJg4cwPa+NVATR833Hv45O0rvYWl0hTBXRqjhrcbDA0NcDoyGivWH8aFK3exZflYWJibAADu3EsEAIz/fg18vMri56l98DA+GXOX7MTHQ39G+OrxMDE2LHQ7hiX0EdS8GprV94K1lTlu3nmEX1fuR5dP52DjklGo4sHXGxHRh/ROoSknJwcHDx5EdHQ0evbsCQB4+PAhlEplkQUoAChXrhxKliyJW7duoUWLFnB0dERCQoJabUlJSdI8KEdHR8THx6v0yX/8tj6FzaUCXs61en1SOume9dtO4fqth1jwQz+tjdm3e1OVx0HNq6GalwtGhC7Hio1HMDj45RdXZ2ZmAQDsbJVYOvsz6Om9PBvuaG+F4d8sw9/hZ9G9Q/1Ct1PTpxxq+vxvnmDLxt5o1bw6AnuGYfr8rfjjp8Fa2yciIno7jec03b17F97e3mjfvj2GDBmCxMSX/5qeNm0axowZo/UCX3X//n08efIEpUqVAgD4+voiJSUFERERUp/9+/cjLy8PdevWlfocPnwYL168kPrs2bMHHh4esLa2lvrs26d6CfqePXvg6+tbpPtDRetp+jNMn78FAz9pAScH6yLdVvvAWrCzVUqn5ADA2PjlF1u39qsuBSYAaN2iOgz09RDxDrcNcC1jh5ZNvHEy4iZyc/kF2UREH5LGoWnEiBGoVasWkpOTYWJiIrV37NhRLXi8TXp6OiIjIxEZGQkAuHPnDiIjIxEbG4v09HSMHTsWJ0+eRExMDPbt24f27dujfPnyCAgIAPDy61sCAwMxYMAAnD59GseOHcPQoUPRvXt3ODk5AQB69uwJQ0ND9OvXD1euXMHatWsxd+5clVNrI0aMQHh4OGbNmoWoqChMmjQJZ8+eVbtCkP5Zfl21Hy9yctHWrwbuPXyCew+fIC4hBQCQmpaJew+fIPtFjta25+RghZS0TOmxQ8mXt+YoaWOh0k9fXw9WlmZIfaWvRtuxt0b2ixxkPst692KJiEhjGp+eO3LkCI4fP65yNRrwcjL4gwcPNBrr7NmzaNasmfQ4P8gEBwdjwYIFuHjxIpYvX46UlBQ4OTnB398fU6ZMUTkttmrVKgwdOhQtWrSAnp4eOnfurHLHcktLS+zevRtDhgxBzZo1UbJkSYSGhkq3GwBe3phz9erV+Oabb/DVV1+hQoUK2Lx5M+/R9A/3IC4JqWmZaNn9e7Vl85ftxvxlu7F95ReoXNH5vbclhMD9R0nwemWsKpVeTtaOT0xV6Zv9IgfJqRmwtX63U9mxD5/AyKiEyj2iiIio6GkcmvLy8pCbq37p8/3792FhYVHAGoVr2rRpoZdrA8CuXbveOoaNjY10I8vC+Pj44MiRI2/s06VLF3Tp0uWt26N/jj7dmsK/iY9K25PkdHwVtgYftamLlo29Nb7NwMsxnsLWWvW1vnLjETxJTkcTX0+prV7N8ihpY4HN4WcxOMQfxkYvT9dt2HYKubl5aFinktQ3KSUdSSnpKO1oI00OL2g7V2/cx97Dl9CkvpfKKT8iIip6Gocmf39/zJkzB4sXLwbw8st709PTMXHiRLRq1UrrBRIVZvm6Q0h7+gzxj18eydl35DLi4lMAAMHdmqBKpTKoUkn1CrP8q+cqliuFgKZVVZZt2nEaDx4l4VlWNgDg9PlbmPdbOACgY6s6cC5lAwBo0G4i2rSsgUruTjAyMsCZyNvYuuccvCo6o1enhtJ4RoYlMH5Ye3w+eSW6fToHHYPq4GF8MpauOYg61dwR2Ox/21++7jDmLtmJPxcMh2/NCgCAoV8thbFxCdT0LgdbG3PcvB2HPzcfh7GxIb4c0k5bTyMREcmkcWiaNWsWAgIC4OXlhefPn6Nnz564efMmSpYsiT///LMoaiQq0OJV+/Hg0f++FiX8wAWEH7gAAOgQVBtKc5PCVi3Q2i0ncOrcLenxiYibOPH/X89Sq5q7FJo6BNZCxMU7CD9wAVlZL1C6lA0+/aQFhvYJULuFQOfWdVGihAEWLN+D7+dthtLcBD07NsDYwW2hr//mI0X+TX2wOfwslqzej/SM57CxNkdgs6oY0T+IX6NCRFQMFOJN58cKkZOTg7Vr1+LChQtIT09HjRo10KtXL5WJ4f81aWlpsLS0RGpqKpRK+XeqJiLKd9u1TXGXQKTTysVs0/qYmvz9fqf7NBkYGKBXr17o1avXOxVIRERE9E8jeybpjRs3cPr0aZW2ffv2oVmzZqhTpw6+/179CiUiIiKifwvZoemLL77Atm3/Oyx2584dtG3bFoaGhvD19UVYWBjmzJlTFDUSERERFTvZp+fOnj2LcePGSY9XrVqFihUrSrcF8PHxwbx58zBy5EitF0mAa51hxV0Ckc6KOT2vuEsgov8A2UeaHj9+DGfn/92478CBA2jbtq30uGnTpoiJidFqcURERES6QnZosrGxwaNHjwC8vMHl2bNnUa9ePWl5dnb2G29USURERPRPJjs0NW3aFFOmTMG9e/cwZ84c5OXloWnTptLyq1evwtXVtQhKJCIiIip+suc0TZ06FS1btoSLiwv09fXx008/wczMTFq+YsUKNG/evEiKJCIiIipuskOTq6srrl27hitXrsDOzg5OTk4qyydPnqwy54mIiIjo30Sjm1saGBigatWqBS4rrJ2IiIjo34Bfk05EREQkA0MTERERkQwMTUREREQyaBSacnJy8O233+L+/ftFVQ8RERGRTtIoNBkYGGDGjBnIyckpqnqIiIiIdJLGp+eaN2+OQ4cOFUUtRERERDpLo1sOAEBQUBC+/PJLXLp0CTVr1lS5wSUAtGvXTmvFEREREekKjUPT4MGDAQA//vij2jKFQoHc3Nz3r4qIiIhIx2gcmvLy8oqiDiIiIiKd9l63HHj+/Lm26iAiIiLSaRqHptzcXEyZMgWlS5eGubk5bt++DQCYMGECfvvtN60XSERERKQLNA5NU6dOxbJlyzB9+nQYGhpK7VWqVMGSJUu0WhwRERGRrtA4NP3xxx9YvHgxevXqBX19fam9atWqiIqK0mpxRERERLpC49D04MEDlC9fXq09Ly8PL1680EpRRERERLpG49Dk5eWFI0eOqLVv2LAB1atX10pRRERERLpG41sOhIaGIjg4GA8ePEBeXh42bdqE69ev448//sC2bduKokYiIiKiYqfxkab27dtj69at2Lt3L8zMzBAaGopr165h69ataNmyZVHUSERERFTsND7SBACNGjXCnj17tF0LERERkc56p9AEAGfPnsW1a9cAvJznVLNmTa0VRURERKRrNA5N9+/fR48ePXDs2DFYWVkBAFJSUlC/fn2sWbMGzs7O2q6RiIiIqNhpPKepf//+ePHiBa5du4akpCQkJSXh2rVryMvLQ//+/YuiRiIiIqJip/GRpkOHDuH48ePw8PCQ2jw8PDBv3jw0atRIq8URERER6QqNjzSVKVOmwJtY5ubmwsnJSStFEREREekajUPTjBkzMGzYMJw9e1ZqO3v2LEaMGIGZM2dqtTgiIiIiXaHx6bmQkBBkZmaibt26MDB4uXpOTg4MDAzQt29f9O3bV+qblJSkvUqJiIiIipHGoWnOnDlFUAYRERGRbtM4NAUHBxdFHUREREQ6TeM5TURERET/RQxNRERERDIwNBERERHJwNBEREREJMN7h6a0tDRs3rxZ+vJeIiIion8jjUNT165d8fPPPwMAnj17hlq1aqFr167w8fHBxo0btV4gERERkS7QODQdPnxY+o65v/76C0IIpKSk4KeffsJ3332n9QKJiIiIdIHGoSk1NRU2NjYAgPDwcHTu3BmmpqZo3bo1bt68qfUCiYiIiHTBO31h74kTJ5CRkYHw8HD4+/sDAJKTk2FsbKz1AomIiIh0gcZ3BB85ciR69eoFc3NzuLi4oGnTpgBenrbz9vbWdn1EREREOkHj0DR48GDUrVsXsbGxaNmyJfT0Xh6sKleuHOc0ERER0b+WRqfnXrx4AXd3d5iamqJjx44wNzeXlrVu3RoNGjTQeoFEREREukCj0FSiRAk8f/68qGohIiIi0lkaTwQfMmQIpk2bhpycnKKoh4iIiEgnaTyn6cyZM9i3bx92794Nb29vmJmZqSzftGmT1oojIiIi0hUahyYrKyt07ty5KGohIiIi0lkah6alS5cWRR1EREREOu2dvrA3JycHe/fuxaJFi/D06VMAwMOHD5Genq7V4oiIiIh0hcZHmu7evYvAwEDExsYiKysLLVu2hIWFBaZNm4asrCwsXLiwKOokIiIiKlYaH2kaMWIEatWqheTkZJiYmEjtHTt2xL59+7RaHBEREZGu0PhI05EjR3D8+HEYGhqqtLu6uuLBgwdaK4yIiIhIl2h8pCkvLw+5ublq7ffv34eFhYVWiiIiIiLSNRqHJn9/f8yZM0d6rFAokJ6ejokTJ6JVq1barI2IiIhIZ2h8em7WrFkICAiAl5cXnj9/jp49e+LmzZsoWbIk/vzzz6KokYiIiKjYaRyanJ2dceHCBaxduxYXLlxAeno6+vXrh169eqlMDCciIiL6N9E4NB0+fBj169dHr1690KtXL6k9JycHhw8fRuPGjbVaIBEREZEu0HhOU7NmzZCUlKTWnpqaimbNmmk01uHDh9G2bVs4OTlBoVBg8+bNKsuFEAgNDUWpUqVgYmICPz8/3Lx5U6VPUlISevXqBaVSCSsrK/Tr10/tJpsXL15Eo0aNYGxsjDJlymD69Olqtaxfvx6VKlWCsbExvL29sWPHDo32hYiIiP7dNA5NQggoFAq19idPnqh9ee/bZGRkoGrVqpg/f36By6dPn46ffvoJCxcuxKlTp2BmZoaAgAA8f/5c6tOrVy9cuXIFe/bswbZt23D48GEMHDhQWp6WlgZ/f3+4uLggIiICM2bMwKRJk7B48WKpz/Hjx9GjRw/069cP58+fR4cOHdChQwdcvnxZo/0hIiKify+FEELI6dipUycAwN9//43AwEAYGRlJy3Jzc3Hx4kV4eHggPDz83QpRKPDXX3+hQ4cOAF6GMycnJ3z++ecYM2YMgJdHsxwcHLBs2TJ0794d165dg5eXF86cOYNatWoBAMLDw9GqVSvcv38fTk5OWLBgAb7++mvExcVJ95b68ssvsXnzZkRFRQEAunXrhoyMDGzbtk2qp169eqhWrZrsO5ynpaXB0tISqampUCqV7/QcvIlrnWFaH5Po3yLm9LziLkErbru2Ke4SiHRauZhtb++kIU3+fss+0mRpaQlLS0sIIWBhYSE9trS0hKOjIwYOHIiVK1e+d/H57ty5g7i4OPj5+anUULduXZw4cQIAcOLECVhZWUmBCQD8/Pygp6eHU6dOSX0aN26scjPOgIAAXL9+HcnJyVKfV7eT3yd/OwXJyspCWlqayg8RERH9e8meCL506VIAL+/8PXbsWJiamhZZUQAQFxcHAHBwcFBpd3BwkJbFxcXB3t5eZbmBgQFsbGxU+ri5uamNkb/M2toacXFxb9xOQcLCwjB58uR32DMiIiL6J9J4TlPv3r0L/LqUmzdvIiYmRhs1/SOMHz8eqamp0s+9e/eKuyQiIiIqQhqHppCQEBw/flyt/dSpUwgJCdFGTQAAR0dHAEB8fLxKe3x8vLTM0dERCQkJKstzcnKQlJSk0qegMV7dRmF98pcXxMjICEqlUuWHiIiI/r00Dk3nz59HgwYN1Nrr1auHyMhIbdQEAHBzc4OjoyP27dsntaWlpeHUqVPw9fUFAPj6+iIlJQURERFSn/379yMvLw9169aV+hw+fBgvXryQ+uzZswceHh6wtraW+ry6nfw++dshIiIi0jg0KRQKPH36VK09NTW1wC/yfZP09HRERkZKYevOnTuIjIxEbGwsFAoFRo4cie+++w5btmzBpUuX0Lt3bzg5OUlX2Hl6eiIwMBADBgzA6dOncezYMQwdOhTdu3eHk5MTAKBnz54wNDREv379cOXKFaxduxZz587F6NGjpTpGjBiB8PBwzJo1C1FRUZg0aRLOnj2LoUOHavr0EBER0b+UxqGpcePGCAsLUwlIubm5CAsLQ8OGDTUa6+zZs6hevTqqV68OABg9ejSqV6+O0NBQAMC4ceMwbNgwDBw4ELVr10Z6ejrCw8NhbGwsjbFq1SpUqlQJLVq0QKtWrdCwYUOVezBZWlpi9+7duHPnDmrWrInPP/8coaGhKvdyql+/PlavXo3FixejatWq2LBhAzZv3owqVapo+vQQERHRv5Ts+zTlu3r1Kho3bgwrKys0atQIAHDkyBGkpaVh//79/9mgwfs0ERUf3qeJ6L/hH3OfpnxeXl64ePEiunbtioSEBDx9+hS9e/dGVFTUfzYwERER0b+fxl/YCwBOTk74/vvvtV0LERERkc56p9AEAJmZmYiNjUV2drZKu4+Pz3sXRURERKRrNA5NiYmJ6NOnD3bu3Fngck2voCMiIiL6J9B4TtPIkSORkpKCU6dOwcTEBOHh4Vi+fDkqVKiALVu2FEWNRERERMVO4yNN+/fvx99//41atWpBT08PLi4uaNmyJZRKJcLCwtC6deuiqJOIiIioWGl8pCkjI0P6klxra2skJiYCALy9vXHu3DntVkdERESkIzQOTR4eHrh+/ToAoGrVqli0aBEePHiAhQsXolSpUlovkIiIiEgXaHx6bsSIEXj06BEAYOLEiQgMDMSqVatgaGiIZcuWabs+IiIiIp2gcWj6+OOPpf+vWbMm7t69i6ioKJQtWxYlS5bUanFEREREukKj03MvXryAu7s7rl27JrWZmpqiRo0aDExERET0r6ZRaCpRogSeP39eVLUQERER6SyNJ4IPGTIE06ZNQ05OTlHUQ0RERKSTNJ7TdObMGezbtw+7d++Gt7c3zMzMVJZv2rRJa8URERER6QqNQ5OVlRU6d+5cFLUQERER6SyNQ9PSpUuLog4iIiIinabxnCYiIiKi/yKNjzQBwIYNG7Bu3TrExsYiOztbZRm/SoWIiIj+jTQ+0vTTTz+hT58+cHBwwPnz51GnTh3Y2tri9u3bCAoKKooaiYiIiIqdxqHpl19+weLFizFv3jwYGhpi3Lhx2LNnD4YPH47U1NSiqJGIiIio2GkcmmJjY1G/fn0AgImJCZ4+fQoA+OSTT/Dnn39qtzoiIiIiHaFxaHJ0dERSUhIAoGzZsjh58iQA4M6dOxBCaLc6IiIiIh2hcWhq3rw5tmzZAgDo06cPRo0ahZYtW6Jbt27o2LGj1gskIiIi0gUaXz23ePFi5OXlAXj5lSq2trY4fvw42rVrh08//VTrBRIRERHpAo1Dk56eHvT0/neAqnv37ujevbtWiyIiIiLSNe90n6aUlBScPn0aCQkJ0lGnfL1799ZKYURERES6ROPQtHXrVvTq1Qvp6elQKpVQKBTSMoVCwdBERERE/0oaTwT//PPP0bdvX6SnpyMlJQXJycnST/5VdURERET/NhqHpgcPHmD48OEwNTUtinqIiIiIdJLGoSkgIABnz54tilqIiIiIdJasOU3592UCgNatW2Ps2LG4evUqvL29UaJECZW+7dq1026FRERERDpAVmjq0KGDWtu3336r1qZQKJCbm/veRRERERHpGlmh6fXbChARERH912g8p4mIiIjov0h2aNq/fz+8vLyQlpamtiw1NRWVK1fG4cOHtVocERERka6QHZrmzJmDAQMGQKlUqi2ztLTEp59+itmzZ2u1OCIiIiJdITs0XbhwAYGBgYUu9/f3R0REhFaKIiIiItI1skNTfHy82u0FXmVgYIDExEStFEVERESka2SHptKlS+Py5cuFLr948SJKlSqllaKIiIiIdI3s0NSqVStMmDABz58/V1v27NkzTJw4EW3atNFqcURERES6QtZ9mgDgm2++waZNm1CxYkUMHToUHh4eAICoqCjMnz8fubm5+Prrr4usUCIiIqLiJDs0OTg44Pjx4xg0aBDGjx8PIQSAl3cBDwgIwPz58+Hg4FBkhRIREREVJ9mhCQBcXFywY8cOJCcn49atWxBCoEKFCrC2ti6q+oiIiIh0gkahKZ+1tTVq166t7VqIiIiIdBa/RoWIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZGJqIiIiIZGBoIiIiIpKBoYmIiIhIBoYmIiIiIhkYmoiIiIhkYGgiIiIikoGhiYiIiEgGhiYiIiIiGRiaiIiIiGRgaCIiIiKSgaGJiIiISAaGJiIiIiIZdDo0TZo0CQqFQuWnUqVK0vLnz59jyJAhsLW1hbm5OTp37oz4+HiVMWJjY9G6dWuYmprC3t4eY8eORU5OjkqfgwcPokaNGjAyMkL58uWxbNmyD7F7RERE9A+i06EJACpXroxHjx5JP0ePHpWWjRo1Clu3bsX69etx6NAhPHz4EJ06dZKW5+bmonXr1sjOzsbx48exfPlyLFu2DKGhoVKfO3fuoHXr1mjWrBkiIyMxcuRI9O/fH7t27fqg+0lERES6zaC4C3gbAwMDODo6qrWnpqbit99+w+rVq9G8eXMAwNKlS+Hp6YmTJ0+iXr162L17N65evYq9e/fCwcEB1apVw5QpU/DFF19g0qRJMDQ0xMKFC+Hm5oZZs2YBADw9PXH06FHMnj0bAQEBhdaVlZWFrKws6XFaWpqW95yIiIh0ic4fabp58yacnJxQrlw59OrVC7GxsQCAiIgIvHjxAn5+flLfSpUqoWzZsjhx4gQA4MSJE/D29oaDg4PUJyAgAGlpabhy5YrU59Ux8vvkj1GYsLAwWFpaSj9lypTRyv4SERGRbtLp0FS3bl0sW7YM4eHhWLBgAe7cuYNGjRrh6dOniIuLg6GhIaysrFTWcXBwQFxcHAAgLi5OJTDlL89f9qY+aWlpePbsWaG1jR8/HqmpqdLPvXv33nd3iYiISIfp9Om5oKAg6f99fHxQt25duLi4YN26dTAxMSnGygAjIyMYGRkVaw1ERET04ej0kabXWVlZoWLFirh16xYcHR2RnZ2NlJQUlT7x8fHSHChHR0e1q+nyH7+tj1KpLPZgRkRERLrjHxWa0tPTER0djVKlSqFmzZooUaIE9u3bJy2/fv06YmNj4evrCwDw9fXFpUuXkJCQIPXZs2cPlEolvLy8pD6vjpHfJ38MIiIiIkDHQ9OYMWNw6NAhxMTE4Pjx4+jYsSP09fXRo0cPWFpaol+/fhg9ejQOHDiAiIgI9OnTB76+vqhXrx4AwN/fH15eXvjkk09w4cIF7Nq1C9988w2GDBkinVr77LPPcPv2bYwbNw5RUVH45ZdfsG7dOowaNao4d52IiIh0jE7Pabp//z569OiBJ0+ewM7ODg0bNsTJkydhZ2cHAJg9ezb09PTQuXNnZGVlISAgAL/88ou0vr6+PrZt24ZBgwbB19cXZmZmCA4Oxrfffiv1cXNzw/bt2zFq1CjMnTsXzs7OWLJkyRtvN0BERET/PQohhCjuIv4N0tLSYGlpidTUVCiVSq2P71pnmNbHJPq3iDk9r7hL0Irbrm2KuwQinVYuZpvWx9Tk77dOn54jIiIi0hUMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNr5k/fz5cXV1hbGyMunXr4vTp08VdEhEREekAhqZXrF27FqNHj8bEiRNx7tw5VK1aFQEBAUhISCju0oiIiKiYMTS94scff8SAAQPQp08feHl5YeHChTA1NcXvv/9e3KURERFRMTMo7gJ0RXZ2NiIiIjB+/HipTU9PD35+fjhx4oRa/6ysLGRlZUmPU1NTAQBpaWlFUl9ebnaRjEv0b1BU77sP7Wnei+IugUinFcV7PX9MIcRb+zI0/b/Hjx8jNzcXDg4OKu0ODg6IiopS6x8WFobJkyertZcpU6bIaiSigllaLi7uEojoQ7C0LLKhnz59Csu3jM/Q9I7G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",
|
||
"text/plain": [
|
||
"<Figure size 640x480 with 1 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"def plot_character_per_second_comparison(\n",
|
||
" hf_stats: Tuple[float, float, float], fst_stats: Tuple[float, float, float], documents: list\n",
|
||
"):\n",
|
||
" # Calculating total characters in documents\n",
|
||
" total_characters = sum(len(doc) for doc in documents)\n",
|
||
"\n",
|
||
" # Calculating characters per second for each model\n",
|
||
" hf_chars_per_sec = total_characters / hf_stats[0] # Mean time is at index 0\n",
|
||
" fst_chars_per_sec = total_characters / fst_stats[0]\n",
|
||
"\n",
|
||
" # Plotting the bar chart\n",
|
||
" models = [\"HF Embed (Torch)\", \"FastEmbed\"]\n",
|
||
" chars_per_sec = [hf_chars_per_sec, fst_chars_per_sec]\n",
|
||
"\n",
|
||
" bars = plt.bar(models, chars_per_sec, color=[\"#1f356c\", \"#dd1f4b\"])\n",
|
||
" plt.ylabel(\"Characters per Second\")\n",
|
||
" plt.title(\"Characters Processed per Second Comparison\")\n",
|
||
"\n",
|
||
" # Adding the number at the top of each bar\n",
|
||
" for bar, chars in zip(bars, chars_per_sec):\n",
|
||
" plt.text(\n",
|
||
" bar.get_x() + bar.get_width() / 2,\n",
|
||
" bar.get_height(),\n",
|
||
" f\"{chars:.1f}\",\n",
|
||
" ha=\"center\",\n",
|
||
" va=\"bottom\",\n",
|
||
" color=\"#1f356c\",\n",
|
||
" fontsize=12,\n",
|
||
" )\n",
|
||
"\n",
|
||
" plt.show()\n",
|
||
"\n",
|
||
"\n",
|
||
"plot_character_per_second_comparison(hf_stats, fst_stats, documents)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Are the Embeddings the same?\n",
|
||
"\n",
|
||
"This is a very important question. Let's see if the embeddings are the same."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"/var/folders/b4/grpbcmrd36gc7q5_11whbn540000gn/T/ipykernel_32737/1522845950.py:7: UserWarning: Creating a tensor from a list of numpy.ndarrays is extremely slow. Please consider converting the list to a single numpy.ndarray with numpy.array() before converting to a tensor. (Triggered internally at /Users/runner/work/pytorch/pytorch/pytorch/torch/csrc/utils/tensor_new.cpp:248.)\n",
|
||
" calculate_cosine_similarity(hf.embed(documents), Tensor(list(embedding_model.embed(documents))))\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"text/plain": [
|
||
"0.9165658950805664"
|
||
]
|
||
},
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"output_type": "execute_result"
|
||
}
|
||
],
|
||
"source": [
|
||
"def calculate_cosine_similarity(embeddings1: Tensor, embeddings2: Tensor) -> float:\n",
|
||
" \"\"\"\n",
|
||
" Calculate cosine similarity between two sets of embeddings\n",
|
||
" \"\"\"\n",
|
||
" return F.cosine_similarity(embeddings1, embeddings2).mean().item()\n",
|
||
"\n",
|
||
"\n",
|
||
"calculate_cosine_similarity(hf.embed(documents), Tensor(list(embedding_model.embed(documents))))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"This indicates the embeddings are quite close to each with a cosine similarity of 0.99 for BAAI/bge-small-en and 0.92 for BAAI/bge-small-en-v1.5. This gives us confidence that the embeddings are the same and we are not sacrificing accuracy for speed."
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "fst",
|
||
"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.9.17"
|
||
},
|
||
"orig_nbformat": 4
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 2
|
||
}
|