我们使用一个简单的 k-means 算法来演示如何进行聚类。聚类可以帮助发现数据中有价值的、隐藏的群体。数据集在 Get_embeddings_from_dataset Notebook 中创建。
# 导入
import numpy as np
import pandas as pd
from ast import literal_eval
# 加载数据
datafile_path = "./data/fine_food_reviews_with_embeddings_1k.csv"
df = pd.read_csv(datafile_path)
df["embedding"] = df.embedding.apply(literal_eval).apply(np.array) # 将字符串转换为 numpy 数组
matrix = np.vstack(df.embedding.values)
matrix.shape
(1000, 1536)
1. 使用 K-means 查找聚类
我们展示了 K-means 最简单的用法。您可以选择最适合您用例的聚类数量。
from sklearn.cluster import KMeans
n_clusters = 4
kmeans = KMeans(n_clusters=n_clusters, init="k-means++", random_state=42)
kmeans.fit(matrix)
labels = kmeans.labels_
df["Cluster"] = labels
df.groupby("Cluster").Score.mean().sort_values()
Cluster 0 4.105691 1 4.191176 2 4.215613 3 4.306590 Name: Score, dtype: float64
from sklearn.manifold import TSNE
import matplotlib
import matplotlib.pyplot as plt
tsne = TSNE(n_components=2, perplexity=15, random_state=42, init="random", learning_rate=200)
vis_dims2 = tsne.fit_transform(matrix)
x = [x for x, y in vis_dims2]
y = [y for x, y in vis_dims2]
for category, color in enumerate(["purple", "green", "red", "blue"]):
xs = np.array(x)[df.Cluster == category]
ys = np.array(y)[df.Cluster == category]
plt.scatter(xs, ys, color=color, alpha=0.3)
avg_x = xs.mean()
avg_y = ys.mean()
plt.scatter(avg_x, avg_y, marker="x", color=color, s=100)
plt.title("使用 t-SNE 在语言 2d 中可视化的聚类")
Text(0.5, 1.0, '使用 t-SNE 在语言 2d 中可视化的聚类')
聚类在 2d 投影中的可视化。在此次运行中,绿色聚类(#1)似乎与其他聚类有很大不同。让我们看一下每个聚类的几个样本。
2. 聚类中的文本样本和命名聚类
让我们展示每个聚类的随机样本。我们将使用 gpt-4 根据该聚类中 5 个随机评论的样本来命名聚类。
from openai import OpenAI
import os
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY", "<your OpenAI API key if not set as env var>"))
# 读取属于每个组的评论。
rev_per_cluster = 5
for i in range(n_clusters):
print(f"Cluster {i} Theme:", end=" ")
reviews = "\n".join(
df[df.Cluster == i]
.combined.str.replace("Title: ", "")
.str.replace("\n\nContent: ", ": ")
.sample(rev_per_cluster, random_state=42)
.values
)
messages = [
{"role": "user", "content": f'What do the following customer reviews have in common?\n\nCustomer reviews:\n"""\n{reviews}\n"""\n\nTheme:'}
]
response = client.chat.completions.create(
model="gpt-4",
messages=messages,
temperature=0,
max_tokens=64,
top_p=1,
frequency_penalty=0,
presence_penalty=0)
print(response.choices[0].message.content.replace("\n", ""))
sample_cluster_rows = df[df.Cluster == i].sample(rev_per_cluster, random_state=42)
for j in range(rev_per_cluster):
print(sample_cluster_rows.Score.values[j], end=", ")
print(sample_cluster_rows.Summary.values[j], end=": ")
print(sample_cluster_rows.Text.str[:70].values[j])
print("-" * 100)
Cluster 0 Theme: 这些客户评论的主题是亚马逊上购买的食品。
5, 喜欢这些无麸质健康能量棒,在亚马逊上订购省了很多钱: 这些 Kind 能量棒又好又健康又无麸质。我的女儿可以
1, 应该更突出地宣传椰子作为一种成分: 首先,这些应该被称为 Mac - 椰子能量棒,因为椰子是第二位的
5, 非常好!!: 就像糖果一样
很棒的味道,绝对值得购买
我甚至 o
5, 很棒的产品: 在搜遍了镇上的每一家店都没有找到橙皮之后
5, 美味: 橡皮蛙是我吃过的最喜欢的糖果。当然
Cluster 1 Theme: 宠物食品评论 2, 杂乱无章,显然不好吃: 我的猫不是非常喜欢。当然,她会舔掉肉汁,但会留下 4, 猫喜欢它: 我的 7 只猫喜欢这种食物,但对人类来说有点难吃。块 5, 停不下来!!!: 我们的迷你狮子狗幼犬怎么也吃不够。每次她看到 1, 食物导致生病: 我把我的猫从 Blue Buffalo Wildnerness 食物换成了这种 5, 我的毛茸茸宝贝们喜欢这些!: 摇晃容器,它们就会跑过来。就连我的公猫,他也不是
Cluster 2 Theme: 所有评论都关于不同种类的咖啡。 5, Fog Chaser Coffee: 这种咖啡口感醇厚,味道浓郁。价格远低于 5, 口味极佳: 对我来说,这是一款很棒的咖啡,一旦您尝试了,就会喜欢它,这 4, 好,但不是 Wolfgang Puck 那么好: 老实说,我不得不承认我期望能好一点。那不是 5, 正是我喜欢的咖啡: Coffee Masters 的榛子咖啡曾经在一个当地的咖啡/茶点店出售/ 5, Rodeo Drive 是疯狂好喝的咖啡!: Rodeo Drive 是我的最爱,我准备订购更多!那
Cluster 3 Theme: 这些客户评论的主题是食品和饮料产品。 5, 苏打水的绝佳替代品: 这是苏打水的绝佳替代品。它为那些 5, 太方便了,而且价格这么低!: 我需要两个香草豆来制作我丈夫的 Love Goddess 蛋糕 2, 不太奶酪: 大约一个月前买的。首先,它闻起来很糟糕……它的味道 5, 美味!: 我不是一个非常喜欢啤酒的人。我确实偶尔喜欢 Blue Moon(所有 3, 一般般: 我买这个牌子是因为它是我家附近的 Ranch 99 唯一有的。我
需要注意的是,聚类不一定会与您打算使用它们的方式相匹配。更多的聚类将侧重于更具体的模式,而少量的聚类通常会侧重于数据中最大的差异。