Repository Public
1# Vector embeddings23> For the complete documentation index, see [llms.txt](/llms.txt). Markdown versions of documentation pages are available by appending `.md` to the page URL.45## What are embeddings?67OpenAI’s text embeddings measure the relatedness of text strings. Embeddings are commonly used for:89- **Search** (where results are ranked by relevance to a query string)10- **Clustering** (where text strings are grouped by similarity)11- **Recommendations** (where items with related text strings are recommended)12- **Anomaly detection** (where outliers with little relatedness are identified)13- **Diversity measurement** (where similarity distributions are analyzed)14- **Classification** (where text strings are classified by their most similar label)1516An embedding is a vector (list) of floating point numbers. The [distance](#which-distance-function-should-i-use) between two vectors measures their relatedness. Small distances suggest high relatedness and large distances suggest low relatedness.1718Visit our [pricing page](https://openai.com/api/pricing/) to learn about embeddings pricing. Requests are billed based on the number of [tokens](https://platform.openai.com/tokenizer) in the [input](https://developers.openai.com/api/reference/resources/embeddings/methods/create#embeddings/create-input).1920## How to get embeddings2122To get an embedding, send your text string to the [embeddings API endpoint](https://developers.openai.com/api/reference/resources/embeddings) along with the embedding model name (e.g., `text-embedding-3-small`):2324Example: Getting embeddings2526```javascript27import OpenAI from "openai";28const openai = new OpenAI();2930const embedding = await openai.embeddings.create({31 model: "text-embedding-3-small",32 input: "Your text string goes here",33 encoding_format: "float",34});3536console.log(embedding);37```3839```python40from openai import OpenAI4142client = OpenAI()4344response = client.embeddings.create(45 input="Your text string goes here", model="text-embedding-3-small"46)4748print(response.data[0].embedding)49```5051```go52package main5354import (55 "context"56 "fmt"5758 "github.com/openai/openai-go/v3"59)6061func main() {62 client := openai.NewClient()6364 embedding, err := client.Embeddings.New(context.Background(), openai.EmbeddingNewParams{65 Model: openai.EmbeddingModelTextEmbedding3Small,66 Input: openai.EmbeddingNewParamsInputUnion{67 OfString: openai.String("Your text string goes here."),68 },69 })70 if err != nil {71 panic(err)72 }7374 fmt.Println(len(embedding.Data[0].Embedding))75}76```7778```java79import com.openai.client.OpenAIClient;80import com.openai.client.okhttp.OpenAIOkHttpClient;81import com.openai.models.embeddings.EmbeddingCreateParams;8283var embedding =84 client85 .embeddings()86 .create(87 EmbeddingCreateParams.builder()88 .model("text-embedding-3-small")89 .input("The food was delicious and the waiter...")90 .build());9192System.out.println(embedding.data().get(0).embedding());93```9495```csharp96using OpenAI.Embeddings;9798string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;99string model = "text-embedding-3-small";100EmbeddingClient client = new(model, key);101102OpenAIEmbedding embedding = await client.GenerateEmbeddingAsync(103 "The food was delicious and the waiter was friendly."104);105106Console.WriteLine($"Dimensions: {embedding.ToFloats().Length}");107```108109```ruby110require "openai"111112client = OpenAI::Client.new113114response = client.embeddings.create(115 model: "text-embedding-3-small",116 input: "The food was delicious and the waiter..."117)118119puts(response.data.fetch(0).embedding)120```121122```bash123curl https://api.openai.com/v1/embeddings \124 -H "Content-Type: application/json" \125 -H "Authorization: Bearer $OPENAI_API_KEY" \126 -d '{127 "input": "Your text string goes here",128 "model": "text-embedding-3-small"129 }'130```131132133The 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.134135```json136{137 "object": "list",138 "data": [139 {140 "object": "embedding",141 "index": 0,142 "embedding": [143 -0.006929283495992422, -0.005336422007530928, -4.547132266452536e-5,144 -0.024047505110502243145 ]146 }147 ],148 "model": "text-embedding-3-small",149 "usage": {150 "prompt_tokens": 5,151 "total_tokens": 5152 }153}154```155156By 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://developers.openai.com/api/reference/resources/embeddings/methods/create#embeddings-create-dimensions). Find more detail on embedding dimensions in the [embedding use case section](#use-cases).157158## Embedding models159160OpenAI offers two powerful third-generation embedding model (denoted by `-3` in the model ID). Read the embedding v3 [announcement blog post](https://openai.com/blog/new-embedding-models-and-api-updates) for more details.161162Usage is priced per input token. Below is an example of pricing pages of text per US dollar (assuming ~800 tokens per page):163164| Model | ~ Pages per dollar | Performance on [MTEB](https://github.com/embeddings-benchmark/mteb) eval | Max input |165| ---------------------- | ------------------ | ------------------------------------------------------------------------ | --------- |166| text-embedding-3-small | 62,500 | 62.3% | 8192 |167| text-embedding-3-large | 9,615 | 64.6% | 8192 |168| text-embedding-ada-002 | 12,500 | 61.0% | 8192 |169170## Use cases171172Here we show some representative use cases, using the [Amazon fine-food reviews dataset](https://www.kaggle.com/snap/amazon-fine-food-reviews).173174### Obtaining the embeddings175176The 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:177178179180181| Product Id | User Id | Score | Summary | Text |182| ---------- | -------------- | ----- | --------------------- | ------------------------------------------------- |183| B001E4KFG0 | A3SGXH7AUHU8GW | 5 | Good Quality Dog Food | I have bought several of the Vitality canned... |184| B00813GRG4 | A1D87F6ZCVE5NK | 1 | Not as Advertised | Product arrived labeled as Jumbo Salted Peanut... |185186187188189Below, 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.190191192193Get_embeddings_from_dataset.ipynb194```javascript195import { mkdir, writeFile } from "node:fs/promises";196import OpenAI from "openai";197198const client = new OpenAI();199const reviews = ["A rich cup of coffee.", "A bright herbal tea."];200201const response = await client.embeddings.create({202 model: "text-embedding-3-small",203 input: reviews.map((review) => review.replaceAll("\n", " ")),204});205206const csvField = (value) => `"${value.replaceAll('"', '""')}"`;207const rows = response.data.map(({ embedding }, index) =>208 [csvField(reviews[index]), csvField(JSON.stringify(embedding))].join(",")209);210211await mkdir("output", { recursive: true });212await writeFile(213 "output/embedded_1k_reviews.csv",214 ["combined,ada_embedding", ...rows].join("\n") + "\n"215);216```217218```python219from openai import OpenAI220221client = OpenAI()222223224def get_embedding(text, model="text-embedding-3-small"):225 text = text.replace("\n", " ")226 return client.embeddings.create(input=[text], model=model).data[0].embedding227228229df["ada_embedding"] = df.combined.apply(230 lambda x: get_embedding(x, model="text-embedding-3-small")231)232df.to_csv("output/embedded_1k_reviews.csv", index=False)233```234235```java236import com.openai.client.OpenAIClient;237import com.openai.client.okhttp.OpenAIOkHttpClient;238import com.openai.models.embeddings.EmbeddingCreateParams;239import java.io.IOException;240import java.nio.file.Files;241import java.nio.file.Path;242import java.util.List;243244static String csvField(String value) {245 return "\"" + value.replace("\"", "\"\"") + "\"";246}247248List<String> reviews = List.of("A rich cup of coffee.", "A bright herbal tea.");249Path output = Path.of("output", "embedded_1k_reviews.csv");250Files.createDirectories(output.getParent());251try (var writer = Files.newBufferedWriter(output)) {252 writer.write("combined,ada_embedding\n");253 for (String review : reviews) {254 var embedding =255 client256 .embeddings()257 .create(258 EmbeddingCreateParams.builder()259 .model("text-embedding-3-small")260 .inputOfArrayOfStrings(List.of(review.replace("\n", " ")))261 .build())262 .data()263 .get(0)264 .embedding();265 writer.write(csvField(review) + "," + csvField(embedding.toString()) + "\n");266 }267}268System.out.println(output);269```270271```ruby272require "csv"273require "fileutils"274require "json"275require "openai"276277client = OpenAI::Client.new278reviews = ["A rich cup of coffee.", "A bright herbal tea."]279280response = client.embeddings.create(281 model: "text-embedding-3-small",282 input: reviews.map { |review| review.tr("\n", " ") }283)284285FileUtils.mkdir_p("output")286CSV.open("output/embedded_1k_reviews.csv", "w") do |csv|287 csv << ["combined", "ada_embedding"]288 response.data.each do |embedding|289 csv << [reviews.fetch(embedding.index), JSON.generate(embedding.embedding)]290 end291end292```293294295To load the data from a saved file, you can run the following:296297```python298import pandas as pd299300df = pd.read_csv("output/embedded_1k_reviews.csv")301df["ada_embedding"] = df.ada_embedding.apply(eval).apply(np.array)302```303304305306307#### Reducing embedding dimensions308309310311Using 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.312313Both of our new embedding models were trained [with a technique](https://arxiv.org/abs/2205.13147) 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` API parameter](https://developers.openai.com/api/reference/resources/embeddings/methods/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](https://openai.com/blog/new-embedding-models-and-api-updates#:~:text=Native%20support%20for%20shortening%20embeddings).314315In 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.316317```javascript318import OpenAI from "openai";319320const client = new OpenAI();321322const response = await client.embeddings.create({323 model: "text-embedding-3-small",324 input: "Testing 123",325 encoding_format: "float",326});327328const shortened = response.data[0].embedding.slice(0, 256);329const magnitude = Math.hypot(...shortened);330const normalized = shortened.map((value) =>331 magnitude === 0 ? 0 : value / magnitude332);333334console.log(normalized);335```336337```python338from openai import OpenAI339import numpy as np340341client = OpenAI()342343344def normalize_l2(x):345 x = np.array(x)346 if x.ndim == 1:347 norm = np.linalg.norm(x)348 if norm == 0:349 return x350 return x / norm351 else:352 norm = np.linalg.norm(x, 2, axis=1, keepdims=True)353 return np.where(norm == 0, x, x / norm)354355356response = client.embeddings.create(357 model="text-embedding-3-small", input="Testing 123", encoding_format="float"358)359360cut_dim = response.data[0].embedding[:256]361norm_dim = normalize_l2(cut_dim)362363print(norm_dim)364```365366```java367import com.openai.client.OpenAIClient;368import com.openai.client.okhttp.OpenAIOkHttpClient;369import com.openai.models.embeddings.EmbeddingCreateParams;370import java.util.List;371372private static List<Double> normalizeL2(List<Float> embedding) {373 double norm = Math.sqrt(embedding.stream().mapToDouble(value -> value * value).sum());374 return embedding.stream().map(value -> norm == 0 ? 0.0 : value / norm).toList();375}376377var embedding =378 client379 .embeddings()380 .create(381 EmbeddingCreateParams.builder()382 .model("text-embedding-3-small")383 .input("Testing 123")384 .encodingFormat(EmbeddingCreateParams.EncodingFormat.FLOAT)385 .build());386387List<Float> shortened = embedding.data().get(0).embedding().subList(0, 256);388System.out.println(normalizeL2(shortened));389```390391```csharp392using OpenAI.Embeddings;393394string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;395string model = "text-embedding-3-small";396EmbeddingClient client = new(model, key);397398OpenAIEmbedding embedding = await client.GenerateEmbeddingAsync("Testing 123");399400float[] shortened = embedding.ToFloats().Span[..256].ToArray();401double magnitude = Math.Sqrt(shortened.Sum(value => value * value));402float[] normalized =403 magnitude == 0404 ? shortened405 : shortened.Select(value => (float)(value / magnitude)).ToArray();406407Console.WriteLine($"Dimensions: {normalized.Length}");408Console.WriteLine($"First value: {normalized[0]:F6}");409Console.WriteLine(410 $"L2 norm: {Math.Sqrt(normalized.Sum(value => value * value)):F3}"411);412```413414```ruby415require "openai"416417client = OpenAI::Client.new418419response = client.embeddings.create(420 model: "text-embedding-3-small",421 input: "Testing 123",422 encoding_format: :float423)424425shortened = response.data.fetch(0).embedding.first(256)426magnitude = Math.sqrt(shortened.sum { |value| value**2 })427normalized = shortened.map { |value| magnitude.zero? ? 0 : value / magnitude }428429puts(normalized)430```431432433Dynamically 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.434435436437438439440441#### Question answering using embeddings-based search442443444445446447 448449Question_answering_using_embeddings.ipynb450 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.451452```javascript453import OpenAI from "openai";454455const client = new OpenAI();456const article =457 "At the 2022 Winter Olympics, Great Britain won women's curling and Sweden won men's curling.";458const question = `Use the article below to answer the question. If the answer cannot be found, say "I don't know."459460Article:461${article}462463Question: Which athletes won the gold medal in curling at the 2022 Winter Olympics?`;464465const response = await client.chat.completions.create({466 model: "gpt-4.1-mini",467 messages: [468 {469 role: "system",470 content: "You answer questions about the 2022 Winter Olympics.",471 },472 { role: "user", content: question },473 ],474 temperature: 0,475});476477console.log(response.choices[0].message.content);478```479480```python481query = 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."482483Article:484\"\"\"485{wikipedia_article_on_curling}486\"\"\"487488Question: Which athletes won the gold medal in curling at the 2022 Winter Olympics?"""489490response = client.chat.completions.create(491 messages=[492 {493 "role": "system",494 "content": "You answer questions about the 2022 Winter Olympics.",495 },496 {"role": "user", "content": query},497 ],498 model=GPT_MODEL,499 temperature=0,500)501502print(response.choices[0].message.content)503```504505```java506import com.openai.client.OpenAIClient;507import com.openai.client.okhttp.OpenAIOkHttpClient;508import com.openai.models.chat.completions.ChatCompletionCreateParams;509510String article =511 "At the 2022 Winter Olympics, Great Britain won women's curling and Sweden won men's curling.";512String question =513 "Use the below article on the 2022 Winter Olympics to answer the subsequent question. "514 + "If the answer cannot be found, write \"I don't know.\"\n\n"515 + "Article:\n"516 + article517 + "\n\nQuestion: Which athletes won the gold medal in curling at the 2022 Winter Olympics?";518519ChatCompletionCreateParams params =520 ChatCompletionCreateParams.builder()521 .model("gpt-4.1-mini")522 .addSystemMessage("You answer questions about the 2022 Winter Olympics.")523 .addUserMessage(question)524 .temperature(0)525 .build();526527client.chat().completions().create(params).choices().stream()528 .flatMap(choice -> choice.message().content().stream())529 .forEach(System.out::println);530```531532```ruby533require "openai"534535client = OpenAI::Client.new536article = "At the 2022 Winter Olympics, Great Britain won women's curling and Sweden won men's curling."537question = <<~QUESTION538 Use the article below to answer the question. If the answer cannot be found, say "I don't know."539540 Article:541 #{article}542543 Question: Which athletes won the gold medal in curling at the 2022 Winter Olympics?544QUESTION545546response = client.chat.completions.create(547 model: "gpt-4.1-mini",548 messages: [549 {550 role: :system,551 content: "You answer questions about the 2022 Winter Olympics."552 },553 {554 role: :user,555 content: question556 }557 ],558 temperature: 0559)560561puts(response.choices.fetch(0).message.content)562```563564565566567568569570571#### Text search using embeddings572573574575576577 578579Semantic_text_search_using_embeddings.ipynb580 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.581582```javascript583import OpenAI from "openai";584585const client = new OpenAI();586const reviews = [587 "A rich cup of coffee.",588 "Smooth beans in tomato sauce.",589 "Dark chocolate with orange.",590];591592const { data } = await client.embeddings.create({593 model: "text-embedding-3-small",594 input: [...reviews, "delicious beans"],595});596597const query = data.at(-1).embedding;598const similarity = (embedding) => {599 const dotProduct = embedding.reduce(600 (total, value, index) => total + value * query[index],601 0602 );603 return dotProduct / (Math.hypot(...embedding) * Math.hypot(...query));604};605606const results = reviews607 .map((review, index) => ({608 review,609 score: similarity(data[index].embedding),610 }))611 .sort((left, right) => right.score - left.score)612 .slice(0, 3);613614console.log(results);615```616617```python618def search_reviews(df, product_description, n=3, pprint=True):619 embedding = get_embedding(product_description, model="text-embedding-3-small")620 df["similarities"] = df.ada_embedding.apply(621 lambda x: cosine_similarity(x, embedding)622 )623 res = df.sort_values("similarities", ascending=False).head(n)624 return res625626627res = search_reviews(df, "delicious beans", n=3)628```629630```java631import com.openai.client.OpenAIClient;632import com.openai.client.okhttp.OpenAIOkHttpClient;633import com.openai.models.embeddings.EmbeddingCreateParams;634import java.util.Comparator;635import java.util.List;636import java.util.stream.IntStream;637638List<String> reviews =639 List.of(640 "A rich cup of coffee.",641 "Smooth beans in tomato sauce.",642 "Dark chocolate with orange.");643var reviewEmbeddings =644 client645 .embeddings()646 .create(647 EmbeddingCreateParams.builder()648 .model("text-embedding-3-small")649 .inputOfArrayOfStrings(reviews)650 .build())651 .data();652List<Float> query =653 client654 .embeddings()655 .create(656 EmbeddingCreateParams.builder()657 .model("text-embedding-3-small")658 .inputOfArrayOfStrings(List.of("delicious beans"))659 .build())660 .data()661 .get(0)662 .embedding();663664IntStream.range(0, reviews.size())665 .boxed()666 .sorted(667 Comparator.comparingDouble(668 (Integer index) ->669 cosineSimilarity(query, reviewEmbeddings.get(index).embedding()))670 .reversed())671 .limit(3)672 .map(reviews::get)673 .forEach(System.out::println);674```675676```ruby677require "openai"678679client = OpenAI::Client.new680reviews = [681 "A rich cup of coffee.",682 "Smooth beans in tomato sauce.",683 "Dark chocolate with orange."684]685686response = client.embeddings.create(687 model: "text-embedding-3-small",688 input: reviews + ["delicious beans"]689)690691query = response.data.fetch(-1).embedding692similarity = lambda do |embedding|693 dot_product = embedding.zip(query).sum { |value, query_value| value * query_value }694 magnitude = Math.sqrt(embedding.sum { |value| value**2 })695 query_magnitude = Math.sqrt(query.sum { |value| value**2 })696 dot_product / (magnitude * query_magnitude)697end698699results = reviews.map.with_index do |review, index|700 {701 review: review,702 score: similarity.call(response.data.fetch(index).embedding)703 }704end.sort_by { |result| -result.fetch(:score) }.first(3)705706puts(results)707```708709710711712713714715716#### Code search using embeddings717718719720721722 723724Code_search.ipynb725 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.726727To 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.728729```javascript730import OpenAI from "openai";731732const client = new OpenAI();733const functions = [734 "function add(a, b) { return a + b; }",735 "function complete(prompt) { return prompt; }",736];737738const { data } = await client.embeddings.create({739 model: "text-embedding-3-small",740 input: [...functions, "Completions API tests"],741});742743const query = data.at(-1).embedding;744const similarity = (embedding) => {745 const dotProduct = embedding.reduce(746 (total, value, index) => total + value * query[index],747 0748 );749 return dotProduct / (Math.hypot(...embedding) * Math.hypot(...query));750};751752const results = functions753 .map((source, index) => ({754 source,755 score: similarity(data[index].embedding),756 }))757 .sort((left, right) => right.score - left.score);758759console.log(results);760```761762```python763df["code_embedding"] = df["code"].apply(764 lambda x: get_embedding(x, model="text-embedding-3-small")765)766767768def search_functions(df, code_query, n=3, pprint=True, n_lines=7):769 embedding = get_embedding(code_query, model="text-embedding-3-small")770 df["similarities"] = df.code_embedding.apply(771 lambda x: cosine_similarity(x, embedding)772 )773774 res = df.sort_values("similarities", ascending=False).head(n)775 return res776777778res = search_functions(df, "Completions API tests", n=3)779```780781```java782import com.openai.client.OpenAIClient;783import com.openai.client.okhttp.OpenAIOkHttpClient;784import com.openai.models.embeddings.EmbeddingCreateParams;785import java.util.Comparator;786import java.util.List;787import java.util.stream.IntStream;788789List<String> functions =790 List.of("def add(a, b): return a + b", "def complete(prompt): return prompt");791var functionEmbeddings =792 client793 .embeddings()794 .create(795 EmbeddingCreateParams.builder()796 .model("text-embedding-3-small")797 .inputOfArrayOfStrings(functions)798 .build())799 .data();800List<Float> query =801 client802 .embeddings()803 .create(804 EmbeddingCreateParams.builder()805 .model("text-embedding-3-small")806 .input("Completions API tests")807 .build())808 .data()809 .get(0)810 .embedding();811IntStream.range(0, functions.size())812 .boxed()813 .sorted(814 Comparator.comparingDouble(815 (Integer index) ->816 cosineSimilarity(query, functionEmbeddings.get(index).embedding()))817 .reversed())818 .map(functions::get)819 .forEach(System.out::println);820```821822```ruby823require "openai"824825client = OpenAI::Client.new826functions = [827 "function add(a, b) { return a + b; }",828 "function complete(prompt) { return prompt; }"829]830831response = client.embeddings.create(832 model: "text-embedding-3-small",833 input: functions + ["Completions API tests"]834)835836query = response.data.fetch(-1).embedding837similarity = lambda do |embedding|838 dot_product = embedding.zip(query).sum { |value, query_value| value * query_value }839 magnitude = Math.sqrt(embedding.sum { |value| value**2 })840 query_magnitude = Math.sqrt(query.sum { |value| value**2 })841 dot_product / (magnitude * query_magnitude)842end843844results = functions.map.with_index do |source, index|845 {846 source: source,847 score: similarity.call(response.data.fetch(index).embedding)848 }849end.sort_by { |result| -result.fetch(:score) }850851puts(results)852```853854855856857858859860861#### Recommendations using embeddings862863864865866867 868869Recommendation_using_embeddings.ipynb870 Because shorter distances between embedding vectors represent greater similarity, embeddings can be useful for recommendation.871872Below, 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](http://groups.di.unipi.it/~gulli/AG_corpus_of_news_articles.html) (sampled down to 2,000 news article descriptions) to return the top 5 most similar articles to any given source article.873874```javascript875import OpenAI from "openai";876877const client = new OpenAI();878const strings = [879 "A cheetah is a fast land animal.",880 "A peregrine falcon is a fast bird.",881 "A tortoise moves slowly.",882];883884const { data } = await client.embeddings.create({885 model: "text-embedding-3-small",886 input: strings,887});888889const query = data[0].embedding;890const recommendations = data891 .map(({ embedding }, index) => {892 const dotProduct = embedding.reduce(893 (total, value, dimension) => total + value * query[dimension],894 0895 );896 const similarity =897 dotProduct / (Math.hypot(...embedding) * Math.hypot(...query));898 return { index, text: strings[index], similarity };899 })900 .sort((left, right) => right.similarity - left.similarity);901902console.log(recommendations);903```904905```python906def recommendations_from_strings(907 strings: list[str],908 index_of_source_string: int,909 model="text-embedding-3-small",910) -> list[int]:911 """Return nearest neighbors of a given string."""912913 # get embeddings for all strings914 embeddings = [embedding_from_string(string, model=model) for string in strings]915916 # get the embedding of the source string917 query_embedding = embeddings[index_of_source_string]918919 # get distances between the source embedding and other embeddings (function from embeddings_utils.py)920 distances = distances_from_embeddings(921 query_embedding, embeddings, distance_metric="cosine"922 )923924 # get indices of nearest neighbors (function from embeddings_utils.py)925 indices_of_nearest_neighbors = indices_of_nearest_neighbors_from_distances(926 distances927 )928 return indices_of_nearest_neighbors929```930931```java932import com.openai.client.OpenAIClient;933import com.openai.client.okhttp.OpenAIOkHttpClient;934import com.openai.models.embeddings.EmbeddingCreateParams;935import java.util.Comparator;936import java.util.List;937import java.util.stream.IntStream;938939List<String> strings =940 List.of(941 "A cheetah is a fast land animal.",942 "A peregrine falcon is a fast bird.",943 "A tortoise moves slowly.");944945var embeddings =946 client947 .embeddings()948 .create(949 EmbeddingCreateParams.builder()950 .model("text-embedding-3-small")951 .inputOfArrayOfStrings(strings)952 .build())953 .data();954955List<Float> query = embeddings.get(0).embedding();956var nearestNeighbors =957 IntStream.range(0, embeddings.size())958 .boxed()959 .sorted(960 Comparator.comparingDouble(961 (Integer index) -> {962 List<Float> candidate = embeddings.get(index).embedding();963 double dotProduct = 0;964 double queryMagnitude = 0;965 double candidateMagnitude = 0;966 for (int dimension = 0; dimension < query.size(); dimension++) {967 dotProduct += query.get(dimension) * candidate.get(dimension);968 queryMagnitude += query.get(dimension) * query.get(dimension);969 candidateMagnitude += candidate.get(dimension) * candidate.get(dimension);970 }971 return 1 - dotProduct / Math.sqrt(queryMagnitude * candidateMagnitude);972 }))973 .toList();974975System.out.println(nearestNeighbors);976```977978```ruby979require "openai"980981client = OpenAI::Client.new982strings = [983 "A cheetah is a fast land animal.",984 "A peregrine falcon is a fast bird.",985 "A tortoise moves slowly."986]987988response = client.embeddings.create(989 model: "text-embedding-3-small",990 input: strings991)992993query = response.data.fetch(0).embedding994similarity = lambda do |embedding|995 dot_product = embedding.zip(query).sum { |value, query_value| value * query_value }996 magnitude = Math.sqrt(embedding.sum { |value| value**2 })997 query_magnitude = Math.sqrt(query.sum { |value| value**2 })998 dot_product / (magnitude * query_magnitude)999end10001001recommendations = response.data.map.with_index do |embedding, index|1002 {1003 index: index,1004 text: strings.fetch(index),1005 similarity: similarity.call(embedding.embedding)1006 }1007end.sort_by { |recommendation| -recommendation.fetch(:similarity) }10081009puts(recommendations)1010```101110121013101410151016101710181019#### Data visualization in 2D102010211022102310241025 10261027Visualizing_embeddings_in_2D.ipynb1028 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.10291030We color the individual reviews based on the star rating which the reviewer has given:10311032- 1-star: red1033- 2-star: dark orange1034- 3-star: gold1035- 4-star: turquoise1036- 5-star: dark green10371038The visualization seems to have produced roughly 3 clusters, one of which has mostly negative reviews.10391040```python1041import numpy as np1042import pandas as pd1043from sklearn.manifold import TSNE1044import matplotlib.pyplot as plt1045import matplotlib10461047df = pd.read_csv("output/embedded_1k_reviews.csv")1048matrix = np.array(df.ada_embedding.apply(eval).to_list())10491050# Create a t-SNE model and transform the data1051tsne = TSNE(1052 n_components=2, perplexity=15, random_state=42, init="random", learning_rate=2001053)1054vis_dims = tsne.fit_transform(matrix)10551056colors = ["red", "darkorange", "gold", "turquoise", "darkgreen"]1057x = [x for x, y in vis_dims]1058y = [y for x, y in vis_dims]1059color_indices = df.Score.values - 110601061colormap = matplotlib.colors.ListedColormap(colors)1062plt.scatter(x, y, c=color_indices, cmap=colormap, alpha=0.3)1063plt.title("Amazon ratings visualized in language using t-SNE")1064```106510661067106810691070107110721073#### Embedding as a text feature encoder for ML algorithms107410751076107710781079 10801081Regression_using_embeddings.ipynb1082 An embedding can be used as a general free-text feature encoder within a machine learning model. Incorporating embeddings will improve the performance of any machine learning model, if some of the relevant inputs are free text. An embedding can also be used as a categorical feature encoder within a ML model. This adds most value if the names of categorical variables are meaningful and numerous, such as job titles. Similarity embeddings generally perform better than search embeddings for this task.10831084We observed that generally the embedding representation is very rich and information dense. For example, reducing the dimensionality of the inputs using SVD or PCA, even by 10%, generally results in worse downstream performance on specific tasks.10851086This code splits the data into a training set and a testing set, which will be used by the following two use cases, namely regression and classification.10871088```python1089from sklearn.model_selection import train_test_split10901091X_train, X_test, y_train, y_test = train_test_split(1092 list(df.ada_embedding.values), df.Score, test_size=0.2, random_state=421093)1094```109510961097#### Regression using the embedding features10981099Embeddings present an elegant way of predicting a numerical value. In this example we predict the reviewer’s star rating, based on the text of their review. Because the semantic information contained within embeddings is high, the prediction is decent even with very few reviews.11001101We assume the score is a continuous variable between 1 and 5, and allow the algorithm to predict any floating point value. The ML algorithm minimizes the distance of the predicted value to the true score, and achieves a mean absolute error of 0.39, which means that on average the prediction is off by less than half a star.11021103```python1104from sklearn.ensemble import RandomForestRegressor11051106rfr = RandomForestRegressor(n_estimators=100)1107rfr.fit(X_train, y_train)1108preds = rfr.predict(X_test)1109```111011111112111311141115111611171118#### Classification using the embedding features111911201121112211231124 11251126Classification_using_embeddings.ipynb1127 This time, instead of having the algorithm predict a value anywhere between 1 and 5, we will attempt to classify the exact number of stars for a review into 5 buckets, ranging from 1 to 5 stars.11281129After the training, the model learns to predict 1 and 5-star reviews much better than the more nuanced reviews (2-4 stars), likely due to more extreme sentiment expression.11301131```python1132from sklearn.ensemble import RandomForestClassifier1133from sklearn.metrics import classification_report, accuracy_score11341135clf = RandomForestClassifier(n_estimators=100)1136clf.fit(X_train, y_train)1137preds = clf.predict(X_test)1138```113911401141114211431144114511461147#### Zero-shot classification114811491150115111521153 11541155Zero-shot_classification_with_embeddings.ipynb1156 We can use embeddings for zero shot classification without any labeled training data. For each class, we embed the class name or a short description of the class. To classify some new text in a zero-shot manner, we compare its embedding to all class embeddings and predict the class with the highest similarity.11571158```javascript1159import OpenAI from "openai";11601161const client = new OpenAI();1162const labels = ["negative", "positive"];11631164const { data } = await client.embeddings.create({1165 model: "text-embedding-3-small",1166 input: [...labels, "The coffee arrived quickly and tastes great."],1167});11681169const review = data.at(-1).embedding;1170const similarity = (embedding) => {1171 const dotProduct = embedding.reduce(1172 (total, value, index) => total + value * review[index],1173 01174 );1175 return dotProduct / (Math.hypot(...embedding) * Math.hypot(...review));1176};11771178const [negative, positive] = data.map(({ embedding }) => similarity(embedding));1179console.log(positive > negative ? "positive" : "negative");1180```11811182```python1183df = df[df.Score != 3]1184df["sentiment"] = df.Score.replace(1185 {1: "negative", 2: "negative", 4: "positive", 5: "positive"}1186)11871188labels = ["negative", "positive"]1189label_embeddings = [get_embedding(label, model=model) for label in labels]119011911192def label_score(review_embedding, label_embeddings):1193 return cosine_similarity(review_embedding, label_embeddings[1]) - cosine_similarity(1194 review_embedding, label_embeddings[0]1195 )119611971198prediction = (1199 "positive" if label_score(get_embedding("Sample Review", model=model), label_embeddings) > 0 else "negative"1200)1201```12021203```java1204import com.openai.client.OpenAIClient;1205import com.openai.client.okhttp.OpenAIOkHttpClient;1206import com.openai.models.embeddings.EmbeddingCreateParams;1207import java.util.List;12081209var embeddings =1210 client1211 .embeddings()1212 .create(1213 EmbeddingCreateParams.builder()1214 .model("text-embedding-3-small")1215 .inputOfArrayOfStrings(List.of("negative", "positive", "Sample Review"))1216 .build())1217 .data();12181219List<Float> review = embeddings.get(2).embedding();1220double negative = cosineSimilarity(review, embeddings.get(0).embedding());1221double positive = cosineSimilarity(review, embeddings.get(1).embedding());1222System.out.println(positive > negative ? "positive" : "negative");1223```12241225```ruby1226require "openai"12271228client = OpenAI::Client.new1229labels = ["negative", "positive"]12301231response = client.embeddings.create(1232 model: "text-embedding-3-small",1233 input: labels + ["The coffee arrived quickly and tastes great."]1234)12351236review = response.data.fetch(-1).embedding1237similarity = lambda do |embedding|1238 dot_product = embedding.zip(review).sum { |value, review_value| value * review_value }1239 magnitude = Math.sqrt(embedding.sum { |value| value**2 })1240 review_magnitude = Math.sqrt(review.sum { |value| value**2 })1241 dot_product / (magnitude * review_magnitude)1242end12431244negative, positive = response.data.first(2).map do |embedding|1245 similarity.call(embedding.embedding)1246end1247puts((positive > negative) ? "positive" : "negative")1248```124912501251125212531254125512561257#### Obtaining user and product embeddings for cold-start recommendation125812591260126112621263 12641265User_and_product_embeddings.ipynb1266 We can obtain a user embedding by averaging over all of their reviews. Similarly, we can obtain a product embedding by averaging over all the reviews about that product. In order to showcase the usefulness of this approach we use a subset of 50k reviews to cover more reviews per user and per product.12671268We evaluate the usefulness of these embeddings on a separate test set, where we plot similarity of the user and product embedding as a function of the rating. Interestingly, based on this approach, even before the user receives the product we can predict better than random whether they would like the product.12691270```python1271user_embeddings = df.groupby("UserId").ada_embedding.apply(np.mean)1272prod_embeddings = df.groupby("ProductId").ada_embedding.apply(np.mean)1273```127412751276127712781279128012811282#### Clustering128312841285128612871288 12891290Clustering.ipynb1291 Clustering is one way of making sense of a large volume of textual data. Embeddings are useful for this task, as they provide semantically meaningful vector representations of each text. Thus, in an unsupervised way, clustering will uncover hidden groupings in our dataset.12921293In this example, we discover four distinct clusters: one focusing on dog food, one on negative reviews, and two on positive reviews.12941295```python1296import numpy as np1297from sklearn.cluster import KMeans12981299matrix = np.vstack(df.ada_embedding.values)1300n_clusters = 413011302kmeans = KMeans(n_clusters=n_clusters, init="k-means++", random_state=42)1303kmeans.fit(matrix)1304df["Cluster"] = kmeans.labels_1305```1306130713081309131013111312## FAQ13131314### How can I tell how many tokens a string has before I embed it?13151316In Python, you can split a string into tokens with OpenAI's tokenizer [`tiktoken`](https://github.com/openai/tiktoken).13171318Example code:13191320```python1321import tiktoken132213231324def num_tokens_from_string(string: str, encoding_name: str) -> int:1325 """Returns the number of tokens in a text string."""1326 encoding = tiktoken.get_encoding(encoding_name)1327 num_tokens = len(encoding.encode(string))1328 return num_tokens132913301331num_tokens_from_string("tiktoken is great!", "cl100k_base")1332```133313341335For third-generation embedding models like `text-embedding-3-small`, use the `cl100k_base` encoding.13361337More details and example code are in the OpenAI Cookbook guide [how to count tokens with tiktoken](https://developers.openai.com/cookbook/examples/how_to_count_tokens_with_tiktoken).13381339### How can I retrieve K nearest embedding vectors quickly?13401341For searching over many vectors quickly, we recommend using a vector database. You can find examples of working with vector databases and the OpenAI API [in our Cookbook](https://developers.openai.com/cookbook/examples/vector_databases/readme) on GitHub.13421343### Which distance function should I use?13441345We recommend [cosine similarity](https://en.wikipedia.org/wiki/Cosine_similarity). The choice of distance function typically doesn't matter much.13461347OpenAI embeddings are normalized to length 1, which means that:13481349- Cosine similarity can be computed slightly faster using just a dot product1350- Cosine similarity and Euclidean distance will result in the identical rankings13511352### Can I share my embeddings online?13531354Yes, customers own their input and output from our models, including in the case of embeddings. You are responsible for ensuring that the content you input to our API does not violate any applicable law or our [Terms of Use](https://openai.com/policies/terms-of-use).13551356### Do V3 embedding models know about recent events?13571358No, the `text-embedding-3-large` and `text-embedding-3-small` models lack knowledge of events that occurred after September 2021. This is generally not as much of a limitation as it would be for text generation models but in certain edge cases it can reduce performance.