Уровень алгоритма

Участник:IanaV/Алгоритм k means: различия между версиями

Материал из Алговики
Перейти к навигации Перейти к поиску
 
(не показано 76 промежуточных версий 5 участников)
Строка 1: Строка 1:
 +
{{Assignment|Zhum|Konshin}}
 +
 +
 +
{{level-a}}
 +
 +
{{algorithm
 +
| name              = Алгоритм k средних (k means)
 +
| serial_complexity = <math>O(iknd)</math>
 +
| input_data        = <math> nd </math>
 +
| output_data      = <math> n </math>
 +
}}
 +
 
Авторы страницы: [[Участник:IanaV|Валуйская Я.А.]], [[Участник:GlotovES|Глотов Е.С.]]
 
Авторы страницы: [[Участник:IanaV|Валуйская Я.А.]], [[Участник:GlotovES|Глотов Е.С.]]
  
Строка 8: Строка 20:
 
=== Общее описание алгоритма ===
 
=== Общее описание алгоритма ===
 
<p>Алгоритм <i>k-means</i> (<i>k средних</i>) - один из наиболее популярных алгоритмов кластеризации. Наиболее простой, но в то же время достаточно неточный метод кластеризации в классической реализации.</p>
 
<p>Алгоритм <i>k-means</i> (<i>k средних</i>) - один из наиболее популярных алгоритмов кластеризации. Наиболее простой, но в то же время достаточно неточный метод кластеризации в классической реализации.</p>
<p>Алгоритм был разработан в 1956 году математиком Гуго Штейнгаузом <ref>[http://www.laurent-duval.eu/Documents/Steinhaus_H_1956_j-bull-acad-polon-sci_division_cmp-k-means.pdf Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.]</ref>, и почти одновременно его изобрел Стюарт Ллойд <ref>[http://www-evasion.imag.fr/people/Franck.Hetroy/Teaching/ProjetsImage/2007/Bib/lloyd-1982.pdf Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper.]</ref>. Особую популярность алгоритм снискал после работы Джеймса Маккуина <ref>[https://pdfs.semanticscholar.org/a718/b85520bea702533ca9a5954c33576fd162b0.pdf 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.]</ref></p>
+
<p>Алгоритм был разработан в 1956 году математиком Гуго Штейнгаузом <ref>[http://www.laurent-duval.eu/Documents/Steinhaus_H_1956_j-bull-acad-polon-sci_division_cmp-k-means.pdf Steinhaus H. (1956). Sur la division des corps materiels en parties. Bull. Acad. Polon. Sci., C1. III vol IV: 801—804.]</ref>, и почти одновременно его изобрел Стюарт Ллойд <ref>[http://www-evasion.imag.fr/people/Franck.Hetroy/Teaching/ProjetsImage/2007/Bib/lloyd-1982.pdf Lloyd S. (1957). Least square quantization in PCM’s. Bell Telephone Laboratories Paper. Опубликовано позже в IEEE Transactions on Information Theory 28 (1982): 129-137.]</ref>. Особую популярность алгоритм снискал после работы Джеймса Маккуина <ref>[https://pdfs.semanticscholar.org/a718/b85520bea702533ca9a5954c33576fd162b0.pdf 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.]</ref></p>
 
<p>Алгоритм кластеризации <i>k-means</i> решает задачу распределения <math>N</math> наблюдений (элементов векторного пространства) на заранее известное число кластеров <math>K</math>. Действие алгоритма таково, что он стремится минимизировать среднеквадратичное отклонение на точках каждого кластера.</p>
 
<p>Алгоритм кластеризации <i>k-means</i> решает задачу распределения <math>N</math> наблюдений (элементов векторного пространства) на заранее известное число кластеров <math>K</math>. Действие алгоритма таково, что он стремится минимизировать среднеквадратичное отклонение на точках каждого кластера.</p>
<p>Основная идея алгоритма заключается в том, что на каждой итерации перевычисляется центр масс для каждого кластера, полученного на предыдущем шаге, затем векторы разбиваются на кластеры вновь в соответствии с тем, какой из новых центров оказался ближе по выбранной метрике. Алгоритм завершается, когда на какой-то итерации не происходит изменения кластеров.</p>  
+
<p>Алгоритм k-means является итерационным алгоритмом кластеризации. На каждой итерации происходит пересчет центра масс для каждого кластера, полученного на предыдущем шаге, затем исходные векторы заново распределяются по кластерам в соответствии с тем, какой из новых центров оказался ближе по выбранной метрике. Алгоритм завершается, когда на какой-то итерации не происходит изменения кластеров.</p>
 +
 
 
=== Математическое описание алгоритма ===
 
=== Математическое описание алгоритма ===
  
Строка 51: Строка 64:
 
# пересчет центров масс кластеров.
 
# пересчет центров масс кластеров.
  
<i>Распределение векторов по кластерам</i> заключается в следующем: для каждого вектора <math>x_i \in X, i = 1, ..., n</math> необходимо посчитать расстояние между этим вектором и центром масс кластера <math>\mu_j^t, j = 1, ..., k</math>. Следовательно, на каждой итерации необходимо выполнить <math>n * k</math> операций вычисления расстояния между векторами.  
+
<i>Распределение векторов по кластерам</i> заключается в следующем: для каждого вектора <math>x_i \in X, i = 1, ..., n</math> необходимо посчитать расстояние между этим вектором и центром масс кластера <math>\mu_j^t, j = 1, ..., k</math>. Следовательно, на каждой итерации необходимо выполнить <math>nk</math> операций вычисления расстояния между векторами.  
  
 
<i>Пересчет центров масс кластеров</i> заключается в следующем: для каждого кластера <math>S_i^t \in S, i = 1, ..., k</math> необходимо пересчитать кластер по формуле, приведенной в пункте выше. Следовательно, на каждой итерации необходимо выполнить <math>k</math> операций пересчета центров масс кластеров.
 
<i>Пересчет центров масс кластеров</i> заключается в следующем: для каждого кластера <math>S_i^t \in S, i = 1, ..., k</math> необходимо пересчитать кластер по формуле, приведенной в пункте выше. Следовательно, на каждой итерации необходимо выполнить <math>k</math> операций пересчета центров масс кластеров.
Строка 59: Строка 72:
 
# Макрооперация <i>"Расстояние между векторами"</i> <br> В данном алгоритме используется Евклидова метрика:  <br><math>v \in R^m</math> и <math>u \in R^m: d(v, u) = \sqrt{\sum_{j=1}^{m} (v_j - u_j)^2} </math>;
 
# Макрооперация <i>"Расстояние между векторами"</i> <br> В данном алгоритме используется Евклидова метрика:  <br><math>v \in R^m</math> и <math>u \in R^m: d(v, u) = \sqrt{\sum_{j=1}^{m} (v_j - u_j)^2} </math>;
 
# Макрооперация <i>"Пересчет центра масс кластера"</i> <br> <math>\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x</math>
 
# Макрооперация <i>"Пересчет центра масс кластера"</i> <br> <math>\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x</math>
 +
 +
<p>Таким образом, алгоритм в терминах макроопераций выглядит следующим образом:</p>
 +
# <i>Инициализация центров масс</i> <p>Метод Forgy: центры масс <math>\mu_j^1, j = 1, ..., k</math> выбираются случайным образом.</p> <p>Метод случайного разбиения: вектора <math>x_i \in X</math> случайным образом распределяются по кластерам. Для каждого кластера выполняется макрооперация "Пересчет центра масс кластера" для нахождения <math>\mu_j^1</math>.</p>
 +
# <i>t-ый шаг итерации:</i>
 +
#* <i>Распределение векторов по кластерам</i> <br>На данном шаге для каждого вектора <math>x_i \in X</math> k раз выполняется макрооперация "Расстояние между векторами" для нахождения расстояния между <math>x_i</math> и <math>\mu_j^t, j = 1, ..., k</math>. Затем для каждого вектора <math>x_i</math> находится минимальное расстояние.
 +
#* <i>Пересчет центров масс кластеров</i> <br>На данном шаге для каждого кластера выполняется макрооперация "Пересчет центра масс" для нахождения <math>\mu_j^{t+1}</math>.
 +
# <i>Критерий останова</i> <br> <math>\mu_i^t = \mu_i^{t+1},</math> для всех <math>i = 1, ..., k</math>
  
 
=== Схема реализации последовательного алгоритма ===
 
=== Схема реализации последовательного алгоритма ===
Строка 84: Строка 104:
  
 
# Инициализация:
 
# Инициализация:
#* Метод Forgy: <math>N_{init} = 0</math>
+
#* Метод Forgy: <math>N_{\rm init} = 0</math>
 
#* Метод случайного разбиения:  
 
#* Метод случайного разбиения:  
#** Количество операций деления: <math>N^{/}_{init} = k * d</math>;
+
#** Количество операций деления: <math>N^{/}_{\rm init} = kd</math>;
#** Количество операций сложения: <math>N^{+}_{init} = d * (n - k)</math>.
+
#** Количество операций сложения: <math>N^{+}_{\rm init} = d(n - k)</math>.
 
# Распределение <math>n</math> векторов <math>x_i \in R^d</math> по <math>k</math> кластерам на одной итерации:
 
# Распределение <math>n</math> векторов <math>x_i \in R^d</math> по <math>k</math> кластерам на одной итерации:
#* Количество операций умножения: <math>N^{*}_{distr} = k * n * d</math>;
+
#* Количество операций умножения: <math>N^{*}_{\rm distr} = knd</math>;
#* Количество операций сложения: <math>N^{+}_{distr} = k * n * (d - 1)</math>;
+
#* Количество операций сложения: <math>N^{+}_{\rm distr} = kn(d - 1)</math>;
#* Количество операций вычитания: <math>N^{-}_{distr} = k * n * d</math>;
+
#* Количество операций вычитания: <math>N^{-}_{\rm distr} = knd</math>;
#* Количество операций взятия минимума: <math>N^{min}_{distr} = n * (k - 1)</math>.
+
#* Количество операций взятия минимума: <math>N^{\rm min}_{\rm distr} = n(k - 1)</math>.
 
# Пересчет центра масс <math>\mu_i \in R^d</math> для <math>k</math> кластеров на одной итерации:
 
# Пересчет центра масс <math>\mu_i \in R^d</math> для <math>k</math> кластеров на одной итерации:
#* Количество операций деления: <math>N^{/}_{update} = k * d</math>;
+
#* Количество операций деления: <math>N^{/}_{\rm update} = kd</math>;
#* Количество операций сложения: <math>N^{+}_{update} = d * (n - k)</math>.
+
#* Количество операций сложения: <math>N^{+}_{\rm update} = d(n - k)</math>.
  
 
Следовательно, в случае если алгоритм сошелся за <math>i</math> итераций, получаем:
 
Следовательно, в случае если алгоритм сошелся за <math>i</math> итераций, получаем:
* Операций сложения/вычитания: <math>N^{+,-}_{k-means} = N^{+}_{init} + i*(N^{+}_{distr} + N^{-}_{distr} + N^{+}_{update}) = d * (n - k) + i*(k*n*(d-1)+k*n*d+d*(n-k)) \thicksim O(i*k*n*d)</math>
+
* Операций сложения/вычитания: <math>N^{+,-}_{\rm k-means} = N^{+}_{\rm init} + i \cdot (N^{+}_{\rm distr} + N^{-}_{\rm distr} + N^{+}_{\rm update}) = d(n - k) + i \cdot (kn(d-1)+knd+d(n-k)) \thicksim O(iknd)</math>
* Операций умножения/деления: <math>N^{*,/}_{k-means} = N^{/}_{init} + i*(N^{*}_{distr} + N^{/}_{update}) = k * d + i*(k*n*d+k*d) \thicksim O(i*k*n*d)</math>
+
* Операций умножения/деления: <math>N^{*,/}_{\rm k-means} = N^{/}_{\rm init} + i \cdot (N^{*}_{\rm distr} + N^{/}_{\rm update}) = kd + i \cdot (knd+kd) \thicksim O(iknd)</math>
  
Таким образом, [[глоссарий#Последовательная сложность|''последовательная сложность'']] алгоритма k-means для <math>n</math> <math>d</math>-мерных векторов и <math>k</math> кластеров за <math>i</math> итераций, требуемых для сходимости алгоритма:  <p align="center"><math>O(i * n * k * d)</math></p>
+
Таким образом, [[глоссарий#Последовательная сложность|''последовательная сложность'']] алгоритма k-means для <math>n</math> <math>d</math>-мерных векторов и <math>k</math> кластеров за <math>i</math> итераций, требуемых для сходимости алгоритма:  <p align="center"><math>O(iknd)</math></p>
  
 
=== Информационный граф ===
 
=== Информационный граф ===
Строка 107: Строка 127:
 
Рассмотрим информационный граф алгоритма k-means.
 
Рассмотрим информационный граф алгоритма k-means.
 
На <i>рисунке 1</i> представлена общая схема алгоритма k-means, согласно разделу [[#Схема реализации последовательного алгоритма|"Схема реализации последовательного алгоритма"]].  
 
На <i>рисунке 1</i> представлена общая схема алгоритма k-means, согласно разделу [[#Схема реализации последовательного алгоритма|"Схема реализации последовательного алгоритма"]].  
Вершина <i>Init</i> - этап инициализации.
+
Вершина <math>\rm Init</math> - этап инициализации.
Вершина <i>Distribute</i> - этап распределения векторов по кластерам.
+
Вершина <math>\rm Distribute</math> - этап распределения векторов по кластерам.
Вершина <i>Update</i> - этап пересчета центров масс кластеров.
+
Вершина <math>\rm Update</math> - этап пересчета центров масс кластеров.
 
[[file:k-means-all.PNG|800px|thumb|center|Рисунок 1. Общая схема алгоритма k-means]]
 
[[file:k-means-all.PNG|800px|thumb|center|Рисунок 1. Общая схема алгоритма k-means]]
  
  
На <i>рисунке 2</i> представлен информационный граф этапа распределения векторов по кластерам. На вход данному этапу подают множество векторов <math>x_1, x_2, ..., x_n</math> из входных данных и множество центров масс кластеров <math>\mu^t_1, ..., \mu^t_k</math>, полученных с шага инициализации или с предыдущей итерации. Для каждой пары векторов <math>x_i, \mu^t_j, i = 1, ..., n; j = 1,..., k</math> считается расстояние между ними (вершина <i>dist</i>). Информационный граф вычисления расстояния между двумя векторами рассмотрен ниже. Затем выполняется операция нахождения минимума (вершина <i>min</i>), в результате которой каждому вектору <math>x_i, i = 1, ..., n</math> приписывается метка <math>l^t_i</math>, равная порядковому номеру кластера <math>S^t_j, j = 1 ..., k</math>, которому принадлежит данный вектор: <math>x_i \in S^t_j</math>.
+
На <i>рисунке 2</i> представлен информационный граф этапа распределения векторов по кластерам. На вход данному этапу подают множество векторов <math>x_1, x_2, ..., x_n</math> из входных данных и множество центров масс кластеров <math>\mu^t_1, ..., \mu^t_k</math>, полученных с шага инициализации или с предыдущей итерации. Для каждой пары векторов <math>x_i, \mu^t_j, i = 1, ..., n; j = 1,..., k</math> считается расстояние между ними (вершина <math>\rm dist</math>). Информационный граф вычисления расстояния между двумя векторами рассмотрен ниже (<i>рисунок 3</i>). Затем выполняется операция нахождения минимума (вершина <math>\rm min</math>), в результате которой каждому вектору <math>x_i, i = 1, ..., n</math> приписывается метка <math>l^t_i</math>, равная порядковому номеру кластера <math>S^t_j, j = 1 ..., k</math>, которому принадлежит данный вектор: <math>x_i \in S^t_j</math>.
 
[[file:k-means-distribute.PNG|800px|thumb|center|Рисунок 2. Схема этапа распределения векторов по кластерам]]
 
[[file:k-means-distribute.PNG|800px|thumb|center|Рисунок 2. Схема этапа распределения векторов по кластерам]]
  
  
На <i>рисунке 3</i> представлен информационный граф вычисления расстояния между двумя векторами <math>x_i = (x_{i1}, ..., x_{id}), i = 1, ..., n</math> и <math>\mu^t_j = (\mu^t_{j1}, ..., \mu^t_{jd}) , j = 1, ..., k</math>. Выполняется операция поэлементного вычитания двух векторов (вершины <i>-</i>), затем возведение в квадрат (вершины <i>sqr</i>), затем суммирование полученных результатов (вершина <i>+</i>). На выходе получаем квадрат расстояния между векторами.
+
На <i>рисунке 3</i> представлен информационный граф вычисления расстояния между двумя векторами <math>x_i = (x_{i1}, ..., x_{id}), i = 1, ..., n</math> и <math>\mu^t_j = (\mu^t_{j1}, ..., \mu^t_{jd}) , j = 1, ..., k</math>. Выполняется операция поэлементного вычитания двух векторов (вершины <math>-</math>), затем возведение в квадрат (вершины <math>\rm sqr</math>), затем суммирование полученных результатов (вершина <math>+</math>). На выходе получаем квадрат расстояния между векторами.
 
[[file:K-means-distance.PNG|800px|thumb|center|Рисунок 3. Схема вычисления расстояния между двумя векторами]]
 
[[file:K-means-distance.PNG|800px|thumb|center|Рисунок 3. Схема вычисления расстояния между двумя векторами]]
  
  
На <i>рисунке 4</i> представлен информационный граф этапа пересчета центров масс кластеров <math>\mu^t_1, \mu^t_2,..., \mu^t_k</math>. Для каждого кластера <math>S^t_j, j = 1, ..., k</math> выполняется операция суммирования (вершина <math>sum_j</math>) векторов, которые принадлежат данному кластеру. Кроме того, для каждого кластера выполняется операция определения его размера (вершина <math>size_j</math>). Затем выполняется операция деления (вершина <i>/</i>). На выходе получаем новые центры масс кластеров <math>\mu^{t+1}_1, \mu^{t+1}_2,..., \mu^{t+1}_k</math>.
+
На <i>рисунке 4</i> представлен информационный граф этапа пересчета центров масс кластеров <math>\mu^t_1, \mu^t_2,..., \mu^t_k</math>. Для каждого кластера <math>S^t_j, j = 1, ..., k</math> выполняется операция суммирования (вершина <math>\rm sum_j</math>) векторов, которые принадлежат данному кластеру. Кроме того, для каждого кластера выполняется операция определения количества элементов в нем (вершина <math>\rm size_j</math>). Затем выполняется операция деления (вершина <math>/</math>). На выходе получаем новые центры масс кластеров <math>\mu^{t+1}_1, \mu^{t+1}_2,..., \mu^{t+1}_k</math>.
 
[[file:k-means-update.PNG|800px|thumb|center|Рисунок 4. Схема этапа пересчета центров масс кластеров]]
 
[[file:k-means-update.PNG|800px|thumb|center|Рисунок 4. Схема этапа пересчета центров масс кластеров]]
  
Строка 130: Строка 150:
 
* Пересчет центров масс кластеров: центры масс кластеров <math>\mu_i, \mu_j, i \neq j; i, j = 1, ..., k</math> могут быть пересчитаны независимо друг от друга.
 
* Пересчет центров масс кластеров: центры масс кластеров <math>\mu_i, \mu_j, i \neq j; i, j = 1, ..., k</math> могут быть пересчитаны независимо друг от друга.
  
Однако, этап пересчета центров масс кластеров зависит от этапа распределения векторов по кластерам, так что эти этапы должны выполняться строго последовательно. Таким образом, параллельная сложность алгоритма может быть вычислена следующим образом: <br> <math>N_{k-means} = i * (N_{distr} + N_{update})</math>, где <math>i</math> - количество итераций, необходимых для сходимости алгоритма, <math>N_{distr}</math> - параллельная сложность этапа распределения векторов по кластерам, <math>N_{update}</math> - параллельная сложность этапа пересчета центров масс кластеров.
+
Однако, этап пересчета центров масс кластеров зависит от этапа распределения векторов по кластерам, так что эти этапы должны выполняться строго последовательно. Таким образом, параллельная сложность алгоритма может быть вычислена следующим образом: <br> <math>N_{\rm k-means} = i \cdot (N_{\rm distr} + N_{\rm update})</math>, где <math>i</math> - количество итераций, необходимых для сходимости алгоритма, <math>N_{\rm distr}</math> - параллельная сложность этапа распределения векторов по кластерам, <math>N_{\rm update}</math> - параллельная сложность этапа пересчета центров масс кластеров.
  
  
Рассчитаем параллельную сложность <math>N_{distr}</math> этапа <i>распределения векторов по кластерам</i> в предположении наличия неограниченного числа процессоров для одной итерации. Для каждой пары векторов <math>(x_i, \mu_j), i = 1,...,n; j = 1, ..., k</math> операция вычисления расстояния выполняется независимо.
+
Рассчитаем параллельную сложность <math>N_{\rm distr}</math> этапа <i>распределения векторов по кластерам</i> в предположении наличия неограниченного числа процессоров для одной итерации. Для каждой пары векторов <math>(x_i, \mu_j), i = 1,...,n; j = 1, ..., k</math> операция вычисления расстояния выполняется независимо.
* При выполнении операция вычисления расстояния операции вычитания и умножения выполняются независимо друг от друга, поэтому требуется только одно вычитание и одно умножение. Число операций сложения: <math>N^{+}_{distr} = O(log(d))</math> согласно [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритму нахождения частичной суммы элементов массива путем сдваивания]];  
+
* При выполнении операция вычисления расстояния операции вычитания и умножения выполняются независимо друг от друга, поэтому требуется только одно вычитание и одно умножение. Число операций сложения: <math>N^{+}_{\rm distr} = O(\log(d))</math> согласно [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритму нахождения частичной суммы элементов массива путем сдваивания]];  
* Для нахождения минимума среди <math>k</math> элементов требуется <math>O(log(k))</math> операций сравнения.
+
* Для нахождения минимума среди <math>k</math> элементов требуется <math>\log(k))</math> операций сравнения.
Таким образом, получаем, что <math>N_{distr} = O(log(k*d))</math>
+
Таким образом, получаем, что <math>N_{\rm distr} = O(\log(kd))</math>
  
  
Рассчитаем параллельную сложность <math>N_{update}</math> этапа <i>пересчета центров масс кластеров</i> в предположении наличия неограниченного числа процессоров для одной итерации. Нахождение центра масс <math>\mu_j, j = 1, ..., k</math> кластера <math>S_j</math> может выполняться независимо от нахождения центров масс других кластеров. Таким образом, получаем, что <math>N_{update} \leq O(log(n))</math> в худшем случае, когда почти все векторы находятся в одном кластере.  
+
Рассчитаем параллельную сложность <math>N_{\rm update}</math> этапа <i>пересчета центров масс кластеров</i> в предположении наличия неограниченного числа процессоров для одной итерации. Нахождение центра масс <math>\mu_j, j = 1, ..., k</math> кластера <math>S_j</math> может выполняться независимо от нахождения центров масс других кластеров. Таким образом, получаем, что <math>N_{\rm update} \leq O(\log(n))</math> в худшем случае, когда почти все векторы находятся в одном кластере.  
  
  
Итоговая параллельная сложность алгоритма: <br> <math>N_{k-means} \leq i * (log(k*d) + log(n)) \thicksim O(i * log(k*d*n)); i</math> - количество итераций, необходимых для сходимости.
+
Итоговая параллельная сложность алгоритма: <br> <math>N_{\rm k-means} \leq i \cdot (\log(kd) + \log(n)) \thicksim O(i \cdot \log(knd)); i</math> - количество итераций, необходимых для сходимости.
  
 
=== Входные и выходные данные алгоритма ===
 
=== Входные и выходные данные алгоритма ===
Строка 151: Строка 171:
  
 
<i>Объем входных данных</i>:
 
<i>Объем входных данных</i>:
<math>n * d</math> действительных чисел, 1 целое положительное число
+
<math>nd</math> действительных чисел, 1 целое положительное число
  
 
<i>Выходные данные</i>:
 
<i>Выходные данные</i>:
Строка 166: Строка 186:
  
 
;Недостатки:
 
;Недостатки:
# Число кластеров является входным аргументом алгоритма. Таким образом, некорректный выбор данного параметра может привести к плохим результатам работы алгоритма, поэтому очень важно проводить диагностические прогоны для подбора оптимального количества кластеров;
+
# Число кластеров является входным аргументом алгоритма. Таким образом, некорректный выбор данного параметра может привести к плохим результатам работы алгоритма, поэтому зачастую проводят несколько прогонов алгоритма с разными значениями <math>k</math>, чтобы подобрать оптимальный;
 
# Алгоритм может сойтись к локальному минимуму (достижение глобального минимума не гарантируется);
 
# Алгоритм может сойтись к локальному минимуму (достижение глобального минимума не гарантируется);
 
# Результат сильно зависит от выбора начальных значений центров масс кластеров. Существует улучшенная версия алгоритма - <b>k-means++ </b><ref>[http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf Arthur, D.; Vassilvitskii, S. (2007). "k-means++: the advantages of careful seeding". Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms. Society for Industrial and Applied Mathematics Philadelphia, PA, USA. pp. 1027–1035.]</ref>, которая предлагает свой способ нахождения начальных оптимальных значений центров масс кластеров;
 
# Результат сильно зависит от выбора начальных значений центров масс кластеров. Существует улучшенная версия алгоритма - <b>k-means++ </b><ref>[http://ilpubs.stanford.edu:8090/778/1/2006-13.pdf Arthur, D.; Vassilvitskii, S. (2007). "k-means++: the advantages of careful seeding". Proceedings of the eighteenth annual ACM-SIAM symposium on Discrete algorithms. Society for Industrial and Applied Mathematics Philadelphia, PA, USA. pp. 1027–1035.]</ref>, которая предлагает свой способ нахождения начальных оптимальных значений центров масс кластеров;
Строка 173: Строка 193:
 
;[[глоссарий#Устойчивость|''Устойчивость'']]: Алгоритм является устойчивым к погрешностям во входных данных, так как при вычислении центров кластеров расстояния между объектами усредняются, что приводит к уменьшению ошибки. Алгоритм также является устойчивым к погрешностям, допускаемых при вычислениях, так как операция сложения на каждой новой итерации выполняется над входными данными.  
 
;[[глоссарий#Устойчивость|''Устойчивость'']]: Алгоритм является устойчивым к погрешностям во входных данных, так как при вычислении центров кластеров расстояния между объектами усредняются, что приводит к уменьшению ошибки. Алгоритм также является устойчивым к погрешностям, допускаемых при вычислениях, так как операция сложения на каждой новой итерации выполняется над входными данными.  
  
;Сбалансированность: todo
+
;Сбалансированность: <p>Множество входных векторов можно равномерно разделить между параллельными вычислителями. Центры всех кластеров можно распространить на все вычислители (так как их заведомо меньше, чем число входных векторов). В таком случае на каждой итерации определение для каждого объекта ближайшего кластера представляет собой вычисление расстояния от каждого входного вектора, находящегося на вычислителе, до каждого кластера, и является сбалансированной операцией. После данного этапа происходит пересчёт центров кластеров. Данная операция является несбалансированной в случаях, когда число  объектов в одном из кластеров значительно больше числа объектов в других кластерах, из-за чего один кластер занимает большое число вычислителей, а остальные занимают по одному или несколько вычислителей. В таком случае для данного кластера потребуется вычислить центр [[Нахождение_частных_сумм_элементов_массива_сдваиванием | алгоритмом нахождения частичной суммы элементов массива путем сдваивания]]. Данная операция представляет несбалансированность порядка <math>O(\log (kn))</math>. После данного шага происходит сбалансированная операция рассылки центров кластеров на все вычислители.</p><p>В идеальном случае параллелизации алгоритм не является сбалансированным, однако в реальных задачах, когда <math>n >> p</math>, где <math>p</math> - количество вычислителей, эта несбалансированность сглаживается наличием объектов из каждого кластера на каждом вычислителе</p>
  
;Соотношение последовательной и параллельной сложности: <math>{O(n*d*k*i) \over O(log(n*d*k)*i)} \thicksim O \bigg( {n*d*k \over log(n*d*k)} \bigg)</math>
+
;Соотношение последовательной и параллельной сложности: <math>{O(iknd) \over O(i \cdot \log(knd))} \thicksim O \bigg( {knd \over \log(knd)} \bigg)</math>
  
;[[глоссарий#Вычислительная мощность|''Вычислительная мощность'']]: <math>{O(n*d*k*i) \over n*d+n+1} \thicksim {O(d*k*i) \over d+1} \thicksim O(k*i)</math>, где <math>n</math> - число входных векторов, <math>d</math> - размерность векторов, <math>k</math> - число кластеров, <math>i</math> - число итераций, требуемое для сходимости
+
;[[глоссарий#Вычислительная мощность|''Вычислительная мощность'']]: <p>Последовательный алгоритм: <math>{O(iknd) \over nd+n+1} \thicksim {O(ikd) \over d+1} \thicksim O(ik)</math>, где <math>n</math> - число входных векторов, <math>d</math> - размерность векторов, <math>k</math> - число кластеров, <math>i</math> - число итераций, требуемое для сходимости</p><p>Параллельный алгоритм: <math>O(i \cdot \log(knd)) \over nd+n+1</math></p>
  
 
;[[глоссарий#Детерминированность|''Детерминированность'']]: Алгоритм является недетерминированным, так как результат зависит от выбора исходных центров кластеров, а их оптимальный выбор неизвестен.
 
;[[глоссарий#Детерминированность|''Детерминированность'']]: Алгоритм является недетерминированным, так как результат зависит от выбора исходных центров кластеров, а их оптимальный выбор неизвестен.
  
 
== Программная реализация алгоритма ==
 
== Программная реализация алгоритма ==
 +
=== Особенности реализации последовательного алгоритма ===
 +
=== Локальность данных и вычислений ===
 +
==== Локальность реализации алгоритма ====
 +
===== Структура обращений в память и качественная оценка локальности =====
 +
===== Количественная оценка локальности =====
 +
=== Возможные способы и особенности параллельной реализации алгоритма ===
 +
=== Масштабируемость алгоритма и его реализации ===
 +
Исследование проводилось на суперкомпьютере "Ломоносов" [http://parallel.ru/cluster Суперкомпьютерного комплекса Московского Государственного Университета имени М.В. Ломоносова]
 +
 +
<p>Набор данных для тестирования состоял из 946000 векторов размерности 2 (координаты на сфере)</p>
 +
<p>Набор и границы значений изменяемых параметров запуска реализации алгоритма:</p>
  
=== Масштабируемость алгоритма и его реализации ===
+
* число процессов (виртуальных ядер) [8 : 512];
 +
* число кластеров [128 : 384].
 +
В результате проведённых экспериментов был получен следующий диапазон эффективности реализации алгоритма:
 +
 
 +
* минимальная эффективность реализации <math>2,47%</math> достигается при делении исходных данных на 128 кластеров с использованием 512 процессов;
 +
* максимальная эффективность реализации <math>7,13%</math> достигается при делении исходных данных на 352 кластера с использованием 8 процессов.
 +
На рисунках 5 и 6 соответственно представлены графики зависимости производительности и эффективности параллельной реализации k means от числа кластеров  и числа процессов.
 +
[[file:kmeans_performance.jpg|thumb|center|720px|Рисунок 5. График зависимости производительности параллельной реализации алгоритма от числа кластеров и числа процессов. ]]
 +
По рис. 5 можно отметить практически полное отсутствие роста производительности с увеличением числа процессов от 256 до 512 при минимальном размере задачи. Это связано с быстрым ростом накладных расходов по отношению к крайне низкому объёму вычислений. При росте размера задачи данный эффект пропадает, и при одновременном пропорциональном увеличении числа кластеров и числа процессов рост производительности становится близким к линейному.
 +
[[file:kmeans_efficiency.jpg|thumb|center|720px|Рисунок 6. График зависимости эффективности параллельной реализации алгоритма от числа кластеров и числа процессов. ]]
 +
Исследовалась [https://github.com/like-a-bauss/kmeans параллельная реализация алгоритма k means на MPI].
 +
<p>Были получены следующие оценки масштабируемости реализации алгоритма k means:</p>
 +
*<i>По числу процессов:</i> <math>-0.02209</math>. Следовательно, с ростом числа процессов эффективность уменьшается. На рис. 6 можно наблюдать плавное и равномерное снижение производительности по мере увеличения числа процессов при неизменном числе кластеров, что свидетельствует об относительно невысоком росте накладных расходов на передачу данных между процессами и преобладании объёма вычислений над объёмом пересылок данных по сети.
 +
*<i>По размеру задачи:</i> <math>0.01252</math>. Следовательно, с ростом размера задачи (числа кластеров) эффективность увеличивается. При этом объём пересылок данных по сети пропорционален <math>(n + k) \cdot p</math> (где <math>k</math> - число кластеров, <math>n</math> - число входных векторов, <math>p</math> - число процессов) таким образом, поскольку <math>k << n</math>, рост накладных расходов с ростом числа кластеров при неизменном числе процессов и входных векторов представляет собой незначительную величину.
 +
*<i>Общая оценка:</i> <math>-0.00081</math>. Таким образом, с ростом и размера задачи, и числа процессов эффективность уменьшается. Это связано с тем, что отношение объёма вычислений к объёму передаваемых данных изменяется пропорционально <math>{kn \over (n + k) \cdot p} \thicksim {k \over p}</math>, что представляет собой невысокий коэффициент, но при этом позволяет параллельной реализации не деградировать до нулевой эффективности при значительном увеличении числа процессов.
  
 +
=== Динамические характеристики и эффективность реализации алгоритма ===
 +
=== Выводы для классов архитектур ===
 
=== Существующие реализации алгоритма ===
 
=== Существующие реализации алгоритма ===
  

Текущая версия на 17:16, 19 декабря 2016

Symbol confirmed.svgЭта работа успешно выполнена
Преподавателю: в основное пространство, в подстраницу

Данное задание было проверено и зачтено.
Проверено Konshin и Zhum.





Алгоритм k средних (k means)
Последовательный алгоритм
Последовательная сложность [math]O(iknd)[/math]
Объём входных данных [math] nd [/math]
Объём выходных данных [math] n [/math]


Авторы страницы: Валуйская Я.А., Глотов Е.С.

Вклад в основную часть работы (сбор и анализ данных) у авторов одинаков, четких разграничений по пунктам статьи сделать невозможно. Итоговое оформление статьи выполнила Валуйская Я.А.


Содержание

1 Свойства и структура алгоритма

1.1 Общее описание алгоритма

Алгоритм k-means (k средних) - один из наиболее популярных алгоритмов кластеризации. Наиболее простой, но в то же время достаточно неточный метод кластеризации в классической реализации.

Алгоритм был разработан в 1956 году математиком Гуго Штейнгаузом [1], и почти одновременно его изобрел Стюарт Ллойд [2]. Особую популярность алгоритм снискал после работы Джеймса Маккуина [3]

Алгоритм кластеризации k-means решает задачу распределения [math]N[/math] наблюдений (элементов векторного пространства) на заранее известное число кластеров [math]K[/math]. Действие алгоритма таково, что он стремится минимизировать среднеквадратичное отклонение на точках каждого кластера.

Алгоритм k-means является итерационным алгоритмом кластеризации. На каждой итерации происходит пересчет центра масс для каждого кластера, полученного на предыдущем шаге, затем исходные векторы заново распределяются по кластерам в соответствии с тем, какой из новых центров оказался ближе по выбранной метрике. Алгоритм завершается, когда на какой-то итерации не происходит изменения кластеров.

1.2 Математическое описание алгоритма

Исходные данные:

  • множество наблюдений [math]X = \{x_1, x_2, ..., x_n\}[/math], где каждое наблюдение [math]x_i \in R^d, i = 1, ..., n[/math];
  • количество кластеров [math]k \in N, k \leq n[/math]

Обозначения:

  • [math]S = \{S_1, S_2, ..., S_k \} [/math] - множество кластеров, которые удовлетворяют следующим условиям:
    • [math]S_i \bigcap S_j = \emptyset, i \neq j[/math];
    • [math]X = {\bigcup \limits _{i = 1}^k S_i} [/math].
  • [math]\mu_i \in R^d, i = 1, ..., k[/math] - центр масс кластера [math]S_i[/math]
  • [math]d(u, v)[/math] - расстояние между векторами [math]u \in R^m, v \in R^m[/math]

Выходные данные:

  • [math]L = (l_1, l_2, ..., l_n)[/math] - набор меток, где метка [math]l_i \in N_{[1, k]}[/math] - является порядковым номером кластера, к которому принадлежит вектор [math]x_i[/math]: [math] x_i \in S_{l_i}[/math]


Цель алгоритма k-means - распределить наблюдения из входного множества [math]X[/math] по [math]k[/math] кластерам [math]S = \{S_1, S_2, ..., S_k \} [/math] таким образом, чтобы сумма квадратов расстояний от каждой точки кластера до его центра масс по всем кластерам была минимальной:

[math]\arg\min_{S} \sum_{i=1}^{k} \sum_{x \in S_i} (d(x, \mu_i))^2 [/math],


Алгоритм состоит из следующих шагов:

  1. Инициализация центров масс
    На данном шаге задаются начальные значения центров масс [math] \mu_1^1, ..., \mu_k^1[/math]. Существует несколько способов их выбора. Они будут рассмотрены ниже.
  2. t-ый шаг итерации:
    • Распределение векторов по кластерам
      На данном шаге каждый вектор [math]x_i \in X[/math] распределяется в свой кластер [math]S_j^t[/math] так, что:
      [math]l_i^t = \arg \min_{j} (d(x_i, \mu_j^t))^2 , j = 1, ..., k; i = 1, ..., n[/math]
    • Пересчет центров масс кластеров
      На данном шаге происходит пересчет центров масс кластеров, полученных на предыдущем этапе:
      [math]\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x[/math]; [math]i = 1, ..., k[/math]
  3. Критерий останова
    [math]\mu_i^t = \mu_i^{t+1},[/math] для всех [math]i = 1, ..., k[/math]


Способы инициализации начальных данных:

  1. Метод Forgy
    Случайным образом выбираются [math]k[/math] векторов из множества наблюдений [math]X[/math]. Эти вектора используются в качестве центров масс кластеров [math]\mu_1, ..., \mu_k[/math]
  2. Метод случайного разбиения (Random Partition) [4]
    Каждый вектор [math]x_i, i = 1, .., n[/math] случайным образом распределяется в кластер [math]S_j, j = 1, .., k[/math], затем для каждого кластера вычисляется его центр масс [math]\mu_j[/math].

1.3 Вычислительное ядро алгоритма

Вычислительном ядром алгоритма является шаг 2, состоящий из следующих этапов:

  1. распределение векторов по кластерам;
  2. пересчет центров масс кластеров.

Распределение векторов по кластерам заключается в следующем: для каждого вектора [math]x_i \in X, i = 1, ..., n[/math] необходимо посчитать расстояние между этим вектором и центром масс кластера [math]\mu_j^t, j = 1, ..., k[/math]. Следовательно, на каждой итерации необходимо выполнить [math]nk[/math] операций вычисления расстояния между векторами.

Пересчет центров масс кластеров заключается в следующем: для каждого кластера [math]S_i^t \in S, i = 1, ..., k[/math] необходимо пересчитать кластер по формуле, приведенной в пункте выше. Следовательно, на каждой итерации необходимо выполнить [math]k[/math] операций пересчета центров масс кластеров.

1.4 Макроструктура алгоритма

  1. Макрооперация "Расстояние между векторами"
    В данном алгоритме используется Евклидова метрика:
    [math]v \in R^m[/math] и [math]u \in R^m: d(v, u) = \sqrt{\sum_{j=1}^{m} (v_j - u_j)^2} [/math];
  2. Макрооперация "Пересчет центра масс кластера"
    [math]\mu_i^{t+1} = \frac{1}{|S_i^t|} \sum_{x \in S_i^t} x[/math]

Таким образом, алгоритм в терминах макроопераций выглядит следующим образом:

  1. Инициализация центров масс

    Метод Forgy: центры масс [math]\mu_j^1, j = 1, ..., k[/math] выбираются случайным образом.

    Метод случайного разбиения: вектора [math]x_i \in X[/math] случайным образом распределяются по кластерам. Для каждого кластера выполняется макрооперация "Пересчет центра масс кластера" для нахождения [math]\mu_j^1[/math].

  2. t-ый шаг итерации:
    • Распределение векторов по кластерам
      На данном шаге для каждого вектора [math]x_i \in X[/math] k раз выполняется макрооперация "Расстояние между векторами" для нахождения расстояния между [math]x_i[/math] и [math]\mu_j^t, j = 1, ..., k[/math]. Затем для каждого вектора [math]x_i[/math] находится минимальное расстояние.
    • Пересчет центров масс кластеров
      На данном шаге для каждого кластера выполняется макрооперация "Пересчет центра масс" для нахождения [math]\mu_j^{t+1}[/math].
  3. Критерий останова
    [math]\mu_i^t = \mu_i^{t+1},[/math] для всех [math]i = 1, ..., k[/math]

1.5 Схема реализации последовательного алгоритма

Псевдокод алгоритма:

Входные данные  : Множество векторов [math]X[/math], количество кластеров [math]k[/math]
Выходные данные : Набор меток [math]L[/math] принадлежности к кластеру
1  Инициализация центров масс [math]\mu_i^1, i = 1, ..., k[/math];
2  t := 1;
3  Для каждого вектора [math]x_i \in X, i = 1, ..., n:[/math] [math]l_i^t = \arg \min_{j} (d(x_i,\mu_j^t))^2 , j = 1, ..., k[/math];
4  Для каждого кластера [math]S_i^t, i = 1, ..., k[/math] выполняется макрооперация "Пересчет центра масс";
5  if ([math]\exists i = 1, ..., k: \mu_i^t \neq \mu_i^{t+1}[/math]) {
6      t := t + 1;
7      goto 3;
8  } else {
9      stop;
10 };

Существующие способы оптимизации алгоритма [5] [6] [7]

1.6 Последовательная сложность алгоритма

Подсчитаем количество операций для каждого шага алгоритма.

  1. Инициализация:
    • Метод Forgy: [math]N_{\rm init} = 0[/math]
    • Метод случайного разбиения:
      • Количество операций деления: [math]N^{/}_{\rm init} = kd[/math];
      • Количество операций сложения: [math]N^{+}_{\rm init} = d(n - k)[/math].
  2. Распределение [math]n[/math] векторов [math]x_i \in R^d[/math] по [math]k[/math] кластерам на одной итерации:
    • Количество операций умножения: [math]N^{*}_{\rm distr} = knd[/math];
    • Количество операций сложения: [math]N^{+}_{\rm distr} = kn(d - 1)[/math];
    • Количество операций вычитания: [math]N^{-}_{\rm distr} = knd[/math];
    • Количество операций взятия минимума: [math]N^{\rm min}_{\rm distr} = n(k - 1)[/math].
  3. Пересчет центра масс [math]\mu_i \in R^d[/math] для [math]k[/math] кластеров на одной итерации:
    • Количество операций деления: [math]N^{/}_{\rm update} = kd[/math];
    • Количество операций сложения: [math]N^{+}_{\rm update} = d(n - k)[/math].

Следовательно, в случае если алгоритм сошелся за [math]i[/math] итераций, получаем:

  • Операций сложения/вычитания: [math]N^{+,-}_{\rm k-means} = N^{+}_{\rm init} + i \cdot (N^{+}_{\rm distr} + N^{-}_{\rm distr} + N^{+}_{\rm update}) = d(n - k) + i \cdot (kn(d-1)+knd+d(n-k)) \thicksim O(iknd)[/math]
  • Операций умножения/деления: [math]N^{*,/}_{\rm k-means} = N^{/}_{\rm init} + i \cdot (N^{*}_{\rm distr} + N^{/}_{\rm update}) = kd + i \cdot (knd+kd) \thicksim O(iknd)[/math]

Таким образом, последовательная сложность алгоритма k-means для [math]n[/math] [math]d[/math]-мерных векторов и [math]k[/math] кластеров за [math]i[/math] итераций, требуемых для сходимости алгоритма:

[math]O(iknd)[/math]

1.7 Информационный граф

Рассмотрим информационный граф алгоритма k-means. На рисунке 1 представлена общая схема алгоритма k-means, согласно разделу "Схема реализации последовательного алгоритма". Вершина [math]\rm Init[/math] - этап инициализации. Вершина [math]\rm Distribute[/math] - этап распределения векторов по кластерам. Вершина [math]\rm Update[/math] - этап пересчета центров масс кластеров.

Рисунок 1. Общая схема алгоритма k-means


На рисунке 2 представлен информационный граф этапа распределения векторов по кластерам. На вход данному этапу подают множество векторов [math]x_1, x_2, ..., x_n[/math] из входных данных и множество центров масс кластеров [math]\mu^t_1, ..., \mu^t_k[/math], полученных с шага инициализации или с предыдущей итерации. Для каждой пары векторов [math]x_i, \mu^t_j, i = 1, ..., n; j = 1,..., k[/math] считается расстояние между ними (вершина [math]\rm dist[/math]). Информационный граф вычисления расстояния между двумя векторами рассмотрен ниже (рисунок 3). Затем выполняется операция нахождения минимума (вершина [math]\rm min[/math]), в результате которой каждому вектору [math]x_i, i = 1, ..., n[/math] приписывается метка [math]l^t_i[/math], равная порядковому номеру кластера [math]S^t_j, j = 1 ..., k[/math], которому принадлежит данный вектор: [math]x_i \in S^t_j[/math].

Рисунок 2. Схема этапа распределения векторов по кластерам


На рисунке 3 представлен информационный граф вычисления расстояния между двумя векторами [math]x_i = (x_{i1}, ..., x_{id}), i = 1, ..., n[/math] и [math]\mu^t_j = (\mu^t_{j1}, ..., \mu^t_{jd}) , j = 1, ..., k[/math]. Выполняется операция поэлементного вычитания двух векторов (вершины [math]-[/math]), затем возведение в квадрат (вершины [math]\rm sqr[/math]), затем суммирование полученных результатов (вершина [math]+[/math]). На выходе получаем квадрат расстояния между векторами.

Рисунок 3. Схема вычисления расстояния между двумя векторами


На рисунке 4 представлен информационный граф этапа пересчета центров масс кластеров [math]\mu^t_1, \mu^t_2,..., \mu^t_k[/math]. Для каждого кластера [math]S^t_j, j = 1, ..., k[/math] выполняется операция суммирования (вершина [math]\rm sum_j[/math]) векторов, которые принадлежат данному кластеру. Кроме того, для каждого кластера выполняется операция определения количества элементов в нем (вершина [math]\rm size_j[/math]). Затем выполняется операция деления (вершина [math]/[/math]). На выходе получаем новые центры масс кластеров [math]\mu^{t+1}_1, \mu^{t+1}_2,..., \mu^{t+1}_k[/math].

Рисунок 4. Схема этапа пересчета центров масс кластеров

1.8 Ресурс параллелизма алгоритма

Вычислительное ядро алгоритма имеет большие возможности для параллелизма:

  • Распределение векторов по кластерам: вектора [math]x_i, x_j, i \neq j; i, j = 1, ..., n[/math] могут быть распределены по кластерам независимо друг от друга;
  • Пересчет центров масс кластеров: центры масс кластеров [math]\mu_i, \mu_j, i \neq j; i, j = 1, ..., k[/math] могут быть пересчитаны независимо друг от друга.

Однако, этап пересчета центров масс кластеров зависит от этапа распределения векторов по кластерам, так что эти этапы должны выполняться строго последовательно. Таким образом, параллельная сложность алгоритма может быть вычислена следующим образом:
[math]N_{\rm k-means} = i \cdot (N_{\rm distr} + N_{\rm update})[/math], где [math]i[/math] - количество итераций, необходимых для сходимости алгоритма, [math]N_{\rm distr}[/math] - параллельная сложность этапа распределения векторов по кластерам, [math]N_{\rm update}[/math] - параллельная сложность этапа пересчета центров масс кластеров.


Рассчитаем параллельную сложность [math]N_{\rm distr}[/math] этапа распределения векторов по кластерам в предположении наличия неограниченного числа процессоров для одной итерации. Для каждой пары векторов [math](x_i, \mu_j), i = 1,...,n; j = 1, ..., k[/math] операция вычисления расстояния выполняется независимо.

  • При выполнении операция вычисления расстояния операции вычитания и умножения выполняются независимо друг от друга, поэтому требуется только одно вычитание и одно умножение. Число операций сложения: [math]N^{+}_{\rm distr} = O(\log(d))[/math] согласно алгоритму нахождения частичной суммы элементов массива путем сдваивания;
  • Для нахождения минимума среди [math]k[/math] элементов требуется [math]\log(k))[/math] операций сравнения.

Таким образом, получаем, что [math]N_{\rm distr} = O(\log(kd))[/math]


Рассчитаем параллельную сложность [math]N_{\rm update}[/math] этапа пересчета центров масс кластеров в предположении наличия неограниченного числа процессоров для одной итерации. Нахождение центра масс [math]\mu_j, j = 1, ..., k[/math] кластера [math]S_j[/math] может выполняться независимо от нахождения центров масс других кластеров. Таким образом, получаем, что [math]N_{\rm update} \leq O(\log(n))[/math] в худшем случае, когда почти все векторы находятся в одном кластере.


Итоговая параллельная сложность алгоритма:
[math]N_{\rm k-means} \leq i \cdot (\log(kd) + \log(n)) \thicksim O(i \cdot \log(knd)); i[/math] - количество итераций, необходимых для сходимости.

1.9 Входные и выходные данные алгоритма

Входные данные:

  • [math]n[/math] векторов [math]x_i \in R^d, i = 1, ..., n[/math];
  • число кластеров [math]k \in N_{[1,n]}[/math].

Объем входных данных: [math]nd[/math] действительных чисел, 1 целое положительное число

Выходные данные:

  • вектор меток [math]L \in N^n[/math].

Объем выходных данных: [math]n[/math] целых неотрицательных чисел.

1.10 Свойства алгоритма

Достоинства
  1. Алгоритм прозрачный и понятный, за счет чего очень прост в реализации;
  2. Алгоритм имеет высокую скорость работы в случае выбора оптимальных начальных значений центров масс кластеров.
Недостатки
  1. Число кластеров является входным аргументом алгоритма. Таким образом, некорректный выбор данного параметра может привести к плохим результатам работы алгоритма, поэтому зачастую проводят несколько прогонов алгоритма с разными значениями [math]k[/math], чтобы подобрать оптимальный;
  2. Алгоритм может сойтись к локальному минимуму (достижение глобального минимума не гарантируется);
  3. Результат сильно зависит от выбора начальных значений центров масс кластеров. Существует улучшенная версия алгоритма - k-means++ [8], которая предлагает свой способ нахождения начальных оптимальных значений центров масс кластеров;
  4. Чувствительность к выбросам и шумам, так как они учитываются при вычислении центров масс кластеров.
Устойчивость
Алгоритм является устойчивым к погрешностям во входных данных, так как при вычислении центров кластеров расстояния между объектами усредняются, что приводит к уменьшению ошибки. Алгоритм также является устойчивым к погрешностям, допускаемых при вычислениях, так как операция сложения на каждой новой итерации выполняется над входными данными.
Сбалансированность

Множество входных векторов можно равномерно разделить между параллельными вычислителями. Центры всех кластеров можно распространить на все вычислители (так как их заведомо меньше, чем число входных векторов). В таком случае на каждой итерации определение для каждого объекта ближайшего кластера представляет собой вычисление расстояния от каждого входного вектора, находящегося на вычислителе, до каждого кластера, и является сбалансированной операцией. После данного этапа происходит пересчёт центров кластеров. Данная операция является несбалансированной в случаях, когда число объектов в одном из кластеров значительно больше числа объектов в других кластерах, из-за чего один кластер занимает большое число вычислителей, а остальные занимают по одному или несколько вычислителей. В таком случае для данного кластера потребуется вычислить центр алгоритмом нахождения частичной суммы элементов массива путем сдваивания. Данная операция представляет несбалансированность порядка [math]O(\log (kn))[/math]. После данного шага происходит сбалансированная операция рассылки центров кластеров на все вычислители.

В идеальном случае параллелизации алгоритм не является сбалансированным, однако в реальных задачах, когда [math]n \gt \gt p[/math], где [math]p[/math] - количество вычислителей, эта несбалансированность сглаживается наличием объектов из каждого кластера на каждом вычислителе

Соотношение последовательной и параллельной сложности
[math]{O(iknd) \over O(i \cdot \log(knd))} \thicksim O \bigg( {knd \over \log(knd)} \bigg)[/math]
Вычислительная мощность

Последовательный алгоритм: [math]{O(iknd) \over nd+n+1} \thicksim {O(ikd) \over d+1} \thicksim O(ik)[/math], где [math]n[/math] - число входных векторов, [math]d[/math] - размерность векторов, [math]k[/math] - число кластеров, [math]i[/math] - число итераций, требуемое для сходимости

Параллельный алгоритм: [math]O(i \cdot \log(knd)) \over nd+n+1[/math]

Детерминированность
Алгоритм является недетерминированным, так как результат зависит от выбора исходных центров кластеров, а их оптимальный выбор неизвестен.

2 Программная реализация алгоритма

2.1 Особенности реализации последовательного алгоритма

2.2 Локальность данных и вычислений

2.2.1 Локальность реализации алгоритма

2.2.1.1 Структура обращений в память и качественная оценка локальности
2.2.1.2 Количественная оценка локальности

2.3 Возможные способы и особенности параллельной реализации алгоритма

2.4 Масштабируемость алгоритма и его реализации

Исследование проводилось на суперкомпьютере "Ломоносов" Суперкомпьютерного комплекса Московского Государственного Университета имени М.В. Ломоносова

Набор данных для тестирования состоял из 946000 векторов размерности 2 (координаты на сфере)

Набор и границы значений изменяемых параметров запуска реализации алгоритма:

  • число процессов (виртуальных ядер) [8 : 512];
  • число кластеров [128 : 384].

В результате проведённых экспериментов был получен следующий диапазон эффективности реализации алгоритма:

  • минимальная эффективность реализации [math]2,47%[/math] достигается при делении исходных данных на 128 кластеров с использованием 512 процессов;
  • максимальная эффективность реализации [math]7,13%[/math] достигается при делении исходных данных на 352 кластера с использованием 8 процессов.

На рисунках 5 и 6 соответственно представлены графики зависимости производительности и эффективности параллельной реализации k means от числа кластеров и числа процессов.

Рисунок 5. График зависимости производительности параллельной реализации алгоритма от числа кластеров и числа процессов.

По рис. 5 можно отметить практически полное отсутствие роста производительности с увеличением числа процессов от 256 до 512 при минимальном размере задачи. Это связано с быстрым ростом накладных расходов по отношению к крайне низкому объёму вычислений. При росте размера задачи данный эффект пропадает, и при одновременном пропорциональном увеличении числа кластеров и числа процессов рост производительности становится близким к линейному.

Рисунок 6. График зависимости эффективности параллельной реализации алгоритма от числа кластеров и числа процессов.

Исследовалась параллельная реализация алгоритма k means на MPI.

Были получены следующие оценки масштабируемости реализации алгоритма k means:

  • По числу процессов: [math]-0.02209[/math]. Следовательно, с ростом числа процессов эффективность уменьшается. На рис. 6 можно наблюдать плавное и равномерное снижение производительности по мере увеличения числа процессов при неизменном числе кластеров, что свидетельствует об относительно невысоком росте накладных расходов на передачу данных между процессами и преобладании объёма вычислений над объёмом пересылок данных по сети.
  • По размеру задачи: [math]0.01252[/math]. Следовательно, с ростом размера задачи (числа кластеров) эффективность увеличивается. При этом объём пересылок данных по сети пропорционален [math](n + k) \cdot p[/math] (где [math]k[/math] - число кластеров, [math]n[/math] - число входных векторов, [math]p[/math] - число процессов) таким образом, поскольку [math]k \lt \lt n[/math], рост накладных расходов с ростом числа кластеров при неизменном числе процессов и входных векторов представляет собой незначительную величину.
  • Общая оценка: [math]-0.00081[/math]. Таким образом, с ростом и размера задачи, и числа процессов эффективность уменьшается. Это связано с тем, что отношение объёма вычислений к объёму передаваемых данных изменяется пропорционально [math]{kn \over (n + k) \cdot p} \thicksim {k \over p}[/math], что представляет собой невысокий коэффициент, но при этом позволяет параллельной реализации не деградировать до нулевой эффективности при значительном увеличении числа процессов.

2.5 Динамические характеристики и эффективность реализации алгоритма

2.6 Выводы для классов архитектур

2.7 Существующие реализации алгоритма

Существуют следующие Open Source реализации алгоритма:

  • ELKI - содержит реализацию алгоритма k-means на языке Java (в том числе реализацию улучшенного алгоритма k-means++)
  • Weka - содержит реализацию k-means на языке Java
  • Apache Mahout - содержит реализацию k-means в парадигме MapReduce
  • Spark Mllib - содержит распределенную реализацию k-means
  • Accord.NET - содержит реализацию k-means на C# (в том числе реализацию улучшенного алгоритма k-means++)
  • MLPACK - содержит реализацию k-means на языке C++
  • OpenCV - содержит реализацию k-means на C++. А также есть обертки для языков Python и Java
  • SciPy - содержит реализацию k-means на языке Python
  • Scikit-learn - содержит реализацию k-means на языке Python
  • Julia - содержит реализацию алгоритма k-means на языке Julia
  • Octave - содержит реализацию k-means на языке Octave
  • R - содержит реализацию k-means на языке R
  • Torch - содержит реализацию k-means на языке Lua

3 Литература