Участник:Ян-Мартин Тамм/Строгий алгоритм С средних
Строгий алгоритм С средних (K-means, Hard C-Means) | |
Последовательный алгоритм | |
Последовательная сложность | O(nkdi) |
Объём входных данных | nd+1 |
Объём выходных данных | nk |
Параллельный алгоритм | |
Высота ярусно-параллельной формы | O(log_2(k)log_2(d)i) |
Ширина ярусно-параллельной формы | O(nkd) |
Содержание
- 1 Описание алгоритма
- 1.1 Математическое описание алгоритма
- 1.2 Вычислительное ядро алгоритма
- 1.3 Макроструктура алгоритма
- 1.4 Схема реализации последовательного алгоритма
- 1.5 Последовательная сложность алгоритма
- 1.6 Информационный граф
- 1.7 Ресурс параллелизма алгоритма
- 1.8 Входные и выходные данные алгоритма
- 1.9 Свойства алгоритма
- 2 Программная реализация
- 3 Литература
1 Описание алгоритма
Строгий алгоритм C средних, более известный как K-средних, решает задачу разделения векторов в многомерном пространстве на заданное количество кластеров. При этом каждый вектор принадлежит только одному кластеру, поэтому алгоритм называется строгим, в то время как нечеткий алгоритм C средних (Fuzzy C-means, Soft K-means) определяет степень принадлежности вектора каждому из кластеров.
Данная задача является NP-трудной, тем не менее описываемый алгоритм сходится к локальному оптимуму. Глобальный оптимум не гарантируется и зависит от первоначального распределения центров кластеров и их количества.
Общая идея алгоритма такова: фиксируем число кластеров k, произвольным образом выбираем центры кластеров, после чего повторяем следующие два действия, пока не будет выполнено условие остановки:
- Распределяем каждый вектор в ближайший кластер
- Пересчитываем значения центров как среднее значение координат векторов в каждом кластере
Минимизируется сумма расстояний векторов от центров их кластеров. Остановка производится если либо значение данной функции становится ниже некоторого порога, либо мало изменяется по сравнению с предыдущей итерацией.
1.1 Математическое описание алгоритма
Дано множество d-мерных векторов U=\{u_1, u_2, ..., u_n\} и число кластеров k \in \mathbb{N}. Требуется разбить множество U на k непересекающихся непустых кластеров C_1, C_2, ..., C_k таких, что {\bigcup \limits _{i = 1}^k C_i} = U .
Для этого минимизируется функция
J = \sum\limits_{i=1}^k J_i = \sum\limits_{i=1}^k \sum\limits_{ u \in C_i} \|u-c_i \|^2
, где c_i— центр масс векторов кластера C_i.
Произвольным образом инициализируем центры кластеров c_1, c_2, ..., c_k, после чего начинается основная часть алгоритма.
- Каждый вектор u_l определяем в ближайший к нему кластер C_i, то есть такой, что \|u_l-c_i\|^2 \leqslant \|u_l-c_j\|^2 \;\; \forall i \neq j; \;\; (i,j=1,2,\ldots,k, \; l = 1,2,\ldots,n) . Хранить информацию о принадлежности векторов кластерам будем в матрице M следующего вида:
- m_{ij} = \begin{cases} 1, & u_j \in C_i \\ 0, & \text{otherwise} \end{cases}
- Считаем значение функции J и прекращаем работу алгоритма, если это значение меньше заданного порога \varepsilon, либо значение мало изменилось после текущей итерации.
- Обновляем значения центров кластеров
- c_i = \frac{1}{|C_i|} \sum\limits_{u \in C_i} u \;\; (i=1,2,\ldots,k)
- Возвращаемся к распределению векторов по кластерам.
1.2 Вычислительное ядро алгоритма
Вычислительным ядром алгоритма является весь алгоритм, а именно два основных его шага:
- Распределение векторов по кластерам
- Пересчёт центров кластеров
1.3 Макроструктура алгоритма
Как уже говорилось раньше, первоначальное распределение векторов по кластерам может быть произвольным, например, можно случайным образом выбрать k векторов из множества всех векторов U.
Далее начинается цикл, его первая операция — распределение векторов по кластерам. В ней для каждого вектора считается расстояние от него, до каждого кластера \rho = \big \| u - c \big \|^2=\sum_{i=1}^d (u_i-c_i)^2, после чего из них выбирается наименьшее и заносится в матрицу.
Подсчёт функции J можно проводить одновременно с первым шагом, так как в ней суммируются те же самые расстояния.
Пересчёт центров кластеров — сумма всех векторов кластера, делённая на количество объектов в кластере.
c = \frac{1}{|C|} \sum\limits_{u \in C} u
1.4 Схема реализации последовательного алгоритма
def kmeans(points, k, cutoff):
# Pick out k random points to use as our initial centroids
initial = random.sample(points, k)
# Create k clusters using those centroids
clusters = [Cluster([p]) for p in initial]
# Loop through the dataset until the clusters stabilize
while True:
# Create a list of lists to hold the points in each cluster
lists = [ [] for c in clusters]
# For every point in the dataset ...
for p in points:
# Get the distance between that point and the centroid of the first cluster.
smallest_distance = getDistance(p, clusters[0].centroid)
# Set the cluster this point belongs to
clusterIndex = 0
# For the remainder of the clusters ...
for i in range(clusterCount - 1):
# calculate the distance of that point to each other cluster's centroid
distance = getDistance(p, clusters[i+1].centroid)
# If it's closer to that cluster's centroid update what we
# think the smallest distance is, and set the point to belong to that cluster
if distance < smallest_distance:
smallest_distance = distance
clusterIndex = i+1
lists[clusterIndex].append(p)
# Set our total_shift to zero for this iteration
total_shift = 0.0
# As many times as there are clusters ...
for i in range(k):
# Calculate how far the centroid moved in this iteration
shift = clusters[i].update(lists[i])
# Keep track of the total moves from all cluster centroid updates
total_shift += shift
# If the centroids have stopped moving much, we're done!
if total_shift < cutoff:
break
return clusters
1.5 Последовательная сложность алгоритма
Распределение по кластерам — для каждого из n векторов и каждого из k кластеров посчитать расстояние, сложив d раз значения, на вычисление каждого из которых нужно 2 операции — O(nkd). Пересчёт центров — n раз сложить и k раз поделить — O(n+k). На завершение алгоритма понадобится i итераций. Итого сложность O(nkdi).
1.6 Информационный граф
1.7 Ресурс параллелизма алгоритма
Самой трудоёмкой операцией является распределение по кластерам. Так, для рассчёта расстояний, можно провести суммирование d элементов за log_2(d) операций. Расстояние от данного вектора до всех кластеров можно найти за одну операцию, после чего нужно найти наименьшее значение из k элементов за log_2(k) операций. И всё это нужно сделать для n векторов, что можно сделать параллельно за одну операцию. Для сходимости всё так же потребуется i итераций, общая сложность получается O(log_2(k)log_2(d)i).
1.8 Входные и выходные данные алгоритма
Входные данные:
- U=\{u_1, u_2, ..., u_n\} \subset \mathbb{R}^d — множество векторов
- k \in \mathbb{N} — количество кластеров
- \varepsilon — порог остановки не обязательно.
Объём входных данных: nd+1.
Выходные данные:
- M — матрица принадлежности векторов кластерам размера nk. При этом данная матрица содержит только n единиц, остальные значения ноль. Возможен вывод в другом виде, например, список списков.
Объём выходных данных: nk.
1.9 Свойства алгоритма
- Алгоритм не является детерминированным, так как инициализация кластеров происходит случайным образом, однако, ничто не мешает проводить инициализацию согласно некоторому детерминированному правилу.
- Алгоритм находит локальный оптимум, поэтому его обычно запускают несколько раз и выбирают лучший результат.
- Вычислительная мощность \frac{kdi}{k+d}
- Алгоритм является устойчивым при одинаковой первоначальной инициализации — малая ошибка в начальных данных или при вычислениях очень незначительно повлияют на результат (скорее всего никак не повлияют). Тем не менее, при случайной инициализации результаты будут различны.
- scikit-learn библиотека машинного обучения для языка Python.
- SciPy библиотека на языке Python, содержащая большое число реализаций различных алгоритмов.
- ELKI библиотека для анализа данных на языке программирования Java, содержит различные алгоритмы кластеризации.
- Spark распределённая версия алгоритма.
- MLPACK библиотека машинного обучения для C++.
- OpenCV библиотека содержит реализацию алгоритма.
- Apache Mahout содержит реализацию алгоритма на базе MapReduce.
- Weka библиотека и инструмент для анализа данных на языке Java.
- Torch библиотека машинного обучения на базе языка программирования Lua.
- Apache Spark MLlib содержит распределенную реализацию алгоритма.
- Accord.NET содержит несколько различных реализаций на языке C#.
- R k-means реализация на языке R.
- Jan Jantzen «Neurofuzzy Modelling».
- Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.
- Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.
- MacQueen J. (1967). Some methods for classification and analysis of multivariate observations. In Proc. 5th Berkeley Symp. on Math. Statistics and Probability, pages 281—297.