Создание системы рекомендаций с использованием методов машинного обучения и Python

Вы собираетесь купить мобильный телефон на Amazon, и Amazon рекомендует вам купить защитное стекло для экрана и чехол вместе с ним. Это также дает вам скидку, если вы покупаете все эти три вещи вместе. Если вы идете на Youtube, чтобы посмотреть свою любимую песню, Youtube рекомендует вам посмотреть похожие другие песни, которые могут вам понравиться. В поиске Google в нижней части страницы результатов поиска отображаются похожие поисковые запросы. Вы везде получаете рекомендации; на Netflix, на Facebook, в Twitter, все сайты социальных сетей создали свои собственные рекомендательные системы. Не только на веб-сайтах, но и в супермаркетах вы найдете рекомендации, которые даются прямо или косвенно. В супермаркетах вещи, которые покупаются вместе, хранятся вместе или предлагают скидки, если вы покупаете вещи вместе. Как они узнают о ваших моделях покупок? Как они узнают о ваших симпатиях и антипатиях? Мы собираемся понять этот секрет в этой статье.

Рекомендательная система — важная и популярная область машинного обучения. Это помогло бизнесу увеличить продажи и прибыль. Сегодня мы не можем представить себе рынок, на котором не было бы рекомендательной системы. Эти компании анализируют данные о наших транзакциях, они пытаются вывести наши модели покупок. Из шаблонов покупок они создают правила ассоциации. Эти правила ассоциации затем используются для формирования рекомендаций и сделок. Существуют различные алгоритмы интеллектуального анализа данных, такие как k-means, Page Rank, интеллектуальный анализ данных EM и априори, которые используются для создания рекомендательных систем. В этой статье мы собираемся построить нашу рекомендательную систему, используя алгоритм Априори.

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

Алгоритм Apriori был предложен Агравалом и Срикантом в 1994 году. Apriori предназначен для работы с базами данных, содержащими транзакции (например, наборы товаров, купленных клиентами, сведения о посещении веб-сайтов или IP-адреса). Apriori использует подход «снизу вверх», когда часто встречающиеся подмножества расширяются по одному элементу за раз (шаг, известный как генерация кандидатов), и группы кандидатов проверяются на данных. Алгоритм завершается, когда не найдено дальнейших успешных расширений.

Как работает Априори?

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

Apriori использует три важных вычислительных фактора для извлечения правил ассоциации из набора входных данных. Этими тремя расчетными факторами являются поддержка, уверенность и подъем. Давайте посмотрим, как их вычислить ниже.

Поддержка: первый шаг Apriori

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

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

Предположим, мы хотим найти поддержку для позиции «томатный соус». Это можно рассчитать как:

Поддержка("томатный соус") = транзакции, содержащие("томатный соус") / общее количество транзакций;

В нашем случае есть три транзакции из пяти, в которых есть товар «томатный соус». Следовательно,

Поддержка («томатный соус») = 3 / 5 = 0,6;

Точно так же Apriori рассчитывает поддержку для всех элементов. См. рассчитанную поддержку для всех элементов в таблице ниже.

Следующим шагом является расчет поддержки для группы элементов, состоящей из двух элементов в группе. Когда в группе более одного элемента, она называется набором элементов. Например, itemset («хлеб, томатный соус»). При создании набора элементов порядок элементов не имеет значения. Набор предметов («хлеб, томатный соус») аналогичен набору предметов («томатный соус, хлеб»).

Формула для расчета поддержки для набора элементов аналогична формуле для элемента. Например, поддержка набора «хлеб, томатный соус» рассчитывается как:

Поддержка("хлеб , томатный соус") = транзакции, содержащие("хлеб , томатный соус") / общее количество транзакций;

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

Support(«хлеб, томатный соус») = 3 / 5 = 0,6;

Аналогичным образом Apriori вычисляет поддержку для всех наборов элементов размера 2. См. расчетную поддержку для всех наборов элементов в таблице ниже.

Точно так же Apriori вычисляет поддержку для всех возможных наборов элементов. Он продолжает генерировать наборы элементов размером от 1 до N-1, где N — общее количество элементов в самой крупной транзакции. В нашем примере все транзакции имеют одинаковое количество элементов, и это значение равно 3. Следовательно, в нашем случае значение N равно 3, следовательно, алгоритм будет генерировать наборы элементов только размера 1 и 2. Он остановится на 2, так как в нашем случае N-1 =2.

Уверенность: второй шаг априори

Уверенность в правиле (хлеб => томатный соус) относится к вероятности покупки томатного соуса, если покупается хлеб. Уверенность — это показатель того, как часто правило оказывалось верным. Уверенность рассчитывается путем нахождения количества транзакций, в которых хлеб и томатный соус покупаются вместе, деленного на общее количество транзакций, в которых покупают хлеб. Математически это представляется как

Надежность ( A=›B) = (транзакции, содержащие как (A, так и B)) / (транзакции, содержащие A);

Приведенное выше уравнение также можно представить в виде:

Уверенность (A=›B) = Поддержка (A и B))/Поддержка(A);

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

Возвращаясь к нашему примеру; есть три транзакции, в которых хлеб и томатный соус покупаются вместе, и четыре транзакции, в которых приносят хлеб. Следовательно ,

Уверенность (хлеб =› томатный соус) = 3/4 = 0,75

Уверенность может дать некоторые важные идеи, но она также имеет большой недостаток. Он учитывает только популярность набора товаров A (в нашем случае A — «хлеб»), а не популярность B (в нашем случае B — «томатный соус»). Если B (томатный соус) так же популярен, как A (хлеб), тогда будет более высокая вероятность того, что транзакция, содержащая A (хлеб), также будет содержать B (томатный соус), что повышает доверие. Этот недостаток преодолевается при расчете подъемной силы.

Лифт: Третий шаг Априори

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

При расчете прироста (A=›B) мы учитываем популярность товаров A и B. Этот коэффициент подъема показывает, насколько вероятно приобретение товара B при покупке A. Математически лифт выражается как

Подъем(A=›B) = Уверенность (A=›B) / Поддержка(B) ;

Or,

Подъем(A=›B) = Поддержка ( A=›B) / ( Поддержка(A) * Поддержка(B) );

Теперь вернемся к нашему примеру и рассчитаем рост для (хлеб => томатный соус).

Подъем (хлеб =› томатный соус) = Уверенность ( хлеб =› томатный соус) / Поддержка (томатный соус);

подъем(хлеб =› томатный соус) = 0,75 / 0,6 = 1,25

Значение подъема больше 1 означает, что предмет B, скорее всего, будет куплен, если будет куплен предмет A, а значение меньше 1 означает, что предмет B вряд ли будет куплен, если куплен предмет A.

Реализация Python:

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

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

Входной CSV-файл не имеет заголовка. Общее количество элементов в каждой строке может не совпадать. На изображении выше вы можете видеть, что клиент номер тринадцать купил пять товаров, а клиент номер шестнадцать купил только один товар. Вы можете найти образец файла данных и всю программу здесь https://github.com/srkhedkar/Recommender

Мы используем пакет apyori для расчета правила ассоциации. Если он не установлен в вашей системе, установите его с помощью команды "pip install apyori"

Теперь давайте напишем код на Python. Здесь мы импортировали необходимые пакеты, определили класс Recommender и определили его конструктор. При создании экземпляра объекта Recommender нам необходимо предоставить ему файл данных.

from apyori import apriori
from apyori import load_transactions

class Recommender():

    def __init__(self, inputFile):
        self.AssociationRulesDictionary = {} # holds final output
        self.dataFile = inputFile # input datafile in csv form        
        self.association_rules = [] # holds output from Apriori algo

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

def computeRules(self):
        """
        Copmputes all association rules.
        :return:
        """
        with open(self.dataFile ) as fileObj:
            transactions = list(load_transactions(fileObj, delimiter=","))
            # remove empty strings if any
            transactions_filtered = []
            for li in transactions:
                li = list(filter(None, li))
                transactions_filtered.append(li)
            # Following line does all computations
            # lift > 1 shows that there is a positive correlation within the itemset, i.e., items in the
            # itemset, are more likely to be bought together.
            # lift < 1 shows that there is a negative correlation within the itemset, i.e., items in the
            # itemset, are unlikely to be bought together.
            # hence we have set min_lift=1.0 to ignore all rules with lift < 1.0
            self.association_rules = apriori(transactions_filtered,       min_support=0.01, min_confidence=0.01, min_lift=1.0,max_length=None)

В приведенном выше коде видно, что все важные вычисления выполняются при вызове функции apriori(), которая исходит из импортированного пакета apyori. Нам нечего делать!! Обо всем позаботится apriori()!! Вот почему я люблю этот язык. Это так просто; это так мощно!! Экосистема настолько богата, что многие вещи уже реализованы. Это напоминает мне известную шутку о питонах. Однажды маленький Джони взлетел в небо!! Маленький Джек спросил его с земли: «Эй, Джони, почему ты летишь так высоко в небе? Что ты сделал?". Маленький Джони ответил: «Ничего… Я только что импортировал один антигравитационный модуль питона!». :Д :Д

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

Априорный вывод:

ordered_statistics=[OrderedStatistic(items_base=frozenset(), items_add=frozenset({'Шоколад', 'Молоко', 'яйца'}), trust=0.16666666666666666, lift=1.0), OrderedStatistic(items_base=frozenset({'Шоколад'} ), items_add=frozenset({'Молоко', 'яйца'}), достоверность=0,5, подъем=1,5), OrderedStatistic(items_base=frozenset({'Молоко'}), items_add=frozenset({'Шоколад', 'яйца '}), достоверность = 0,5, подъем = 2,0), OrderedStatistic (items_base = frozenset ({'яйца'}), items_add = frozenset ({'Молоко', 'Шоколад'}), доверие = 0,2222222222222222, подъем = 1,333333333333333333), OrderedStatistic(items_base=frozenset({'Молоко', 'Шоколад'}), items_add=frozenset({'яйца'}), trust=1.0, lift=1.3333333333333333), OrderedStatistic(items_base=frozenset({'яйца', 'Шоколад '}), items_add=frozenset({'Молоко'}), trust=0.6666666666666666, lift=2.0), OrderedStatistic(items_base=frozenset({'Молоко', 'яйца'}), items_add=frozenset({'Шоколад'} ), достоверность=0,5, подъем=1,5)])

Чтобы извлечь правила из вывода apriori(), мы определили extractRules(). Эта функция извлекает правила и сохраняет их в AssociationRulesDictionary в порядке убывания значений подъема.

def extractRules(self):
        for item in self.association_rules:
            # first index of the inner list
            # Contains base item and add item
            if len(item[0]) < 2:
                continue
            for k in item[2]:
                baseItemList = list(k[0])
                # if base item set is empty then go to the next record.
                if not baseItemList:
                    continue
                # sort the baseItemList before adding it as a key to the AssociationRules dictionary
                baseItemList.sort()
                baseItemList_key = tuple(baseItemList)
                if baseItemList_key not in self.AssociationRulesDictionary.keys():                    self.AssociationRulesDictionary[baseItemList_key] = []                self.AssociationRulesDictionary[baseItemList_key].append((list(k[1]), k[3]))
                # if something goes wrong, then use the following print block to print values
                #print("Base item: ", baseItemList_key)
                #print("Target item: ", list(k[1]))
                #print("Confidence: " + str(k[2]))
                #print("Lift: " + str(k[3]))
        # sort the rules in descending order of lift values.
        for ruleList in self.AssociationRulesDictionary:            self.AssociationRulesDictionary[ruleList].sort(key=lambda x: x[1], reverse=True)

Определенные выше два метода не должны вызываться вне определения класса. Я, будучи старомодным разработчиком C++, должен был придерживаться этого ограничения. Я оставляю это вам, ребята; если вы хотите вызвать эти две функции вне класса, вы можете это сделать.

Мы определили метод studyRules(), который вызывает две указанные выше функции computeRules() и extractRules(). Эта функция studyRules() здесь играет роль шаблонного метода. Если вы хотите расширить класс Recommender и использовать другой алгоритм вместо Apriori для вычисления правил ассоциации, тогда studyRules() поможет вам получить полиморфное поведение. Объект класса Recommender должен вызывать studyRules() для вычисления и извлечения правила ассоциации из файла входных данных.

def studyRules(self):
    """
    This is a template method for computation and rule extraction.
    :return:
    """
    self.computeRules()
    self.extractRules()

Теперь мы выполнили все вычисления и все правила ассоциации извлечены в словарь. Давайте определим некоторые API для возврата рекомендаций и сделок с использованием вычисляемых правил ассоциации.

def recommend(self, itemList, Num=1):
    """
    itemList is a list of items selected by user
    Num is total recommendations required.
    :param item:
    :return:
    """
    # convert itemList to itemTuple as our dictionary key is a sorted tuple
    itemList.sort()
    itemTuple = tuple(itemList)
    if itemTuple not in self.AssociationRulesDictionary.keys():
        return []
    return self.AssociationRulesDictionary[itemTuple][:Num]

recommend() возвращает рекомендации для выбранного элемента/набора элементов. Например, если вы хотите узнать, что является наиболее предпочтительным товаром наряду с «красным вином», вы можете получить это значение, вызвав recommend(), как показано ниже.

objectName.recommend(['красное вино'], 1)

В нашем случае для файла данных, который мы используем, он возвращает нам

[([‘спагетти’], 2.095966120638976)]

Это говорит нам о том, что «спагетти» является наиболее предпочтительным продуктом наряду с «красным вином».

Теперь давайте сформулируем сделки из этих рекомендаций. API showDeals() предназначен для создания сделок с использованием рекомендаций, возвращаемых функцией recommend(). В showDeals() мы рассчитываем процент скидки, используя значение повышения рекомендованного набора товаров. API recommend() возвращает несколько абстрактный вывод; в showDeals() мы пытаемся придать этому смысл.

def showDeals(self, itemList, Num=1):
    """
    we are converting the recommendations into deals. The lift value    is used to calculate discount percentage
    discount percentage = 10 * lift
    itemList is a list of items selected by user
    Num is total deals required.
    :return:
    """
    recommendations = self.recommend(itemList, Num)
    for item in recommendations:
        print( "If you buy ", item[0], " along with ", itemList, " then you will get ", round((item[1] * 10), 2), \
               "% discount on total cost!!" )

Теперь вся наша рекомендательная система готова!! Давайте поиграем с ним, чтобы увидеть, как он себя ведет.

# Create a recommender Alexa, give her the datafile containing transactions.
Alexa = Recommender("store_data.csv")
# Request Alexa to study rules from the provided datafile
Alexa.studyRules()
 
# Ask Alexa for the deals
Alexa.showDeals(['red wine'], 2)

Привет! Алекса! покажите мне две самые выгодные сделки на «Красное вино». И Alexa показывает вам следующие предложения.

1. При покупке [‘спагетти’] вместе с [‘красным вином’] вы получите скидку 20,96 % от общей стоимости!!

2. При покупке [«минеральной воды»] вместе с [«красным вином»] вы получаете скидку 16,3 % от общей стоимости!!

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

Michael = Recommender("songs_data.csv)
Michael.studyRules()
Michael.recommend(['summer of 69', 'Billie Jean'], 5)

Здесь Майкл порекомендовал бы пять песен, которые чаще всего слушают вместе с «summer of 69» и «Billie Jean»!!

Вся программа вместе с образцом файла данных загружена на GitHub здесь https://github.com/srkhedkar/Recommender.

Вы также можете найти полную программу ниже. Если у вас есть какие-либо комментарии / проблемы с реализацией, пожалуйста, не стесняйтесь обращаться ко мне по адресу [email protected].

Полный скрипт рекомендательной системы Python:

from apyori import apriori
from apyori import load_transactions

class Recommender():
    def __init__(self, inputFile):
        self.AssociationRulesDictionary = {} # holds final output
        self.dataFile = inputFile # input datafile in csv form
        self.association_rules = [] # holds output from Apriori algo
    def computeRules(self):
        """
        Computes all association rules.
        :return:
        """
        with open(self.dataFile ) as fileObj:
            transactions = list(load_transactions(fileObj, delimiter=","))
            # remove empty strings if any
            transactions_filtered = []
            for li in transactions:
                li = list(filter(None, li))
                transactions_filtered.append(li)
            # Following line does all computations
            # lift > 1 shows that there is a positive correlation within the itemset, i.e., items in the
            # itemset, are more likely to be bought together.
            # lift < 1 shows that there is a negative correlation within the itemset, i.e., items in the
            # itemset, are unlikely to be bought together.
            # hence we have set min_lift=1.0 to ignore all rules with lift < 1.0
            self.association_rules = apriori(transactions_filtered, min_support=0.01, min_confidence=0.01, min_lift=1.0,
                                        max_length=None)
    def extractRules(self):
        for item in self.association_rules:
            # first index of the inner list
            # Contains base item and add item
            if len(item[0]) < 2:
                continue
            for k in item[2]:
                baseItemList = list(k[0])
                # if base item set is empty then go to the next record.
                if not baseItemList:
                    continue
                # sort the baseItemList before adding it as a key to the AssociationRules dictionary
                baseItemList.sort()
                baseItemList_key = tuple(baseItemList)
                if baseItemList_key not in self.AssociationRulesDictionary.keys():
                    self.AssociationRulesDictionary[baseItemList_key] = []
                self.AssociationRulesDictionary[baseItemList_key].append((list(k[1]), k[3]))
                # if something goes wrong, then use the following print block to print values
                #print("Base item: ", baseItemList_key)
                #print("Target item: ", list(k[1]))
                #print("Confidence: " + str(k[2]))
                #print("Lift: " + str(k[3]))
        # sort the rules in descending order of lift values.
        for ruleList in self.AssociationRulesDictionary:
            self.AssociationRulesDictionary[ruleList].sort(key=lambda x: x[1], reverse=True)

    def recommend(self, itemList, Num=1):
        """
        itemList is a list of items selected by user
        Num is total recommendations required.
        :param item:
        :return:
        """
        # convert itemList to itemTuple as our dictionary key is a sorted tuple
        itemList.sort()
        itemTuple = tuple(itemList)
        if itemTuple not in self.AssociationRulesDictionary.keys():
            return []
        return self.AssociationRulesDictionary[itemTuple][:Num]
    def studyRules(self):
        """
        This is a template method for computation and rule extraction.
        :return:
        """
        self.computeRules()
        self.extractRules()
    def showDeals(self, itemList, Num=1):
        """
        we are converting the recommendations into deals. The lift value is used to calculate discount percentage
        discount percentage = 10 * lift
        itemList is a list of items selected by user
        Num is total deals required.
        :return:
        """
        recommendations = self.recommend(itemList, Num)
        for item in recommendations:
            print( "If you buy ", item[0], " along with ", itemList, " then you will get ", round((item[1] * 10), 2), \
                   "% discount on total cost!!" )

Alexa = Recommender("store_data.csv")
Alexa.studyRules()
print (Alexa.recommend(['red wine'], 1))
Alexa.showDeals(['red wine'], 2)