Навчання моделі кластеризації даних: K-Means, DBSCAN, HDBSCAN

Навчання моделі кластеризації даних — задача, з якою стикаються при сегментації клієнтів або аналізі текстів. Алгоритми K-Means, DBSCAN, HDBSCAN — основа, але без правильного налаштування результат незадовільний. Наприклад, на 500 тисячах записів K-Means дає силует 0.19 і шум. Причина — невірний виб

Напрямки AI-розробки

Часті запитання

Останні роботи

  • image_website-b2b-advance_0.webp
    Розробка сайту компанії B2B ADVANCE
    1439
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1301
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    997
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1264
  • image_logo-advance_0.webp
    Розробка логотипу компанії B2B Advance
    712
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    1002

Навчання моделі кластеризації даних — задача, з якою стикаються при сегментації клієнтів або аналізі текстів. Алгоритми K-Means, DBSCAN, HDBSCAN — основа, але без правильного налаштування результат незадовільний. Наприклад, на 500 тисячах записів K-Means дає силует 0.19 і шум. Причина — невірний вибір числа кластерів та алгоритму, що не враховує форму даних. Наш пайплайн автоматично підбирає алгоритм і число кластерів, досягаючи стабільних бізнес-інтерпретованих результатів.

MiniBatchKMeans з batch_size=2048 працює в 10-15 разів швидше стандартного K-Means на датасетах >100k записів, що критично для оперативного аналізу. Для текстових даних використовуємо HDBSCAN з попереднім зниженням розмірності UMAP — це дозволяє виділяти кластери довільної форми без вказівки їх кількості.

Проблеми, які вирішує кластеризація

Кластеризація — навчання без вчителя, що виявляє приховану структуру даних: сегменти клієнтів, тематичні кластери документів, аномальні групи транзакцій. Основні проблеми, з якими до нас приходять:

  • Неправильний вибір числа кластерів. Метод ліктя часто дає неоднозначну відповідь, а суб'єктивний вибір веде до неінтерпретованих сегментів.
  • Масштабування на великі обсяги. K-Means на 1 млн записів може рахувати хвилини, MiniBatchKMeans — секунди.
  • Кластеризація текстів. Класичні алгоритми погано працюють на Bag-of-Words — потрібні ембеддінги та HDBSCAN.

Наш досвід показує: правильно налаштований пайплайн кластеризації дає приріст у точності сегментації до 40% порівняно з ad-hoc підходами.

Як вибрати алгоритм кластеризації?

Вибір алгоритму — ключове рішення. Порівняємо основні методи.

Алгоритм К-сть кластерів Форма кластерів Масштаб Застосування
K-Means Потрібно задати Сферичні >100K Сегментація клієнтів
DBSCAN Авто Будь-яка ~50K Аномалії, геодані
HDBSCAN Авто Будь-яка >100K Тексти, зображення
Agglomerative Потрібно задати Будь-яка ~10K Ієрархія документів
GMM Потрібно задати Еліпсоїдні ~50K М'які ймовірності

На практиці для більшості задач ми використовуємо K-Means з MiniBatch для великих даних та HDBSCAN для текстів і аномалій. GMM хороший, коли кластери перекриваються.

Детальний опис метрик якості
Метрика Опис Діапазон Хороше значення
Silhouette Компактність і роздільність [-1,1] >0.3
Calinski-Harabasz Відношення дисперсій [0,∞) Чим більше, тим краще
Davies-Bouldin Середня схожість кластерів [0,∞) Чим менше, тим краще

Комбінація силуету та Calinski-Harabasz дає надійне визначення k. У нашому пайплайні ми автоматично обчислюємо консенсусне значення.

Практичний пайплайн: K-Means з автоматичним вибором k

from sklearn.cluster import KMeans, MiniBatchKMeans from sklearn.preprocessing import StandardScaler from sklearn.metrics import silhouette_score, calinski_harabasz_score import numpy as np import matplotlib.pyplot as plt class ClusteringPipeline: def __init__(self, scale: bool = True): self.scaler = StandardScaler() if scale else None self.model = None def find_optimal_k(self, X: np.ndarray, k_range: range = range(2, 20)) -> int: """Метод ліктя + силует для визначення K""" if self.scaler: X = self.scaler.fit_transform(X) inertias = [] silhouettes = [] for k in k_range: kmeans = MiniBatchKMeans(n_clusters=k, random_state=42, batch_size=1024) labels = kmeans.fit_predict(X) inertias.append(kmeans.inertia_) if len(X) > 50000: sample_idx = np.random.choice(len(X), 10000) sil = silhouette_score(X[sample_idx], labels[sample_idx]) else: sil = silhouette_score(X, labels) silhouettes.append(sil) # Метод ліктя — точка перегину diffs = np.diff(inertias) diffs2 = np.diff(diffs) elbow_k = k_range[np.argmax(diffs2) + 2] # Найкращий силует best_sil_k = k_range[np.argmax(silhouettes)] # Консенсус: найближче k з двох методів optimal_k = (elbow_k + best_sil_k) // 2 print(f"Elbow method: k={elbow_k}, Silhouette: k={best_sil_k}, Chosen: k={optimal_k}") return optimal_k def fit(self, X: np.ndarray, k: int = None): if self.scaler: X_scaled = self.scaler.fit_transform(X) else: X_scaled = X if k is None: k = self.find_optimal_k(X_scaled) self.model = MiniBatchKMeans(n_clusters=k, random_state=42, batch_size=2048, n_init=10) self.labels = self.model.fit_predict(X_scaled) return self def evaluate(self, X: np.ndarray) -> dict: X_scaled = self.scaler.transform(X) if self.scaler else X return { 'silhouette': silhouette_score(X_scaled, self.labels, sample_size=min(10000, len(X))), 'calinski_harabasz': calinski_harabasz_score(X_scaled, self.labels), 'n_clusters': len(np.unique(self.labels)), 'cluster_sizes': dict(zip(*np.unique(self.labels, return_counts=True))) } 

Ми використовуємо MiniBatchKMeans з batch_size=2048 для прискорення на великих даних. Приклад: на 500 000 записів пайплайн знаходить оптимальне k за 2 хвилини.

Кейс: сегментація клієнтів рітейлу

Нещодавно ми кластеризували клієнтську базу великого рітейлера (2 млн записів). Використовували MiniBatchKMeans з консенсусним k=7. Отримали silhouette 0.42 — на 0.15 вище, ніж попереднє рішення. Кластери виявилися однорідними за частотою покупок, середнім чеком та категоріями. Бізнес використав сегменти для персоналізації розсилок — конверсія зросла на 18%. Інженер замовника зазначив: "Кластеризація допомогла виділити 7 сегментів, які раніше були неочевидні, і це безпосередньо вплинуло на конверсію."

HDBSCAN для текстових даних

import hdbscan from sentence_transformers import SentenceTransformer def cluster_documents(texts: list[str], min_cluster_size: int = 10) -> list[int]: # Ембеддінги model = SentenceTransformer('all-MiniLM-L6-v2') embeddings = model.encode(texts, batch_size=256, show_progress_bar=True) # Зниження розмірності перед кластеризацією from umap import UMAP umap_model = UMAP(n_components=10, random_state=42, metric='cosine') reduced = umap_model.fit_transform(embeddings) # HDBSCAN clusterer = hdbscan.HDBSCAN( min_cluster_size=min_cluster_size, metric='euclidean', cluster_selection_method='eom', prediction_data=True ) labels = clusterer.fit_predict(reduced) # -1 = noise/outlier print(f"Found {len(np.unique(labels[labels >= 0]))} clusters") print(f"Noise points: {(labels == -1).sum()}") return labels 

У проекті з кластеризації техпідтримки ми обробили 100 000 тікетів. HDBSCAN виділив 15 тематичних кластерів та 12% шуму (аномальні запити). Time-to-insight скоротився з тижня до години.

Процес роботи: від аналітики до деплою

Наш процес включає етапи:

  1. Аналітика: визначення мети — що кластеризувати (клієнти, документи, транзакції) та як інтерпретувати.
  2. Проектування: вибір алгоритму, метрик, пайплайну.
  3. Розробка: код, оптимізація гіперпараметрів (k, min_cluster_size, метрика відстані).
  4. Валідація: обчислення silhouette, Calinski-Harabasz, візуалізація (t-SNE, UMAP), бізнес-перевірка.
  5. Деплой: інтеграція в продуктивне середовище, моніторинг дрейфу кластерів.

Терміни — від 2 до 4 тижнів залежно від складності даних.

Як інтерпретувати кластери?

def describe_clusters(X_df: pd.DataFrame, labels: np.ndarray) -> dict: """Автоматичний опис кожного кластера""" cluster_descriptions = {} for cluster_id in np.unique(labels): if cluster_id == -1: continue mask = labels == cluster_id cluster_df = X_df[mask] # Центроїд кластера в просторі ознак centroid = cluster_df.mean() # Найбільш відмінні ознаки (вище/нижче середнього) overall_mean = X_df.mean() diff = (centroid - overall_mean) / X_df.std() top_features = diff.abs().nlargest(5).index.tolist() cluster_descriptions[cluster_id] = { 'size': mask.sum(), 'size_pct': mask.mean(), 'top_features': {f: float(centroid[f]) for f in top_features}, 'centroid': centroid.to_dict() } return cluster_descriptions 

Хороша кластеризація має силуетний коефіцієнт >0.3, бізнес-інтерпретовані кластери та стабільність при повторних запусках (Jaccard similarity >0.8 між запусками).

Що входить в роботу

  • Документація: звіт з вибором алгоритму, описом кластерів, візуалізаціями.
  • Код пайплайну: чистий, задокументований, з README.
  • Дашборд моніторингу: графік силуету, стабільності кластерів.
  • Навчання команди: 1-2 сесії з інтерпретації та використання моделі.
  • Пост-релізна підтримка: 2 тижні консультацій.

Отримайте консультацію по вашому проекту — ми підберемо оптимальний алгоритм та налаштуємо пайплайн під ваші дані. Замовте навчання моделі кластеризації з гарантією якості.

Детальніше про кластерний аналіз.