{ "cells": [ { "cell_type": "markdown", "id": "b8b69235", "metadata": {}, "source": [ "# Lesson 45: Document search with embeddings activity\n", "\n", "In this activity, you will build a simple document search system using word embeddings.\n", "\n", "1. **Document embeddings** - Create document vectors by averaging word embeddings\n", "2. **Similarity search** - Find documents similar to a query\n", "3. **Comparison** - Compare embedding-based search with TF-IDF search\n", "\n", "## Notebook set-up\n", "\n", "### Imports" ] }, { "cell_type": "code", "execution_count": null, "id": "29f4144b", "metadata": {}, "outputs": [], "source": [ "import numpy as np\n", "import gensim.downloader as api\n", "from sklearn.feature_extraction.text import TfidfVectorizer\n", "from sklearn.metrics.pairwise import cosine_similarity\n", "\n", "# Load pre-trained GloVe embeddings (this may take a moment)\n", "print('Loading GloVe embeddings...')\n", "glove = api.load('glove-wiki-gigaword-100')\n", "print(f'Loaded {len(glove)} word vectors of dimension {glove.vector_size}')" ] }, { "cell_type": "markdown", "id": "41eca8f6", "metadata": {}, "source": [ "## 1. Sample document corpus" ] }, { "cell_type": "code", "execution_count": null, "id": "c8486f39", "metadata": {}, "outputs": [], "source": [ "# Sample documents about different topics\n", "documents = [\n", " 'The quick brown fox jumps over the lazy dog',\n", " 'Machine learning algorithms can classify images and text',\n", " 'Neural networks are inspired by the human brain',\n", " 'Python is a popular programming language for data science',\n", " 'Deep learning has revolutionized computer vision',\n", " 'Natural language processing helps computers understand text',\n", " 'The cat sat on the mat near the window',\n", " 'Artificial intelligence is transforming many industries',\n", " 'Dogs and cats are popular household pets',\n", " 'Data scientists use statistics and machine learning',\n", "]\n", "\n", "print(f'Corpus contains {len(documents)} documents')" ] }, { "cell_type": "markdown", "id": "62a018b8", "metadata": {}, "source": [ "## 2. Create document embeddings\n", "\n", "### Task 1: Implement document embedding function\n", "\n", "Complete the function below to create a document embedding by averaging the word embeddings of all words in the document.\n", "\n", "**Hints:**\n", "- Tokenize the document using `.lower().split()`\n", "- Check if each word is in the embedding vocabulary using `word in glove`\n", "- Get the embedding with `glove[word]`\n", "- Average all valid word vectors using `np.mean(vectors, axis=0)`\n", "- Return a zero vector if no words are found" ] }, { "cell_type": "code", "execution_count": null, "id": "84c1294b", "metadata": {}, "outputs": [], "source": [ "def get_document_embedding(document: str, embeddings) -> np.ndarray:\n", " '''Create document embedding by averaging word embeddings.\n", " \n", " TODO: Implement this function.\n", " '''\n", " \n", " # Step 1: Tokenize (lowercase and split)\n", " words = None # YOUR CODE HERE\n", " \n", " # Step 2: Get embeddings for words that exist in vocabulary\n", " vectors = []\n", "\n", " for word in words:\n", " # YOUR CODE HERE - check if word is in embeddings and append vector\n", " pass\n", " \n", " # Step 3: Return average or zero vector\n", " if len(vectors) > 0:\n", " return None # YOUR CODE HERE - return mean of vectors\n", "\n", " else:\n", " return np.zeros(embeddings.vector_size)" ] }, { "cell_type": "code", "execution_count": null, "id": "9147c193", "metadata": {}, "outputs": [], "source": [ "# Create embeddings for all documents\n", "doc_embeddings = np.array([get_document_embedding(doc, glove) for doc in documents])\n", "print(f'Document embeddings shape: {doc_embeddings.shape}')" ] }, { "cell_type": "markdown", "id": "1ef3cbab", "metadata": {}, "source": [ "## 3. Similarity search\n", "\n", "### Task 2: Implement document search function\n", "\n", "Complete the search function to find the most similar documents to a query.\n", "\n", "**Hints:**\n", "- Get the query embedding using `get_document_embedding`\n", "- Compute cosine similarity with `cosine_similarity`\n", "- Sort by similarity score and return top N results" ] }, { "cell_type": "code", "execution_count": null, "id": "550955aa", "metadata": {}, "outputs": [], "source": [ "def search_documents(query: str, documents: list, doc_embeddings: np.ndarray, top_n: int = 3):\n", " '''Search for documents similar to query using embeddings.\n", " \n", " TODO: Implement this function.\n", " '''\n", " \n", " # Step 1: Get query embedding\n", " query_embedding = None # YOUR CODE HERE\n", " \n", " # Step 2: Compute cosine similarity with all documents\n", " # Hint: cosine_similarity expects 2D arrays, so reshape query_embedding\n", " similarities = None # YOUR CODE HERE\n", " \n", " # Step 3: Get indices sorted by similarity (descending)\n", " sorted_indices = None # YOUR CODE HERE\n", " \n", " # Step 4: Return top N results as list of (document, score) tuples\n", " results = []\n", "\n", " for idx in sorted_indices[:top_n]:\n", " results.append((documents[idx], similarities[0][idx]))\n", " \n", " return results" ] }, { "cell_type": "code", "execution_count": null, "id": "769900c5", "metadata": {}, "outputs": [], "source": [ "# Test the search function\n", "queries = [\n", " 'artificial intelligence and deep neural networks',\n", " 'pets and animals',\n", " 'programming code'\n", "]\n", "\n", "print('Embedding-based search results:\\n')\n", "\n", "for query in queries:\n", "\n", " print(f'Query: \"{query}\"')\n", " results = search_documents(query, documents, doc_embeddings)\n", "\n", " for doc, score in results:\n", " print(f' [{score:.3f}] {doc}')\n", "\n", " print()" ] }, { "cell_type": "markdown", "id": "85fce739", "metadata": {}, "source": [ "## 4. Comparison with TF-IDF search\n", "\n", "### Task 3: Compare embedding search with TF-IDF search\n", "\n", "Run the TF-IDF search below and compare results with embedding-based search." ] }, { "cell_type": "code", "execution_count": null, "id": "187bc875", "metadata": {}, "outputs": [], "source": [ "# TF-IDF based search for comparison\n", "tfidf = TfidfVectorizer()\n", "tfidf_matrix = tfidf.fit_transform(documents)\n", "\n", "def search_tfidf(query: str, top_n: int = 3):\n", " '''Search using TF-IDF similarity.'''\n", "\n", " query_vec = tfidf.transform([query])\n", " similarities = cosine_similarity(query_vec, tfidf_matrix)[0]\n", " sorted_indices = np.argsort(similarities)[::-1]\n", " \n", " results = []\n", "\n", " for idx in sorted_indices[:top_n]:\n", " results.append((documents[idx], similarities[idx]))\n", "\n", " return results\n", "\n", "print('TF-IDF search results:\\n')\n", "\n", "for query in queries:\n", "\n", " print(f'Query: \"{query}\"')\n", " results = search_tfidf(query)\n", "\n", " for doc, score in results:\n", " print(f' [{score:.3f}] {doc}')\n", "\n", " print()" ] }, { "cell_type": "markdown", "id": "e4534c6a", "metadata": {}, "source": [ "## 5. Analysis questions\n", "\n", "After completing the tasks above, answer these questions:\n", "\n", "1. Which search method (embeddings vs TF-IDF) handles synonyms and related concepts better? Give an example.\n", "2. What queries where embeddings outperform TF-IDF, and vice versa?\n", "3. What are the trade-offs between these two approaches in terms of speed, memory, and accuracy?\n", "4. How might you combine both approaches for better search results?" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }