Files
fastembed/docs/examples/HFvsFastEmbed.ipynb

270 lines
38 KiB
Plaintext
Raw 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": 24,
"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": 25,
"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": 26,
"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",
" 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": 27,
"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:"
]
},
{
"cell_type": "code",
"execution_count": 28,
"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": 29,
"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": 30,
"metadata": {},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt"
]
},
{
"cell_type": "code",
"execution_count": 31,
"metadata": {},
"outputs": [],
"source": [
"hf_stats = calculate_time_stats(hf.embed, documents, k=100)\n",
"fst_stats = calculate_time_stats(embedding_model.embed, documents, k=100)"
]
},
{
"cell_type": "code",
"execution_count": 32,
"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",
"plot_character_per_second_comparison(hf_stats, fst_stats, documents)"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"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
}