嵌入(embedding)是指将高维数据映射为低维表示的过程。在机器学习和自然语言处理中,嵌入通常用于将离散的符号或对象表示为连续的向量空间中的点。
在自然语言处理中,词嵌入(word embedding)是一种常见的技术,它将单词映射到实数向量,以便计算机可以更好地理解和处理文本。通过词嵌入,单词之间的语义和语法关系可以在向量空间中得到反映。
接下来,将从openai的Embedding文档出发,对Embeddings进行更为系统地介绍。
什么是嵌入[2]
OpenAI的文本嵌入度量了文本字符串之间的相关性。嵌入通常用于以下方面:
•搜索 (结果按与查询字符串的相关性进行排序) •聚类 (根据相似性将文本字符串分组) •推荐 (推荐与文本字符串相关的项目) •异常检测 (识别与相关性较低的异常值) •多样性测量 (分析相似性分布) •分类 (根据最相似的标签对文本字符串进行分类)
嵌入是一个浮点数(列表)的向量。两个向量之间的距离度量了它们的相关性。小的距离表示高相关性,而大的距离表示低相关性。
访问我们的定价页面[3]了解嵌入的定价信息。请求的计费是基于发送的input[4]中的token[5]数量。
要查看嵌入的实际应用,请查看我们的代码示例
•分类 •主题聚类 •搜索 •推荐
如何获取嵌入[6]
要获取嵌入,将您的文本字符串发送到嵌入API端点[7],并选择嵌入模型ID(例如,text-embedding-ada-002
)。响应将包含一个嵌入,您可以提取、保存和使用。
示例请求:
示例:获取嵌入
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-ada-002"
}'
响应示例:
{
"data": [
{
"embedding": [
-0.006929283495992422,
-0.005336422007530928,
...
-4.547132266452536e-05,
-0.024047505110502243
],
"index": 0,
"object": "embedding"
}
],
"model": "text-embedding-ada-002",
"object": "list",
"usage": {
"prompt_tokens": 5,
"total_tokens": 5
}
}
请在OpenAI Cookbook[8]中查看更多Python代码示例。
在使用OpenAI嵌入时,请注意其限制和风险[9]。
嵌入模型[10]
OpenAI提供了一个第二代嵌入模型(在模型ID中以-002
表示)和16个第一代模型(在模型ID中以-001
表示)。
我们建议几乎所有用例都使用text-embedding-ada-002。它更好、更便宜且更简单易用。阅读博客文章公告[11]。
| 模型代数 | 分词器 | 最大输入token数 | 知识截断 | | V2 | cl100k_base | 8191 | 2021年9月 | | V1 | GPT-2/GPT-3 | 2046 | 2020年8月 |
使用按输入token计费,费率为每1000个token 0.0004美元,或者大约每美元可处理约3000页(假设每页大约800个token):
| 模型 | 每美元可处理的大致页数 | 在BEIR[12]搜索评估中的示例性能 | | text-embedding-ada-002 | 3000 | 53.9 | | -davinci--001 | 6 | 52.8 | | -curie--001 | 60 | 50.9 | | -babbage--001 | 240 | 50.4 | | -ada--001 | 300 | 49.0 |
第二代模型[13]
| 模型名称 | 分词器 | 最大输入token数 | 输出维度 | | text-embedding-ada-002 | cl100k_base | 8191 | 1536 |
第一代模型(不推荐使用)
所有第一代模型(以-001结尾的模型)使用GPT-3分词器[14],最大输入为2046个token。
第一代嵌入模型由五个不同的模型系列生成,针对三个不同的任务进行了调优:文本搜索、文本相似性和代码搜索。搜索模型成对出现:一个用于短查询,一个用于长文档。每个系列包含多达四个模型,质量和速度各不相同:
| 模型 | 输出维度 | | Ada | 1024 | | Babbage | 2048 | | Curie | 4096 | | Davinci | 12288 |
Davinci是最强大的模型,但速度较慢且更昂贵。Ada的功能最弱,但速度更快且更便宜。
相似性嵌入[15]
相似性模型最擅长捕捉文本之间的语义相似性。
| 使用场景 | 可用模型 |
| 聚类、回归、异常检测、可视化 | text-similarity-ada-001
text-similarity-babbage-001
text-similarity-curie-001
text-similarity-davinci-001
|
文本搜索嵌入[16]
文本搜索模型有助于衡量长文档与短搜索查询之间的相关性。使用两个模型:一个用于嵌入搜索查询,另一个用于嵌入待排序的文档。与查询嵌入最接近的文档嵌入应该是最相关的。
| 使用场景 | 可用模型 |
| 搜索、上下文相关性、信息检索 | text-search-ada-doc-001
text-search-ada-query-001
text-search-babbage-doc-001
text-search-babbage-query-001
text-search-curie-doc-001
text-search-curie-query-001
text-search-davinci-doc-001
text-search-davinci-query-001
|
代码搜索嵌入[17]
与搜索嵌入类似,代码搜索嵌入也有两种类型:一种用于嵌入自然语言搜索查询,另一种用于嵌入要检索的代码片段。
| 使用场景 | 可用模型 |
| 代码搜索和相关性 | code-search-ada-code-001
code-search-ada-text-001
code-search-babbage-code-001
code-search-babbage-text-001
|
对于-001文本嵌入(不是-002,也不是代码嵌入),我们建议将输入中的换行符(\n)替换为一个空格,因为我们发现当存在换行符时结果会变差。
使用场景[18]
在这里,我们展示一些典型的使用场景。我们将使用 Amazon fine-food reviews 数据集[19] 作为以下示例的数据集。
获取嵌入[20]
该数据集包含截至2012年10月,亚马逊用户留下的总共 568,454 条食品评论。为了说明的目的,我们将使用最近的 1,000 条评论的子集。这些评论是用英语撰写的,倾向于正面或负面。每个评论都有一个产品 ID、用户 ID、评分、评论标题(Summary)和评论正文(Text)。例如:
| 产品 ID | 用户 ID | 评分 | 摘要 | 正文 | | B001E4KFG0 | A3SGXH7AUHU8GW | 5 | 优质的狗粮 | 我买了几罐 Vitality 品牌的狗粮... | | B00813GRG4 | A1D87F6ZCVE5NK | 1 | 与广告不符 | 产品上标着巨型盐味花生... |
我们将评论摘要和评论正文合并为单个组合文本。模型将对这个组合文本进行编码,并输出一个单一的向量嵌入。
dataset.ipynb[21]
def get_embedding(text, model="text-embedding-ada-002"):
text = text.replace("\n", " ")
return openai.Embedding.create(input = [text], model=model)['data'][0]['embedding']
df['ada_embedding'] = df.combined.apply(lambda x: get_embedding(x, model='text-embedding-ada-002'))
df.to_csv('output/embedded_1k_reviews.csv', index=False)
你可以使用下面的代码来从一个文件中加载数据:
import pandas as pd
df = pd.read_csv('output/embedded_1k_reviews.csv')
df['ada_embedding'] = df.ada_embedding.apply(eval).apply(np.array)
Visualizing_embeddings_in_2D.ipynb[22]
嵌入的大小取决于底层模型的复杂性。为了将这个高维数据可视化,我们使用 t-SNE 算法将数据转换为二维空间。
我们根据评价者给出的星级评分对每个评论进行着色:
•1 星:红色 •2 星:深橙色 •3 星:金色 •4 星:青绿色 •5 星:深绿色
使用 tSNE 可视化的亚马逊评级
这个可视化结果似乎产生了大约 3 个簇群,其中一个簇群主要包含了负面评价。
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()
# Create a t-SNE model and transform the data
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[23]
嵌入可以作为机器学习模型中通用的自由文本特征编码器。如果一些相关输入是自由文本的话,加入嵌入将提高任何机器学习模型的性能。嵌入还可以作为机器学习模型中的分类特征编码器。如果分类变量的名称具有意义且数量众多,例如职位标题,这将增加最大的价值。在这项任务中,相似性嵌入通常比搜索嵌入表现更好。
我们观察到,嵌入表示通常非常丰富和信息密集。例如,使用奇异值分解(SVD)或主成分分析(PCA)将输入的维度降低10%,通常会导致特定任务的下游性能变差。
此代码将数据分为训练集和测试集,后续的回归和分类任务将使用这两个数据集。
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
)
Regression using the embedding features[24]
嵌入提供了一种优雅的方式来预测一个数值。在这个例子中,我们根据评论的文本来预测评论者的星级评分。由于嵌入中包含的语义信息很高,即使只有很少的评论,预测结果也是不错的。
我们假设评分是一个介于1和5之间的连续变量,并允许算法预测任何浮点数值。机器学习算法通过最小化预测值与真实评分之间的距离,达到了平均绝对误差为0.39的结果,这意味着平均预测误差不到半颗星。
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(n_estimators=100)
rfr.fit(X_train, y_train)
preds = rfr.predict(X_test)
Classification using the embedding features[25]
这次,我们不再让算法预测一个介于1和5之间的任意值,而是尝试将评价的星级分类为5个桶,范围从1星到5星。
在训练之后,模型学会了更好地预测1星和5星的评价,而对于更细微的评价(2-4星),预测结果可能不太准确,这可能是由于更极端的情感表达所致。
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[26]
我们可以使用嵌入来进行零样本分类,而无需任何标记的训练数据。对于每个类别,我们将类名或类别的简短描述进行嵌入。为了以零样本的方式对一些新的文本进行分类,我们将其嵌入与所有类别嵌入进行比较,并预测相似度最高的类别。
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[27]
我们可以通过对用户的所有评论进行平均来获得用户嵌入。同样地,我们可以通过对关于该产品的所有评论进行平均来获得产品嵌入。为了展示这种方法的有用性,我们使用了一个包含50,000条评论的子集,以覆盖更多用户和产品的评论。
我们在一个单独的测试集上评估这些嵌入的有用性,通过绘制用户和产品嵌入的相似性作为评分的函数。有趣的是,基于这种方法,即使在用户收到产品之前,我们也能比随机更好地预测他们是否会喜欢该产品。
评分分组的箱线图
user_embeddings = df.groupby('UserId').ada_embedding.apply(np.mean)
prod_embeddings = df.groupby('ProductId').ada_embedding.apply(np.mean)
Clustering.ipynb[28]
聚类是理解大量文本数据的一种方法。嵌入在这个任务中非常有用,因为它们提供了每个文本的语义有意义的向量表示。因此,通过无监督的方式,聚类可以揭示数据集中的隐藏分组。
在这个例子中,我们发现了四个不同的聚类:一个聚焦于狗粮,一个聚焦于负面评论,以及两个聚焦于正面评论。
使用tSNE在语言空间中可视化的聚类
Semantic_text_search_using_embeddings.ipynb[29]
为了检索最相关的文档,我们使用查询的嵌入向量与每个文档的嵌入向量之间的余弦相似度,并返回得分最高的文档。
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-ada-002')
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[30]
代码搜索与基于嵌入的文本搜索类似。我们提供了一种方法,可以从给定代码库中的所有Python文件中提取Python函数。然后,每个函数都由text-embedding-ada-002
模型进行索引。
要执行代码搜索,我们将查询以自然语言形式嵌入到相同的模型中。然后,我们计算结果查询嵌入与每个函数嵌入之间的余弦相似度。具有最高余弦相似度的结果最相关。
from openai.embeddings_utils import get_embedding, cosine_similarity
df['code_embedding'] = df['code'].apply(lambda x: get_embedding(x, model='text-embedding-ada-002'))
def search_functions(df, code_query, n=3, pprint=True, n_lines=7):
embedding = get_embedding(code_query, model='text-embedding-ada-002')
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[31]
由于嵌入向量之间的较短距离表示更大的相似度,因此嵌入可以用于推荐。
下面,我们展示了一个基本的推荐系统。它接收一个字符串列表和一个“源”字符串,计算它们的嵌入,然后返回一个按相似性从高到低排列的字符串排名。作为一个具体的例子,下面链接的笔记本将该函数的一个版本应用于AG新闻数据集[32](缩减到2000条新闻文章描述),以返回与给定源文章最相似的前5篇文章。
def recommendations_from_strings(
strings: List[str],
index_of_source_string: int,
model="text-embedding-ada-002",
) -> 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
我们的嵌入模型在某些情况下可能不可靠或存在社会风险,在没有减轻措施的情况下可能会造成伤害。
社会偏见
我们的模型通过对性别化的姓名、地域性姓名和一些刻板印象进行测试,发现了模型中存在偏见的证据。
例如,我们发现相比非洲裔美国人的姓名,我们的模型更强烈地将欧洲裔美国人的姓名与积极情感联系在一起,并将负面刻板印象与黑人女性联系在一起。
这些测试在几个方面存在局限性:(a)它们可能无法推广到您特定的用例中,(b)它们仅对可能存在的社会偏见的一小部分进行测试。
这些测试是初步的,我们建议针对您特定的用例运行测试。 这些结果应该被视为存在现象的证据,而不是对您的用例的确切描述。请查看我们的使用政策[33]以获取更多详细信息和指导。
如果您有任何问题,请通过聊天联系我们的支持团队[34],我们很乐意为您提供帮助。
我们的模型在训练时使用的数据集包含有关现实世界事件的一些信息,截至2020年8月。如果您依赖于模型对最近事件的表达,它们可能表现不佳。
常见问题解答
在嵌入字符串之前,如何确定字符串有多少个标记?
在Python中,您可以使用OpenAI的标记器tiktoken[35]将字符串拆分为标记。
示例代码:
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-ada-002
这样的第二代嵌入模型,请使用 cl100k_base
编码。
有关详细信息和示例代码,请参阅OpenAI Cookbook指南如何使用tiktoken计数标记[36]。
如何快速检索K个最近的嵌入向量?
为了快速搜索多个向量,我们建议使用向量数据库。您可以在GitHub上的我们的Cookbook中找到使用向量数据库和OpenAI API的示例。
向量数据库的选项包括:
•Pinecone[37],一个完全托管的向量数据库 •Weaviate[38],一个开源的向量搜索引擎 •Redis[39],作为向量数据库 •Qdrant[40],一个向量搜索引擎 •Milvus[41],一个专为可扩展相似性搜索而构建的向量数据库 •Chroma[42],一个开源的嵌入存储库 •Typesense[43],快速的开源向量搜索 •Zilliz[44],由Milvus提供支持的数据基础架构
我应该使用哪个距离函数?
我们推荐使用余弦相似度[45]。距离函数的选择通常并不重要。
OpenAI的嵌入向量已经被归一化为长度1,这意味着:
•使用点积计算余弦相似度可以稍微更快一些 •余弦相似度和欧氏距离将得到相同的排序结果
我可以在网上分享我的嵌入向量吗?
在我们的模型中,客户拥有他们的输入和输出,包括嵌入向量。您有责任确保您输入到我们的API中的内容不违反任何适用法律或我们的使用条款[46]。
如何0到1制作专属智能文档查询助手?
制作一个专属智能文档查询助手(ChatBot)的实现流程分为两个阶段:
第一阶段:数据准备
1.知识库信息提取和分块:从领域知识库中提取相关的文本信息,并将其分块处理。这可以包括将长文本拆分为段落或句子,提取关键词或实体等。这样可以将知识库的内容更好地组织和管理。 2.调用LLM接口生成embedding:利用LLM(如OpenAI)提供的接口,将分块的文本信息输入到模型中,并生成相应的文本embedding。这些embedding将捕捉文本的语义和语境信息,为后续的搜索和匹配提供基础。 3.存储embedding信息:将生成的文本embedding信息、文本分块以及文本关联的metadata信息存入如PostgreSQL这类支持向量存储的数据库中。
第二阶段:问答
1.用户提问的输入信息。 2.通过OpenAI提供的embedding接口创建该问题的embedding。 3.通过像PostgreSQL pgvector这一类的向量数据库过滤出数据库中相似度大于一定阈值的文档块,将结果返回。
一般流程图如下:
imagepng
本文翻译整理自以下内容:
• OpenAI Platform[47]
本文的主要目的是对AI相关技术知识进行梳理,同时也对AI感兴趣的同学进行一定的技术知识科普,感兴趣的同学请点赞、收藏。
References
[1]
Embeddings: https://platform.openai.com/docs/guides/embeddings/embeddings
[2]
什么是嵌入: https://platform.openai.com/docs/guides/embeddings/what-are-embeddings
[3]
定价页面: https://openai.com/api/pricing/
[4]
input: https://platform.openai.com/docs/api-reference/embeddings/create#embeddings/create-input
[5]
token: https://platform.openai.com/tokenizer
[6]
如何获取嵌入: https://platform.openai.com/docs/guides/embeddings/how-to-get-embeddings
[7]
嵌入API端点: https://platform.openai.com/docs/api-reference/embeddings
[8]
OpenAI Cookbook: https://github.com/openai/openai-cookbook/
[9]
限制和风险: https://platform.openai.com/docs/guides/embeddings/limitations-risks
[10]
嵌入模型: https://platform.openai.com/docs/guides/embeddings/embedding-models
[11]
博客文章公告: https://openai.com/blog/new-and-improved-embedding-model
[12]
BEIR: https://paperswithcode.com/sota/zero-shot-text-search-on-beir
[13]
第二代模型: https://platform.openai.com/docs/guides/embeddings/second-generation-models
[14]
GPT-3分词器: https://platform.openai.com/tokenizer
[15]
相似性嵌入: https://platform.openai.com/docs/guides/embeddings/similarity-embeddings
[16]
文本搜索嵌入: https://platform.openai.com/docs/guides/embeddings/text-search-embeddings
[17]
代码搜索嵌入: https://platform.openai.com/docs/guides/embeddings/code-search-embeddings
[18]
使用场景: https://platform.openai.com/docs/guides/embeddings/use-cases
[19]
Amazon fine-food reviews 数据集: https://www.kaggle.com/snap/amazon-fine-food-reviews
[20]
获取嵌入: https://platform.openai.com/docs/guides/embeddings/obtaining-the-embeddings
[21]
dataset.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/Obtain\_dataset.ipynb
[22]
Visualizing_embeddings_in_2D.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/Visualizing\_embeddings\_in\_2D.ipynb
[23]
Regression_using_embeddings.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/Regression\_using\_embeddings.ipynb
[24]
Regression using the embedding features: https://platform.openai.com/docs/guides/embeddings/regression-using-the-embedding-features
[25]
Classification using the embedding features: https://github.com/openai/openai-cookbook/blob/main/examples/Classification\_using\_embeddings.ipynb
[26]
Zero-shot classification with embeddings: https://github.com/openai/openai-cookbook/blob/main/examples/Zero-shot\_classification\_with\_embeddings.ipynb
[27]
User_and_product_embeddings.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/User\_and\_product\_embeddings.ipynb
[28]
Clustering.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/Clustering.ipynb
[29]
Semantic_text_search_using_embeddings.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/Semantic\_text\_search\_using\_embeddings.ipynb
[30]
Code_search.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/Code\_search.ipynb
[31]
Recommendation_using_embeddings.ipynb: https://github.com/openai/openai-cookbook/blob/main/examples/Recommendation\_using\_embeddings.ipynb
[32]
AG新闻数据集: http://groups.di.unipi.it/~gulli/AG\_corpus\_of\_news\_articles.html
[33]
使用政策: https://openai.com/policies/usage-policies
[34]
聊天联系我们的支持团队: https://help.openai.com/en/
[35]
tiktoken: https://github.com/openai/tiktoken
[36]
如何使用tiktoken计数标记: https://github.com/openai/openai-cookbook/blob/main/examples/How\_to\_count\_tokens\_with\_tiktoken.ipynb
[37]
Pinecone: https://github.com/openai/openai-cookbook/tree/main/examples/vector\_databases/pinecone
[38]
Weaviate: https://github.com/openai/openai-cookbook/tree/main/examples/vector\_databases/weaviate
[39]
Redis: https://github.com/openai/openai-cookbook/tree/main/examples/vector\_databases/redis
[40]
Qdrant: https://github.com/openai/openai-cookbook/tree/main/examples/vector\_databases/qdrant
[41]
Milvus: https://github.com/openai/openai-cookbook/blob/main/examples/vector\_databases/Using\_vector\_databases\_for\_embeddings\_search.ipynb
[42]
Chroma: https://github.com/chroma-core/chroma
[43]
Typesense: https://typesense.org/docs/0.24.0/api/vector-search.html
[44]
Zilliz: https://github.com/openai/openai-cookbook/tree/main/examples/vector\_databases/zilliz
[45]
余弦相似度: https://en.wikipedia.org/wiki/Cosine\_similarity
[46]
使用条款: https://openai.com/policies/terms-of-use
[47]
OpenAI Platform: https://platform.openai.com/docs/guides/embeddings?spm=a2c63.p38356.0.0.1e067305uj9vrV