Giving Models Memory: Embeddings & Similarity

Business Scenario

Welcome!

Previously, you installed Ollama, ran an open-weight model locally, integrated it through LangChain and compared cloud and local latency. You estimated cloud token costs, examined shopper data privacy, and made SmartCart AI v2 backend-swappable using get_llm(), testing both backends on the same conversation.​

Today, your manager has assigned you a task to​ build a standalone semantic search demo for SmartCart AI. You will use embeddings to compare the meaning of a shopper's query with product names and descriptions, then rank products by how closely they match the query. This will help SmartCart find relevant products even when shoppers use different words and lay the foundation for connecting catalogue search to Kart. ​

Git Pull

git pull origin branchName

Pre-Lab Preparation

Dataset :

Topic : Giving Models Memory Embedding & Similarity

1) Text Embeddings

2) Embedding Models

3) Cosine Similarity

Task 1: Load an Embedding Model

!pip install -q langchain-huggingface sentence-transformers
!echo "langchain-huggingface" >> requirements.txt
!echo "sentence-transformers" >> requirements.txt
import numpy as np
import pandas as pd
from langchain_huggingface import HuggingFaceEmbeddings
embedder = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
print("Embedding model ready")

Task 2 : What am Embedding looks like

vector = embedder.embed_query("wireless earbuds for the gym")
print("Type :", type(vector).__name__)
print("Length :", len(vector))
print("First 8 :", np.round(vector[:8], 3))

Output

Task 3: Cosine Similarity from Scratch

def cosine_similarity(a, b):
 """1 = same direction, 0 = unrelated, -1 = opposite."""
 a, b = np.array(a), np.array(b)
 return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
print(round(cosine_similarity([1, 2], [2, 4]), 2)) # same direction
print(round(cosine_similarity([1, 2], [2, -1]), 2)) # right angle
print(round(cosine_similarity([1, 2], [-1, -2]), 2)) # opposite

Write the function and test it on tiny two-number vectors.

1

Compare all real shopping phrases

2

pairs = [
 ("wireless earbuds", "bluetooth headphones"),
 ("wireless earbuds", "earbuds wireless"),
 ("wireless earbuds", "basmati rice 5 kg"),
 ("formal shirt for work", "office ke liye formal shirt"),]
for a, b in pairs:
 score = cosine_similarity(embedder.embed_query(a), embedder.embed_query(b))
 print(f"{score:.2f} '{a}' vs '{b}'")

Output

Output

Task 4 : Embeddings vs TF-IDF

from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity as sklearn_cosine
a, b = "wireless earbuds", "bluetooth headphones"
tfidf = TfidfVectorizer().fit_transform([a, b])
print("TF-IDF similarity :", round(sklearn_cosine(tfidf[0], tfidf[1])[0][0], 2))
print("Embedding similarity :", round(cosine_similarity(embedder.embed_query(a),
 embedder.embed_query(b)), 2))

Output

Task 5 : Ranking products by Meaning

Embed the Catalogue

1

products = pd.read_csv("smartcart_products.csv")
product_texts = (products["name"] + ". " + products["description"]).tolist()
product_vectors = embedder.embed_documents(product_texts)
print(len(product_vectors), "products embedded,", len(product_vectors[0]), "numbers each")
print(products[["product_id", "name", "category",
"price_rs"]].head(3).to_string(index=False))

Output

def search_products(query, top_k=3):
 """Return the top_k products closest in meaning to the query."""
 query_vector = embedder.embed_query(query)
 scores = [cosine_similarity(query_vector, v) for v in product_vectors]
 ranked = products.assign(score=np.round(scores, 2))
 return ranked.sort_values("score", ascending=False).head(top_k)
for query in ["something to listen to music while running",
 "healthy breakfast",
 "clothes for a wedding"]:
 print("Query:", query)
 print(search_products(query)[["name", "category", "price_rs",
"score"]].to_string(index=False), "\n")

Search the catalogue with phrases a keyword search would miss

2

Output

Task 6 : Find Similar Products

def similar_products(product_id, top_k=3):
 """Return the top_k products most similar to the given product."""
 row = products.index[products["product_id"] == product_id][0]
 scores = [cosine_similarity(product_vectors[row], v) for v in product_vectors]
 ranked = products.assign(score=np.round(scores, 2))
 ranked = ranked[ranked["product_id"] != product_id] # skip the product itself
 return ranked.sort_values("score", ascending=False).head(top_k)
print("Customers who viewed boAt Airdopes 141 also viewed:")
print(similar_products("P01")[["name", "price_rs", "score"]].to_string(index=False))

Save the script

1

%%writefile smartcart_similarity.py
import numpy as np
import pandas as pd
from langchain_huggingface import HuggingFaceEmbeddings

embedder = HuggingFaceEmbeddings(
    model_name="sentence-transformers/all-MiniLM-L6-v2")

products = pd.read_csv("smartcart_products.csv")
product_vectors = embedder.embed_documents(
    (products["name"] + ". " + products["description"]).tolist())

def cosine_similarity(a, b):
    """1 = same direction, 0 = unrelated, -1 = opposite."""
    a, b = np.array(a), np.array(b)
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
    
    

Output

Task 7 : Build the Standalone Similarity Demo

%%writefile smartcart_similarity.py
import numpy as np
import pandas as pd
from langchain_huggingface import HuggingFaceEmbeddings

embedder = HuggingFaceEmbeddings(
    model_name="sentence-transformers/all-MiniLM-L6-v2")

products = pd.read_csv("smartcart_products.csv")
product_vectors = embedder.embed_documents(
    (products["name"] + ". " + products["description"]).tolist())

def cosine_similarity(a, b):
    """1 = same direction, 0 = unrelated, -1 = opposite."""
    a, b = np.array(a), np.array(b)
    return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))

def rank_products(query, top_k=3):
    """Return the top_k products closest in meaning to the query."""
    query_vector = embedder.embed_query(query)
    scores = [
        cosine_similarity(query_vector, v)
        for v in product_vectors]
    ranked = products.assign(score=np.round(scores, 2))
    return ranked.sort_values("score", ascending=False).head(top_k)

if __name__ == "__main__":
    print(
        f"SmartCart similarity demo -- {len(products)} products. "
        "Type 'quit' to exit.")

    while True:
        query = input("\nSearch: ").strip()

        if query.lower() in ["quit", "exit", "bye"]:
            break

        if query == "":
            continue

        for _, p in rank_products(query).iterrows():
            bar = "#" * int(max(0, p["score"]) * 20)
            print(
                f" {p['score']:.2f} {bar:<20} "
                f"{p['name']} (Rs. {p['price_rs']:,})")

Run it

2

%run smartcart_similarity.py

Output

Test where the meaning based search falls short

3

from smartcart_similarity import rank_products
for query in ["earbuds under 1000 rupees", "earbuds that are not boAt", "M185"]:
 top = rank_products(query, top_k=1).iloc[0]
 print(f"{query:<28} -> {top['name']} (Rs. {top['price_rs']:,}, score {top['score']})")

Output

 

Great job!

You have successfully explored dimensional text embeddings, implemented cosine similarity from scratch, compared semantic matching with TF-IDF. You ranked the catalogue by meaning, built a “Customers Also Viewed” list, created smartcart_similarity.py, identifying its limitations with prices & exact product codes.​​

Checkpoint

   Git Push

git push origin branchName

Next-Lab Preparation

Topic : Vector Databases, Semantic Search & RAG Data Layer

1) Vector Databases (Chroma & FAISS)

2) Document Loading & Chunking

3) RAG Architecture

GenAI - 14

By Content ITV

GenAI - 14

  • 274