|
import numpy as np |
|
from collections import defaultdict |
|
from typing import List, Tuple, Callable |
|
from aimakerspace.openai_utils.embedding import EmbeddingModel |
|
import asyncio |
|
|
|
|
|
def cosine_similarity(vector_a: np.array, vector_b: np.array) -> float: |
|
"""Computes the cosine similarity between two vectors.""" |
|
dot_product = np.dot(vector_a, vector_b) |
|
norm_a = np.linalg.norm(vector_a) |
|
norm_b = np.linalg.norm(vector_b) |
|
return dot_product / (norm_a * norm_b) |
|
|
|
|
|
class VectorDatabase: |
|
def __init__(self, embedding_model: EmbeddingModel = None): |
|
self.vectors = defaultdict(np.array) |
|
self.embedding_model = embedding_model or EmbeddingModel() |
|
|
|
def insert(self, key: str, vector: np.array) -> None: |
|
self.vectors[key] = vector |
|
|
|
def search( |
|
self, |
|
query_vector: np.array, |
|
k: int, |
|
distance_measure: Callable = cosine_similarity, |
|
) -> List[Tuple[str, float]]: |
|
scores = [ |
|
(key, distance_measure(query_vector, vector)) |
|
for key, vector in self.vectors.items() |
|
] |
|
return sorted(scores, key=lambda x: x[1], reverse=True)[:k] |
|
|
|
def search_by_text( |
|
self, |
|
query_text: str, |
|
k: int, |
|
distance_measure: Callable = cosine_similarity, |
|
return_as_text: bool = False, |
|
) -> List[Tuple[str, float]]: |
|
query_vector = self.embedding_model.get_embedding(query_text) |
|
results = self.search(query_vector, k, distance_measure) |
|
return [result[0] for result in results] if return_as_text else results |
|
|
|
def retrieve_from_key(self, key: str) -> np.array: |
|
return self.vectors.get(key, None) |
|
|
|
async def abuild_from_list(self, list_of_text: List[str]) -> "VectorDatabase": |
|
embeddings = await self.embedding_model.async_get_embeddings(list_of_text) |
|
for text, embedding in zip(list_of_text, embeddings): |
|
self.insert(text, np.array(embedding)) |
|
return self |
|
|
|
|
|
if __name__ == "__main__": |
|
list_of_text = [ |
|
"I like to eat broccoli and bananas.", |
|
"I ate a banana and spinach smoothie for breakfast.", |
|
"Chinchillas and kittens are cute.", |
|
"My sister adopted a kitten yesterday.", |
|
"Look at this cute hamster munching on a piece of broccoli.", |
|
] |
|
|
|
vector_db = VectorDatabase() |
|
vector_db = asyncio.run(vector_db.abuild_from_list(list_of_text)) |
|
k = 2 |
|
|
|
searched_vector = vector_db.search_by_text("I think fruit is awesome!", k=k) |
|
print(f"Closest {k} vector(s):", searched_vector) |
|
|
|
retrieved_vector = vector_db.retrieve_from_key( |
|
"I like to eat broccoli and bananas." |
|
) |
|
print("Retrieved vector:", retrieved_vector) |
|
|
|
relevant_texts = vector_db.search_by_text( |
|
"I think fruit is awesome!", k=k, return_as_text=True |
|
) |
|
print(f"Closest {k} text(s):", relevant_texts) |
|
|