Apriori — масштабируемый алгоритм поиска ассоциативных правил

15 апреля 2020
0 комментариев

Apriori – один из наиболее популярных алгоритмов поиска ассоциативных правил. Благодаря использованию свойства анти-монотонности, он способен обрабатывать большие объемы данных за приемлемое время. Разбираем работу алгоритма и особенности его реализации.

Современные базы данных имеют очень большие размеры, достигающие гига- и терабайтов, и тенденцию к дальнейшему увеличению. И поэтому, для нахождения ассоциативных правил требуются эффективные масштабируемые алгоритмы, позволяющие решить задачу за приемлемое время. Об одном из таких алгоритмов и пойдет речь в данной статье. Мы опишем алгоритм Apriori. Терминология и обозначения, которыми мы будем пользоваться, даны в статье «Введение в анализ ассоциативных правил».

Для того чтобы было возможно применить алгоритм, необходимо провести предобработку данных:

  • привести все данные к бинарному виду;
  • изменить структуру данных.
Номер транзакцииНаименование элементаКоличество
1001А2
1001D3
1001E1
1002А2
1002F1
1003B2
1003А2
1003C2
.........

Таблица 1. Обычный вид базы данных транзакций

TIDABCDEFGHIK...
10011001100000...
10021000010000...
10031110000010...

Таблица 2. Нормализованный вид

Количество столбцов в таблице равно количеству элементов, присутствующих во множестве транзакций  D. Каждая запись соответствует транзакции, где в соответствующем столбце стоит 1, если элемент присутствует в транзакции, и 0 — в противном случае. (см. Определение 1). Заметим, что исходный вид таблицы может быть отличным от приведенного в Таблице 1. Главное, чтобы данные были преобразованы к нормализованному виду, иначе алгоритм не применим.

Все элементы таблицы упорядочены в алфавитном порядке (если это числа, они должны быть упорядочены в числовом порядке). Это сделано неслучайно.

Итак, данные преобразованы, теперь можно приступить к описанию самого алгоритма. Как было сказано в предыдущей статье, такие алгоритмы работают в два этапа, не является исключением и рассматриваемый нами алгоритм Apriori.

На первом шаге необходимо найти часто встречающиеся наборы элементов, а затем, на втором, извлечь из них правила. Количество элементов в наборе будем называть размером набора, а набор, состоящий из k элементов, — k-элементным набором.

Свойство анти-монотонности

Выявление часто встречающихся наборов элементов — операция, требующая много вычислительных ресурсов и, соответственно, времени. Примитивный подход к решению данной задачи — простой перебор всех возможных наборов элементов. Это потребует O(^{|2I|}) операций, где |I| — количество элементов. Apriori использует одно из свойств поддержки, гласящее: поддержка любого набора элементов не может превышать минимальной поддержки любого из его подмножеств.

Например, поддержка 3-элементного набора { Хлеб, Масло, Молоко } будет всегда меньше или равна поддержке 2-элементных наборов { Хлеб, Масло }, { Хлеб, Молоко }, { Масло, Молоко }. Дело в том, что любая транзакция, содержащая { Хлеб, Масло, Молоко }, также должна содержать { Хлеб, Масло }, { Хлеб, Молоко }, { Масло, Молоко }, причем обратное не верно.

Это свойство носит название анти-монотонности и служит для снижения размерности пространства поиска. Не имей мы в наличии такого свойства, нахождение многоэлементных наборов было бы практически невыполнимой задачей в связи с экспоненциальным ростом вычислений.

Свойству анти-монотонности можно дать и другую формулировку: с ростом размера набора элементов поддержка уменьшается, либо остается такой же. Из всего вышесказанного следует, что любой k-элементный набор будет часто встречающимся тогда и только тогда, когда все его (k-1) -элементные подмножества будут часто встречающимися.

Все возможные наборы элементов из I можно представить в виде решетки, начинающейся с пустого множества, затем на 1 уровне — 1-элементные наборы, на 2-м — 2-элементные и т.д. На k уровне представлены k-элементные наборы, связанные со всеми своими (k-1) -элементными подмножествами.

Рассмотрим Рисунок 1, иллюстрирующий набор элементов I - {A, B, C, D}. Предположим, что набор из элементов \{ A, B \} имеет поддержку ниже заданного порога и, соответственно, не является часто встречающимся. Тогда, согласно свойству анти-монотонности, все его супермножества также не являются часто встречающимися и отбрасываются. Вся эта ветвь, начиная с \{ A, B \}, выделена синим. Использование этой эвристики позволяет существенно сократить пространство поиска.

Алгоритм Apriori

На первом шаге алгоритма подсчитываются 1-элементные часто встречающиеся наборы. Для этого необходимо пройтись по всему набору данных и подсчитать для них поддержку, т.е. сколько раз встречается в базе.

Следующие шаги будут состоять из двух частей: генерации потенциально часто встречающихся наборов элементов (их называют кандидатами) и подсчета поддержки для кандидатов.

Описанный выше алгоритм можно записать в виде следующего псевдокода:

  1. F_1 = \{ Часто встречающиеся 1-элементные наборы\};
  2.   \text{ для } (k=2, F_{k-1} \verb!<>! \varnothing, k\verb!++! )
  3.     \{
  4.       C_k = \{ Генерация кандидатов \ k на основе \ Apriorigen ( F_{k-1} ) \}
  5.        для всех транзакций t∈D
  6.       \{
  7.         C_t = subset(C_k,t) // Удаление избыточных правил
  8.           для всех кандидатов c \in C_t
  9.           c.count\verb!++!
  10.       \}
  11.       F_k = [c \in C_k | c.count > = minsupp ] // Отбор кандидатов
  12.     \}
  13. Результат \cup F_k

 

Опишем функцию генерации кандидатов. На этот раз нет никакой необходимости вновь обращаться к базе данных. Для того, чтобы получить k-элементные наборы, воспользуемся (k-1) -элементными наборами, которые были определены на предыдущем шаге и являются часто встречающимися.

Вспомним, что наш исходный набор хранится в упорядоченном виде. Генерация кандидатов также будет состоять из двух шагов.

Объединение. Каждый кандидат C_k будет формироваться путем расширения часто встречающегося набора размера (k-1) добавлением элемента из другого (k-1) -элементного набора. Приведем алгоритм этой функции Apriorigen в виде небольшого SQL-подобного запроса.

INSERT INTO

          C_k

SELECT

          p.item_1, p.item_2, ..., p.item_{k-1}, q.item_{k-1}

FROM

          F_{k-1} p, F_{k-1} q

WHERE

          p.item_1=q.item_1

          p.item_2=q.item_2,...,

          p.item_{k-2}=q.item_{k-2}

          p.item_{k-1}<q.item_{k-1}

Удаление избыточных правил. На основании свойства анти-монотонности, следует удалить все наборы c C_k, если хотя бы одно из его (k-1) подмножеств не является часто встречающимся.

После генерации кандидатов следующей задачей является подсчет поддержки для каждого кандидата. Очевидно, что количество кандидатов может быть очень большим и нужен эффективный способ подсчета. Самый тривиальный способ – сравнить каждую транзакцию с каждым кандидатом. Но это далеко не лучшее решение.

Гораздо быстрее и эффективнее использовать подход, основанный на хранении кандидатов в хэш-дереве. Внутренние узлы дерева содержат хэш-таблицы с указателями на потомков, а листья – на кандидатов. Это дерево нам пригодится для быстрого подсчета поддержки для кандидатов.

Хэш-дерево строится каждый раз, когда формируются кандидаты. Первоначально дерево состоит только из корня, который является листом, и не содержит никаких кандидатов-наборов. Каждый раз когда формируется новый кандидат, он заносится в корень дерева, и так до тех пор, пока количество кандидатов в корне-листе не превысит некоего порога.

Как только количество кандидатов становится больше порога, корень преобразуется в хэш-таблицу, т.е. становится внутренним узлом, и для него создаются потомки-листья. И все примеры распределяются по узлам-потомкам согласно хэш-значениям элементов, входящих в набор, и т.д. Каждый новый кандидат хэшируется на внутренних узлах, пока он не достигнет первого узла-листа, где он и будет храниться, пока количество наборов опять же не превысит порога.

Хэш-дерево с кандидатами-наборами построено, теперь, используя хэш-дерево, легко подсчитать поддержку для каждого кандидата. Для этого нужно «пропустить» каждую транзакцию через дерево и увеличить счетчики для тех кандидатов, чьи элементы также содержатся и в транзакции, т.е. C_k∩Ti=C_k. На корневом уровне хэш-функция применяется к каждому элементу из транзакции.

Далее, на втором уровне, хэш-функция применяется ко вторым элементам и т.д. На k-уровне хэшируется k-элемент. И так до тех пор, пока не достигнем листа. Если кандидат, хранящийся в листе, является подмножеством рассматриваемой транзакции, тогда увеличиваем счетчик поддержки этого кандидата на единицу.

После того, как каждая транзакция из исходного набора данных «пропущена» через дерево, можно проверить удовлетворяют ли значения поддержки кандидатов минимальному порогу. Кандидаты, для которых это условие выполняется, переносятся в разряд часто встречающихся. Кроме того, следует запомнить и поддержку набора, она нам пригодится при извлечении правил. Эти же действия применяются для нахождения (k+1) -элементных наборов и т.д.

После того как найдены все часто встречающиеся наборы элементов, можно приступить непосредственно к генерации правил.

Извлечение правил – менее трудоемкая задача. Во-первых, для подсчета достоверности правила достаточно знать поддержку самого набора и множества, лежащего в условии правила. Например, имеется часто встречающийся набор \{ A, B, C \} и требуется подсчитать достоверность для правила AB⇒C. Поддержка самого набора нам известна, но и его множество \{ A, B \}, лежащее в условии правила, также является часто встречающимся в силу свойства анти-монотонности, и значит его поддержка нам известна. Тогда мы легко сможем подсчитать достоверность. Это избавляет нас от нежелательного просмотра базы транзакций, который потребовался в том случае если бы это поддержка была неизвестна.

Чтобы извлечь правило из часто встречающегося набора F, следует найти все его непустые подмножества. И для каждого подмножества s мы сможем сформулировать правило s ⇒ (F - s), если достоверность правила conf(s ⇒ (F - s)) = supp(F)/supp(s) не меньше порога minconf.

Заметим, что числитель остается постоянным. Тогда достоверность имеет минимальное значение, если знаменатель имеет максимальное значение, а это происходит в том случае, когда в условии правила имеется набор, состоящий из одного элемента. Все супермножества данного множества имеют меньшую или равную поддержку и, соответственно, большее значение достоверности. Это свойство может быть использовано при извлечении правил.

Если мы начнем извлекать правила, рассматривая сначала только один элемент в условии правила, и это правило имеет необходимую поддержку, тогда все правила, где в условии стоят супермножества этого элемента, также имеют значение достоверности выше заданного порога. Например, если правило A ⇒ BCDE удовлетворяет минимальному порогу достоверности minconf, тогда AB ⇒ CDE также удовлетворяет.

Для того чтобы извлечь все правила используется рекурсивная процедура. Важное замечание: любое правило, составленное из часто встречающегося набора, должно содержать все элементы набора. Например, если набор состоит из элементов \{ A, B, C \} , то правило A⇒B не должно рассматриваться.

Литература

  1. R. Agrawal, R. Srikant. «Fast Discovery of Association Rules», In Proc. of the 20th International Conference on VLDB, Santiago, Chile, September 1994.
  2. R. Agrawal, T. Imielinski, A. Swami. 1993. Mining Associations between Sets of Items in Massive Databases. In Proc. of the 1993 ACM-SIGMOD Int’l Conf. on Management of Data, 207-216.

 

Другие материалы по теме:

Введение в анализ ассоциативных правил 

Выявление обобщенных ассоциативных правил

«Ситилинк» задействует Loginom в управлении товарными запасами

Поиск последовательных шаблонов| Часть 1

#ритейл#data mining

Смотрите также