Files
fastembed/experiments/Throughput_Across_Models.ipynb
2024-02-05 10:49:12 +05:30

287 lines
40 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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 Embedding\n",
"import matplotlib.pyplot as plt\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": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"model_id = \"BAAI/bge-small-en\""
]
},
{
"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",
" Based on https://huggingface.co/BAAI/bge-base-en\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=model_id)\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 = Embedding(model_id=model_id)"
]
},
{
"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.06128048896789551, 0.06189894676208496, 0.060662031173706055)\n",
"FastEmbed (Average, Max, Min): (0.039009809494018555, 0.04126381874084473, 0.03675580024719238)\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": "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g3fffRdz585FdnY2wsPD//knhoiIiPROqYamhQsXAgCaNWum1r506VKEhYUBAObMmQMDAwN06dIFOTk5CA4Oxvfffy/1NTQ0xJYtWzBw4EAEBATA0tISoaGh+PLLL6U+Xl5e2Lp1K0aOHIl58+bBzc0NP/74I4KDg6U+H3zwAVJSUjBx4kQkJSWhVq1aiI6O1pgcTkRERP9NenWfpn8z3qeJiN4U79NE9HK8TxMRERHRvwBDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMjA0EREREcnA0EREREQkA0MTERERkQwMTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJIORnE6jRo2SPeDs2bNfuxgiIiIifSUrNJ0+fVrt8alTp5Cfn48qVaoAAK5cuQJDQ0P4+/vrvkIiIiIiPSArNO3Zs0f69+zZs2FtbY3ly5fDzs4OAJCWlobw8HA0bty4ZKokIiIiKmUKIYTQZoVy5cphx44dqF69ulr7uXPnEBQUhLt37+q0wH+LzMxM2NjYICMjA0qlsrTLIaJ/oeue7Uq7BCK9ViFhi87H1Ob3t9YTwTMzM5GSkqLRnpKSgkePHmk7HBEREdG/gtahqVOnTggPD8eGDRtw+/Zt3L59G+vXr0dERAQ6d+5cEjUSERERlTpZc5qet2jRInzyySfo2bMn8vLyng1iZISIiAjMmDFD5wUSERER6QOt5zQVys7ORnx8PADA29sblpaWOi3s34ZzmojoTXFOE9HLlfacJq2PNBWytLSEn5/f665ORERE9K+idWjKzs7GN998g927d+P+/ftQqVRqy69fv66z4oiIiIj0hdahqV+/fti3bx969+6NsmXLQqFQlERdRERERHpF69C0bds2bN26FQ0bNiyJeoiIiIj0kta3HLCzs4O9vX1J1EJERESkt7QOTV999RUmTpyIx48fl0Q9RERE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w1dffYVbt25h7ty5UKlUaNasmbT8woUL8PT0LIESiYiIiEqf7DlNU6dORatWreDh4QFDQ0N8++23sLS0lJb/+uuvaNGiRYkUSURERFTaZIcmT09PXLx4EefPn4ejo6P03W6FJk+erDbniYiIiOhtotXNLY2MjFCzZs0ilxXXTkRERPQ20PoLe4mIiIj+ixiaiIiIiGRgaCIiIiKSQavQlJ+fjy+//BK3b98uqXqIiIiI9JJWocnIyAgzZsxAfn5+SdVDREREpJe0Pj3XokUL7Nu3ryRqISIiItJbWt1yAABat26NTz/9FGfPnoW/v7/aDS4BoEOHDjorjoiIiEhfaB2aBg0aBACYPXu2xjKFQoGCgoI3r4qIiIhIz2gdmlQqVUnUQURERKTX3uiWA0+fPtVVHURERER6TevQVFBQgK+++grlypWDlZUVrl+/DgCYMGECfvrpJ50XSERERKQPtA5NU6dOxbJlyzB9+nSYmJhI7TVq1MCPP/6o0+KIiIiI9IXWoemXX37BkiVL0KtXLxgaGkrtNWvWxKVLl3RaHBEREZG+0Do03blzBxUrVtRoV6lUyMvL00lRRERERPpG69BUrVo1HDhwQKN93bp1qF27tk6KIiIiItI3Wt9yYOLEiQgNDcWdO3egUqmwYcMGXL58Gb/88gu2bNlSEjUSERERlTqtjzS999572Lx5M3bt2gVLS0tMnDgRFy9exObNm9GqVauSqJGIiIio1Gl9pAkAGjdujJ07d+q6FiIiIiK99VqhCQBOnDiBixcvAng2z8nf319nRRERERHpG61D0+3bt9GjRw8cOnQItra2AID09HQ0aNAAq1atgpubm65rJCIiIip1Ws9p6tevH/Ly8nDx4kWkpqYiNTUVFy9ehEqlQr9+/UqiRiIiIqJSp/WRpn379uHw4cOoUqWK1FalShXMnz8fjRs31mlxRERERPpC6yNN7u7uRd7EsqCgAK6urjopioiIiEjfaB2aZsyYgaFDh+LEiRNS24kTJzB8+HDMnDlTp8URERER6QutT8+FhYXh8ePHqFevHoyMnq2en58PIyMj9O3bF3379pX6pqam6q5SIiIiolKkdWiaO3duCZRBREREpN+0Dk2hoaElUQcRERGRXtN6ThMRERHRfxFDExEREZEMDE1EREREMjA0EREREcnwxqEpMzMTGzdulL68l4iI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8gTvvvsuBg8ejOHDh6vNH1q6dCn69OmD0aNHo0qVKujYsSOOHz+O8uXLA3g2J+qHH37AvHnzULNmTezYseOlN+osFBERgb/++kvjEuSidOzYEfPmzcPMmTNRvXp1LF68GEuXLkWzZs1eul6zZs2wdu1abNq0CbVq1UKLFi005nS9yp9//ony5cujcePGWq1HpEs1a9bE7NmzMW3aNNSoUQMrVqxAVFSUWp+CggIMHjwYPj4+CAkJQeXKlaVL+MuVK4fJkyfj008/hbOzM4YMGSKtFx0drfHZ0qhRozeuuWLFiujcuTPatGmDoKAg+Pn5qd1SoF+/fvjxxx+xdOlS+Pr6omnTpli2bJl0pMnKygqbN2/G2bNnUbt2bXz++eeYNm3aG9dFb04hxHM3piGif8T777+Pd955B+PHjy/tUopVv359DBs2DD179iztUoiI9AKPNBGVghkzZrzRhPKS9uDBA3Tu3Bk9evQo7VKIiPQGjzQRERERycAjTUREREQyMDQRERERycDQRERERCQDQxMRERGRDAxNRERERDIwNBERERHJwNBEREREJANDExEREZEMDE1EREREMvwf62bmZeDf0F0AAAAASUVORK5CYII=",
"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)"
]
}
],
"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
}