新嵌入模型
text-embedding-3-small and text-embedding-3-large,我们最新且性能最高的嵌入模型现已发布。它们具有更低的成本、更高的多语言性能,以及用于控制整体大小的新参数。
OpenAI 的文本嵌入可测量文本字符串之间的相关性。嵌入通常用于:
搜索 (其中结果按与查询字符串的相关性进行排名)
聚类 (其中文本字符串按相似性进行分组)
推荐 (其中会推荐具有相关文本字符串的物品)
异常检测 (其中几乎没有相关性的异常值会被识别出来)
多样性测量 (其中相似度分布会被分析)
分类 (其中文本字符串按其最相似的标签进行分类)
嵌入是一个由浮点数组成的向量(列表)。两个向量之间的 距离 衡量了它们的相关性。距离小表示相关性高,距离大表示相关性低。
访问我们的 中找到 以了解嵌入定价。请求根据数量进行计费 token in the 输入 .
要获取嵌入,请将您的文本字符串发送给 embeddings API 端点 以及嵌入模型名称(例如, text-embedding-3-small):
1
2
3
4
5
6
7
8
9
10
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); 1
2
3
4
5
6
7
8
9
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) 1
2
3
4
5
6
7
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"
}'
响应中包含嵌入向量(浮点数列表)以及一些附加元数据。您可以提取嵌入向量,将其保存到向量数据库中,并用于许多不同的用例。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{
"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
}
}
默认情况下,嵌入向量的长度为 1536 for text-embedding-3-small or 3072 for text-embedding-3-large。要在不损失其概念表示特性的情况下减少嵌入的维度,请传入 dimensions 参数 。请在 嵌入用例部分 .
OpenAI 提供两种强大的第三代嵌入模型(在模型 ID 中以 -3 表示)。阅读嵌入 v3 公告博客文章 for more details.
使用量按输入 token 计费。下表展示了每美元可处理文本页数的定价示例(假设每页约 800 个 token):
模型 ~ 每美元页数 在 MTEB 评估 最大输入 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
在此,我们展示一些典型的使用场景,使用的是 Amazon 美食评论数据集 .
该数据集包含截至 2012 年 10 月 Amazon 用户留下的共计 568,454 条食品评论。为便于说明,我们使用了最近的 1000 条评论作为子集。这些评论均为英文,往往带有正面或负面情绪。每条评论都有一个 ProductId, UserId, Score, 评论标题(Summary)和评价正文(Text)。例如:
产品 Id 用户 Id 评分 摘要 文本 B001E4KFG0 A3SGXH7AUHU8GW 5 优质狗粮 我已经买了几罐 Vitality…… B00813GRG4 A1D87F6ZCVE5NK 1 与宣传不符 收到的产品标签写着是特级咸花生…
下面,我们将评论摘要和评论正文合并为一段组合文本。模型对该组合文本进行编码,并输出一个向量嵌入。
Get_embeddings_from_dataset.ipynb
1
2
3
4
5
6
7
8
9
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 )
要从已保存的文件中加载数据,你可以运行以下命令:
1
2
3
4
import pandas as pd
df = pd.read_csv( 'output/embedded_1k_reviews.csv' )
df[ 'ada_embedding' ] = df.ada_embedding.apply( eval ).apply(np.array)
使用较大的嵌入(例如将其存储在向量存储库中进行检索),通常比使用较小的嵌入需要更高的成本,并消耗更多的计算、内存和存储资源。
我们的两款新嵌入模型都经过了训练, with a technique 这使得开发者可以在使用嵌入时权衡性能与成本。具体而言,开发者可以通过传入 dimensions API 参数,来缩短嵌入(即从序列末尾移除一些数字),而不会使其丢失表示概念的特性。例如, 。例如,在 MTEB 基准测试中,一个 text-embedding-3-large 嵌入可以缩短至 256 维,同时性能依然优于未缩短的、维度为 1536 的 text-embedding-ada-002 嵌入。您可以在我们的 embeddings v3 发布博客文章 .
In general, using the dimensions 在创建嵌入时,通过参数指定维度是推荐的做法。在某些情况下,您可能需要在生成嵌入后更改其维度。手动更改维度时,您需要确保对嵌入的维度进行归一化处理,如下所示。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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) 动态调整维度带来了极大的灵活性。例如,当使用的向量数据库最多仅支持 1024 维的嵌入时,开发者现在仍然可以使用我们最好的嵌入模型, text-embedding-3-large 并为 dimensions API 参数指定值为 1024,这将把嵌入从 3072 维缩短,通过牺牲部分精度来换取更小的向量体积。
Question_answering_using_embeddings.ipynb
在很多常见场景中,模型训练所用的数据并不包含某些关键事实和信息,而您希望在生成用户查询响应时能够访问这些内容。解决这个问题的一种方法(如下所示)是将额外信息放入模型的上下文窗口中。这在许多用例中都很有效,但会导致更高的 Token 成本。在本指南中,我们探讨了这种方法与基于嵌入搜索之间的权衡。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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)
Semantic_text_search_using_embeddings.ipynb
为了检索最相关的文档,我们使用查询的嵌入向量与每个文档的嵌入向量之间的余弦相似度,并返回得分最高的文档。
1
2
3
4
5
6
7
8
9
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.ipynb
代码搜索的工作原理类似于基于嵌入的文本搜索。我们提供了一种方法,用于从给定代码库中的所有 Python 文件里提取 Python 函数。然后使用 text-embedding-3-small model.
对每个函数进行索引。要执行代码搜索,我们使用相同的模型对自然语言查询进行嵌入。然后我们计算生成的查询嵌入与每个函数嵌入之间的余弦相似度。余弦相似度最高的结果即为最相关的结果。
1
2
3
4
5
6
7
8
9
10
11
12
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 )
Recommendation_using_embeddings.ipynb
由于嵌入向量之间的距离越短表示相似度越高,因此嵌入可用于推荐。
下面我们展示了一个基础的推荐器。它接收一个字符串列表和一个‘源’字符串,计算它们的嵌入,然后返回这些字符串的排名,按相似度从高到低排序。作为一个具体示例,下面的链接指南将此函数的一个版本应用于 AG 新闻数据集 (抽样缩减至 2,000 篇新闻文章描述),以返回与任何给定源文章最相似的前 5 篇文章。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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."""
embeddings = [embedding_from_string(string, model=model) for string in strings]
query_embedding = embeddings[index_of_source_string]
distances = distances_from_embeddings(query_embedding, embeddings, distance_metric= "cosine" )
indices_of_nearest_neighbors = indices_of_nearest_neighbors_from_distances(distances)
return indices_of_nearest_neighbors
Visualizing_embeddings_in_2D.ipynb
嵌入的维度随底层模型的复杂性而变化。为了可视化这些高维数据,我们使用 t-SNE 算法将数据转换为二维。
我们根据评论者给出的星级评分对各个评论进行着色:
1 星:红色
2 星:深橙色
3 星:金色
4 星:青绿色
5 星:深绿色
可视化结果似乎大致生成了 3 个聚类,其中一个主要包含负面评论。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()
tsne = TSNE(n_components= 2 , perplexity= 15 , random_state= 42 , init= 'random' , learning_rate= 200 )
vis_dims = tsne.fit_transform(matrix)
colors = [ "red" , "darkorange" , "gold" , "turquiose" , "darkgreen" ]
x = [x for x,y in vis_dims]
y = [y for x,y in vis_dims]
color_indices = df.Score.values - 1
colormap = matplotlib.colors.ListedColormap(colors)
plt.scatter(x, y, c=color_indices, cmap=colormap, alpha= 0.3 )
plt.title( "Amazon ratings visualized in language using t-SNE" )
Regression_using_embeddings.ipynb
嵌入可以作为机器学习模型中通用的自由文本特征编码器。如果某些相关输入是自由文本,引入嵌入将提升任何机器学习模型的性能。嵌入还可以用作 ML 模型中的分类特征编码器。如果分类变量的名称具有实际意义且数量众多(例如职位名称),这种方法的价值最大。对于此任务,相似度嵌入通常比搜索嵌入表现更好。
我们观察到,通常嵌入表示非常丰富且信息密集。例如,使用 SVD 或 PCA 降低输入的维度,即使只降低 10%,通常也会导致特定下游任务的性能下降。
此代码将数据拆分为训练集和测试集,以下两个用例(即回归和分类)将使用它们。
1
2
3
4
5
6
7
8
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(
list (df.ada_embedding.values),
df.Score,
test_size = 0.2 ,
random_state= 42
) 嵌入提供了一种预测数值的优雅方法。在这个示例中,我们根据评论文本预测评论者的星级评分。由于嵌入中包含的语义信息非常丰富,即使评论数量很少,预测结果也相当不错。
我们假设分数是 1 到 5 之间的连续变量,并允许算法预测任何浮点数值。ML 算法会最小化预测值与真实分数之间的距离,并实现了 0.39 的平均绝对误差,这意味着平均而言预测偏差不到半颗星。
1
2
3
4
5
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(n_estimators= 100 )
rfr.fit(X_train, y_train)
preds = rfr.predict(X_test)
Classification_using_embeddings.ipynb
这一次,我们将不再让算法预测 1 到 5 之间的任意值,而是尝试将评论的精确星级分类到 1 到 5 星的 5 个类别中。
训练后,模型学会预测 1 星和 5 星评论的能力远好于预测更细微的评论(2-4 星),这可能是因为更极端的情感表达更容易识别。
1
2
3
4
5
6
from sklearn.ensemble import RandomForestClassifier
from sklearn.metrics import classification_report, accuracy_score
clf = RandomForestClassifier(n_estimators= 100 )
clf.fit(X_train, y_train)
preds = clf.predict(X_test)
Zero-shot_classification_with_embeddings.ipynb
我们可以在没有任何标记训练数据的情况下使用嵌入进行零样本分类。对于每个类别,我们嵌入类别名称或类别的简短描述。为了以零样本的方式对新文本进行分类,我们将其嵌入与所有类别嵌入进行比较,并预测相似度最高的类别。
1
2
3
4
5
6
7
8
9
10
11
12
from openai.embeddings_utils import cosine_similarity, get_embedding
df= df[df.Score!= 3 ]
df[ 'sentiment' ] = df.Score.replace({ 1 : 'negative' , 2 : 'negative' , 4 : 'positive' , 5 : 'positive' })
labels = [ 'negative' , 'positive' ]
label_embeddings = [get_embedding(label, model=model) for label in labels]
def label_score ( review_embedding, label_embeddings ):
return cosine_similarity(review_embedding, label_embeddings[ 1 ]) - cosine_similarity(review_embedding, label_embeddings[ 0 ])
prediction = 'positive' if label_score( 'Sample Review' , label_embeddings) > 0 else 'negative'
User_and_product_embeddings.ipynb
我们可以通过对用户的所有评论取平均值来获取用户嵌入。类似地,我们可以通过对某产品的所有评论取平均值来获取产品嵌入。为了展示此方法的实用性,我们使用 5 万条评论的子集,以涵盖每个用户和每个产品的更多评论。
我们在一个单独的测试集上评估这些嵌入的实用性,并在其中绘制了用户和产品嵌入的相似度与评分的关系图。有趣的是,基于这种方法,甚至在用户收到产品之前,我们就能预测他们是否会喜欢该产品,而且准确率高于随机猜测。
user_embeddings = df.groupby( 'UserId' ).ada_embedding.apply(np.mean)
prod_embeddings = df.groupby( 'ProductId' ).ada_embedding.apply(np.mean)
Clustering.ipynb
聚类是理解大量文本数据的一种方式。嵌入对于此任务非常有用,因为它们为每个文本提供了在语义上有意义的向量表示。因此,在无监督的情况下,聚类将揭示我们数据集中隐藏的分组。
在这个示例中,我们发现了四个不同的聚类:一个关于狗粮,一个关于负面评论,另外两个关于正面评论。
1
2
3
4
5
6
7
8
9
import numpy as np
from sklearn.cluster import KMeans
matrix = np.vstack(df.ada_embedding.values)
n_clusters = 4
kmeans = KMeans(n_clusters = n_clusters, init= 'k-means++' , random_state= 42 )
kmeans.fit(matrix)
df[ 'Cluster' ] = kmeans.labels_
在 Python 中,您可以使用 OpenAI 的 tokenizer 将字符串拆分为 Token tiktoken .
示例代码:
1
2
3
4
5
6
7
8
9
import tiktoken
def num_tokens_from_string ( string: str , encoding_name: str ) -> int :
"""Returns the number of tokens in a text string."""
encoding = tiktoken.get_encoding(encoding_name)
num_tokens = len (encoding.encode(string))
return num_tokens
num_tokens_from_string( "tiktoken is great!" , "cl100k_base" )
对于第三代嵌入模型(如 text-embedding-3-small,使用 cl100k_base encoding.
更多详情和示例代码请参阅 OpenAI Cookbook 指南 如何使用 tiktoken 计算 token .
为了快速搜索大量向量,我们建议使用向量数据库。您可以在我们的向量数据库集成页面中找到使用向量数据库和 OpenAI API 的示例 in our Cookbook 在 GitHub 上。
我们推荐 余弦相似度 。距离函数的选择通常影响不大。
OpenAI 嵌入已归一化为长度 1,这意味着:
只需使用点积即可稍微更快地计算出余弦相似度
余弦相似度和欧氏距离将产生相同的排名
可以,客户拥有其模型的输入和输出,包括嵌入。您有责任确保输入到我们 API 的内容不违反任何适用法律或我们的 使用条款 .
不,这些 text-embedding-3-large and text-embedding-3-small 模型缺乏关于 2021 年 9 月之后发生的的知识。对于文本生成模型而言,这通常算不上太大的限制,但在某些极端情况下可能会降低性能。