Кластеризація SOM для номінальних / кругових змінних


11

Цікаво, чи хтось знайомий з кластеризацією номінальних входів. Я розглядав SOM як рішення, але, мабуть, він працює лише з числовими характеристиками. Чи є розширення для категоричних ознак? Зокрема, мені було цікаво про "Дні тижня" як про можливі функції. Звичайно, можна перетворити його в числову ознаку (тобто пн - нд, що відповідає № 1-7), проте тоді евклідова відстань між Сонцем та пн (1 і 7) не буде такою ж, як відстань від пн до вт (1 і 2 ). Будь-які пропозиції чи ідеї були б вдячні.


(+1) дуже цікаве запитання
steffen

2
Циклічні змінні найкраще розглядати як елементи одиничного кола в площині Комплексу. Таким чином, було б природно відобразити дні тижня на (скажімо) точки , ; тобто , , , ... . досвід(2jπi/7)j=0,,6(cos(0),sin(0))(cos(2π/7),гріх(2π/7))(cos(12π/7),гріх(12π/7))
whuber

1
Чи потрібно мені кодувати власну матрицю відстані, а потім специфічну для циклічних змінних? просто цікаво, чи існували вже алгоритми для цього типу кластеризації. thx
Майкл

@Michael: Я вважаю, що ви хочете вказати свою власну метрику відстані, яка підходить для вашої програми, і вона визначена у всіх вимірах у ваших даних, а не лише у DOW. Формально, дозволяючи x, y позначати точки у вашому просторі даних, потрібно визначити метричну функцію d (x, y) зі звичайними властивостями: d (x, x) = 0, d (x, y) = d (y , x) і d (x, z) <= d (x, y) + d (y, z). Після того, як ви це зробите, створення SOM стає механічним. Творчим завданням є визначення d () таким чином, щоб відображати поняття "подібності", відповідне вашій програмі.
Артур Малий

Відповіді:


7

Фон:

Найбільш логічний спосіб перетворити годину на дві змінні, які змінюються синхронізовано назад і назад. Уявіть положення кінця годинної руки цілодобового годинника. В xколиванні позиції вперед і назад з синхронізації з yпозицією. Для 24-годинний ви можете зробити це з x=sin(2pi*hour/24), y=cos(2pi*hour/24).

Вам потрібні обидві змінні, або правильний рух через час втрачено. Це пов’язано з тим, що похідна або sin, або cos змінюється в часі, тоді як (x,y)положення плавно змінюється, коли воно об'їжджає одиничне коло.

Нарешті, подумайте, чи варто додати третю особливість для відстеження лінійного часу, який може бути побудований як години (або хвилини або секунди) від початку першого запису, або часовий штамп Unix або щось подібне. Ці три особливості надають проксі для циклічного та лінійного прогресування часу, наприклад, ви можете витягнути циклічні явища, такі як цикли сну в русі людей, а також лінійний приріст, як кількість населення проти часу.

Приклад виконання, якщо:

# Enable inline plotting
%matplotlib inline

#Import everything I need...

import numpy as np
import matplotlib as mp

import matplotlib.pyplot as plt
import pandas as pd

# Grab some random times from here: https://www.random.org/clock-times/
# put them into a csv.
from pandas import DataFrame, read_csv
df = read_csv('/Users/angus/Machine_Learning/ipython_notebooks/times.csv',delimiter=':')
df['hourfloat']=df.hour+df.minute/60.0
df['x']=np.sin(2.*np.pi*df.hourfloat/24.)
df['y']=np.cos(2.*np.pi*df.hourfloat/24.)

df

введіть тут опис зображення

def kmeansshow(k,X):

    from sklearn import cluster
    from matplotlib import pyplot
    import numpy as np

    kmeans = cluster.KMeans(n_clusters=k)
    kmeans.fit(X)

    labels = kmeans.labels_
    centroids = kmeans.cluster_centers_
    #print centroids

    for i in range(k):
        # select only data observations with cluster label == i
        ds = X[np.where(labels==i)]
        # plot the data observations
        pyplot.plot(ds[:,0],ds[:,1],'o')
        # plot the centroids
        lines = pyplot.plot(centroids[i,0],centroids[i,1],'kx')
        # make the centroid x's bigger
        pyplot.setp(lines,ms=15.0)
        pyplot.setp(lines,mew=2.0)
    pyplot.show()
    return centroids

Тепер спробуємо:

kmeansshow(6,df[['x', 'y']].values)

введіть тут опис зображення

Ви ледве можете побачити, що є кілька після півночі, включені до зеленого скупчення до півночі. Тепер зменшимо кількість кластерів і покажемо, що до і після півночі можна більш детально з'єднатись в один кластер:

kmeansshow(3,df[['x', 'y']].values)

введіть тут опис зображення

Подивіться, як синій кластер містить часи, що передують до і після півночі, які об'єднані в один кластер ...

Ви можете це робити час, або день тижня, або тиждень місяця, або день місяця, або сезон, або все що завгодно.


Корисні (+1). Це одне додаток, коли графіки, що мають квадрат, а не довгасті, дуже важливі. Я не знаю вашого програмного забезпечення, але я думаю, що ви можете встановити співвідношення сторін на 1, не за замовчуванням.
Нік Кокс

Це правда @NickCox. Або ви можете просто виконати лінійну трансформацію в голові ;-)
user1745038

2

Зазвичай номінальні змінні кодуються фіксованими під час використання в SOM (наприклад, одна змінна для з 1 за понеділок 0 не для понеділка, інша для вівторка тощо).

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


2

Для номінальних змінних типове кодування в нейромережевому або електротехнічному контекстах називається "гарячим" - вектором усіх 0s, причому один 1 у відповідному положенні для значення для змінної. Наприклад, для днів тижня є сім днів, тож ваші вегетаріанці мають тривалість сім. Тоді понеділок буде представлений як [1 0 0 0 0 0 0], вівторок як [0 1 0 0 0 0 0] тощо.

Як натякав Тім, цей підхід можна легко узагальнити, щоб охопити довільні булеві вектори ознак, де кожна позиція у векторі відповідає ознаці, яка цікавить ваші дані, а позиція встановлена ​​в 1 або 0, що вказує на наявність або відсутність цього особливість.

Після того, як у вас є двійкові вектори, відстань Хеммінга стає природною метрикою, хоча також використовується евклідова відстань. Для двома гарячими бінарними векторами SOM (або інший аппроксиматор функції) буде, природно, інтерполювати між 0 і 1 для кожного положення вектора. У цьому випадку ці вектори часто трактуються як параметри розподілу Больцмана або софтмакс на простір номінальної змінної; це лікування дає можливість використовувати вектори і в якомусь сценарії розбіжності KL.

Циклічні змінні значно хитріші. Як сказав Артур у коментарях, вам потрібно було б самостійно визначити метрику відстані, яка включає циклічну природу змінної.


1

Припустимо, що день тижня (вниз) йде від [0, 6], замість того, щоб проектувати дані на коло, іншим варіантом є використання:

dist = min(abs(dow_diff), 7 - abs(dow_diff))

Щоб зрозуміти чому, розгляньте падіння як годинник

  6  0
5      1
4      2
    3

різниця між 6 і 1 може бути 6 - 1 = 5 (рухається за годинниковою стрілкою від 1 до 6) або 7 - (6 - 1) = 2. Виконуючи мінімум обох варіантів, слід зробити трюк.

Загалом ви можете використовувати: min(abs(diff), range - abs(diff))


0

Я успішно кодував Дні тижня (і місяці року) як кордон (cos, гріх), як підкреслив у своєму коментарі. Чим використана евклідова відстань.

Це приклад коду в r:

circularVariable = function(n, r = 4){
 #Transform a circular variable (e.g. Month so the year or day of the week) into two new variables (tuple).
 #n = upper limit of the sequence. E.g. for days of the week this is 7.
 #r =  number of digits to round generated variables.
 #Return
 #
 coord = function(y){
   angle = ((2*pi)/n) *y
   cs = round(cos(angle),r)
   s = round(sin(angle),r)
   c(cs,s)
 }
 do.call("rbind", lapply((0:(n-1)), coord))
}

Евклідова відстань між 0 і 6 дорівнює 0 і 1.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.