Ієрархічна кластеризація та DBSCAN

k-means питає, де стоять центри. Ці два методи питають інше: де густо — і що з чим зливається раніше. Відповіді виходять зовсім не ті.

Попередня тема закінчилась трьома картинками, на яких відповідь була очевидно неправильною. Дві довгі смуги, які k-means розрізав упоперек, — 58 % точок на своїх місцях. Коло всередині кільця — 60 %. Щільна купка поруч із розсіяною хмарою — 89 %. Людське око бачить правильну відповідь за півсекунди, а метод її не бачить узагалі.

Причина не в налаштуваннях. Вона в самій будові k-means: кожна точка йде до найближчого центра, тому межа між кластерами — завжди пряма, а кластер — завжди приблизно кругла грудка. Плюс друга незручність: кількість груп k доводиться назвати наперед, до того як ти щось про дані дізнався.

Дві відповідіДві відповіді на одну невдачу

Обидві проблеми мають розвʼязок, і розвʼязки ці зовсім різні.

DBSCAN викидає з опису поняття центра. Замість «де середина групи» він питає «де точки стоять густо» — і нарощує кластер від сусіда до сусіда, куди б той не тягнувся. Смуга, кільце, підкова: якщо ланцюжок сусідів веде туди, кластер піде туди. Заразом він уміє те, чого k-means не вміє принципово, — сказати про окрему точку «це не кластер, це шум».

Ієрархічна кластеризація відповідає на другу проблему. Вона не видає одне розбиття. Вона будує дерево: спочатку кожен обʼєкт сам по собі, далі найближча пара зливається, далі наступна — аж поки все не стане однією групою. Кількість груп обираєш ти, але після побудови, дивлячись на дерево, а не наосліп до неї.

Тут легко зробити хибний висновок: мовляв, це два способи полагодити те саме, і треба вибрати кращий. Це не так. Вони лагодять різні поломки й відповідають на різні питання — тому й не заміняють один одного.

методпитання, на яке він відповідаєщо на виході
k-meansде стоять k центрів?рівно k груп, кожна точка обовʼязково в якійсь
DBSCANде точки стоять густо?групи довільної форми — скільки вийде — плюс окрема мітка «шум»
Ієрархічнащо з чим зливається раніше?дерево злиттів; будь-яке розбиття дістається з нього зрізом

Звідси й правило вибору, до якого ми повернемось у розділі 12 вже з числами на руках. Ієрархічну беруть, коли не знаєш, скільки груп шукати: вона показує всі варіанти одразу, і кількість обираєш по дереву, а не наосліп. DBSCAN беруть, коли не знаєш, якої вони форми — або коли окремі дивні обʼєкти цікавлять тебе не менше за самі групи. Питання «скільки?» і питання «якої форми?» незалежні одне від одного, тож буває, що в задачі потрібні обидва методи: дерево — щоб прикинути кількість, щільність — щоб знайти форму й викиди.

І одразу про ціну, щоб вона не стала сюрпризом наприкінці. Ієрархічна кластеризація тримає в памʼяті всі попарні відстані, тому вона метод для тисяч рядків, а не для мільйонів; DBSCAN такої межі не має. Точні числа — у розділі 10.

Працюємо на тих самих даних, що й у минулій темі. Наскрізний приклад — дошка оголошень про вживані телефони з теми 08: 1 100 оголошень із відомою ціною, дві ознаки — логарифм ціни й рік випуску, обидві приведені до z-оцінок. Плюс три синтетичні набори, на яких k-means провалився: витягнуті смуги, вкладені кільця, різна щільність. Ті самі точки, що й у темі 26, — щоб порівняння було чесним.

Масштаб і тут обовʼязковий. DBSCAN міряє відстані радіусом, ієрархічна — відстанями між групами. Обидва методи мовчки віддадуть усю владу тій колонці, у якої випадково найбільші числа. Правило з розділу 7 попередньої теми діє без винятків.

ЩільністьКластер — це там, де густо

Уся ідея DBSCAN (density-based spatial clustering of applications with noise — кластеризація за щільністю з урахуванням шуму) тримається на одному спостереженні: усередині кластера точки стоять близько одна до одної, а між кластерами — порожньо. Не «кругло», не «навколо центра» — просто густо.

Щоб перетворити це на алгоритм, потрібні два числа, і задаєш їх ти.

Перші граблі, і вони тихі. У scikit-learn min_samples рахує разом із самою точкою. Тобто min_samples=3 означає «сама точка плюс щонайменше двоє сусідів», а не троє сусідів. Помилка на одиницю тут не викине винятку — вона просто змістить межу щільності.

Далі кожна точка отримує одну з трьох ролей, і ці три слова — весь словник методу.

рольумоващо це означає
Ядерна (core)у радіусі eps навколо неї — щонайменше min_samples точоквона стоїть у густому місці й має право нарощувати кластер далі
Гранична (border)сусідів замало, але вона сама лежить у радіусі якоїсь ядерноївона в кластері, але з неї кластер далі не росте — це край
Шум (noise)ні те, ні тежодного кластера; алгоритм чесно каже «ця точка ні з ким»

Візьмімо ті самі десять оголошень, на яких у минулій темі крутились ітерації k-means, і роздамо їм ролі. Радіус eps = 1.2 (нагадаю: одиниця тут — одне стандартне відхилення), min_samples = 3.

Три ролі точки

Десять оголошень із розділу 3 попередньої теми. Навколо кожної точки — коло радіуса 1.2; підраховуємо, скільки точок у нього потрапило разом із нею самою.

оголошенняz (ціна, рік)точок у радіусі 1.2ролькластер
A1−0.5, 1.13ядерна1
A2−1.0, −1.12гранична2
A3−2.5, −1.52гранична2
A40.1, 1.54ядерна1
A50.6, 1.56ядерна1
A61.1, 0.74ядерна1
A71.0, 1.55ядерна1
A81.7, 1.54ядерна1
A90.4, −1.51—шум
A10−2.0, −0.63ядерна2
Сім ядерних, дві граничні, один шум. A2 і A3 самі по собі стоять рідко — у їхніх колах лише по дві точки. Але обидві потрапили в коло ядерної A10 (відстані 1.12 і 1.03), тому їх приймають у другий кластер як край. У A9 в радіусі немає взагалі нікого: найближче до нього A2 на відстані 1.46. Це той самий Gamma X Ultra 2017 року, у якого в темі 26 силует був 0.205 — k-means мусив кудись його віднести, а DBSCAN просто каже «ні з ким».

АлгоритмЯк розростається кластер

Ролі роздано — далі алгоритм майже тривіальний. Він обходить точки одну за одною:

1. беремо чергову ще не розглянуту точку 2. якщо вона не ядерна — поки що позначаємо як шум і йдемо далі 3. якщо ядерна — відкриваємо новий кластер і кладемо в чергу її сусідів 4. дістаємо точку з черги, приписуємо до цього кластера; якщо вона теж ядерна — дописуємо в чергу і її сусідів 5. черга спорожніла — кластер закінчився, повертаємось до кроку 1

Прокрутімо це на наших десяти. Перша точка — A1, вона ядерна, відкриваємо кластер 1 і кладемо в чергу її сусідів A4 і A5. Дістаємо A4 — вона ядерна, тож її сусіди теж ідуть у чергу: A5 і A7 (сама A1 там уже була). Дістаємо A5 — ядерна, дописує A6, A7, A8. Черга крутиться, поки не вичерпається, і кластер 1 виходить такий: A1, A4, A5, A6, A7, A8. Далі беремо A2 — не ядерна, позначаємо шумом тимчасово. A3 — те саме. A9 — теж не ядерна. Аж ось A10: ядерна, відкриваємо кластер 2, у чергу йдуть A2 і A3 — обидві не ядерні, тому нікого далі не тягнуть, і кластер 2 = A10, A2, A3. A9 так і лишається шумом.

Як росте кластер

Ті самі десять оголошень, eps = 1.2, min_samples = 3. Тягни повзунок і дивись на чергу: саме вона й веде кластер від сусіда до сусіда.

кластерів—
у черзі—
розподілено—
поки шум—
Кластер росте не з центра, а з черги. Дванадцять кроків — і жодного разу алгоритм не питає, де середина групи: він лише дістає точку з черги й дивиться, чи достатньо в неї сусідів, щоб рости далі. На кроках 7–9 A2, A3 і A9 стають шумом — і це рішення тимчасове: на кроках 11–12 A2 і A3 повертаються в кластер 2 як граничні, бо потрапили в коло ядерної A10. A9 не повертається нізвідки, бо в її колі немає нікого. Саме через цей порядок «спершу шум, потім, може, забрали» гранична точка й дістається тому кластеру, чия черга дійшла до неї першою. І одна дрібниця, яку легко прийняти за помилку: у черзі те саме імʼя інколи стоїть двічі — точку туди поклали дві різні ядерні сусідки. Коли черга дійде до неї вдруге, її просто пропустять: мітка вже стоїть.

Зверни увагу на два наслідки, які видно вже на десяти точках.

Перший: кластер може мати будь-яку форму. Він росте ланцюжком, від сусіда до сусіда, і жодного «центра» ніхто не рахує. Довга змія завтовшки в два кроки — законний кластер DBSCAN і неможливий кластер k-means.

Другий, дрібніший і неприємний: гранична точка може дістатись різним кластерам. Якби A2 лежала в колах двох різних ядерних точок із різних кластерів, її забрав би той, до кого черга дійшла першою, — тобто результат залежав би від порядку рядків у таблиці. Ядерних точок це не стосується: вони розподіляються однозначно. На практиці граничних точок мало, але якщо два запуски на перетасованих даних дали трохи різні мітки — причина зазвичай саме тут.

ПараметриДва повзунки, дві катастрофи

Тепер найважливіше про DBSCAN: усе тримається на двох числах. Кількість кластерів він і справді не питає — але eps і min_samples задають її неявно, і крайні значення дають дві протилежні катастрофи.

Візьмімо ті самі дві витягнуті смуги, які завалили k-means, і додамо до них пʼятнадцять розкиданих поодиноких оголошень — щоб було на що подивитись у графі «шум». Разом 145 точок: 130 у двох смугах і 15 самих по собі.

eps і min_samples

Тягни обидва повзунки. Заповнені кружечки — ядерні точки, порожні — граничні, сірі хрестики — шум. Колір = кластер.

кластерів—
шум—
ядерних—
частка шуму—
Що шукати. При eps = 0.45 і min_samples = 5 DBSCAN знаходить рівно дві смуги й оголошує шумом рівно ті 15 точок, які ми туди й підкинули. Зменш радіус до 0.15 при min_samples = 10 — і густих місць не лишиться взагалі: усі 145 точок стануть шумом. Збільш до 1.30 — і смуги зіллються в один суцільний кластер. Між цими краями є вузька смужка правильних відповідей, і ширина цієї смужки — головна практична проблема методу.

Обидва параметри тягнуть в один бік, але по-різному. eps вирішує, які точки взагалі бачать одна одну; min_samples — наскільки густо має бути, щоб місце вважалось кластером. Більший min_samples при тому самому радіусі означає більше шуму й менше кластерів.

Типова порада для min_samples: бери щонайменше подвоєну кількість ознак. У нас ознак дві, отже 4–5 — і саме тому в усіх прикладах цієї теми стоїть пʼятірка. Порада груба, але вона хоч якось привʼязує число до даних, і за нею стоїть проста думка: що більше вимірів, то легше кільком точкам випадково опинитись поруч, тож поріг «густо» доводиться піднімати.

І ще один наслідок, який зазвичай не називають, а він корисний. Будь-який кластер DBSCAN містить щонайменше min_samples точок, і це видно з означення: щоб кластер узагалі відкрився, потрібна ядерна точка, а в її колі за означенням уже стоїть min_samples точок — і всі вони одразу дістають її мітку. Тобто min_samples — це заразом і мінімальний розмір групи, яку метод згоден визнати групою. Якщо в задачі відомо, що менш ніж вісім оголошень сегментом не вважаються, це вже готове значення параметра.

Вибір epsЗвідки брати eps

Це головне практичне питання всього методу: без відповіді на нього DBSCAN не запустиш. З min_samples ще можна жити за грубою порадою, а eps — відстань, і «правильної» відстані ніхто наперед не знає: вона залежить від даних, від масштабування, від кількості ознак. Порада «підбери на око» тут не відповідь, тож нижче два робочі способи — і спосіб перевірити результат, не підглядаючи у правильну відповідь, якої в реальній задачі немає.

Спосіб перший — графік k-ї відстані. Він дає перше наближення з самих даних:

1. для кожної точки знайти відстань до її k-го найближчого сусіда 2. відсортувати всі ці відстані за зростанням 3. намалювати криву й шукати на ній «коліно» — місце, де вона з пологої різко йде вгору

Яке брати k? Те, що вже стоїть у min_samples, мінус один — і ось чому. Точка стає ядерною, коли в її колі опиняється min_samples точок разом із нею самою (розділ 2). Отже сусідів має набратись min_samples − 1, і найдальший із них — це min_samples − 1-й найближчий сусід. Відстань до нього і є той радіус, за якого ця точка щойно стає ядерною. У нас min_samples = 5, тому k = 4, і на графіку скрізь стоїть «четвертий сусід».

Рахується це пʼятьма рядками:

from sklearn.neighbors import NearestNeighbors nn = NearestNeighbors(n_neighbors=5).fit(X) # 5 = сама точка + 4 сусіди d, _ = nn.kneighbors(X) # d[:, 4] - відстань до 4-го сусіда kdist = np.sort(d[:, 4]) # відсортували за зростанням plt.plot(kdist) # і дивимось, де злам

Сенс кривої простий. Поки вона йде полого, ми рухаємось по точках, які сидять у густих місцях: до четвертого сусіда їм рукою подати. Коли крива задирається вгору — почались точки, у яких сусідів поблизу немає. Висота коліна і є та відстань, яка ще вважається «близько».

Відстань до четвертого сусіда, відсортована

Ліворуч — наші 145 точок зі смугами. Праворуч — 1 100 справжніх оголошень із дошки.

Ліворуч коліно є, праворуч його немає. На синтетичних смугах крива тримається нижче 0.5 для 129 точок і різко злітає на останніх шістнадцяти — це наші підкинуті одинаки плюс кілька крайніх точок смуг. Беремо eps з коліна — 0.45–0.50 — і отримуємо ідеальний розподіл. На дошці оголошень крива піднімається плавно: у половини оголошень четвертий сусід ближче ніж за 0.04, у 95 % — ближче ніж за 0.23, а далі відстань росте без жодного зламу. Це такий самий нечіткий критерій, як лікоть у k-means: він окреслює розумний діапазон і не називає числа.

Спосіб другий — те, що робити, коли коліна немає. А немає його частіше, ніж є: на правій половині схеми крива піднімається без жодного зламу. Тоді працює інший, чесніший прийом: прогнати eps по сітці й подивитись, де відповідь перестає стрибати. Ось та сама розгортка на наших 145 точках із двома смугами, min_samples = 5:

epsкластеріву шумічастка шуму
0.20124631.7 %
0.3092718.6 %
0.4051510.3 %
0.4521510.3 %
0.5021510.3 %
0.5521510.3 %
0.602117.6 %
0.701106.9 %
1.00174.8 %

Дивитись треба на полицю — проміжок, де відповідь не міняється. Тут вона одна й помітна: від 0.45 до 0.55 виходить рівно два кластери й рівно 15 шумових точок, тобто три різні радіуси дають ту саму відповідь. Це й є ознака, що поділ справжній, а не намальований радіусом: справжня структура витримує зсув параметра, вигадана — ні. При 0.60 кластерів усе ще два, але в шум уже потрапило 11 точок замість 15 — чотири одинаки прилипли до смуг, тобто край полиці почався. А коліно на схемі показало 0.45–0.50: два незалежні способи привели в те саме місце, і це найкраще підтвердження, яке взагалі можна отримати без розмітки.

Далі три числа, за якими відповідь перевіряють на здоровий глузд:

eps живе в одиницях ознак. Це не частка й не відсоток, а відстань у тому просторі, у який ти привів дані. Перемасштабував інакше — усі числа цього розділу протухли, і підбір починається спочатку. Тому eps, знайдений на z-оцінках, безглуздо переносити на ті самі дані в гривнях і роках, і саме тому масштабування тут не порада, а умова.

ШумШум — це відповідь

Тепер про властивість, якої немає в жодного методу з попередньої теми. DBSCAN не зобовʼязаний віднести кожну точку до кластера. У scikit-learn такі точки дістають мітку −1.

Звучить як вада — «алгоритм не впорався». Насправді це чесність. Коли k-means приписує дике оголошення до найближчого сегмента, він не додає знання, а ховає його: у групі «дешеві старі» тепер сидить телефон за 130 грн, який не схожий узагалі ні на що.

Подивись, що з цього виходить на справжніх даних. Запускаємо DBSCAN на всіх 1 100 оголошеннях дошки з eps = 0.3 і min_samples = 5. Результат такий: 12 кластерів і 26 оголошень у шумі. І ось найцікавіше число всієї теми: із цих 26 оголошень 25 позначені в таблиці як шахрайські. Алгоритм цієї колонки не бачив.

що вважаємо підозрілимточністьповнотаF1
шум DBSCAN (26 оголошень)0.9620.1800.303
найдешевший кластер k-means (тема 26)0.2060.5250.296
випадковий тик, 355 оголошень0.1260.3230.182

Читається це так. Шум DBSCAN майже не помиляється: з 26 піднятих тривог хибна одна. Але й ловить він мало — 25 шахраїв зі 139, тобто 18 %. Це ціни-приманки на кшталт 130 і 190 грн і завищені 45 320 та 83 500 грн: те, що стоїть на самому краю. Звичайного шахрая з ціною «трохи нижче ринку» жодна щільність не виловить, бо він лежить у гущі.

Це і є місток до наступної теми. Задача «знайди дивне, не маючи жодного прикладу дивного» зветься пошуком аномалій, і шум DBSCAN — її найпростіший інструмент. У темі 28 будуть методи, які роблять це прицільно й уміють ранжувати підозрілість числом, а не лише ставити мітку −1.
А тепер чесно про ті 12 кластерів. Це не 12 сегментів ринку. Рік випуску — ціле число, і в z-оцінках сусідні роки стоять на відстані 0.43. Поки eps менший за 0.43, вертикальних звʼязків між роками просто немає, і дошка розпадається на горизонтальні смуги «всі оголошення 2019-го», «всі оголошення 2020-го». Варто підняти eps до 0.5 — і 1 089 оголошень зливаються в один суцільний кластер. Ніякої проміжної відповіді на цих даних немає: або смуги за роками, або одна хмара. Це не поламаний DBSCAN — це чесна відповідь про дошку: щільнісно відокремлених груп на ній немає.

ВадаОдна щільність на всіх

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

Це рівно той третій випадок із минулої теми — щільна купка й розсіяна хмара поруч, де k-means дав 89 %. З параметрами, які ідеально спрацювали на смугах і кільцях (eps = 0.6, min_samples = 5), DBSCAN дає на ньому 70 %: щільну купку знаходить цілком — усі 95 її точок в одному кластері, разом із пʼятьма найближчими точками хмари, — а саму хмару рве на уламки й 30 точок зі 150 оголошує шумом.

Можна підібрати eps під розсіяну хмару — 0.85. Тоді кластерів рівно два, шуму лише 9 точок, а на своїх місцях 88 %. Це найкраще, що DBSCAN взагалі може видати на цих даних, — і воно все одно трохи гірше за k-means. Причому дійти до цього числа можна, тільки підглядаючи у правильну відповідь, якої в реальній задачі немає.

Один радіус на дві щільності

Ті самі 150 оголошень: 95 у щільній купці ліворуч, 55 у розсіяній хмарі праворуч. Однакові дані, однаковий min_samples = 5, різний eps.

Радіус, за якого хмара стає групою, уже склеює частину хмари з купкою. Ліворуч eps = 0.6: навколо точки в серці купки в таке коло потрапляє 89 точок, а навколо точки в серці хмари — 4, тобто хмара для алгоритму просто порожнеча. Праворуч eps = 0.85 хмару нарешті видно, але й купка розповзлась: у більшій групі вже 104 точки замість 95. Проміжного радіуса, за якого обидві групи знайдено точно, на цих даних не існує — і саме це, а не помилка в налаштуваннях, називають головною вадою DBSCAN.

Причину видно двома числами. Медіанна відстань до четвертого сусіда в щільній купці — 0.102, у розсіяній хмарі — 0.773: густина відрізняється всемеро з половиною. Тому коло одного й того самого радіуса 0.6 накриває навколо половини точок купки понад вісімдесят сусідів, а навколо половини точок хмари — не більш як три, разом із нею самою. При min_samples = 5 перша ядерна із запасом, друга — шум. Один радіус фізично не може означати «густо» і тут, і там: те, що для купки просторо, для хмари вже космос.

Як помітити це на своїх даних, де правильної відповіді немає. Ознака така: один кластер забрав майже все, решта — крихти, і при цьому шуму багато. Тут при eps = 0.6 вийшли групи 100, 10, 5 і 5 та ще 30 точок у шумі — це не «чотири сегменти», а одна знайдена група й уламки другої. Друга ознака — з розділу 5: якщо полиці немає, тобто відповідь стрибає з «усе розсипалось» одразу в «усе злиплось», оминаючи проміжний стан, — щільності в даних різні, і DBSCAN на них не інструмент. Саме так поводилась дошка оголошень: або смуги за роками, або одна хмара.

Розширення, яке це лагодить. HDBSCAN (hierarchical DBSCAN) не бере eps взагалі. Він проганяє DBSCAN подумки одразу для всіх радіусів, будує з цього дерево — так, те саме дерево, що й у другій половині цієї теми, — і залишає ті кластери, які тримаються найдовше. Через це він здатен знаходити групи різної щільності однією моделлю. Чарівної палички з нього теж не виходить. На тому самому наборі «різна щільність» зі 150 точок, на якому спіткнувся DBSCAN, HDBSCAN дає збіг зі справжніми групами 0.78 за скоригованим індексом Ренда — проти 0.90 у DBSCAN із найвдалішим eps. І параметр у нього свій: при min_cluster_size 10 виходить 0.78, при 15 — уже нуль, бо метод оголошує шумом усе. Тобто eps не зник, а перетворився на іншу ручку, яку так само доводиться крутити руками. Ціна — складніша реалізація й окремий пакет (hdbscan, а у свіжих версіях scikit-learn — HDBSCAN). Ідею варто знати; коли eps ніяк не підбирається, це перше, що слід спробувати.

ДеревоДерево замість розбиття

Другий метод теми починається з іншого питання. Не «де густо», а «що з чим схоже найбільше».

Уся ідея. Спочатку кожне оголошення — окремий кластер, їх рівно стільки, скільки рядків. Знаходимо дві найближчі групи й зливаємо їх в одну. Знову знаходимо найближчі та зливаємо. І так n − 1 разів, поки все не стане однією групою. Записуємо, хто з ким і на якій відстані злився, — це і є результат.

Такий напрямок — від окремих точок догори — зветься агломеративним (agglomerative, від «нагромаджувати»). Буває й зворотний, дивізивний: почати з однієї групи й ділити її. На практиці майже завжди беруть агломеративний, бо він простіший і передбачуваний.

Результат — не набір міток, а дендрограма (dendrogram, «деревна діаграма»). Читається вона так:

А тепер найкорисніше. Проведи горизонтальну лінію на будь-якій висоті — і кожна вертикаль, яку вона перетне, стане окремою групою. Одне дерево, і скільки завгодно розбиттів. Кількість груп обирається після побудови, і обирається очима.

Дендрограма з рухомим зрізом

Двадцять вісім оголошень із дошки, злиття за середньою відстанню. Тягни повзунок — і дивись, скільки груп дає зріз і як вони лягають на площину.

груп—
найбільша—
одинаків—
наст. злиття—
Осі. На дендрограмі знизу — 28 оголошень, по вертикалі — відстань злиття в z-оцінках. На правій картинці — ті самі оголошення: по горизонталі ціна (логарифмічна шкала), по вертикалі рік випуску. Найвище злиття тут на висоті 2.81 — остання, вимушена перемичка: воно зʼєднує девʼять свіжих оголошень 2023–2024 років з усіма іншими девʼятнадцятьма. Опустись до 1.30 — і дерево дає пʼять груп: свіжі діляться на дешевші й дорогі, старі — на три цінові смуги. Довга вертикаль без перемичок означає групу, яка довго ні з ким не зливалась, тобто добре відокремлену.

LinkageЧим міряти відстань між групами

В описі методу є одне слово, повз яке легко проскочити: «знаходимо дві найближчі групи». А що таке відстань між групами? Між двома точками вона очевидна. Між купкою з семи точок і купкою з дванадцяти — ні.

Відповідей чотири, вони звуться звʼязком (linkage), і вибір між ними міняє результат сильніше, ніж будь-що інше в методі.

звʼязоквідстань між групами — це…що з цього виходить
Одиночний (single)відстань між двома найближчими точками групгрупи ростуть ланцюжками; знаходить змії й кільця, але злипається через один випадковий місток
Повний (complete)відстань між двома найдальшими точкамигрупи виходять компактні, приблизно кулясті; довгу смугу розріже
Середній (average)середнє з усіх попарних відстаней між групамикомпроміс між першими двома, найчастіше — розумна поведінка за замовчуванням
Ворда (ward)наскільки зросте сума квадратів відстаней усередині груп, якщо їх злитимінімізує рівно те, що й інерція k-means; кулясті групи схожого розміру

Таблиця називає поведінку, але не пояснює, звідки вона береться. А береться вона цілком із того, яку саме пару чисел звʼязок дивиться.

Чому одиночний тягнеться ланцюжком. Він міряє відстань між групами однією парою точок — найближчою, і решта про групи його не обходить: ані розмір, ані форма, ані те, як далеко решта точок. Тому дві купки, між якими лежить доріжка з проміжних точок, зливаються покроково, ланка за ланкою, і кожен крок однаково дешевий. Девʼяти чисел на прямій досить, щоб це побачити:

точки: 0 1 2 4 6 8 10 11 12 └ купка ┘ └ доріжка ┘ └ купка ┘ одиночний: чотири злиття на висоті 1, чотири на висоті 2. зріз 1.5 → 5 груп, зріз 2.0 → 1 група. двох груп у цьому дереві немає взагалі Ворда: {0 1 2 4 6 8} і {10 11 12} — поділ є

Порахуй руками. Усередині купок сусіди стоять на відстані 1, уздовж доріжки — на відстані 2, і перехід від купки до доріжки — теж 2. Для одиночного звʼязку всі ці злиття однаково дешеві, тому він проходить усю пряму рівним кроком і жодного розриву не бачить. Попроси в нього дві групи — і він не зможе їх дати: усі чотири останні злиття стоять на тій самій висоті 2, тож зріз трохи нижче дає пʼять груп, а зріз трохи вище — одразу одну. Розбиття на дві в цьому дереві просто не існує. Це і є ланцюжковий ефект — одна доріжка з випадкових точок склеює те, що око розділяє миттєво. Він же й дає одиночному звʼязку його силу: рівно так само він проходить уздовж витягнутої смуги чи кільця, куди інші три не підуть.

Чому Ворд збирає кулі. Він міряє не відстань, а шкоду: наскільки зросте розкид усередині груп, якщо їх злити. Формула одна:

Δ = nA·nB / (nA + nB) × d2

Словами: ціна злиття — це квадрат відстані між центрами двох груп, помножений на множник, який росте разом із їхніми розмірами. Тепер кожен символ окремо. nA і nB — скільки точок у першій і в другій групі. d — відстань між їхніми центрами; центр групи — це просто середнє її точок по кожній ознаці. d2 — та сама відстань, помножена сама на себе. Δ — велика грецька літера «дельта», якою в математиці позначають приріст; тут це приріст суми квадратів відстаней від точок до центра своєї групи. На кожному кроці Ворд зливає ту пару, у якої Δ найменша.

Порахуймо на шести числах. Нехай група P — це 6 точок із центром у (0, 0), група Q — 6 точок із центром у (3, 0), а між ними одна самотня точка R у (1.5, 0), рівно посередині:

злити R і P: (1·6)/(1+6) × 1.5² = 0.857 × 2.25 = 1.93 злити R і Q: (1·6)/(1+6) × 1.5² = 0.857 × 2.25 = 1.93 злити P і Q: (6·6)/(6+6) × 3.0² = 3.000 × 9.00 = 27.00

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

Повний і середній — між цими двома крайнощами. Повний звʼязок дивиться на найдальшу пару, тобто питає «яким стане діаметр групи, якщо злити»: група приймає нового члена, лише поки лишається щільною. Звідси компактність — і звідси ж чутливість до одного далекого викиду, який сам собою роздуває діаметр. Середній усереднює всі попарні відстані, тому жодна окрема пара — ані найближча, ані найдальша — його не зрушить. Саме тому середній і став розумним вибором за замовчуванням там, де немає причин узяти щось інше.

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

одиночний 1 094 · 5 · 1 ← один велетень і дві порошинки середній 1 089 · 8 · 3 ← те саме повний 656 · 433 · 11 Ворда 439 · 360 · 301 ← єдине розбиття, з яким можна працювати

А тепер те саме на синтетиці, де правильна відповідь відома, — і виявиться, що переможець міняється місцями з невдахою.

Чотири звʼязки, два набори даних

Угорі — дві витягнуті смуги без підкинутих одинаків, рівно 130 точок по 65 у кожній смузі; унизу — щільна купка (95) і розсіяна хмара (55). Обидва рази ріжемо дерево на дві групи.

Дзеркальний результат. На смугах одиночний звʼязок дає ідеальні 65 і 65 точок — рівно дві смуги, 100 %. Решта три ріжуть картинку впоперек, як k-means. На різній щільності все навпаки: одиночний і середній відкушують від хмари лише по дві точки, повний — шість, і всі троє лишають велетня на 144–148, тобто не дають нічого (64.7 %, 67.3 %, 64.7 %), а Ворд знаходить обидві групи майже точно — 96 %. Один і той самий звʼязок виграє в одному наборі й програє в іншому, і вибирати доводиться до того, як побачиш відповідь.

ЦінаСкільки це коштує

За зручність «одразу вся ієрархія» доводиться платити, і платня росте квадратично. Щоб знайти найближчу пару, треба знати всі попарні відстані — а їх для n обʼєктів рівно n·(n − 1)/2, тобто приблизно n²/2. Кожна відстань — дробове число, яке numpy зберігає у восьми байтах (тип float64). Далі просто множення:

n відстаней × 8 байтів 1 000 500 тисяч 4 МБ 10 000 50 мільйонів 400 МБ 100 000 5 мільярдів 40 ГБ ← кінець 1 000 000 500 мільярдів 4 ТБ ← навіть не смішно

І це нижня оцінка, бо в ній лише сама матриця. Заміряно на одній машині: AgglomerativeClustering зі звʼязком Ворда на 20 000 точках забрав 3 055 МБ, тоді як гола матриця відстаней для 20 000 точок важить 1 526 МБ. Тобто реальний апетит удвічі більший за формулу — бібліотека тримає ще одну копію, поки зливає групи.

З часом та сама історія. Наївний пошук найближчої пари на кожному з n − 1 кроків дає n³ операцій; сучасні реалізації (алгоритм ланцюжка найближчих сусідів, яким рахують Ворда, повний і середній звʼязок) знімають один множник і працюють за n². Квадрат нікуди не дівається: матрицю все одно треба хоч раз прочитати цілком.

рядківієрархічна (Ворд)DBSCANрізниця
2 0000.08 с0.009 с×9
4 0000.43 с0.020 с×21
8 0001.65 с0.050 с×33
16 0007.79 с0.132 с×59

Читати цю таблицю треба не по секундах, а по тому, як вони ростуть. Подвоєння даних коштує ієрархічній учетверо-упʼятеро більше часу — це і є квадрат. DBSCAN на тому самому подвоєнні дорожчає лише приблизно у два з половиною рази. Тому розрив у правому стовпці росте разом із таблицею, і на десятках тисяч рядків вибору вже фактично немає.

Абсолютні секунди тут ні про що не говорять. Вони заміряні на одній конкретній машині, і на іншій будуть іншими — швидшими або повільнішими в рази. Переносити з цієї таблиці можна тільки два висновки: у скільки разів дорожчає кожен метод від подвоєння даних і як росте розрив між ними.

Чому DBSCAN дешевший. Наївно він теж квадратичний: для кожної точки перебрати всі інші. Але йому не потрібні всі відстані — лише сусіди в радіусі eps, а це вміє просторовий індекс: структура, яка заздалегідь ділить простір на комірки й дозволяє питати «хто лежить у цьому колі», не обходячи весь набір. scikit-learn будує таке дерево (KD-дерево або ball-дерево) сам, і на двох-трьох ознаках один запит коштує приблизно log n замість n, а весь прогін — n·log n. Тому DBSCAN на мільйоні рядків реальний, а дендрограма на мільйоні — ні.

Але індекс рятує не завжди. Що більше ознак, то гірше він відсіює: у просторі високої розмірності майже кожна комірка перетинається з колом запиту, і дерево вироджується в той самий повний перебір. Практична межа — приблизно два десятки ознак. Тобто «DBSCAN тримає мільйони рядків» правдиве для двох-трьох ознак і неправдиве для двохсот; у таких випадках спершу знижують розмірність методом головних компонент, а вже потім кластеризують.

Що робити, коли рядків забагато для дерева. Ієрархічну майже завжди рятує підвибірка: візьми випадкові кілька тисяч рядків, побудуй по них дендрограму й прочитай із неї кількість груп — та відповідь, заради якої метод здебільшого й беруть. Далі цю кількість віддаєш k-means, який спокійно пройде по всіх даних. Другий шлях — AgglomerativeClustering з аргументом connectivity: якщо заздалегідь сказати, які пари взагалі мають право злитись (скажімо, лише сусіди по графу k найближчих), матриця стає розрідженою і памʼять перестає бути квадратичною. Ціна — той самий ланцюжковий ризик, бо зʼєднання вздовж графа якраз і є ланцюжок.

І ще одна практична дрібниця, яку варто знати наперед. Ієрархічна кластеризація не вміє приписати новий обʼєкт до вже готових груп: щоб додати один рядок, треба перебудувати дерево. У k-means для цього досить порівняти відстані до центрів. DBSCAN теж не має такого механізму — у scikit-learn у нього немає навіть методу predict.

РазомТри форми, три методи

Час скласти все докупи. Ось три набори даних, на яких провалився k-means, і три методи на кожному з них. Девʼять станів — перебери всі й подивись, хто де виграє.

Девʼять відповідей

Перемикай форму й метод. Числа під картинкою пораховані на тих самих точках, що й у попередній темі.

форма даних
метод
на своїх місцях—
кластерів—
шум—
розміри—
Умови однакові для всіх. k-means — при k = 2, з найкращого з кількох стартів. DBSCAN — з eps = 0.6 і min_samples = 5: одні параметри на всі три набори, як і було б у житті, поки не почнеш підбирати. Ієрархічна — звʼязок Ворда (він стоїть за замовчуванням у AgglomerativeClustering), дерево розрізане на дві групи. У вердикті для порівняння названо, що дав би на цьому ж наборі одиночний звʼязок.

Підсумок у трьох рядках, і жоден із них не про перемогу:

З цих девʼяти станів витягується правило, яке працює й поза цією сторінкою: метод не буває «кращим» чи «гіршим» — він робить припущення й виграє там, де припущення справдилось. k-means припускає кулі схожого розміру — і на кулях виграє. DBSCAN припускає однакову щільність усередині всіх груп — і виграє формою там, де щільність рівна. Ворд припускає те саме, що й k-means, тому й помиляється там само. Виходить, питання «який метод брати» насправді звучить як «що я готовий припустити про ці дані» — і відповідає на нього не метод, а той, хто на дані дивиться.

Практичний наслідок: якщо два методи на одних даних дали схожі групи, це сильний сигнал, що групи справжні. Якщо розійшлись — жоден із них не «правий», просто дані не задовольняють щонайменше одне з двох припущень, і треба зрозуміти, яке саме.

ВибірЩо обирати й що далі

Ось три методи в одній таблиці — не «який кращий», а що кожен припускає й на чому ламається.

k-meansDBSCANІєрархічна
Що припускає про данікулясті групи схожого розміруоднакова щільність усередині всіх групнічого — усе вирішує обраний звʼязок
Що треба задатиkeps, min_samplesзвʼязок; кількість груп — після побудови
Форми кластерівлише опуклі, межі прямібудь-якіодиночний — будь-які, Ворда — кулясті
Що робить із дивними точкамиприписує до найближчого центраокрема мітка «шум»лишає крихітними групами на великій висоті
Скільки рядків витримуємільйонимільйони (з індексом)десятки тисяч
Новий обʼєкт без перерахункутакніні
Головна пасткавипадковий старт, потреба знати kпідбір eps, різна щільністьвибір звʼязку міняє все

А якщо стисло, то відповідь на питання «що брати» вміщується у три рядки:

Практичний порядок дій, який працює в більшості випадків: почни з ієрархічної на підвибірці в кілька тисяч рядків, подивись на дендрограму — вона покаже, скільки груп узагалі варто шукати. Далі, якщо групи схожі на грудки, візьми k-means на всіх даних із цим k. Якщо на дендрограмі видно щось витягнуте або якщо цікавлять саме дивні обʼєкти — бери DBSCAN і підбирай eps за коліном.

Лишилось одне питання, яке ми весь час обходили: усі картинки цієї теми двовимірні, бо намалювати можна лише два виміри. А оголошення описується сімома числами, клієнт банку — сотнею. Дивитись на кластери в семи вимірах ніхто не вміє, і тут стає в пригоді метод головних компонент із теми 11: він стискає багато ознак у дві так, щоб на них можна було подивитись і побачити те, що справді є в даних. Для DBSCAN у нього є й друга користь, суто технічна: просторовий індекс, без якого метод квадратичний, працює лише на невеликій кількості ознак (розділ 10).

А наступна тема бере мітку −1 із цього уроку й робить із неї окрему задачу. Шум DBSCAN піймав на дошці 25 шахраїв зі 139 і майже не помилився — але це все, на що він здатен: поставити мітку «так» або «ні». Пошук аномалій — про методи, які ранжують підозрілість числом, працюють, коли дивних обʼєктів одиниці на тисячу, і не вимагають, щоб решта даних узагалі розпадалась на кластери.