こんにちはHALDATAのTNです。
今回はレビューデータを使ったクラスタリングの記事を書きます。
はじめに
商品やサービスに対するレビューは、ユーザーの本音が詰まった貴重なデータです。しかし、その量が多くなると、一つひとつ目を通すのはなかなか大変です。
そこで今回は、OpenAIのEmbeddingを使ってレビューを数値化し、UMAPで次元を圧縮、さらにHDBSCANで自動的にグループ分けすることで、レビュー内容を意味的に整理・分析する方法を紹介します。
データ準備
まず、必要なライブラリをインストールします:
pip install datasets pandas openai numpy umap-learn hdbscan python-dotenv
データの取得とEmbedding生成
1.get_data.py
では、Hugging FaceのAmazonレビューデータセットを使用し、OpenAI Embeddingを生成します:
import pandas as pd
import numpy as np
from datasets import load_dataset
from openai import OpenAI
import os
from dotenv import load_dotenv
load_dotenv()
# Hugging FaceからAmazonレビューのデータセットを読み込み
dataset = load_dataset("SetFit/amazon_reviews_multi_ja")
df = pd.DataFrame(dataset['train'])
review_texts = df['text'].tolist()[:100] # テスト用に100件に制限
# OpenAI Embeddingの取得
client = OpenAI()
client.api_key = os.getenv("OPENAI_API_KEY")
embeddings = []
for text in review_texts:
response = client.embeddings.create(
input=text,
model="text-embedding-3-small"
)
embeddings.append(response.data[0].embedding)
embeddings = np.array(embeddings)
次元削減とクラスタリング
UMAPによる次元削減
2.compute_umap.py
では、高次元のEmbeddingを2次元に削減します:
import umap
import numpy as np
# UMAP設定
reducer = umap.UMAP(
n_neighbors=15, # 近傍点数(局所構造の詳細度)
min_dist=0.1, # 低次元での最小距離(密集度)
n_components=2, # 2次元に削減
metric='cosine', # Embeddingには余弦距離が適している
random_state=42
)
# 次元削減実行
reduced_embeddings = reducer.fit_transform(embeddings)
HDBSCANクラスタリング
3.clustering.py
では、次元削減されたデータに対してクラスタリングを実行します:
import hdbscan
import numpy as np
# クラスタリング実行
clusterer = hdbscan.HDBSCAN(
min_cluster_size=5, # 最小クラスターサイズ
min_samples=3, # コアポイントになる最小近傍数
metric='euclidean', # 削減後はユークリッド距離
cluster_selection_method='eom' # 'eom'か'leaf'
)
cluster_labels = clusterer.fit_predict(reduced_embeddings)
UMAPの有無で可視化
4.display_result.py
では、クラスタリング結果を分析し、各クラスターの特徴を抽出します:
import numpy as np
import matplotlib.pyplot as plt
import hdbscan
import japanize_matplotlib
# データの読み込み
embeddings = np.load('embeddings.npy')
reduced_embeddings = np.load('reduced_embeddings.npy')
# 元の高次元データでのクラスタリング
clusterer_original = hdbscan.HDBSCAN(
min_cluster_size=5,
min_samples=3,
metric='euclidean',
cluster_selection_method='eom'
)
labels_original = clusterer_original.fit_predict(embeddings)
# UMAPで削減したデータでのクラスタリング
clusterer_reduced = hdbscan.HDBSCAN(
min_cluster_size=5,
min_samples=3,
metric='euclidean',
cluster_selection_method='eom'
)
labels_reduced = clusterer_reduced.fit_predict(reduced_embeddings)
# 結果の集計
n_clusters_original = len(set(labels_original)) - (1 if -1 in labels_original else 0)
n_noise_original = list(labels_original).count(-1)
n_clusters_reduced = len(set(labels_reduced)) - (1 if -1 in labels_reduced else 0)
n_noise_reduced = list(labels_reduced).count(-1)
# 可視化
plt.figure(figsize=(15, 6))
# 元のデータでのクラスタリング結果
plt.subplot(1, 2, 1)
scatter1 = plt.scatter(embeddings[:, 0], embeddings[:, 1],
c=labels_original, cmap='tab20', alpha=0.6)
plt.title(f'元のデータでのクラスタリング\nクラスター数: {n_clusters_original}, ノイズ: {n_noise_original}')
plt.xlabel('次元1')
plt.ylabel('次元2')
# UMAP削減後のクラスタリング結果
plt.subplot(1, 2, 2)
scatter2 = plt.scatter(reduced_embeddings[:, 0], reduced_embeddings[:, 1],
c=labels_reduced, cmap='tab20', alpha=0.6)
plt.title(f'UMAP削減後のクラスタリング\nクラスター数: {n_clusters_reduced}, ノイズ: {n_noise_reduced}')
plt.xlabel('UMAP1')
plt.ylabel('UMAP2')
plt.tight_layout()
plt.savefig('clustering_comparison.png')
plt.close()
print(f"元のデータでのクラスタリング結果:")
print(f"- クラスター数: {n_clusters_original}")
print(f"- ノイズ点の数: {n_noise_original}")
print(f"\nUMAP削減後のクラスタリング結果:")
print(f"- クラスター数: {n_clusters_reduced}")
print(f"- ノイズ点の数: {n_noise_reduced}")
可視化の結果
元々はサンプルに入れたデータが少ないのも影響してノイズばかり(左)なのが、
UMAP後ではノイズが減少してクラスターと判定されるようになりました。
まとめ
今回紹介した手法では、OpenAIのEmbeddingを使ってレビューを意味ベースで数値化し、UMAPで次元を圧縮、さらにHDBSCANで自然なクラスタに分類することができます。
これにより、意味は似ていても表現の異なるレビューを自動でグループ化でき、全体の傾向をつかみやすくなります。
また、クラスタ数をあらかじめ指定する必要がないため、実データに柔軟に対応できるのも利点です。
さらに、明らかに他と異なるレビュー(ノイズ)も自動で検出されるため、内容の分析やフィルタリングにも活用しやすくなります。
参考記事