mirror of
https://github.com/qdrant/fastembed.git
synced 2026-08-03 00:20:59 -05:00
* - Rename '03_HF_vs_FastEmbed.ipynb' to 'HF_vs_FastEmbed.ipynb' * - Rename '01_Retrieval_with_FastEmbed.ipynb' to 'Retrieval_with_FastEmbed
293 lines
42 KiB
Plaintext
293 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": 2,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import time\n",
|
||
"from pathlib import Path\n",
|
||
"from typing import Any, Callable, List, Tuple\n",
|
||
"\n",
|
||
"import numpy as np\n",
|
||
"import torch.nn.functional as F\n",
|
||
"from fastembed.embedding import DefaultEmbedding\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": 3,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"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",
|
||
"]"
|
||
]
|
||
},
|
||
{
|
||
"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": 4,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class HF:\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 average_pool(self, last_hidden_states: Tensor, attention_mask: Tensor) -> Tensor:\n",
|
||
" last_hidden = last_hidden_states.masked_fill(~attention_mask[..., None].bool(), 0.0)\n",
|
||
" return last_hidden.sum(dim=1) / attention_mask.sum(dim=1)[..., None]\n",
|
||
"\n",
|
||
" def embed(self, texts: List[str]):\n",
|
||
" # Tokenize the input texts\n",
|
||
" batch_dict = self.tokenizer(texts, max_length=512, padding=True, truncation=True, return_tensors=\"pt\")\n",
|
||
"\n",
|
||
" outputs = self.model(**batch_dict)\n",
|
||
" last_hidden_state = model_output[0]\n",
|
||
" # Perform mean pooling with attention weighting\n",
|
||
" input_mask_expanded = np.broadcast_to(np.expand_dims(attention_mask, -1), last_hidden_state.shape)\n",
|
||
" embeddings = np.sum(last_hidden_state * input_mask_expanded, 1) / np.clip(\n",
|
||
" input_mask_expanded.sum(1), a_min=1e-9, a_max=None\n",
|
||
" )\n",
|
||
" embeddings = self.average_pool(outputs.last_hidden_state, batch_dict[\"attention_mask\"])\n",
|
||
"\n",
|
||
" return embeddings.detach().numpy()\n",
|
||
"\n",
|
||
"\n",
|
||
"hf = HF(model_id=\"BAAI/bge-small-en\")"
|
||
]
|
||
},
|
||
{
|
||
"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": 5,
|
||
"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": 6,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def calculate_time_stats(embed_func: Callable, documents: list, k: int) -> Tuple[float, float, float]:\n",
|
||
" times = []\n",
|
||
"\n",
|
||
" for _ in range(k):\n",
|
||
" # Timing the embed_func call\n",
|
||
" start_time = time.time()\n",
|
||
" embeddings = embed_func(documents)\n",
|
||
" end_time = time.time()\n",
|
||
"\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))"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 7,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Average time taken by HF(PyTorch): 0.06770326375961304\n",
|
||
"Average time taken by FastEmbed: 2.4080276489257813e-07\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"hf_stats = calculate_time_stats(hf.embed, documents, k=100)\n",
|
||
"fst_stats = calculate_time_stats(embedding_model.embed, documents, k=100)\n",
|
||
"print(f\"Average time taken by HF(PyTorch): {hf_stats[0]}\")\n",
|
||
"print(f\"Average time taken by FastEmbed: {fst_stats[0]}\")"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"metadata": {},
|
||
"source": [
|
||
"## 📈 Results\n",
|
||
"\n",
|
||
"Let's run the comparison and see the results."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 8,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"huggingface/tokenizers: The current process just got forked, after parallelism has already been used. Disabling parallelism to avoid deadlocks...\n",
|
||
"To disable this warning, you can either:\n",
|
||
"\t- Avoid using `tokenizers` before the fork if possible\n",
|
||
"\t- Explicitly set the environment variable TOKENIZERS_PARALLELISM=(true | false)\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"!pip install matplotlib --quiet"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 9,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import matplotlib.pyplot as plt"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"image/png": "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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)"
|
||
]
|
||
}
|
||
],
|
||
"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
|
||
}
|