Обычная кластеризация — K-Means, DBSCAN — живёт в статике: собрал данные, обучил, заморозил. В streaming-пайплайнах с редкими категориями (аномалии в IoT, новые сегменты пользователей, нетипичные события) это разваливается. Появляется новый редкий кластер — модель либо относит его к шуму, либо требует полного переобучения. Оба варианта плохи, если данных много, а кластеров мало и они возникают редко. Хранить всю историю нельзя, а моделировать динамику кластеров без дампа — ключевой trade-off.
Адаптивное обновление центроидов
Решение в экспоненциальном сглаживании с контролем дрейфа. Храним центроиды C₁...Cₖ и счётчики n₁...nₖ (эффективная память с decay γ). Когда приходит батч точек для каждой ищем ближайший центроид. Если расстояние меньше порога (например 0.3) обновляем: Cᵢ_new = (nᵢ * Cᵢ + β * x) / (nᵢ + β), где β — learn rate (0.1-0.3). Если расстояние больше порога и точка не шум — создаём новый центроид с малой начальной памятью (
n = β), которая укрепляется при повторном подтверждении.Динамический порог и подавление шума
Чтобы не переобучаться на шум: каждые T батчей применяем decay — nᵢ умножаем на 0.95. Порог создания кластера делаем динамическим: среднее k-е расстояние плюс 2 сигмы. На практике это даёт раннее обнаружение редких событий: например в мониторинге аномалий, где аномалия — редкий стабильный кластер, а не выброс.
def update_centroids(X_batch, centroids, counts, threshold=0.3, beta=0.2, gamma=0.99):
for x in X_batch:
distances = np.linalg.norm(centroids - x, axis=1)
min_dist_idx = np.argmin(distances)
if distances[min_dist_idx] < threshold:
counts[min_dist_idx] *= gamma + beta
centroids[min_dist_idx] += beta * (x - centroids[min_dist_idx]) / counts[min_dist_idx]
else:
centroids = np.vstack([centroids, x])
counts = np.append(counts, beta)
Ошибка: игнорировать γ как гиперпараметр
Типичная ошибка — фиксировать γ наугад. Быстрое забывание (γ=0.9) убивает редкие паттерны: кластер исчезает после одного батча без подтверждения. Медленное забывание (γ=0.999) сохраняет шумовые центры навсегда. Настраивайте γ под частоту появления кластеров: для редких (раз в 100 батчей) γ >= 0.995, для частых — 0.95-0.98. Тестируйте на синтетических stream-данных, где вы точно знаете, когда и какой кластер появляется.
Подход близок к BIRCH и incremental clustering (Frigui & Krishnapuram, 1996; Charikar et al., 1997), но с динамическим порогом и явным контролем памяти. Плюсы: не надо хранить все данные; редкие кластеры обнаруживаются без задержки; шум не ломает центры.
Вывод: Адаптивное обновление центроидов с gamma-decay и динамическим порогом решает задачу обнаружения редких кластеров в streaming-пайплайнах без переобучения, но требует калибровки gamma под частоту событий и тестирования на синтетических данных с контролируемым дрейфом.