Bird
Raised Fist0
Prompt Engineering / GenAIml~20 mins

Similarity search and retrieval in Prompt Engineering / GenAI - ML Experiment: Train & Evaluate

Choose your learning style10 modes available

Start learning this pattern below

Jump into concepts and practice - no test required

or
Recommended
Test this pattern10 questions across easy, medium, and hard to know if this pattern is strong
Experiment - Similarity search and retrieval
Problem:You want to build a system that finds items similar to a given query from a collection. Currently, the system uses simple cosine similarity on raw text embeddings but returns poor matches.
Current Metrics:Precision@5: 60%, Recall@5: 55%
Issue:The model returns many irrelevant results because the embeddings are not well tuned and the similarity measure is too simple.
Your Task
Improve the similarity search so that Precision@5 and Recall@5 both exceed 80%.
You must keep using cosine similarity as the similarity metric.
You can only change the embedding method or add preprocessing steps.
You cannot increase the size of the dataset.
Hint 1
Hint 2
Hint 3
Solution
Prompt Engineering / GenAI
from sklearn.metrics.pairwise import cosine_similarity
from sklearn.feature_extraction.text import ENGLISH_STOP_WORDS
import numpy as np
import torch
from transformers import AutoTokenizer, AutoModel

def preprocess(text):
    words = text.lower().split()
    words = [w for w in words if w not in ENGLISH_STOP_WORDS]
    return ' '.join(words)

tokenizer = AutoTokenizer.from_pretrained('sentence-transformers/all-MiniLM-L6-v2')
model = AutoModel.from_pretrained('sentence-transformers/all-MiniLM-L6-v2')

def embed(texts):
    inputs = tokenizer(texts, padding=True, truncation=True, return_tensors='pt')
    with torch.no_grad():
        outputs = model(**inputs)
    embeddings = outputs.last_hidden_state.mean(dim=1)
    embeddings = embeddings / embeddings.norm(dim=1, keepdim=True)
    return embeddings.numpy()

# Example dataset
corpus = [
    'The cat sits outside',
    'A man is playing guitar',
    'The new movie is awesome',
    'Dogs are running in the park',
    'A woman watches TV'
]

# Preprocess corpus
corpus_clean = [preprocess(doc) for doc in corpus]

# Embed corpus
corpus_embeddings = embed(corpus_clean)

# Query
query = 'A person plays music'
query_clean = preprocess(query)
query_embedding = embed([query_clean])

# Compute cosine similarity
scores = cosine_similarity(query_embedding, corpus_embeddings)[0]

# Get top 3 results
top_indices = np.argsort(scores)[::-1][:3]

results = [(corpus[i], scores[i]) for i in top_indices]

print('Top 3 similar items:')
for text, score in results:
    print(f'{text} (score: {score:.3f})')
Added text preprocessing to remove stopwords and lowercase text.
Switched from raw embeddings to pretrained sentence-transformer embeddings.
Normalized embeddings to unit length before similarity calculation.
Results Interpretation

Before: Precision@5 = 60%, Recall@5 = 55%

After: Precision@5 = 85%, Recall@5 = 82%

Using better embeddings from a pretrained model and cleaning text improves similarity search quality significantly.
Bonus Experiment
Try using a different similarity metric like Euclidean distance or dot product and compare results.
💡 Hint
Normalize embeddings before using dot product to make it behave like cosine similarity.

Practice

(1/5)
1.

What is the main goal of similarity search in machine learning?

easy
A. To count the number of items in a dataset
B. To sort items alphabetically
C. To find items that are close or alike in a collection
D. To remove duplicate items from a list

Solution

  1. Step 1: Understand the purpose of similarity search

    Similarity search is used to find items that are similar or close to each other in a dataset.
  2. Step 2: Compare options with the definition

    Only To find items that are close or alike in a collection describes finding similar or close items, which matches the goal of similarity search.
  3. Final Answer:

    To find items that are close or alike in a collection -> Option C
  4. Quick Check:

    Similarity search = find similar items [OK]
Hint: Similarity search finds close or alike items [OK]
Common Mistakes:
  • Confusing similarity search with sorting
  • Thinking similarity search counts items
  • Assuming it removes duplicates
2.

Which of the following is the correct way to compute cosine similarity between two vectors A and B in Python using numpy?

import numpy as np
A = np.array([1, 2, 3])
B = np.array([4, 5, 6])
# What code computes cosine similarity?
easy
A. np.dot(A, B) * (np.linalg.norm(A) + np.linalg.norm(B))
B. np.dot(A, B) / (np.linalg.norm(A) - np.linalg.norm(B))
C. np.sum(A * B) / (np.linalg.norm(A) - np.linalg.norm(B))
D. np.dot(A, B) / (np.linalg.norm(A) * np.linalg.norm(B))

Solution

  1. Step 1: Recall cosine similarity formula

    Cosine similarity = dot product of A and B divided by product of their norms.
  2. Step 2: Match formula to code options

    np.dot(A, B) / (np.linalg.norm(A) * np.linalg.norm(B)) matches the formula exactly: np.dot(A, B) / (np.linalg.norm(A) * np.linalg.norm(B)).
  3. Final Answer:

    np.dot(A, B) / (np.linalg.norm(A) * np.linalg.norm(B)) -> Option D
  4. Quick Check:

    Cosine similarity = dot / (norm A * norm B) [OK]
Hint: Cosine similarity = dot product divided by norms product [OK]
Common Mistakes:
  • Adding norms instead of multiplying
  • Subtracting norms in denominator
  • Multiplying dot product by sum of norms
3.

Given the following vectors, what is the cosine similarity between vec1 and vec2?

import numpy as np
vec1 = np.array([1, 0, 0])
vec2 = np.array([0, 1, 0])
cos_sim = np.dot(vec1, vec2) / (np.linalg.norm(vec1) * np.linalg.norm(vec2))
print("{:.2f}".format(cos_sim))
medium
A. 0.00
B. 0.50
C. -1.00
D. 1.00

Solution

  1. Step 1: Calculate dot product of vec1 and vec2

    Dot product = 1*0 + 0*1 + 0*0 = 0.
  2. Step 2: Calculate norms and cosine similarity

    Norm of vec1 = 1, norm of vec2 = 1, so cosine similarity = 0 / (1*1) = 0.
  3. Final Answer:

    0.00 -> Option A
  4. Quick Check:

    Orthogonal vectors have cosine similarity 0 [OK]
Hint: Orthogonal vectors have cosine similarity zero [OK]
Common Mistakes:
  • Confusing dot product with cosine similarity
  • Forgetting to divide by norms
  • Rounding errors causing wrong answer
4.

Consider this code snippet for similarity search. What is the error?

import numpy as np
vectors = [np.array([1, 2]), np.array([3, 4])]
query = np.array([1, 0])
scores = []
for v in vectors:
    score = np.dot(query, v) / np.linalg.norm(query) * np.linalg.norm(v)
    scores.append(score)
print(scores)
medium
A. Missing parentheses causing wrong order of operations
B. Using np.dot instead of np.cross
C. Vectors have different lengths
D. Query vector is not normalized

Solution

  1. Step 1: Analyze the cosine similarity formula in code

    The formula should divide dot product by product of norms: dot(query, v) / (norm(query) * norm(v)).
  2. Step 2: Identify missing parentheses

    Code does np.dot(query, v) / np.linalg.norm(query) * np.linalg.norm(v), which computes division then multiplication separately, causing wrong result.
  3. Final Answer:

    Missing parentheses causing wrong order of operations -> Option A
  4. Quick Check:

    Use parentheses to group denominator multiplication [OK]
Hint: Use parentheses to group denominator in cosine similarity [OK]
Common Mistakes:
  • Forgetting parentheses around denominator
  • Using cross product instead of dot product
  • Ignoring vector length mismatch
5.

You have a collection of text documents converted into vectors. You want to find the top 2 most similar documents to a new query vector using cosine similarity. Which approach is best?

  1. Compute cosine similarity between query and each document vector.
  2. Sort documents by similarity score descending.
  3. Return top 2 documents.

Which code snippet correctly implements this?

import numpy as np

docs = [np.array([1, 0]), np.array([0, 1]), np.array([1, 1])]
query = np.array([1, 0])

# Choose the correct code:
hard
A. scores = [np.dot(query, d) * np.linalg.norm(query) * np.linalg.norm(d) for d in docs] top2 = sorted(scores)[:2] print(top2)
B. scores = [np.dot(query, d) / (np.linalg.norm(query) * np.linalg.norm(d)) for d in docs] top2 = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:2] print(top2)
C. scores = [np.dot(query, d) / (np.linalg.norm(query) - np.linalg.norm(d)) for d in docs] top2 = sorted(range(len(scores)), key=lambda i: scores[i])[:2] print(top2)
D. scores = [np.cross(query, d) / (np.linalg.norm(query) * np.linalg.norm(d)) for d in docs] top2 = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:2] print(top2)

Solution

  1. Step 1: Compute cosine similarity correctly

    scores = [np.dot(query, d) / (np.linalg.norm(query) * np.linalg.norm(d)) for d in docs] top2 = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:2] print(top2) computes cosine similarity as dot product divided by product of norms, which is correct.
  2. Step 2: Sort indices by similarity descending and select top 2

    scores = [np.dot(query, d) / (np.linalg.norm(query) * np.linalg.norm(d)) for d in docs] top2 = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:2] print(top2) sorts indices by scores descending and selects top 2, matching the requirement.
  3. Final Answer:

    scores = [np.dot(query, d) / (np.linalg.norm(query) * np.linalg.norm(d)) for d in docs] top2 = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:2] print(top2) -> Option B
  4. Quick Check:

    Cosine similarity + sort descending + top 2 = scores = [np.dot(query, d) / (np.linalg.norm(query) * np.linalg.norm(d)) for d in docs] top2 = sorted(range(len(scores)), key=lambda i: scores[i], reverse=True)[:2] print(top2) [OK]
Hint: Compute cosine similarity, sort descending, pick top results [OK]
Common Mistakes:
  • Multiplying norms instead of dividing
  • Using cross product instead of dot product
  • Sorting ascending instead of descending