Skip to content

Commit

Permalink
feat: optimize sparse use in hybrid index
Browse files Browse the repository at this point in the history
  • Loading branch information
jamescalam committed Nov 24, 2024
1 parent 7752b08 commit 2d4395d
Show file tree
Hide file tree
Showing 7 changed files with 442 additions and 61 deletions.
378 changes: 378 additions & 0 deletions docs/encoders/aurelio-bm25.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,378 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/aurelio-labs/semantic-router/blob/main/docs/encoders/aurelio-bm25.ipynb) [![Open nbviewer](https://raw.githubusercontent.com/pinecone-io/examples/master/assets/nbviewer-shield.svg)](https://nbviewer.org/github/aurelio-labs/semantic-router/blob/main/docs/encoders/aurelio-bm25.ipynb)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Using Aurelio AI BM25 Encoder"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The 3rd generation embedding models from OpenAI (`text-embedding-3-small` and `text-embedding-3-large`) can both be used with our `OpenAIEncoder` and usage is primarily the same as with the 2nd generation `text-embedding-ada-002`. However, there is a new `dimensions` parameter — which we will discuss below."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Getting Started"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We start by installing semantic-router. Support for the new `dimensions` parameter was added in `semantic-router==0.0.19` and `openai==1.10.0`."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"!pip install -qU \"semantic-router==0.1.0.dev2\""
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We start by defining a dictionary mapping routes to example phrases that should trigger those routes."
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/jamesbriggs/Library/Caches/pypoetry/virtualenvs/semantic-router-C1zr4a78-py3.12/lib/python3.12/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n"
]
}
],
"source": [
"from semantic_router import Route\n",
"\n",
"politics = Route(\n",
" name=\"politics\",\n",
" utterances=[\n",
" \"isn't politics the best thing ever\",\n",
" \"why don't you tell me about your political opinions\",\n",
" \"don't you just love the president\",\n",
" \"don't you just hate the president\",\n",
" \"they're going to destroy this country!\",\n",
" \"they will save the country!\",\n",
" ],\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Let's define another for good measure:"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
"chitchat = Route(\n",
" name=\"chitchat\",\n",
" utterances=[\n",
" \"how's the weather today?\",\n",
" \"how are things going?\",\n",
" \"lovely weather today\",\n",
" \"the weather is horrendous\",\n",
" \"let's go to the chippy\",\n",
" ],\n",
")\n",
"\n",
"routes = [politics, chitchat]"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we initialize our embedding model, we will use the `-3-large` model alongside a `dimensions` value of `256`. This will produce _tiny_ 256-dimensional vectors that — according to OpenAI — outperform the 1536-dimensional vectors produced by `text-embedding-ada-002`."
]
},
{
"cell_type": "code",
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
"from semantic_router.encoders.aurelio import AurelioSparseEncoder\n",
"\n",
"sparse_encoder = AurelioSparseEncoder(name=\"bm25\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from getpass import getpass\n",
"from semantic_router.encoders import OpenAIEncoder\n",
"\n",
"os.environ[\"OPENAI_API_KEY\"] = os.getenv(\"OPENAI_API_KEY\") or getpass(\n",
" \"Enter OpenAI API Key: \"\n",
")\n",
"\n",
"encoder = OpenAIEncoder(\n",
" name=\"text-embedding-3-large\", score_threshold=0.5, dimensions=256\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We will specify our index:"
]
},
{
"cell_type": "code",
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
"from semantic_router.index.hybrid_local import HybridLocalIndex\n",
"\n",
"index = HybridLocalIndex()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Now we define the `RouteLayer`. When called, the route layer will consume text (a query) and output the category (`Route`) it belongs to — to initialize a `RouteLayer` we need our `encoder` model and a list of `routes`."
]
},
{
"cell_type": "code",
"execution_count": 7,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-24 12:25:32 - semantic_router.utils.logger - INFO - hybrid.py:157 - add() - Encoding route politics\n",
"2024-11-24 12:25:32 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n",
"2024-11-24 12:25:33 - semantic_router.utils.logger - INFO - hybrid.py:157 - add() - Encoding route chitchat\n",
"2024-11-24 12:25:33 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
}
],
"source": [
"from semantic_router.routers import HybridRouter\n",
"\n",
"router = HybridRouter(\n",
" encoder=encoder,\n",
" sparse_encoder=sparse_encoder,\n",
" routes=routes,\n",
" index=index,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We can check the dimensionality of our vectors by looking at the `index` attribute of the `RouteLayer`."
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(11, 256)"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"router.index.index.shape"
]
},
{
"cell_type": "code",
"execution_count": 9,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"11"
]
},
"execution_count": 9,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"len(router.index.sparse_index)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"We do have 256-dimensional vectors. Now let's test them:"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-24 12:25:37 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"data": {
"text/plain": [
"RouteChoice(name='politics', function_call=None, similarity_score=1.2995813276471633)"
]
},
"execution_count": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"router(\"don't you love politics?\")"
]
},
{
"cell_type": "code",
"execution_count": 11,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-24 12:25:38 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"data": {
"text/plain": [
"RouteChoice(name='chitchat', function_call=None, similarity_score=1.8563758628277611)"
]
},
"execution_count": 11,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"router(\"how's the weather today?\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Both are classified accurately, what if we send a query that is unrelated to our existing `Route` objects?"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"2024-11-24 12:25:41 - httpx - INFO - _client.py:1013 - _send_single_request() - HTTP Request: POST https://api.openai.com/v1/embeddings \"HTTP/1.1 200 OK\"\n"
]
},
{
"data": {
"text/plain": [
"RouteChoice(name=None, function_call=None, similarity_score=None)"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"router(\"I'm interested in learning about llama 2\")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"In this case, we return `None` because no matches were identified. We always recommend optimizing your `RouteLayer` for optimal performance, you can see how in [this notebook](https://github.com/aurelio-labs/semantic-router/blob/main/docs/06-threshold-optimization.ipynb)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"---"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "decision-layer",
"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.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
2 changes: 2 additions & 0 deletions semantic_router/encoders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ def __init__(self, type: str, name: Optional[str]):
self.model = CohereEncoder(name=name)
elif self.type == EncoderType.OPENAI:
self.model = OpenAIEncoder(name=name)
elif self.type == EncoderType.AURELIO:
self.model = AurelioSparseEncoder(name=name)
elif self.type == EncoderType.BM25:
if name is None:
name = "bm25"
Expand Down
2 changes: 1 addition & 1 deletion semantic_router/encoders/aurelio.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from aurelio_sdk import AurelioClient, AsyncAurelioClient, EmbeddingResponse

from semantic_router.encoders import BaseEncoder
from semantic_router.encoders.base import BaseEncoder


class AurelioSparseEncoder(BaseEncoder):
Expand Down
Loading

0 comments on commit 2d4395d

Please sign in to comment.