OpenAi 嵌入使用案例
嵌入的大小随基础模型的复杂性而变化。为了可视化这些高维数据,我们使用t-SNE算法将数据转换为二维数据。
我们根据评论者给出的星级评分为个人评论着色:
- 1星:红色
- 2星:深橙色
- 3星:金色
- 4星:绿松石
- 5星:深绿色
可视化似乎产生了大约 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 模型中的分类特征编码器。如果分类变量的名称有意义且数量众多,例如职务,则这将增加最大价值。对于此任务,相似性嵌入通常比搜索嵌入性能更好。
我们观察到,通常嵌入表示非常丰富且信息密集。例如,使用 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
)
使用嵌入特征的回归
嵌入提供了一种预测数值的优雅方法。在此示例中,我们根据评论文本预测评论者的星级。因为嵌入中包含的语义信息很高,所以即使很少有评论,预测也是不错的。
我们假设分数是 1 到 5 之间的连续变量,并允许算法预测任何浮点值。ML 算法将预测值与真实分数的距离最小化,并实现了 0.39 的平均绝对误差,这意味着预测平均偏差不到半颗星。
from sklearn.ensemble import RandomForestRegressor
rfr = RandomForestRegressor(n_estimators=100)
rfr.fit(X_train, y_train)
preds = rfr.predict(X_test)
这一次,我们不是让算法预测 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)
我们可以使用嵌入进行零镜头分类,而无需任何标记的训练数据。对于每个类,我们嵌入类名或类的简短描述。为了以零镜头的方式对一些新文本进行分类,我们将它的嵌入与所有类嵌入进行比较,并预测相似度最高的类。
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
我们可以通过平均用户的所有评论来获得用户嵌入。同样,我们可以通过平均有关该产品的所有评论来获得产品嵌入。为了展示这种方法的有用性,我们使用 50k 条评论的子集来涵盖每个用户和每个产品的更多评论。
我们在单独的测试集上评估这些嵌入的有用性,在该测试集中,我们将用户和产品嵌入的相似性绘制为评级的函数。有趣的是,基于这种方法,甚至在用户收到产品之前,我们就可以比随机更好地预测他们是否喜欢该产品。
user_embeddings = df.groupby('UserId').ada_embedding.apply(np.mean)
prod_embeddings = df.groupby('ProductId').ada_embedding.apply(np.mean)
聚类是理解大量文本数据的一种方法。嵌入对于此任务很有用,因为它们提供了每个文本的语义上有意义的向量表示。因此,以无监督的方式,聚类将发现我们数据集中的隐藏分组。
在这个例子中,我们发现了四个不同的集群:一个专注于狗粮,一个关注负面评论,两个关注正面评论。
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_
为了检索最相关的文档,我们使用查询的嵌入向量和每个文档之间的余弦相似性,并返回得分最高的文档。
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)
为了检索最相关的文档,我们使用查询的嵌入向量和每个文档之间的余弦相似性,并返回得分最高的文档。
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)
由于嵌入向量之间的距离越短表示更大的相似性,因此嵌入可用于推荐。
下面,我们说明一个基本的推荐器。它接受字符串列表和一个“源”字符串,计算它们的嵌入,然后返回字符串的排名,从最相似到最不相似排列。作为一个具体示例,下面的链接笔记本将此函数的一个版本应用于 AG 新闻数据集(采样到 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