0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

UMAP × HDBSCAN クラスタリング

0
Posted at

2,500件のユーザーペインを UMAP × HDBSCAN + LLM自動命名で高精度クラスタリングしてみた

前回の記事では、ネットの投稿から OpenAI Structured Outputs と text-embedding-3-small (1536次元) を用いて「お金を払ってでも解決したいペイン」を自動抽出し、PostgreSQL (pgvector) に保存する手法を紹介しました。

今回は、蓄積された 2,500件のペインベクトルデータを UMAP × HDBSCAN + LLM自動命名 でクラスタリング し、市場に存在する課題領域を全自動で可視化したデータ分析手法を解説します。


🛠️ なぜ K-Means や DBSCAN ではなく HDBSCAN なのか?

文章の埋め込みベクトル(Embedding)をクラスタリングする際、定番のアルゴリズムには以下のような課題があります。

アルゴリズム 課題・デメリット
K-Means 最初にクラスタ数 $K$ を人間が指定する必要があり、未知の課題領域の発見に向かない。
DBSCAN 距離しきい値 eps を固定で指定する必要があり、データの密度が均一でない場合に全データが1つの巨大クラスタに結合してしまう(今回の初期検証で全データが1クラスタに結合する問題が発生)。
HDBSCAN eps の固定指定が不要。異なる密度のクラスタを階層的に自動検出し、ノイズ(単発のお悩み)を綺麗に除外できる。

🐍 分析パイプラインの実装コード

import numpy as np
from sklearn.preprocessing import normalize
from umap import UMAP
import hdbscan
from openai import OpenAI

def run_clustering(embeddings: np.ndarray, min_cluster_size: int = 5, min_samples: int = 3):
    """
    1. L2 正規化
    2. UMAP による次元削減 (1536次元 -> 10次元)
    3. HDBSCAN による密度ベースのクラスタリング
    """
    # 1. L2 正規化 (コサイン類似度ベースにするため)
    normed = normalize(embeddings, norm="l2")

    # 2. UMAP 次元削減
    reducer = UMAP(
        n_components=10,
        n_neighbors=15,
        min_dist=0.0,
        metric="cosine",
        random_state=42
    )
    reduced = reducer.fit_transform(normed)

    # 3. HDBSCAN クラスタリング
    clusterer = hdbscan.HDBSCAN(
        min_cluster_size=min_cluster_size,
        min_samples=min_samples,
        metric="euclidean",
        cluster_selection_method="eom"
    )
    labels = clusterer.fit_predict(reduced)
    return labels

🤖 LLM (gpt-4o-mini) によるクラスタ自動命名

HDBSCAN で分割された各クラスタ(cluster_id)に含まれる上位のペイン文言をまとめて gpt-4o-mini に渡し、「このクラスタは何の課題群か」を自動で命名・要約させます。

def name_cluster_with_llm(openai_client: OpenAI, cluster_id: int, pain_items: list) -> str:
    items_text = "\n".join([
        f"- {item['core_pain']} (タグ: {', '.join(item['pain_keywords'])})" 
        for item in pain_items[:6]
    ])

    prompt = f"""以下は同一クラスタに分類された複数のユーザーペインの一覧です。
これらのペインを端的にまとめた「クラスタ名(15文字以内)」と「要約(1文)」をJSONで出力してください。

ペイン一覧:
{items_text}
"""
    res = openai_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"},
    )
    return res.choices[0].message.content

📊 2,520件の実際の分析結果サマリー

2,520件の全データセットを通した結果、138個の非常に具体的で鮮明な課題クラスタ が自動生成されました。

クラスタID LLM自動命名クラスタ 件数 平均収益化スコア 平均切実度
#100 ニキビ跡・クレーターの自力治療限界 22件 0.79 0.88
#63 多汗症・手汗・ワキガの日常支障ケア 10件 0.77 0.81
#74 酒さ・赤ら顔の皮膚バリアケア 13件 0.75 0.76
#8 PMDD(重度月経前不快障害)と定期処方 9件 0.74 0.84
#103 AGA・薄毛の継続治療と月額定額ニーズ 7件 0.74 0.87

💡 まとめと考察

  • 次元削減 + HDBSCAN の相性が抜群: 高次元なEmbeddingベクトルをUMAPで10次元に圧縮してからHDBSCANにかけることで、密度の異なる細かい悩み(ニキビ跡、手汗、PMDDなど)が綺麗に分離されました。
  • ノイズの排除: 単発の雑談や分類不能な疑問は cluster_id = -1(ノイズ)として自動除外されるため、純度の高いペインだけを抽出できます。

次回は、抽出されたクラスタから「単発で終わるペイン」と「毎月継続課金(サブスク)されるペイン」のビジネス構造の違いを分析します。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?