什么是嵌入?
OpenAI 的文本嵌入衡量文本字符串的相關性。嵌入通常用于:
搜索(結(jié)果按與查詢字符串的相關性排序)
聚類(其中文本字符串按相似性分組)
推薦(推薦具有相關文本字符串的項目)
異常檢測(識別出相關性很小的異常值)
多樣性測量(分析相似性分布)
分類(其中文本字符串按其最相似的標簽分類)
嵌入是浮點數(shù)的向量(列表)。兩個向量之間的距離衡量它們的相關性。小距離表示高相關性,大距離表示低相關性。
訪問我們的定價頁面以了解嵌入定價。請求根據(jù)發(fā)送的輸入中的令牌數(shù)量計費。
如何獲得嵌入
要獲得嵌入,請將您的文本字符串連同選擇的嵌入模型 ID(例如,text-embedding-ada-002)一起發(fā)送到嵌入 API 端點。響應將包含一個嵌入,您可以提取、保存和使用它。
示例請求:
python | curl |
|
|
示例響應:
{
"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 中查看更多 Python 代碼示例。
嵌入模型
OpenAI 提供了一個第二代嵌入模型(在模型 ID 中用 -002 表示)和 16 個第一代模型(在模型 ID 中用 -001 表示)。
我們建議對幾乎所有用例使用 text-embedding-ada-002。它更好、更便宜、更易于使用。
模型生成 | 分詞器 | 最大輸入token | 數(shù)據(jù)來源截止至 |
---|---|---|---|
V2 | cl100k_base | 8191 | Sep 2021 |
V1 | GPT-2/GPT-3 | 2046 | Aug 2020 |
使用量按輸入令牌定價,每 1000 個令牌 0.0004 美元,或每美元約 3,000 頁(假設每頁約 800 個令牌):
模型 | 每美元粗略頁數(shù) | BEIR SEARCH EVAL 的性能示例 |
---|---|---|
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 |
第二代模型
模型名稱 | 分詞器 | 最大輸入token | 輸出 |
---|---|---|---|
text-embedding-ada-002 | cl100k_base | 8191 | 1536 |
第一代模型(不推薦)
所有第一代模型(以 -001 結(jié)尾的模型)都使用 GPT-3 分詞器,最大輸入為 2046 個分詞。
第一代嵌入由五個不同的模型系列生成,這些模型系列針對三個不同的任務進行了調(diào)整:文本搜索、文本相似性和代碼搜索。搜索模型成對出現(xiàn):一個用于短查詢,一個用于長文檔。每個系列最多包括四種質(zhì)量和速度不同的型號:
模型 輸出 Ada 1024 Babbage 2048 Curie 4096 Davinci 12288 Davinci 是最有能力的,但比其他型號更慢且更昂貴。 Ada 的能力最差,但速度更快,成本更低。
相似性嵌入
相似性模型最擅長捕捉文本片段之間的語義相似性。
用例 可用模型 Clustering, regression, anomaly detection, visualization text-similarity-ada-001
text-similarity-babbage-001
text-similarity-curie-001
text-similarity-davinci-001
文本搜索嵌入
文本搜索模型有助于衡量哪些長文檔與短搜索查詢最相關。使用了兩種模型:一種用于嵌入搜索查詢,一種用于嵌入要排名的文檔。最接近查詢嵌入的文檔嵌入應該是最相關的。
用例 可用模型 Search, context relevance, information retrieval 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
代碼搜索嵌入
與搜索嵌入類似,有兩種類型:一種用于嵌入自然語言搜索查詢,一種用于嵌入要檢索的代碼片段。
用例 可用模型 Code search and relevance code-search-ada-code-001
code-search-ada-text-001
code-search-babbage-code-001
code-search-babbage-text-001
對于 -001 文本嵌入(不是 -002,也不是代碼嵌入),我們建議將輸入中的換行符 (\n) 替換為單個空格,因為當存在換行符時我們已經(jīng)看到更糟糕的結(jié)果。
在這里,我們展示了一些有代表性的用例。我們將在以下示例中使用亞馬遜美食評論數(shù)據(jù)集。
獲取嵌入
該數(shù)據(jù)集包含截至 2012 年 10 月亞馬遜用戶留下的總共 568,454 條食品評論。我們將使用 1,000 條最新評論的子集用于說明目的。評論是英文的,往往是正面的或負面的。每條評論都有一個 ProductId、UserId、Score、評論標題(Summary)和評論正文(Text)。例如:
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... |
我們會將評論摘要和評論文本合并為一個組合文本。該模型將對該組合文本進行編碼并輸出單個向量嵌入。
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)
要從保存的文件中加載數(shù)據(jù),您可以運行以下命令:
import pandas as pd
df = pd.read_csv('output/embedded_1k_reviews.csv')
df['ada_embedding'] = df.ada_embedding.apply(eval).apply(np.array)
二維數(shù)據(jù)可視化
Visualizing_embeddings_in_2D.ipynb
嵌入的大小隨底層模型的復雜性而變化。為了可視化這種高維數(shù)據(jù),我們使用 t-SNE 算法將數(shù)據(jù)轉(zhuǎn)換為二維。
我們根據(jù)評論者給出的星級評分為各個評論著色:
可視化似乎產(chǎn)生了大約 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")
嵌入作為 ML 算法的文本特征編碼器
Regression_using_embeddings.ipynb
嵌入可以用作機器學習模型中的通用自由文本特征編碼器。如果一些相關輸入是自由文本,則合并嵌入將提高任何機器學習模型的性能。嵌入也可以用作 ML 模型中的分類特征編碼器。如果分類變量的名稱有意義且數(shù)量眾多,例如職位名稱,那么這會增加最大的價值。對于此任務,相似性嵌入通常比搜索嵌入表現(xiàn)更好。
我們觀察到,通常嵌入表示非常豐富且信息密集。例如,使用 SVD 或 PCA 降低輸入的維度,即使降低 10%,通常也會導致特定任務的下游性能變差。
此代碼將數(shù)據(jù)拆分為訓練集和測試集,將由以下兩個用例使用,即回歸和分類。
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
)
使用嵌入特征進行回歸
嵌入提供了一種預測數(shù)值的優(yōu)雅方法。在這個例子中,我們根據(jù)評論的文本預測評論者的星級。因為嵌入中包含的語義信息很高,所以即使評論很少,預測也不錯。
我們假設分數(shù)是 1 到 5 之間的連續(xù)變量,并允許算法預測任何浮點值。 ML 算法最小化預測值與真實分數(shù)的距離,并實現(xiàn) 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_embeddings.ipynb
這一次,我們不再讓算法預測 1 到 5 之間的任何值,而是嘗試將評論的確切星數(shù)分類為 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.ipynb
我們可以在沒有任何標記訓練數(shù)據(jù)的情況下使用嵌入進行零鏡頭分類。對于每個類,我們嵌入類名或類的簡短描述。為了以零樣本方式對一些新文本進行分類,我們將其嵌入與所有類嵌入進行比較,并預測具有最高相似度的類。
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'
獲取用于冷啟動推薦的用戶和產(chǎn)品嵌入
User_and_product_embeddings.ipynb
我們可以通過對他們的所有評論進行平均來獲得用戶嵌入。同樣,我們可以通過對有關該產(chǎn)品的所有評論進行平均來獲得產(chǎn)品嵌入。為了展示這種方法的實用性,我們使用 50k 評論的子集來覆蓋每個用戶和每個產(chǎn)品的更多評論。
我們在單獨的測試集上評估這些嵌入的有用性,我們將用戶和產(chǎn)品嵌入的相似性繪制為評分的函數(shù)。有趣的是,基于這種方法,甚至在用戶收到產(chǎn)品之前,我們就可以比隨機預測更好地預測他們是否喜歡該產(chǎn)品。
user_embeddings = df.groupby('UserId').ada_embedding.apply(np.mean)
prod_embeddings = df.groupby('ProductId').ada_embedding.apply(np.mean)
聚類是理解大量文本數(shù)據(jù)的一種方式。嵌入對于這項任務很有用,因為它們提供了每個文本的語義上有意義的向量表示。因此,以一種無監(jiān)督的方式,聚類將揭示我們數(shù)據(jù)集中隱藏的分組。
在這個例子中,我們發(fā)現(xiàn)了四個不同的集群:一個專注于狗食,一個專注于負面評論,兩個專注于正面評論。
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_
使用嵌入的文本搜索
Semantic_text_search_using_embeddings.ipynb
為了檢索最相關的文檔,我們使用查詢的嵌入向量與每個文檔之間的余弦相似度,并返回得分最高的文檔。
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)
使用嵌入的代碼搜索
代碼搜索的工作方式類似于基于嵌入的文本搜索。我們提供了一種從給定存儲庫中的所有 Python 文件中提取 Python 函數(shù)的方法。然后每個函數(shù)都由 text-embedding-ada-002 模型索引。
為了執(zhí)行代碼搜索,我們使用相同的模型將查詢嵌入到自然語言中。然后我們計算結(jié)果查詢嵌入和每個函數(shù)嵌入之間的余弦相似度。最高的余弦相似度結(jié)果是最相關的。
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
因為嵌入向量之間的距離越短表示相似度越高,嵌入可用于推薦。
下面,我們說明了一個基本的推薦系統(tǒng)。它接受一個字符串列表和一個“源”字符串,計算它們的嵌入,然后返回字符串的排名,從最相似到最不相似。作為一個具體示例,下面鏈接的筆記本將此函數(shù)的一個版本應用于 AG 新聞數(shù)據(jù)集(采樣到 2,000 篇新聞文章描述)以返回與任何給定源文章最相似的前 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
局限性和風險
我們的嵌入模型可能不可靠或在某些情況下會帶來社會風險,并且在沒有緩解措施的情況下可能會造成傷害。
社會偏見
局限性:模型對社會偏見進行編碼,例如通過對某些群體的刻板印象或負面情緒。
我們通過運行 SEAT(May 等人,2019 年)和 Winogender(Rudinger 等人,2018 年)基準測試發(fā)現(xiàn)了模型中存在偏差的證據(jù)。這些基準一起包含 7 個測試,用于衡量模型在應用于性別名稱、區(qū)域名稱和某些刻板印象時是否包含隱性偏見。
例如,我們發(fā)現(xiàn),與非裔美國人的名字相比,我們的模型更強烈地將 (a) 歐裔美國人的名字與積極情緒聯(lián)系起來,以及 (b) 對黑人女性的負面刻板印象。
這些基準在幾個方面存在局限性:(a) 它們可能無法推廣到您的特定用例,以及 (b) 它們僅測試極小部分可能的社會偏見。
這些測試是初步的,我們建議針對您的特定用例運行測試。這些結(jié)果應被視為該現(xiàn)象存在的證據(jù),而不是對您的用例的明確描述。請參閱我們的使用政策以獲取更多詳細信息和指導。
如果您有任何問題,請通過聊天聯(lián)系我們的支持團隊;我們很樂意就此提供建議。
對最近發(fā)生的事件視而不見
局限性:模型缺乏對 2020 年 8 月之后發(fā)生的事件的了解。
我們的模型在包含 8/2020 之前真實世界事件的一些信息的數(shù)據(jù)集上進行訓練。如果你依賴于代表最近事件的模型,那么它們可能表現(xiàn)不佳。
在嵌入字符串之前,如何知道它有多少個標記?
在 Python 中,您可以使用 OpenAI 的分詞器 tiktoken 將字符串拆分為分詞。
示例代碼:
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 計算令牌。
如何快速檢索 K 個最近的嵌入向量?
為了快速搜索多個矢量,我們建議使用矢量數(shù)據(jù)庫。您可以在 GitHub 上的 Cookbook 中找到使用矢量數(shù)據(jù)庫和 OpenAI API 的示例。
矢量數(shù)據(jù)庫選項包括:
我們推薦余弦相似度。distance 函數(shù)的選擇通常無關緊要。
OpenAI 嵌入被歸一化為長度 1,這意味著:
僅使用點積可以稍微更快地計算余弦相似度
余弦相似度和歐幾里德距離將導致相同的排名
我可以在線共享我的嵌入嗎?
客戶擁有我們模型的輸入和輸出,包括嵌入的情況。您有責任確保您輸入到我們 API 的內(nèi)容不違反任何適用法律或我們的使用條款。
更多建議: