-
Notifications
You must be signed in to change notification settings - Fork 224
Adds OpenAI inference service notebook #154
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
a61f99d
Adds openai inference service notebook.
szabosteve 4c7ab00
Apply suggestions from code review
szabosteve 130b25c
[DOCS] Refines notebook.
szabosteve 127ce4f
[DOCS] Resolves conflict.
szabosteve 26d77c6
[DOCS] Updates notebook.
szabosteve e23a2a3
Adds output to search.
szabosteve 9c4cf2b
Update notebooks/search/07-inference.ipynb
szabosteve 020a82b
Cleans up outputs.
szabosteve File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,396 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"id": "7a765629", | ||
"metadata": {}, | ||
"source": [ | ||
"# Semantic Search using the Inference API\n", | ||
"\n", | ||
"[](https://colab.research.google.com/github/elastic/elasticsearch-labs/blob/main/notebooks/search/07-inference.ipynb)\n", | ||
"\n", | ||
"\n", | ||
"Learn how to use the [Inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/inference-apis.html) for semantic search." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "f9101eb9", | ||
"metadata": {}, | ||
"source": [ | ||
"# 🧰 Requirements\n", | ||
"\n", | ||
"For this example, you will need:\n", | ||
"\n", | ||
"- An Elastic deployment:\n", | ||
" - We'll be using [Elastic Cloud](https://www.elastic.co/guide/en/cloud/current/ec-getting-started.html) for this example (available with a [free trial](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook))\n", | ||
" \n", | ||
"- A paid [OpenAI account](https://openai.com/) is required to use the Inference API with \n", | ||
"the OpenAI service as the OpenAI free trial API usage is limited. " | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "4cd69cc0", | ||
"metadata": {}, | ||
"source": [ | ||
"# Create Elastic Cloud deployment\n", | ||
"\n", | ||
"If you don't have an Elastic Cloud deployment, sign up [here](https://cloud.elastic.co/registration?utm_source=github&utm_content=elasticsearch-labs-notebook) for a free trial." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "f27dffbf", | ||
"metadata": {}, | ||
"source": [ | ||
"# Install packages and connect with Elasticsearch Client\n", | ||
"\n", | ||
"To get started, we'll need to connect to our Elastic deployment using the Python client.\n", | ||
"Because we're using an Elastic Cloud deployment, we'll use the **Cloud ID** to identify our deployment.\n", | ||
"\n", | ||
"First we need to `pip` install the following packages:\n", | ||
"\n", | ||
"- `elasticsearch`" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "8c4b16bc", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"!pip install elasticsearch" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "41ef96b3", | ||
"metadata": {}, | ||
"source": [ | ||
"Next, we need to import the modules we need. 🔐 NOTE: getpass enables us to securely prompt the user for credentials without echoing them to the terminal, or storing it in memory." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "690ff9af", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"from elasticsearch import Elasticsearch, helpers, exceptions\n", | ||
"from urllib.request import urlopen\n", | ||
"import getpass\n", | ||
"import json\n", | ||
"import time" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "23fa2b6c", | ||
"metadata": {}, | ||
"source": [ | ||
"Now we can instantiate the Python Elasticsearch client.\n", | ||
"\n", | ||
"First we prompt the user for their password and Cloud ID.\n", | ||
"Then we create a `client` object that instantiates an instance of the `Elasticsearch` class." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "195cc597", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#finding-your-cloud-id\n", | ||
"ELASTIC_CLOUD_ID = getpass(\"Elastic Cloud ID: \")\n", | ||
"\n", | ||
"# https://www.elastic.co/search-labs/tutorials/install-elasticsearch/elastic-cloud#creating-an-api-key\n", | ||
"ELASTIC_API_KEY = getpass(\"Elastic Api Key: \")\n", | ||
"\n", | ||
"# Create the client instance\n", | ||
"client = Elasticsearch(\n", | ||
" # For local development\n", | ||
" # hosts=[\"http://localhost:9200\"] \n", | ||
" cloud_id=ELASTIC_CLOUD_ID,\n", | ||
" api_key=ELASTIC_API_KEY,\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "b1115ffb", | ||
"metadata": {}, | ||
"source": [ | ||
"Confirm that the client has connected with this test:" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "cc0de5ea", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"print(client.info())" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "659c5890", | ||
"metadata": {}, | ||
"source": [ | ||
"Refer to [the documentation](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect to a self-managed deployment.\n", | ||
"\n", | ||
"Read [this page](https://www.elastic.co/guide/en/elasticsearch/client/python-api/current/connecting.html#connect-self-managed-new) to learn how to connect using API keys." | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "840d92f0", | ||
"metadata": {}, | ||
"source": [ | ||
"## Create the inference task\n", | ||
"\n", | ||
"Let's create the inference task by using the [Create inference API](https://www.elastic.co/guide/en/elasticsearch/reference/current/put-inference-api.html).\n", | ||
"\n", | ||
"You'll need an OpenAI API key for this that you can find in your OpenAI account under the [API keys section](https://platform.openai.com/api-keys). A paid membership is required to complete the steps in this notebook as the OpenAI free trial API usage is limited." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "0d007737", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"API_KEY = getpass.getpass('OpenAI API key: ')\n", | ||
"\n", | ||
"client.inference.put_model(\n", | ||
" task_type=\"text_embedding\",\n", | ||
" model_id=\"my_openai_embedding_model\",\n", | ||
" body={\n", | ||
" \"service\": \"openai\",\n", | ||
" \"service_settings\": {\n", | ||
" \"api_key\": API_KEY\n", | ||
" },\n", | ||
" \"task_settings\": {\n", | ||
" \"model\": \"text-embedding-ada-002\"\n", | ||
" }\n", | ||
" }\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "1024d070", | ||
"metadata": {}, | ||
"source": [ | ||
"## Create an ingest pipeline with an inference processor\n", | ||
"\n", | ||
"Create an ingest pipeline with an inference processor by using the [`put_pipeline`](https://www.elastic.co/guide/en/elasticsearch/reference/master/put-pipeline-api.html) method. Reference the OpenAI model created above to infer against the data that is being ingested in the pipeline." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "6ace9e2e", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"client.ingest.put_pipeline(\n", | ||
" id=\"openai_embeddings_pipeline\", \n", | ||
" description=\"Ingest pipeline for OpenAI inference.\",\n", | ||
" processors=[\n", | ||
" {\n", | ||
" \"inference\": {\n", | ||
" \"model_id\": \"my_openai_embedding_model\",\n", | ||
" \"input_output\": {\n", | ||
" \"input_field\": \"plot\",\n", | ||
" \"output_field\": \"plot_embedding\"\n", | ||
" }\n", | ||
" }\n", | ||
" }\n", | ||
" ]\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "76d07567", | ||
"metadata": {}, | ||
"source": [ | ||
"Let's note a few important parameters from that API call:\n", | ||
"\n", | ||
"- `inference`: A processor that performs inference using a machine learning model.\n", | ||
"- `model_id`: Specifies the ID of the machine learning model to be used. In this example, the model ID is set to `my_openai_embedding_model`. Use the model ID you defined when created the inference task.\n", | ||
"- `input_output`: Specifies input and output fields.\n", | ||
"- `input_field`: Field name from which the `dense_vector` representation is created.\n", | ||
"- `output_field`: Field name which contains inference results. " | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "28e12d7a", | ||
"metadata": {}, | ||
"source": [ | ||
"## Create index\n", | ||
"\n", | ||
"The mapping of the destination index - the index that contains the embeddings that the model will create based on your input text - must be created. The destination index must have a field with the [dense_vector](https://www.elastic.co/guide/en/elasticsearch/reference/current/dense-vector.html) field type to index the output of the OpenAI model.\n", | ||
"\n", | ||
"Let's create an index named `openai-movie-embeddings` with the mappings we need." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "6ddcbca3", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"client.indices.delete(index=\"openai-movie-embeddings\", ignore_unavailable=True)\n", | ||
"client.indices.create(\n", | ||
" index=\"openai-movie-embeddings\",\n", | ||
" settings={\n", | ||
" \"index\": {\n", | ||
" \"default_pipeline\": \"openai_embeddings_pipeline\"\n", | ||
" }\n", | ||
" },\n", | ||
" mappings={\n", | ||
" \"properties\": {\n", | ||
" \"plot_embedding\": { \n", | ||
" \"type\": \"dense_vector\", \n", | ||
" \"dims\": 1536, \n", | ||
" \"similarity\": \"dot_product\" \n", | ||
" },\n", | ||
" \"plot\": {\n", | ||
" \"type\": \"text\"\n", | ||
" }\n", | ||
" }\n", | ||
" }\n", | ||
")" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "07c187a9", | ||
"metadata": {}, | ||
"source": [ | ||
"## Insert Documents\n", | ||
"\n", | ||
"Let's insert our example dataset of 12 movies. You need a paid OpenAI account to complete this step, otherwise the documentation ingest will time out due to the API request rate limits." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "d68737cb", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"url = \"https://raw.githubusercontent.com/elastic/elasticsearch-labs/main/notebooks/search/movies.json\"\n", | ||
"response = urlopen(url)\n", | ||
"\n", | ||
"# Load the response data into a JSON object\n", | ||
"data_json = json.loads(response.read())\n", | ||
"\n", | ||
"# Prepare the documents to be indexed\n", | ||
"documents = []\n", | ||
"for doc in data_json:\n", | ||
" documents.append({\n", | ||
" \"_index\": \"openai-movie-embeddings\",\n", | ||
" \"_source\": doc,\n", | ||
" })\n", | ||
"\n", | ||
"# Use helpers.bulk to index\n", | ||
"helpers.bulk(client, documents)\n", | ||
"\n", | ||
"print(\"Done indexing documents into `openai-movie-embeddings` index!\")\n", | ||
"time.sleep(3)" | ||
] | ||
}, | ||
{ | ||
"cell_type": "markdown", | ||
"id": "cf0f6df7", | ||
"metadata": {}, | ||
"source": [ | ||
"## Semantic search\n", | ||
"\n", | ||
"After the dataset has been enriched with the embeddings, you can query the data using [semantic search](https://www.elastic.co/guide/en/elasticsearch/reference/current/knn-search.html#knn-semantic-search). Pass a `query_vector_builder` to the k-nearest neighbor (kNN) vector search API, and provide the query text and the model you have used to create the embeddings." | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": null, | ||
"id": "d9b21b71", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Score: 0.91674197\n", | ||
"Title: Fight Club\n", | ||
"Plot: An insomniac office worker and a devil-may-care soapmaker form an underground fight club that evolves into something much, much more.\n", | ||
"\n", | ||
"Score: 0.9069592\n", | ||
"Title: Pulp Fiction\n", | ||
"Plot: The lives of two mob hitmen, a boxer, a gangster and his wife, and a pair of diner bandits intertwine in four tales of violence and redemption.\n", | ||
"\n", | ||
"Score: 0.8992071\n", | ||
"Title: The Dark Knight\n", | ||
"Plot: When the menace known as the Joker wreaks havoc and chaos on the people of Gotham, Batman must accept one of the greatest psychological and physical tests of his ability to fight injustice.\n", | ||
"\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"response = client.search(\n", | ||
" index='openai-movie-embeddings', \n", | ||
" size=3,\n", | ||
" knn={\n", | ||
" \"field\": \"plot_embedding\",\n", | ||
" \"query_vector_builder\": {\n", | ||
" \"text_embedding\": {\n", | ||
" \"model_id\": \"my_openai_embedding_model\",\n", | ||
" \"model_text\": \"Fighting movie\"\n", | ||
" }\n", | ||
" },\n", | ||
" \"k\": 10,\n", | ||
" \"num_candidates\": 100\n", | ||
" }\n", | ||
")\n", | ||
"\n", | ||
"for hit in response['hits']['hits']:\n", | ||
" doc_id = hit['_id']\n", | ||
" score = hit['_score']\n", | ||
" title = hit['_source']['title']\n", | ||
" plot = hit['_source']['plot']\n", | ||
" print(f\"Score: {score}\\nTitle: {title}\\nPlot: {plot}\\n\")" | ||
szabosteve marked this conversation as resolved.
Show resolved
Hide resolved
|
||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Python 3 (ipykernel)", | ||
"language": "python", | ||
"name": "python3" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": { | ||
"name": "ipython", | ||
"version": 3 | ||
}, | ||
"file_extension": ".py", | ||
"mimetype": "text/x-python", | ||
"name": "python", | ||
"nbconvert_exporter": "python", | ||
"pygments_lexer": "ipython3", | ||
"version": "3.9.12" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 5 | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.