# Vector embeddings

Learn how to turn text into numbers, unlocking use cases like search.

New embedding models

`text-embedding-3-small` and `text-embedding-3-large`, our newest and most
performant embedding models, are now available. They feature lower costs, higher
multilingual performance, and new parameters to control the overall size.

## What are embeddings?

OpenAIâs text embeddings measure the relatedness of text strings. Embeddings are
commonly used for:

- **Search** (where results are ranked by relevance to a query string)
- **Clustering** (where text strings are grouped by similarity)
- **Recommendations** (where items with related text strings are recommended)
- **Anomaly detection** (where outliers with little relatedness are identified)
- **Diversity measurement** (where similarity distributions are analyzed)
- **Classification** (where text strings are classified by their most similar
  label)

An embedding is a vector (list) of floating point numbers. The
[distance](https://platform.openai.com/docs/guides/embeddings#which-distance-function-should-i-use)
between two vectors measures their relatedness. Small distances suggest high
relatedness and large distances suggest low relatedness.

Visit our pricing page to learn about embeddings pricing. Requests are billed
based on the number of [tokens](/tokenizer) in the
[input](https://platform.openai.com/docs/api-reference/embeddings/create#embeddings/create-input).

## How to get embeddings

To get an embedding, send your text string to the
[embeddings API endpoint](https://platform.openai.com/docs/api-reference/embeddings)
along with the embedding model name (e.g., `text-embedding-3-small`):

```javascript
import OpenAI from "openai";
const openai = new OpenAI();

const embedding = await openai.embeddings.create({
  model: "text-embedding-3-small",
  input: "Your text string goes here",
  encoding_format: "float",
});

console.log(embedding);
```

```python
from openai import OpenAI
client = OpenAI()

response = client.embeddings.create(
    input="Your text string goes here",
    model="text-embedding-3-small"
)

print(response.data[0].embedding)
```

```bash
curl https://api.openai.com/v1/embeddings \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "input": "Your text string goes here",
    "model": "text-embedding-3-small"
  }'
```

The response contains the embedding vector (list of floating point numbers)
along with some additional metadata. You can extract the embedding vector, save
it in a vector database, and use for many different use cases.

```json
{
  "object": "list",
  "data": [
    {
      "object": "embedding",
      "index": 0,
      "embedding": [
        -0.006929283495992422, -0.005336422007530928, -4.547132266452536e-5,
        -0.024047505110502243
      ]
    }
  ],
  "model": "text-embedding-3-small",
  "usage": {
    "prompt_tokens": 5,
    "total_tokens": 5
  }
}
```

By default, the length of the embedding vector is `1536` for
`text-embedding-3-small` or `3072` for `text-embedding-3-large`. To reduce the
embedding's dimensions without losing its concept-representing properties, pass
in the
[dimensions parameter](https://platform.openai.com/docs/api-reference/embeddings/create#embeddings-create-dimensions).
Find more detail on embedding dimensions in the
[embedding use case section](https://platform.openai.com/docs/guides/embeddings#use-cases).

## Embedding models

OpenAI offers two powerful third-generation embedding model (denoted by `-3` in
the model ID). Read the embedding v3 announcement blog post for more details.

Usage is priced per input token. Below is an example of pricing pages of text
per US dollar (assuming ~800 tokens per page):

| Model                  | ~ Pages per dollar | Performance on MTEB eval | Max input |
| ---------------------- | ------------------ | ------------------------ | --------- |
| text-embedding-3-small | 62,500             | 62.3%                    | 8192      |
| text-embedding-3-large | 9,615              | 64.6%                    | 8192      |
| text-embedding-ada-002 | 12,500             | 61.0%                    | 8192      |

## Use cases

Here we show some representative use cases, using the Amazon fine-food reviews
dataset.

### Obtaining the embeddings

The dataset contains a total of 568,454 food reviews left by Amazon users up to
October 2012. We use a subset of the 1000 most recent reviews for illustration
purposes. The reviews are in English and tend to be positive or negative. Each
review has a `ProductId`, `UserId`, `Score`, review title (`Summary`) and review
body (`Text`). For example:

| Product Id | User Id        | Score | Summary               | Text                                              |
| ---------- | -------------- | ----- | --------------------- | ------------------------------------------------- |
| B001E4KFG0 | A3SGXH7AUHU8GW | 5     | Good Quality Dog Food | I have bought several of the Vitality canned...   |
| B00813GRG4 | A1D87F6ZCVE5NK | 1     | Not as Advertised     | Product arrived labeled as Jumbo Salted Peanut... |

Below, we combine the review summary and review text into a single combined
text. The model encodes this combined text and output a single vector embedding.

[Get_embeddings_from_dataset.ipynb](https://cookbook.openai.com/examples/get_embeddings_from_dataset)

```python
from openai import OpenAI
client = OpenAI()

def get_embedding(text, model="text-embedding-3-small"):
    text = text.replace("\n", " ")
    return client.embeddings.create(input = [text], model=model).data[0].embedding

df['ada_embedding'] = df.combined.apply(lambda x: get_embedding(x, model='text-embedding-3-small'))
df.to_csv('output/embedded_1k_reviews.csv', index=False)
```

To load the data from a saved file, you can run the following:

```python
import pandas as pd

df = pd.read_csv('output/embedded_1k_reviews.csv')
df['ada_embedding'] = df.ada_embedding.apply(eval).apply(np.array)
```

Reducing embedding dimensions

Using larger embeddings, for example storing them in a vector store for
retrieval, generally costs more and consumes more compute, memory and storage
than using smaller embeddings.

Both of our new embedding models were trained with a technique that allows
developers to trade-off performance and cost of using embeddings. Specifically,
developers can shorten embeddings (i.e. remove some numbers from the end of the
sequence) without the embedding losing its concept-representing properties by
passing in the
[dimensions](https://platform.openai.com/docs/api-reference/embeddings/create#embeddings-create-dimensions).
For example, on the MTEB benchmark, a `text-embedding-3-large` embedding can be
shortened to a size of 256 while still outperforming an unshortened
`text-embedding-ada-002` embedding with a size of 1536. You can read more about
how changing the dimensions impacts performance in our embeddings v3 launch blog
post.

In general, using the `dimensions` parameter when creating the embedding is the
suggested approach. In certain cases, you may need to change the embedding
dimension after you generate it. When you change the dimension manually, you
need to be sure to normalize the dimensions of the embedding as is shown below.

```python
from openai import OpenAI
import numpy as np

client = OpenAI()

def normalize_l2(x):
    x = np.array(x)
    if x.ndim == 1:
        norm = np.linalg.norm(x)
        if norm == 0:
            return x
        return x / norm
    else:
        norm = np.linalg.norm(x, 2, axis=1, keepdims=True)
        return np.where(norm == 0, x, x / norm)


response = client.embeddings.create(
    model="text-embedding-3-small", input="Testing 123", encoding_format="float"
)

cut_dim = response.data[0].embedding[:256]
norm_dim = normalize_l2(cut_dim)

print(norm_dim)
```

Dynamically changing the dimensions enables very flexible usage. For example,
when using a vector data store that only supports embeddings up to 1024
dimensions long, developers can now still use our best embedding model
`text-embedding-3-large` and specify a value of 1024 for the `dimensions` API
parameter, which will shorten the embedding down from 3072 dimensions, trading
off some accuracy in exchange for the smaller vector size.

Question answering using embeddings-based search

[Question_answering_using_embeddings.ipynb](https://cookbook.openai.com/examples/question_answering_using_embeddings)

There are many common cases where the model is not trained on data which
contains key facts and information you want to make accessible when generating
responses to a user query. One way of solving this, as shown below, is to put
additional information into the context window of the model. This is effective
in many use cases but leads to higher token costs. In this notebook, we explore
the tradeoff between this approach and embeddings bases search.

```python
query = f"""Use the below article on the 2022 Winter Olympics to answer the subsequent question. If the answer cannot be found, write "I don't know."

Article:
\"\"\"
{wikipedia_article_on_curling}
\"\"\"

Question: Which athletes won the gold medal in curling at the 2022 Winter Olympics?"""

response = client.chat.completions.create(
    messages=[
        {'role': 'system', 'content': 'You answer questions about the 2022 Winter Olympics.'},
        {'role': 'user', 'content': query},
    ],
    model=GPT_MODEL,
    temperature=0,
)

print(response.choices[0].message.content)
```

Text search using embeddings

[Semantic_text_search_using_embeddings.ipynb](https://cookbook.openai.com/examples/semantic_text_search_using_embeddings)

To retrieve the most relevant documents we use the cosine similarity between the
embedding vectors of the query and each document, and return the highest scored
documents.

```python
from openai.embeddings_utils import get_embedding, cosine_similarity

def search_reviews(df, product_description, n=3, pprint=True):
    embedding = get_embedding(product_description, model='text-embedding-3-small')
    df['similarities'] = df.ada_embedding.apply(lambda x: cosine_similarity(x, embedding))
    res = df.sort_values('similarities', ascending=False).head(n)
    return res

res = search_reviews(df, 'delicious beans', n=3)
```

Code search using embeddings

[Code_search.ipynb](https://cookbook.openai.com/examples/code_search_using_embeddings)

Code search works similarly to embedding-based text search. We provide a method
to extract Python functions from all the Python files in a given repository.
Each function is then indexed by the `text-embedding-3-small` model.

To perform a code search, we embed the query in natural language using the same
model. Then we calculate cosine similarity between the resulting query embedding
and each of the function embeddings. The highest cosine similarity results are
most relevant.

```python
from openai.embeddings_utils import get_embedding, cosine_similarity

df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, model='text-embedding-3-small'))

def search_functions(df, code_query, n=3, pprint=True, n_lines=7):
    embedding = get_embedding(code_query, model='text-embedding-3-small')
    df['similarities'] = df.code_embedding.apply(lambda x: cosine_similarity(x, embedding))

    res = df.sort_values('similarities', ascending=False).head(n)
    return res

res = search_functions(df, 'Completions API tests', n=3)
```

Recommendations using embeddings

[Recommendation_using_embeddings.ipynb](https://cookbook.openai.com/examples/recommendation_using_embeddings)

Because shorter distances between embedding vectors represent greater
similarity, embeddings can be useful for recommendation.

Below, we illustrate a basic recommender. It takes in a list of strings and one
'source' string, computes their embeddings, and then returns a ranking of the
strings, ranked from most similar to least similar. As a concrete example, the
linked notebook below applies a version of this function to the AG news dataset
(sampled down to 2,000 news article descriptions) to return the top 5 most
similar articles to any given source article.

```python
def recommendations_from_strings(
    strings: List[str],
    index_of_source_string: int,
    model="text-embedding-3-small",
) -> List[int]:
    """Return nearest neighbors of a given string."""

    # get embeddings for all strings
    embeddings = [embedding_from_string(string, model=model) for string in strings]

    # get the embedding of the source string
    query_embedding = embeddings[index_of_source_string]

    # get distances between the source embedding and other embeddings (function from embeddings_utils.py)
    distances = distances_from_embeddings(query_embedding, embeddings, distance_metric="cosine")

    # get indices of nearest neighbors (function from embeddings_utils.py)
    indices_of_nearest_neighbors = indices_of_nearest_neighbors_from_distances(distances)
    return indices_of_nearest_neighbors
```

Data visualization in 2D

[Visualizing_embeddings_in_2D.ipynb](https://cookbook.openai.com/examples/visualizing_embeddings_in_2d)

The size of the embeddings varies with the complexity of the underlying model.
In order to visualize this high dimensional data we use the t-SNE algorithm to
transform the data into two dimensions.

We color the individual reviews based on the star rating which the reviewer has
given:

- 1-star: red
- 2-star: dark orange
- 3-star: gold
- 4-star: turquoise
- 5-star: dark green

![Amazon ratings visualized in language using t-SNE](https://cdn.openai.com/API/docs/images/embeddings-tsne.png)

The visualization seems to have produced roughly 3 clusters, one of which has
mostly negative reviews.

```python
import pandas as pd
from sklearn.manifold import TSNE
import matplotlib.pyplot as plt
import matplotlib

df = pd.read_csv('output/embedded_1k_reviews.csv')
matrix = df.ada_embedding.apply(eval).to_list()