diff --git a/private-ai-services-container/introduction/introduction.md b/private-ai-services-container/introduction/introduction.md index 0709f5556..ed819b9ee 100644 --- a/private-ai-services-container/introduction/introduction.md +++ b/private-ai-services-container/introduction/introduction.md @@ -28,7 +28,7 @@ In the following labs you will work not only with in-database embedding but **sp - run cosine similarity search - build a simple image search app that used multimodal embedding models -Estimated Workshop Time: 90 minutes +Estimated Workshop Time: 60 minutes ### Architecture at a Glance @@ -56,4 +56,4 @@ In this workshop, you will: ## Acknowledgements - **Author** - Oracle LiveLabs Team -- **Last Updated By/Date** - Oracle LiveLabs Team, March 2026 +- **Last Updated By/Date** - Oracle LiveLabs Team, April 2026 diff --git a/private-ai-services-container/lab1-verify-environment/images/curl.png b/private-ai-services-container/lab1-verify-environment/images/curl.png new file mode 100644 index 000000000..38e03b7f4 Binary files /dev/null and b/private-ai-services-container/lab1-verify-environment/images/curl.png differ diff --git a/private-ai-services-container/lab1-verify-environment/images/jl.png b/private-ai-services-container/lab1-verify-environment/images/jl.png new file mode 100644 index 000000000..06ef36a7b Binary files /dev/null and b/private-ai-services-container/lab1-verify-environment/images/jl.png differ diff --git a/private-ai-services-container/lab1-verify-environment/images/models.png b/private-ai-services-container/lab1-verify-environment/images/models.png new file mode 100644 index 000000000..a316a7e83 Binary files /dev/null and b/private-ai-services-container/lab1-verify-environment/images/models.png differ diff --git a/private-ai-services-container/lab1-verify-environment/images/services.png b/private-ai-services-container/lab1-verify-environment/images/services.png new file mode 100644 index 000000000..720c581b9 Binary files /dev/null and b/private-ai-services-container/lab1-verify-environment/images/services.png differ diff --git a/private-ai-services-container/lab1-verify-environment/lab1-verify-environment.md b/private-ai-services-container/lab1-verify-environment/lab1-verify-environment.md index d59d0b655..c4ab12010 100644 --- a/private-ai-services-container/lab1-verify-environment/lab1-verify-environment.md +++ b/private-ai-services-container/lab1-verify-environment/lab1-verify-environment.md @@ -14,28 +14,32 @@ In this lab, you will: - Verify internal container DNS resolution from JupyterLab - Validate Private AI health and model list - Confirm Oracle AI Database and ORDS reachability from JupyterLab -- Confirm `/home/.env` is available to the notebook ### Prerequisites This lab assumes: -- You can open a terminal in JupyterLab (`File` -> `New` -> `Terminal`) +- You can open a terminal in JupyterLab (`File` -> `New` -> `Terminal`) or click the `Terminal` tile on the home page + ## Task 1: Verify Internal Hostname Resolution 1. In JupyterLab, open a new terminal. + ![JL Login](./images/jl.png) + 2. Verify that runtime service names resolve: ```bash - getent hosts privateai aidbfree ords + getent hosts privateai aidbfree ords | sort -u ``` Expected: one IP entry for each service name. -## Task 2: Validate Private AI REST Endpoints and list available models + ![Services](./images/services.png) -1. Health endpoint: +## Task 2: Validate Private AI Services Container REST Endpoints and list available models + +1. Let's first check that the Health REST endpoint of the Private AI Services Container: ```bash curl -sS -i http://privateai:8080/health @@ -43,7 +47,9 @@ This lab assumes: Expected: HTTP `200`. -2. List deployed models: + ![Curl result](./images/curl.png) + +2. Now we list all deployed models that come with the Private AI Services Container: ```bash curl -sS http://privateai:8080/v1/models | jq . @@ -51,7 +57,27 @@ This lab assumes: Expected: JSON payload with a `data` array of model IDs. + ![Models](./images/models.png) + +3. Review the models + + Use the table below as a quick model-selection guide after you run `/v1/models`: + + | Model ID | Capabilities | Model Size | Typical Use Case | When to Choose It | + | --- | --- | --- | --- | --- | + | `clip-vit-base-patch32-img` | `IMAGE_EMBEDDINGS` | `335.38M` | Create embeddings for images/photos. | Choose for image indexing and visual similarity search. Pair with `clip-vit-base-patch32-txt` for text-to-image retrieval. | + | `clip-vit-base-patch32-txt` | `TEXT_EMBEDDINGS` | `243.57M` | Create text embeddings compatible with CLIP image vectors. | Choose for natural-language queries against a CLIP image index. | + | `all-minilm-112-v2` | `TEXT_EMBEDDINGS` | `127.13M` | Lightweight general-purpose text embeddings. | Choose for fastest startup and lower resource usage in English-focused semantic search. | + | `all-mpnet-base-v2` | `TEXT_EMBEDDINGS` | `415.82M` | Higher-quality English semantic embeddings. | Choose when retrieval quality matters more than model size and latency. | + | `multilingual-e5-base` | `TEXT_EMBEDDINGS` | `1.04G` | Multilingual semantic search across many languages. | Choose for mixed-language corpora or non-English user queries. | + | `multilingual-e5-large` | `TEXT_EMBEDDINGS` | `2.09G` | Large text embedding model Multilingual semantic search across many languages.. | Choose when you want strongest semantic coverage and can accept higher memory/latency cost. | + {: title="Models deployed in Oracle Private AI Services Container"} + + Rule of thumb: + - For image search: use the CLIP pair (`clip-vit-base-patch32-img` + `clip-vit-base-patch32-txt`). + - For text-only search: start with `all-minilm-112-v2`, then move to larger models if quality needs improvement. + ## Acknowledgements - **Author** - Oracle LiveLabs Team -- **Last Updated By/Date** - Oracle LiveLabs Team, March 2026 +- **Last Updated By/Date** - Oracle LiveLabs Team, April 2026 diff --git a/private-ai-services-container/lab2-db-model-embeddings/images/refresh.png b/private-ai-services-container/lab2-db-model-embeddings/images/refresh.png new file mode 100644 index 000000000..f597f604f Binary files /dev/null and b/private-ai-services-container/lab2-db-model-embeddings/images/refresh.png differ diff --git a/private-ai-services-container/lab2-db-model-embeddings/lab2-db-model-embeddings.md b/private-ai-services-container/lab2-db-model-embeddings/lab2-db-model-embeddings.md index 3144c3cc8..78747604d 100644 --- a/private-ai-services-container/lab2-db-model-embeddings/lab2-db-model-embeddings.md +++ b/private-ai-services-container/lab2-db-model-embeddings/lab2-db-model-embeddings.md @@ -5,7 +5,7 @@ In this lab you run vector search using an embedding model that is already stored in Oracle AI Database (for example `ALL_MINILM_L12_V2`). -Estimated Time: 20 minutes +Estimated Time: 15 minutes ### Objectives @@ -35,6 +35,8 @@ This lab assumes: ## Task 2: Open the Notebook 1. From the sidebar, double-click on the folder notebooks and then open the notebook (in case you do not see the folder click on refresh): + + ![refresh](./images/refresh.png) ``` database-model-embeddings.ipynb @@ -46,6 +48,8 @@ This lab assumes: Run this cell: +This cell imports the Python modules used throughout the notebook. You need these libraries to read environment settings, connect to Oracle Database, format JSON parameters, and safely validate model names before using them in SQL. + ```python import os import json @@ -54,16 +58,18 @@ import oracledb from dotenv import dotenv_values ``` -## Task 3: Load Database Configuration +## Task 4: Load Database Configuration Run this cell: +This cell loads your database connection settings and preferred model name from environment variables. + ```python ENV_PATH = os.getenv('LAB_ENV_FILE', '/home/.env') env = dotenv_values(ENV_PATH) if os.path.exists(ENV_PATH) else {} DB_USER = os.getenv('DB_USER') or env.get('USERNAME') or 'ADMIN' -DB_PASSWORD = os.getenv('DB_PASSWORD') or env.get('DBPASSWORD') +DB_PASSWORD = os.getenv('ORACLE_PWD') DB_HOST = os.getenv('DB_HOST', 'aidbfree') DB_PORT = os.getenv('DB_PORT', '1521') DB_SERVICE = os.getenv('DB_SERVICE', 'FREEPDB1') @@ -76,13 +82,15 @@ print('DB dsn :', DB_DSN) print('Preferred DB model:', PREFERRED_DB_MODEL) if not DB_PASSWORD: - raise ValueError('DB password not found. Set DB_PASSWORD or DBPASSWORD in /home/.env') + raise ValueError('DB password not found. Set ORACLE_PWD') ``` -## Task 4: Connect and Discover Stored Models +## Task 5: Connect and Discover Stored Models Run this cell: +This cell connects to Oracle AI Database, lists available embedding models from `USER_MINING_MODELS`, and picks the model to use for the rest of the lab. All later embedding and vector-search steps depend on selecting a valid deployed model first. + ```python conn = oracledb.connect(user=DB_USER, password=DB_PASSWORD, dsn=DB_DSN) cur = conn.cursor() @@ -108,10 +116,12 @@ if not re.match(r'^[A-Z][A-Z0-9_$#]*$', MODEL_NAME): raise ValueError(f'Unsafe model identifier: {MODEL_NAME}') ``` -## Task 5: Determine Embedding Dimension +## Task 6: Determine Embedding Dimension Run this cell: +This cell performs a small probe embedding and asks the database for the vector dimension returned by the selected model. This can be relevant because the in table schema a user can specify same dimension in its `VECTOR(...)` column. + ```python db_params = json.dumps({ 'provider': 'database', @@ -129,10 +139,12 @@ EMBEDDING_DIM = int(cur.fetchone()[0]) print('Embedding dimension:', EMBEDDING_DIM) ``` -## Task 6: Create Table and Store Embeddings +## Task 7: Create Table and Store Embeddings Run this cell: +This cell creates a demo table, embeds sample text rows, and stores both text and vectors in Oracle AI Database. Here we build the vector dataset that you will query in the similarity search step. + ```python TABLE_NAME = 'PRIVATEAI_DOCS_DBMODEL' @@ -173,10 +185,12 @@ conn.commit() print('Inserted rows:', inserted) ``` -## Task 7: Run Similarity Search +## Task 8: Run Similarity Search Run this cell: +This cell embeds the user query and compares it to stored vectors using cosine distance, then returns the top matching rows. This demonstrates the full semantic-search flow in SQL, not just exact keyword matching. + ```python query_text = 'Which approach keeps embedding generation inside Oracle Database?' @@ -207,10 +221,12 @@ for idx, (title, score, preview) in enumerate(rows, 1): print(f' {preview}') ``` -## Task 8: Optional Cleanup +## Task 9: Optional Cleanup Run optional cleanup: +This optional cell drops the demo table so you can reset the lab and rerun from a clean state. + ```python # cur.execute(f'DROP TABLE {TABLE_NAME} PURGE') # conn.commit() @@ -218,6 +234,8 @@ Run optional cleanup: Close the connection: +This final cell closes database resources. Closing cursors and connections is good practice and prevents resource leaks in longer notebook sessions. + ```python cur.close() conn.close() @@ -230,4 +248,4 @@ print('Connection closed.') ## Acknowledgements - **Author** - Oracle LiveLabs Team -- **Last Updated By/Date** - Oracle LiveLabs Team, March 2026 +- **Last Updated By/Date** - Oracle LiveLabs Team, April 2026 diff --git a/private-ai-services-container/lab3-privateai-container-embeddings/lab3-privateai-container-embeddings.md b/private-ai-services-container/lab3-privateai-container-embeddings/lab3-privateai-container-embeddings.md index 57f0f4360..d91293522 100644 --- a/private-ai-services-container/lab3-privateai-container-embeddings/lab3-privateai-container-embeddings.md +++ b/private-ai-services-container/lab3-privateai-container-embeddings/lab3-privateai-container-embeddings.md @@ -7,10 +7,10 @@ In this lab you run vector search using Oracle Private AI Services Container (`p This lab mirrors the notebook: ```text -lab3-privateai-container-embeddings/files/privateai-container-embeddings.ipynb +privateai-container-embeddings.ipynb ``` -Estimated Time: 25 minutes +Estimated Time: 15 minutes ### Objectives @@ -55,6 +55,9 @@ This lab assumes: Run this cell: +This cell imports the Python modules used throughout the notebook. You need these libraries to read environment settings, connect to Oracle Database, format JSON parameters, and safely validate model names before using them in SQL. + + ```python import os import json @@ -63,16 +66,18 @@ import oracledb from dotenv import dotenv_values ``` -## Task 3: Load Configuration +## Task 4: Load Configuration Run this cell: +This step reads database and Private AI settings from environment variables and prints the active values. It gives you a quick sanity check that the notebook is pointing to the correct services before any API or SQL calls run. + ```python ENV_PATH = os.getenv('LAB_ENV_FILE', '/home/.env') env = dotenv_values(ENV_PATH) if os.path.exists(ENV_PATH) else {} DB_USER = os.getenv('DB_USER') or env.get('USERNAME') or 'ADMIN' -DB_PASSWORD = os.getenv('DB_PASSWORD') or env.get('DBPASSWORD') +DB_PASSWORD = os.getenv('ORACLE_PWD') DB_HOST = os.getenv('DB_HOST', 'aidbfree') DB_PORT = os.getenv('DB_PORT', '1521') DB_SERVICE = os.getenv('DB_SERVICE', 'FREEPDB1') @@ -88,13 +93,15 @@ print('Private AI URL:', PRIVATEAI_BASE_URL) print('Preferred model:', PREFERRED_MODEL) if not DB_PASSWORD: - raise ValueError('DB password not found. Set DB_PASSWORD or DBPASSWORD in /home/.env') + raise ValueError('DB password not found. Set ORACLE_PWD) ``` -## Task 4: Validate Private AI and Choose Model +## Task 5: Validate Private AI and Choose Model Run this cell: +This block checks that the Private AI service is healthy, fetches the deployed model list, filters to text-embedding-capable models, and selects one model ID. You do this now so the rest of the lab uses a model that is actually available in your environment. + ```python health = requests.get(f'{PRIVATEAI_BASE_URL}/health', timeout=20) print('Health status:', health.status_code) @@ -129,10 +136,12 @@ print() print('Selected model:', MODEL_ID) ``` -## Task 5: Call `/v1/embeddings` Directly +## Task 6: Send embedding request to REST API Run this cell: +Here you send a direct embeddings request to the Private AI endpoint with sample text. The response confirms end-to-end API behavior and reveals the embedding dimension needed for the database `VECTOR` column. + ```python payload = { 'model': MODEL_ID, @@ -156,22 +165,26 @@ print('Returned vectors:', len(embed_json.get('data', []))) print('Embedding dimension:', EMBEDDING_DIM) ``` -## Task 6: Connect to Oracle Database +## Task 7: Connect to Oracle Database Run this cell: +This step opens a database session and verifies the connected user. It establishes the SQL connection required to create tables, insert vectors, and run similarity search queries. + ```python conn = oracledb.connect(user=DB_USER, password=DB_PASSWORD, dsn=DB_DSN) cur = conn.cursor() -cur.execute('select user from dual') +cur.execute('select user') print('Connected as:', cur.fetchone()[0]) ``` -## Task 7: Create Table and Store Private AI Embeddings +## Task 8: Create Table and Store Private AI Embeddings Run this cell: +This cell creates a demo table, defines Private AI embedding parameters, and inserts sample rows with vectors generated through Private AI Services Container. The result is a searchable vector dataset stored in Oracle AI Database. + ```python TABLE_NAME = 'PRIVATEAI_DOCS_CONTAINER' @@ -219,10 +232,12 @@ conn.commit() print('Inserted rows:', inserted) ``` -## Task 8: Run Similarity Search +## Task 9: Run Similarity Search Run this cell: +This query converts the user question into an embedding and ranks stored rows by cosine similarity. It demonstrates semantic retrieval, where meaning-based matches can be returned even when keywords differ. + ```python query_text = 'How can I run embeddings locally and use them for semantic search?' @@ -253,10 +268,12 @@ for idx, (title, score, preview) in enumerate(rows, 1): print(f' {preview}') ``` -## Task 9: Optional Cleanup +## Task 10: Optional Cleanup Run optional cleanup: +Use this optional command to drop the demo table when you want to reset and rerun the lab from a clean state. + ```python # cur.execute(f'DROP TABLE {TABLE_NAME} PURGE') # conn.commit() @@ -264,6 +281,8 @@ Run optional cleanup: Close the connection: +This final cell closes the cursor and connection so the session ends cleanly and no resources remain open in the notebook kernel. + ```python cur.close() conn.close() @@ -279,4 +298,4 @@ print('Connection closed.') ## Acknowledgements - **Author** - Oracle LiveLabs Team -- **Last Updated By/Date** - Oracle LiveLabs Team, March 2026 +- **Last Updated By/Date** - Oracle LiveLabs Team, April 2026 diff --git a/private-ai-services-container/lab4-flask-image-search/files/__pycache__/app.cpython-314.pyc b/private-ai-services-container/lab4-flask-image-search/files/__pycache__/app.cpython-314.pyc new file mode 100644 index 000000000..bc4e9b2d5 Binary files /dev/null and b/private-ai-services-container/lab4-flask-image-search/files/__pycache__/app.cpython-314.pyc differ diff --git a/private-ai-services-container/lab4-flask-image-search/files/app.py b/private-ai-services-container/lab4-flask-image-search/files/app.py index 8b1595b56..5cb5383b1 100644 --- a/private-ai-services-container/lab4-flask-image-search/files/app.py +++ b/private-ai-services-container/lab4-flask-image-search/files/app.py @@ -2,6 +2,7 @@ import json import mimetypes import os +import subprocess from pathlib import Path import oracledb @@ -27,11 +28,11 @@ def _load_env(): - db_password = (os.getenv("DBPASSWORD") or "").strip() + db_password = (os.getenv("ORACLE_PWD") or "").strip() if not db_password: - raise RuntimeError("Database password not found in env variable DBPASSWORD.") + raise RuntimeError("Database password not found in env variable ORACLE_PWD.") - return DB_USER, DB_DSN, db_password, "DBPASSWORD env" + return DB_USER, DB_DSN, db_password, "ORACLE_PWD env" def _get_connection(): @@ -208,6 +209,21 @@ def _table_count(): return int(cur.fetchone()[0]) +def _detect_public_ip(): + try: + result = subprocess.run( + ["curl", "-s", "ifconfig.me"], + capture_output=True, + text=True, + timeout=10, + check=True, + ) + public_ip = result.stdout.strip() + return public_ip or None + except Exception: + return None + + @APP.route("/", methods=["GET", "POST"]) def index(): _ensure_table() @@ -264,5 +280,9 @@ def index(): ) _ensure_table() print("Starting Flask image search app on port 5500") - print("Open in browser: http://:5500/") + public_ip = _detect_public_ip() + if public_ip: + print(f"Open in browser: http://{public_ip}:5500/") + else: + print("Open in browser: http://:5500/") APP.run(host="0.0.0.0", port=5500, debug=True) diff --git a/private-ai-services-container/lab4-flask-image-search/images/app.png b/private-ai-services-container/lab4-flask-image-search/images/app.png new file mode 100644 index 000000000..c9f32f5f3 Binary files /dev/null and b/private-ai-services-container/lab4-flask-image-search/images/app.png differ diff --git a/private-ai-services-container/lab4-flask-image-search/images/archive.png b/private-ai-services-container/lab4-flask-image-search/images/archive.png new file mode 100644 index 000000000..2e29c9670 Binary files /dev/null and b/private-ai-services-container/lab4-flask-image-search/images/archive.png differ diff --git a/private-ai-services-container/lab4-flask-image-search/images/startapp.png b/private-ai-services-container/lab4-flask-image-search/images/startapp.png new file mode 100644 index 000000000..4f2d7cf4d Binary files /dev/null and b/private-ai-services-container/lab4-flask-image-search/images/startapp.png differ diff --git a/private-ai-services-container/lab4-flask-image-search/lab4-flask-image-search.md b/private-ai-services-container/lab4-flask-image-search/lab4-flask-image-search.md index fadae098f..5fb11bf7b 100644 --- a/private-ai-services-container/lab4-flask-image-search/lab4-flask-image-search.md +++ b/private-ai-services-container/lab4-flask-image-search/lab4-flask-image-search.md @@ -2,7 +2,15 @@ ## Introduction -In this lab you build a small Flask application that performs semantic image search. +In this lab, you build a small Flask application that performs semantic image search. + +This pattern is useful when users need to find visual content by meaning, not just by file name or manual tags. By combining CLIP embeddings with Oracle AI Vector Search, you can support natural-language queries over image libraries while keeping model inference inside your private environment. + +The same approach can solve real problems across industries, for example: +- Retail and e-commerce: find visually similar products for recommendations and catalog operations. +- Manufacturing and field service: retrieve matching equipment photos for troubleshooting and parts identification. +- Healthcare and life sciences: organize and search large medical or lab image collections with controlled infrastructure. +- Media, publishing, and marketing: speed up creative asset discovery across large internal media libraries. The workflow is: - Download and unzip an image archive with `wget` @@ -46,6 +54,11 @@ This lab assumes: find ~/image-search-lab/data/pdimagearchive -type f \( -iname "*.jpg" -o -iname "*.jpeg" -o -iname "*.png" \) | head -n 5 ``` + This command shows the first five images from the archive. + + ![Archive](./images/archive.png) + + ## Task 2: Create Flask App Files 1. Create project folders: @@ -56,12 +69,21 @@ This lab assumes: Create `~/image-search-lab/app.py` with the following code: + `app.py` is the full lab application. It provides the Flask web UI, loads image files, generates embeddings, stores vectors and image bytes in Oracle AI Database, and runs top-K similarity search when a user submits a query. + + Private AI Services Container integration points in this file: + - `PRIVATEAI_BASE_URL` and `PRIVATEAI_EMBED_URL` define the Private AI endpoint (`http://privateai:8080/v1/embeddings`). + - `_embed_with_privateai(...)` calls the Private AI `/v1/embeddings` API with `requests.post(...)`. + - In `_load_images_into_db(...)`, each image is base64-encoded and sent with `IMAGE_MODEL_ID` (`clip-vit-base-patch32-img`) to create image embeddings. + - In `_search_images(...)`, the query text is sent with `TEXT_MODEL_ID` (`clip-vit-base-patch32-txt`) so text and image vectors can be compared in the same vector space. + ```python import base64 import json import mimetypes import os + import subprocess from pathlib import Path import oracledb @@ -87,11 +109,11 @@ This lab assumes: def _load_env(): - db_password = (os.getenv("DBPASSWORD") or "").strip() + db_password = (os.getenv("ORACLE_PWD") or "").strip() if not db_password: - raise RuntimeError("Database password not found in env variable DBPASSWORD.") + raise RuntimeError("Database password not found in env variable ORACLE_PWD.") - return DB_USER, DB_DSN, db_password, "DBPASSWORD env" + return DB_USER, DB_DSN, db_password, "ORACLE_PWD env" def _get_connection(): @@ -268,6 +290,21 @@ This lab assumes: return int(cur.fetchone()[0]) + def _detect_public_ip(): + try: + result = subprocess.run( + ["curl", "-s", "ifconfig.me"], + capture_output=True, + text=True, + timeout=10, + check=True, + ) + public_ip = result.stdout.strip() + return public_ip or None + except Exception: + return None + + @APP.route("/", methods=["GET", "POST"]) def index(): _ensure_table() @@ -324,12 +361,18 @@ This lab assumes: ) _ensure_table() print("Starting Flask image search app on port 5500") - print("Open in browser: http://:5500/") + public_ip = _detect_public_ip() + if public_ip: + print(f"Open in browser: http://{public_ip}:5500/") + else: + print("Open in browser: http://:5500/") APP.run(host="0.0.0.0", port=5500, debug=True) ``` -2. Open a terminal and run the following command to copy the `index.html` to the templates folder: +2. Open a terminal and run the following command to copy the `index.html` into the templates folder: + + In Flask, a template file defines the HTML page rendered by `render_template(...)`. This `index.html` template controls the app UI, including the load/search forms and the results grid. ```bash @@ -346,28 +389,22 @@ This lab assumes: python app.py ``` -2. Expected output includes: - - ```text - Starting Flask image search app on port 5500 - Open in browser: http://:5500/ - ``` - ## Task 4: Open the Web UI 1. Open the Flask app directly: ```text - http://:5500/ + 💥💥 CLICK HERE 💥💥 Open in browser: http://[public IP]:5500/ 💥💥 CLICK HERE 💥💥 ``` + ![Start](./images/startapp.png) + ## Task 5: Load Images and Build Embeddings 1. In the app: -2. - 1. Keep the default image root (`~/image-search-lab/data/pdimagearchive`) or adjust if needed. - 2. Click **Load + Embed Images**. - 3. Wait for completion message with inserted/skipped counts. + 1. Keep the default image root (`~/image-search-lab/data/pdimagearchive`) or adjust if needed. + 2. Click **Load + Embed Images**. + 3. Wait for completion message with inserted/skipped counts. This step generates image embeddings with: - `clip-vit-base-patch32-img` @@ -381,10 +418,17 @@ This step generates image embeddings with: 2. Click **Search Top 10**. -The app embeds the query using: -- `clip-vit-base-patch32-txt` + The app embeds the query using: + - `clip-vit-base-patch32-txt` + + Then it returns and displays the 10 most similar images using cosine similarity. + + How to interpret the score shown in the app: + - **Higher score means more similar**. + - The SQL sorts by cosine distance (`VECTOR_DISTANCE(..., COSINE)`), where lower is better. + - The displayed `similarity` value is already calculated as `1 - VECTOR_DISTANCE(...)`. -Then it returns and displays the 10 most similar images using cosine similarity. + ![Final app](./images/app.png) ## Task 7: Clean Up Database Objects @@ -462,4 +506,4 @@ Then it returns and displays the 10 most similar images using cosine similarity. ## Acknowledgements - **Author** - Oracle LiveLabs Team -- **Last Updated By/Date** - Oracle LiveLabs Team, March 2026 +- **Last Updated By/Date** - Oracle LiveLabs Team, April 2026