С косинусным сходством, установлением пороговых значений рейтинга и другими настраиваемыми методами

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

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

Требования:

  • Python 3
  • тупой
  • панды
  • nltk

Я предполагаю, что вы знакомы с Python на протяжении всего процесса разработки этих статей. Я не буду сосредотачиваться и подробно описывать код Python, поскольку основная цель этих статей - научить использовать CS и другие методы для создания RS. Также стоит упомянуть, что может быть какой-то код, который я напишу, который мог бы быть лучше, поскольку я не гуру Python. Если вы обнаружите такие детали, пожалуйста, дайте мне знать в комментариях, чтобы я смог реорганизовать его позже :)

Мы разработаем 4 разные версии, чтобы исследовать разные подходы к улучшению нашей системы с каждой из них. Во-первых, мы начнем с рекомендательной системы с косинусным подобием.

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

Пожалуйста, скачайте набор данных, на котором будет основан наш RS, здесь.

Некоторые из функций (столбцов) этого набора данных являются подлинной информацией, которую я ранее получил с веб-сайта TripAdvisor, а некоторые из них - просто случайные функции, которые я добавил для реализации и отображения различных методов позже. Наш набор данных состоит из 25 городов со следующими характеристиками: город, популярность, описание, изображение, рейтинг, rating_count, positive_review, negative_review. Вы можете увидеть предварительный просмотр набора данных для первых 5 городов ниже.

Характеристики, которые я приобрел на TripAdvisor: город, популярность, описание и изображение. Остальные функции созданы мной случайным образом. Мы не будем использовать эти функции для первой версии RS, но они будут использоваться для различных методов в других частях этой серии.

Давайте рассмотрим, что означает каждая функция:

  • city: Название города
  • популярность: для города сохранено количество отзывов.
  • описание: Запись в блоге о городе
  • image: Фоновое изображение города
  • рейтинг: Средняя оценка по городу (0–10).
  • rating_count: количество оценок, полученных от пользователей
  • postive_review: Количество положительных отзывов.
  • negative_review: Количество отрицательных отзывов.

Теперь, когда мы загрузили наш набор данных и узнали о функциях, мы можем приступить к разработке. Как упоминалось ранее, для первой версии мы будем использовать только функции города и описания.

Версия-1

Первая версия RS будет давать рекомендации, основанные на характеристиках описания городов в нашем наборе данных. RS вычислит сходство между описанием городов и ключевыми словами, относящимися к категории поездки, которую пользователь может выбрать, с помощью функции «Косинусное сходство», а затем вернет 5 лучших городов с наивысшим показателем сходства.

Косинусное подобие

Косинусное сходство - это мера сходства между двумя векторами путем вычисления косинуса угла между двумя векторами, проецируемыми в многомерное пространство. Его можно применять к элементам, доступным в наборе данных, для вычисления сходства друг с другом с помощью ключевых слов или других показателей. Сходство между двумя векторами (A и B) вычисляется путем деления скалярного произведения двух векторов на значение величины, как показано в уравнении ниже. Мы можем просто сказать, что оценка CS двух векторов увеличивается по мере уменьшения угла между ними.

Предварительная обработка

Прежде всего, нам нужно выполнить некоторую предварительную обработку нашего набора данных, чтобы подготовить его для использования в наших методах расчета CS. Давайте создадим файл Python с именем pre_processing.py в папке, содержащей набор данных.

Мы собираемся только очистить функцию описания нашего набора данных. Нам нужно удалить стоп-слова из описаний. Стоп-слова - это слова без какого-либо контекстного значения, например; the, for, an, a, or, what и т. д. Мотивация удаления этих слов состоит в том, чтобы убедиться, что оценка сходства в дальнейшем не будет снижена из-за неконтекстных слов. Это отрицательно повлияет на оценку, поскольку каждое слово создает разные измерения в пространстве, и соответствующее значение для этих измерений всегда будет равно нулю, поскольку ни одно из наших ключевых слов не будет включать неконтекстные слова.

import numpy as np
import pandas as pd
from nltk.corpus import stopwords
def clear(city):
    city = city.lower()
    city = city.split()
    city_keywords = [word for word in city if word not in stopwords.words('english')]
    merged_city = " ".join(city_keywords)
    return merged_city

Мы будем очищать описания городов с помощью метода очистки (города) выше. Это работает следующим образом:

  • Требуется строковый параметр с именем city
  • Понижает каждый символ строки с помощью метода .lower ().
  • Создает список слов с помощью метода .split ().
  • Инициализирует city_keywords путем удаления стоп-слов для английского языка (структура nltk)
  • Объединяет строку из очищенных слов и возвращает ее

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

for index, row in df.iterrows():
    clear_desc = clear(row['description'])
    df.at[index, 'description'] = clear_desc
updated_dataset = df.to_csv('city_data_cleared.csv')

Этот блок кода очистит все описания для всех городов в нашем наборе данных, а затем сохранит обновленное как файл city_data_cleared.csv. С этого момента мы будем использовать этот очищенный набор данных.

pre_processing.py суть:

Расчет показателя сходства с помощью функции косинусного сходства

Теперь, когда мы очистили описания городов, мы можем приступить к реализации класса, который будет отвечать за вычисление оценки сходства. Давайте создадим файл Python с именем cosine_similarity.py.

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

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

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

import re, math
from collections import Counter
class CosineSimilarity:
    def __init__(self):
        print("Cosine Similarity initialized")
    
    @staticmethod
    def cosine_similarity_of(text1, text2):
        first = re.compile(r"[\w']+").findall(text1)
        second = re.compile(r"[\w']+").findall(text2)
        vector1 = Counter(first)
        vector2 = Counter(second)
        common = set(vector1.keys()).intersection(set(vector2.keys()))
        dot_product = 0.0
        for i in common:
          
            dot_product += vector1[i] * vector2[i]
        squared_sum_vector1 = 0.0
        squared_sum_vector2 = 0.0
        for i in vector1.keys():
            squared_sum_vector1 += vector1[i]**2
        for i in vector2.keys():
            squared_sum_vector2 += vector2[i]**2
        magnitude = math.sqrt(squared_sum_vector1) * math.sqrt(squared_sum_vector2)
        if not magnitude:
           return 0.0
        else:
           return float(dot_product) / magnitude

Метод cosine_similarity_of работает следующим образом:

  • Принимает два строковых параметра
  • Получает слова для обеих строк с помощью Regex
  • Инициализирует словарь с помощью счетчика (слов, количества слов), где ключи соответствуют слову, а значение - количеству этого конкретного слова.
  • Получает общие слова, которые существуют в обоих векторах
  • Вычисляет косинусное сходство по формуле, введенной в разделе «Косинусное сходство», и возвращает значение.

cosine_similarity.py суть:

Рекомендательный движок

Далее мы напишем движок, который будет отвечать за рекомендации.

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

Recommender_engine.py:

Метод get_recommendations (ключевые слова) работает следующим образом:

  • Принимает строковый параметр для вычисления косинусного сходства описаний городов с
  • Вычисляет CS для каждого города с заданным параметром и сохраняет его в виде словаря как ‹индекс города, оценка›
  • Создает пустой фрейм данных с такими характеристиками, как город, популярность, описание и оценка.
  • Добавляет 5 городов с наибольшим рейтингом в этот фрейм данных
  • Преобразует фрейм данных в JSON и возвращает его.

Код заявки

Теперь протестируем наш движок (а вместе с ним и вычислительную функцию CS). Давайте создадим файл Python request.py.

Мы будем тестировать наш рекомендательный движок в трех разных категориях:

  • Культура, искусство и история
  • Пляж и солнце
  • Ночная жизнь и вечеринки

Я изучил описания всех городов в нашем наборе данных и вручную определил ключевые слова для каждой категории следующим образом:

  • [история историческое искусство архитектура город культура]
  • [пляж пляжи парк природа отдых море приморский песок солнце солнце солнечно]
  • [ночной клуб ночные клубы ночная жизнь бар бары паб пабы вечеринка пиво]

Давайте добавим следующий код, который отправит запрос на получение 5 лучших городов по заданному ключевому слову вместе с заранее определенными ключевыми словами:

from recommender_engine import RecommenderEngine
culture_keywords = "history historical art architecture city culture"
beach_n_sun_keywords = "beach beaches park nature holiday sea seaside sand sunshine sun sunny"
nightlife_keywords = "nightclub nightclubs nightlife bar bars pub pubs party beer"
def get_recommendations(keywords):
    result = RecommenderEngine.get_recommendations(keywords)
    return result

Затем давайте напишем вспомогательный метод для получения названий городов и очков из JSON следующим образом:

def get_top_5_city_names_out_of_json(json_string):
    list = json.loads(json_string)
    result = []
    max = len(list)
    i = 0
    while i < max:
        result.append(list[i]['city'])
        i += 1
    return result

Теперь мы сделаем 3 запроса к рекомендателю для 3 категорий, а затем распечатаем 5 лучших городов вместе с их оценкой сходства для каждой категории:

top_5_cultural_cities = get_recommendations(culture_keywords)
city_names_for_cultural = get_top_5_city_names_out_of_json(top_5_cultural_cities)
print(city_names_for_cultural)
print("#################")
top_5_summer_cities = get_recommendations(beach_n_sun_keywords)
city_names_for_summer = get_top_5_city_names_out_of_json(top_5_summer_cities)
print(city_names_for_summer)
print("#################")
top_5_party_cities = get_recommendations(nightlife_keywords)
city_names_for_party = get_top_5_city_names_out_of_json(top_5_party_cities)
print(city_names_for_party)
print("#################")

Мы получим рекомендации для всех трех категорий при запуске кода, но давайте исследуем результаты, которые мы получаем только для категорий Культура, Искусство и История:

[('Athens', 0.21629522817435007),
 ('St. Petersburg', 0.16666666666666666),
 ('Stockholm', 0.14962640041614492),
 ('Milan', 0.140028008402801),
 ('Rome', 0.12171612389003691)]

Как показано выше, показатель сходства составляет 21,6% для Афин и 12,17% для Рима. Результаты могли быть меньше, чем вы ожидали. Оценки ниже, потому что в описании каждого города, естественно, больше слов, чем ключевых слов, которые мы предоставили для сравнения. Различные слова создают разные измерения в пространстве, и, поскольку наши ключевые слова не содержат этих слов, соответствующее значение для этих параметров будет равно нулю, что приведет к более низкому показателю сходства. Если вы добавите / вычтите слова из ключевых слов, вы увидите, что результаты также изменятся.

request.py суть:

Заключение по Версии-1

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

Даже если оценки сходства низкие, когда вы исследуете 5 лучших городов по каждой категории; Вы можете видеть, что наша система рекомендаций рекомендует значимые и подходящие города. Вы можете также прочитать описание города, чтобы убедиться :)

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

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

Версия 2 (Рейтинговый вклад)

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

Формирование окончательной оценки с учетом CS и рейтинга

Сначала мы опускаем рейтинг. Мы по-прежнему будем рассчитывать косинусное сходство, но теперь, помимо этого, у нас будет рейтинговый вклад в окончательный результат. Мы создадим новый метод, который будет определять вклад рейтинга в итоговую оценку. У нас будет два параметра, Q и r. Где r представляет рейтинг, а Q представляет важность (вес рейтинга для расчета окончательной оценки). Играя с параметром Q, мы сможем увеличивать или уменьшать влияние значения рейтинга на генерацию окончательного результата.

Новая функция будет либо увеличивать, либо уменьшать вывод подобия косинуса в зависимости от того, больше ли рейтинг или меньше 5 пропорционально заданному параметру Q (при условии, что города со средним рейтингом меньше 5 не приветствуются и должны быть не одобрены, а больше 5 - нет). как упоминалось в предыдущей главе, диапазон рейтингов составляет от 0 до 10, а диапазон выходных рейтинговых взносов - от -Q до + Q.

Например: если Q задано как 10, максимальное значение рейтинга может дать окончательную оценку рекомендации, добавив 10% оценки CS к оценке CS, а для минимальной оценки окончательная оценка будет получена путем вычитания 10% оценки CS из оценка CS.

Формула, которую мы будем использовать в методе, найдет точный результат для данного рейтинга (найдя точную точку на синей линии). Визуальное представление этого метода генератора рейтинговых взносов для Q = 10 показано на рисунке ниже:

Давайте создадим новый файл с именем rating_extractor.py и добавим следующий код

from math import e
class RatingExtractor:
    def __init__(self):
        print("initialized")
#Returns value between -q and q. for rating input between 0 and 10.
    #Parameters:
        #rating: indicates the rating for the destination
        #q: indicates the percentage of rating for general score. (default is 10.)
    @staticmethod
    def get_rating_weight(rating, q=10):
        if rating > 10 or rating < 0:
            return None
        else:
            m = (2*q) / 10 #10 because rating varies between 0 and 10
            b = -q
            return (m*rating) + b

Метод get_rating_weight () выполняет некоторые вычисления, чтобы определить результат вклада для данного рейтинга и параметров Q, а затем возвращает значение. Как упоминалось ранее и также показано на рисунке выше, этот метод может давать как отрицательные, так и положительные значения. Имея в виду; этот метод будет либо отрицательно, либо положительно влиять на окончательный подсчет баллов. (Обратите внимание, что значение параметра Q по умолчанию установлено на 10).

Реализация нового метода в Recommender Engine

Теперь мы добавим новый метод в класс RecommenderEngine для расчета окончательной оценки, используя как оценку сходства косинуса, так и вклад в оценку. Добавьте метод ниже в класс RecommenderEngine. (Добавил сразу после инициализации).

def calculate_final_score(cs, r):
    amount = (cs / 100) * r
    return cs + amount

Метод работает следующим образом:

  • Принимает параметры оценки CS и рейтингового вклада r.
  • Вычисляет + -r процент оценки CS в переменной количества
  • Добавляет сумму к оценке CS и возвращает ее.

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

Стоит отметить, что этот метод сильно зависит от оценки CS. Поскольку он работает, беря r процентов CS и прибавляя его к исходному значению CS. Города с более высоким значением CS будут сильно затронуты этим новым вычислением окончательной оценки, особенно если для метода get_rating_weight () заданы более высокие значения Q.

Теперь давайте добавим еще один метод в RecommenderEngine, чтобы использовать этот новый метод для расчета баллов (мы сохраним старый метод рекомендаций).

Метод get_recommendations_include_rating (ключевые слова) будет работать аналогично методу get_recommendations (ключевые слова), реализованному в первой главе. Но теперь он рассчитает окончательную оценку как с оценкой CS, так и со значением рейтингового вклада, давайте посмотрим, как работает метод:

  • Принимает параметр ключевых слов и выполняет следующие действия для каждого города в наборе данных.
  • Вычисляет оценку CS
  • Вычисляет рейтинг рейтингового вклада с Q = 10
  • Вычисляет окончательную оценку, используя обе оценки в методе calculate_final_score
  • Получает 5 лучших городов с наивысшим итоговым баллом и возвращается в формате JSON.

Запрос

Теперь, когда у нас есть метод, мы можем делать запросы для получения рекомендаций. Сначала давайте откроем файл request.py и добавим метод для получения рекомендаций из класса RecommenderEngine:

def get_recommendations_include_rating(keywords):
    return RecommenderEngine.get_recommendations_include_rating(keywords)

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

# Version 2 requests are below:
top_5_cultural_with_rating = get_recommendations_include_rating(culture_keywords)
city_names_for_cultural_rating = get_top_5_city_names_out_of_json(top_5_cultural_with_rating)
print(city_names_for_cultural_rating)
print("#################")
top_5_summer_with_rating = get_recommendations_include_rating(beach_n_sun_keywords)
city_names_for_summer_rating = get_top_5_city_names_out_of_json(top_5_summer_with_rating)
print(city_names_for_summer_rating)
print("#################")
top_5_party_with_rating = get_recommendations_include_rating(nightlife_keywords)
city_names_for_party_rating = get_top_5_city_names_out_of_json(top_5_party_with_rating)
print(city_names_for_party_rating)
print("#################")

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

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

Сравнение методов get_recommendations и get_recommendations_include_rating:

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

top_5_cultural_cities = get_recommendations(culture_keywords)
city_names_for_cultural = get_top_5_city_names_out_of_json(top_5_cultural_cities)
print(city_names_for_cultural)
print("#################")
top_5_cultural_with_rating = get_recommendations_include_rating(culture_keywords)
city_names_for_cultural_rating = get_top_5_city_names_out_of_json(top_5_cultural_with_rating)
print(city_names_for_cultural_rating)
print("#################")

Здесь мы наблюдаем результат двух разных методов:

[('Athens', 0.21629522817435007),
 ('St. Petersburg', 0.16666666666666666),
 ('Stockholm', 0.14962640041614492),
 ('Milan', 0.140028008402801),
 ('Rome', 0.12171612389003691)]
#################
[('Athens', 0.22927294186481106),
 ('Stockholm', 0.1556114564327907),
 ('St. Petersburg', 0.15333333333333332),
 ('Milan', 0.15123024907502508),
 ('Rome', 0.13145341380123987)]

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

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

Сравнение метода get_recommendations_include_rating с Q = 10 и Q = 100:

Теперь сравним наш новый метод с разными Q-параметрами. Меньший Q означает меньший вклад рейтинга в итоговую оценку, а более высокий Q означает больший вклад. Как мы распечатали ранее, следующие рекомендации мы получаем для категории «Культура, искусство и история» с Q = 10:

[('Athens', 0.22927294186481106),
 ('Stockholm', 0.1556114564327907),
 ('St. Petersburg', 0.15333333333333332),
 ('Milan', 0.15123024907502508),
 ('Rome', 0.13145341380123987)]

Вы можете перейти на файл Recommender_engine.py и заменить 10 на 100, чтобы увеличить параметр Q в методе get_recommendations_include_rating:

rating_contribution = RatingExtractor.get_rating_weight(rating,100)

Теперь посмотрим, как меняются результаты:

[('Athens', 0.3460723650789601),
 ('Milan', 0.2520504151250418),
 ('Rome', 0.21908902300206645),
 ('Stockholm', 0.2094769605826029),
 ('Venice', 0.17777777777777776)]

Мы видим, что сейчас наши результаты сильно отличаются.

  • Санкт-Петербург больше не входит в пятерку лучших, так как имеет рейтинг 1, при более высоком Q городу настоятельно не рекомендуется рекомендовать
  • Милан и Рим поднялись на второе и третье места соответственно, а Стокгольм опустился на четвертое, поскольку Милан и Рим имеют более высокие рейтинги, чем Стокгольм.

Я рекомендую вам проверить результаты для других категорий, а также то, как они меняются с разными значениями Q.

Заключение по Версии-2

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

Вы можете ознакомиться со всем кодом, написанным для второй версии, здесь.

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

Версия-3 (пороговое значение рейтинга)

Тот факт, что контент имеет хороший рейтинг, не означает, что он является надежным. Представьте, что у нас есть два содержимого: A и B, средний рейтинг A составляет 4,7 от 500 000 пользователей, а для B - рейтинг 5 только от 10 пользователей. Какой из них вы бы порекомендовали своему другу? Насколько надежным, по вашему мнению, может быть B, если рейтинг дали всего 10 пользователей? С помощью функции rating_count мы придумаем пороговый параметр, чтобы наша рекомендательная система могла справиться с контентом (в нашем случае с городами) с низким рейтингом, так как не придавала большого значения рейтинговому вкладу.

Создание рейтинга веса с функциями рейтинга и подсчета

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

Эта формула приводит к M, которое является значением множителя, которое будет умножено на полученное нами значение рейтингового взноса, прежде чем использовать его для расчета окончательной оценки рекомендации. Здесь T представляет собой пороговое значение, а c представляет собой рейтинг. Эта формула построена таким образом, что обладает следующими свойствами:

  • Диапазон вывода составляет от 0,0 до 1,0.
  • Если параметры T и c равны друг другу, формула всегда дает 0,5.

Впрочем, в этой формуле нет ничего особенного с числом e, это может быть любое число (тогда нужно было изменить и 0,68). Я просто использовал e, чтобы это выглядело круто: P

Таким образом, основная важность метода заключается в том, что он дает 0,50 при T = c. Вот как будет работать порог. Мы установим пороговое значение T для подсчета рейтингов, так что, если количество рейтингов меньше порогового значения, выходные данные будут в диапазоне 0,0–0,50 (в зависимости от того, насколько низко), а если количество рейтингов больше порогового значения, выход будет от 0,50 до 1,0, но никогда не может превышать 1,0.

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

Теперь перейдем к файлу rating_extractor.py и создадим новый метод. Мы просто умножим значение рейтингового взноса на множитель, но давайте создадим новый метод, чтобы вы могли использовать другой подход и использовать его как есть.

Сначала нам нужно импортировать e из математики:

from math import e

Затем в классе RatingExtractor добавьте новый метод:

@staticmethod
def get_rating_weight_with_quantity(rating, c, T, q=10):
    if rating > 10 or rating < 0:
        return None
    else:
        m = (2*q) / 10 #10 because rating varies between 0 and 10
        b = -q
        val = (m*rating) + b
        M = e**((-T*0.68)/c)
        return val * M

Метод работает следующим образом:

  • Он принимает параметры rating, c (rating_count), T (threshold) и q.
  • Мы уже знаем параметры rating и q из предыдущей главы.
  • c относится к количеству пользователей, предоставивших оценку
  • T обозначает пороговое значение, указанное выше.
  • Рассчитывает размер рейтингового взноса
  • Вычисляет значение множителя M
  • Возвращает рейтинговый вес, умножая вклад на M.

Реализация нового метода в Recommender Engine

Давайте откроем файл Recommender_engine.py и добавим новый метод в класс RecommenderEngine (мы сохраним методы, реализованные в предыдущих версиях). На самом деле он почти такой же, как метод, который мы добавили в RecommenderEngine в предыдущей главе, но на этот раз мы пропустим количество оценок и пороговое значение, а также описание города и характеристики рейтинга:

Метод работает следующим образом:

  • Принимает параметр ключевых слов и выполняет следующие действия для каждого города в наборе данных.
  • Вычисляет оценку CS
  • Получает вес вклада в рейтинг, передавая рейтинг города, счетчик рейтингов, пороговое значение как 1 миллион (города в наборе данных имеют счетчик рейтингов от 100k до 5M, я изначально выбрал 1 миллион, но мы поэкспериментируем с этим) и Q = 10
  • Вычисляет окончательную оценку с помощью метода calculate_final_score (реализованного в предыдущей главе) с использованием оценки CS и рейтингового веса.
  • Получает 5 лучших городов с наивысшим итоговым баллом и возвращается в формате JSON.

Запрос

Мы добавим новые запросы в файл request.py, чтобы с новым методом получать рекомендации по 3 категориям.

Сначала давайте добавим метод для получения рекомендаций с новой реализацией из класса RecommenderEngine:

def get_recommendations_include_rating_count_threshold(keywords):
    return RecommenderEngine.get_recommendations_include_rating_count_threshold(keywords)

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

# Version 3 requests are below:
top_5_cultural_with_rating_count_threshold = get_recommendations_include_rating_count_threshold(culture_keywords)
city_names_for_cultural_rating_count_threshold = get_top_5_city_names_out_of_json(top_5_cultural_with_rating_count_threshold)
print(city_names_for_cultural_rating_count_threshold)
print("#################")
top_5_summer_with_rating_count_threshold = get_recommendations_include_rating_count_threshold(beach_n_sun_keywords)
city_names_for_summer_rating_count_threshold = get_top_5_city_names_out_of_json(top_5_summer_with_rating_count_threshold)
print(city_names_for_summer_rating_count_threshold)
print("#################")
top_5_party_with_rating_count_threshold = get_recommendations_include_rating_count_threshold(nightlife_keywords)
city_names_for_party_rating_count_threshold = get_top_5_city_names_out_of_json(top_5_party_with_rating_count_threshold)
print(city_names_for_party_rating_count_threshold)
print("#################")

Приведенный выше код получит рекомендации с их окончательной оценкой, вы можете запустить его и увидеть результаты, которые вы получите для всех категорий. Но мы будем исследовать результаты только для категории «Культура, искусство и история».

Сравнение результатов с разными пороговыми значениями

Давайте сделаем экспериментальные запросы с разными пороговыми значениями (T) для категорий «Культура», «Искусство» и «История». Вы можете изменить порог в методе get_recommendations_include_rating_count_threshold класса RecommenderEngine. Также на этот раз давайте изменим параметр Q (параметр важности рейтингового вклада, представленный в предыдущей главе) на 100, чтобы мы могли лучше увидеть пороговый эффект.

Результаты для порога = 100,000:

[('Athens', 0.33318171469723395),
 ('Milan', 0.24587898720843948),
 ('Rome', 0.21192640793273687),
 ('Stockholm', 0.18358642633975064),
 ('Venice', 0.17262307588744202)]

Результаты для порога = 1.000.000:

[('Athens', 0.26188415260156817), 
('Milan', 0.2035910531885378), 
('Rome', 0.16707033294390228), 
('Stockholm', 0.14983344608755947), 
('Barcelona', 0.14757848986361075)]

Результаты для порога = 2.500.000:

[('Athens', 0.2257870828894539), 
('Milan', 0.16719580286435054), 
('St. Petersburg', 0.158824470447676), 
('Stockholm', 0.14962644254339), 
('Rome', 0.13613352041126298)]

Как видите, для порога 100К и 1М; 5-е место отличается, когда пороговое значение ниже, у нас Венеция на 5-м месте и у нас есть Барселона, когда значение выше. Посмотрим, почему:

У них обоих рейтинг 8, но у Барселоны 1 200 000 оценок, в то время как у Венеции 845 000, а также у Венеции больше очков CS, чем у Барселоны. Таким образом, когда порог равен 100000, оба города могут обеспечить хороший рейтинг, а поскольку Венеция имеет более высокий балл CS, мы видим его на 5-м месте.

Но когда порог равен 1.000.000, тогда баллы за вклад следующие (Q = 100):

  • Барселона: 34
  • Венеция: 26,8

Поскольку у «Барселоны» сейчас более высокая результативность и поскольку Q также велик, окончательный расчет для «Барселоны» будет больше, чем для Венеции, и, таким образом, «Барселона» находится на 5-м месте.

Когда пороговое значение равно 2.500.000, вы можете видеть, что у нас Санкт-Петербург находится на 3-м месте, в то время как у нас даже не было его на четвертом или пятом месте, когда пороговое значение было ниже. Я оставлю вам расследование. Проверьте набор данных для Санкт-Петербурга, просмотрите, как работают наши реализации, и посмотрите, сможете ли вы понять, почему у нас есть Санкт-Петербург для более высоких пороговых значений. Дайте мне знать, если у вас возникнут вопросы по комментариям :)

Кроме того, я рекомендую вам поиграть со всеми этими параметрами, проверить функции для набора данных (это должно быть хорошо, поскольку у нас всего 25 городов) и попытаться понять, как эти методы могут быть эффективны для рекомендательных систем.

Заключение для Версии-3

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

Вы можете ознакомиться со всем кодом, написанным для третьей версии, здесь.

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

Версия-4

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

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

Иногда у нас могут быть разные типы отзывов о нашем контенте. Такие как обзоры и рейтинги. Как вы можете догадаться, это не совсем тот же вид обратной связи, отзывы оцениваются по заданной шкале (от 0 до 10 в нашем случае), а обзор обычно дается в текстовом формате. Допустим, мы классифицировали обзоры как положительные и отрицательные (возможно, мы сможем исследовать классификацию обзоров по другой статье), тогда отзывы об обзоре можно проанализировать как бинарные отзывы (0 или 1); значение, отрицательный или положительный отзыв.

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

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

Преобразование отзывов в шкалу оценок

Базовый подход к этому - выбрать два значения рейтинга как для отрицательной, так и для положительной обратной связи, а затем рассматривать каждый отзыв как оценку и пересчитывать среднюю оценку элемента. Но такой подход не был бы идеальным. Например, если оценки выбраны как 0 и 10 для отрицательных и положительных отзывов, то влияние отзывов будет больше, чем фактических данных рейтинга, особенно когда оценка элемента близка к 0 или 10. Можно было бы выберите различные значения рейтинга, чтобы уменьшить влияние обзора на рейтинг. Например, если выбраны оценки 2,5 и 7,5, тогда возникнет другая проблема, когда положительный отзыв для элемента со средней оценкой выше 7,5 все равно будет способствовать снижению оценки, тогда как отрицательный отзыв для элемента со средней оценкой меньше чем 2,5 будет способствовать увеличению рейтинга. Следовательно, необходим другой подход, чтобы лучше объединить обе формы.

Наш подход будет вести себя следующим образом как для положительных, так и для отрицательных отзывов соответственно:

  • Для любого положительного отзыва к рейтингам добавляется новое значение рейтинга путем вычисления расстояния между средней оценкой элемента и максимальным значением рейтинга (в нашем случае 10), а затем добавлением половины рассчитанного расстояния к средней оценке для определения оценки. значение для обзора.
  • Для любого отрицательного отзыва к рейтингам добавляется новое значение оценки путем вычисления расстояния между средней оценкой элемента и минимальным значением оценки (в нашем случае 0), а затем вычитанием половины рассчитанного расстояния из средней оценки для определения оценки. значение для обзора.

Формулы преобразования положительных и отрицательных отзывов в значения рейтинга Rp и Rn соответственно для данного элемента со средней оценкой r приведены ниже:

Например, для элемента со средней оценкой 6 (диапазон 0–10) для каждого отрицательного отзыва к рейтингам добавляется новая оценка со значением 3, тогда как для каждого положительного отзыва добавляется новая оценка со значением 8. рейтингам. Затем снова рассчитывается средний рейтинг с новыми оценками, прежде чем он будет введен в функцию оценки. Преобразование отзывов в рейтинговые отзывы показаны в таблице ниже для различных оценок, оценок и количества отзывов.

Окончательные результаты рейтинга показывают, что при текущей реализации, когда значение рейтинга приближается либо к максимальному, либо к минимальному значению, вычисленный рейтинг имеет тенденцию отдавать предпочтение противоположному значению рейтинга. Например, для случая, когда оценка равна 7,2, а количество положительных и отрицательных отзывов одинаково, результатом будет 6,65, поскольку он приближается к минимальной оценке по сравнению с исходным значением. Это связано с тем, что расстояние между 0 и 7,2 больше, чем расстояние между 7,2 и 10, аналогичное поведение также наблюдается, когда исходный рейтинг равен 2,8 с равными положительными и отрицательными значениями рейтинга. Более того, влияние обзоров минимально, поскольку тестовые примеры включали больше значений рейтинга, чем обзор, что часто бывает в коммерческих рекомендательных системах. Эффект можно было бы увеличить, введя дополнительный параметр для изменения важности обзоров, чтобы сделать этот подход более эффективным (например, для каждого обзора мы могли бы сгенерировать 10 значений рейтинга с рассчитанным значением).

Реализация

Теперь мы исследовали, как он работает и какие результаты он может генерировать, давайте продолжим и реализуем новый метод в классе RatingExtractor:

    @staticmethod
    def get_rating_with_count_and_reviews(r, rc, pf, bf):
        if r > 10 or r < 0:
            return None
        else:
            positive_diff = (10 - r) / 2
            positive_rating = r + positive_diff
            negative_diff = r / 2
            negative_rating = r - negative_diff
            updated_rating = ((r * rc) + (pf * positive_rating) + (bf * negative_rating)) / (rc + pf + bf)
return RatingExtractor.get_rating_weight_with_quantity(updated_rating,rc,1000000,10)

Метод работает следующим образом:

  • Принимает параметры r (рейтинг), rc (количество оценок), pf (количество положительных отзывов) и bf (количество отрицательных отзывов).
  • Вычисляет преобразованное значение рейтинга для положительных отзывов.
  • Вычисляет преобразованное значение рейтинга для отрицательных отзывов.
  • Затем вычисляет обновленную среднюю оценку со старой средней оценкой и новыми значениями рейтинга для каждого положительного и отрицательного отзыва.
  • Затем вызывает метод, реализованный в предыдущей версии, с обновленным средним рейтингом, подсчетом рейтингов, T = 1.000.000 (порог) и Q = 100 (параметр важности рейтинга) и возвращает результат в качестве рейтингового вклада.

Реализация нового метода в Recommender Engine

Давайте откроем файл Recommender_engine.py и добавим новый метод в класс RecommenderEngine. Это будет похоже на методы, которые мы реализовали в предыдущих главах, но на этот раз мы пройдем количество положительных и отрицательных отзывов вместе с описанием, рейтингом, количеством оценок, пороговыми значениями:

Метод работает следующим образом:

  • Принимает параметр ключевых слов и выполняет следующие действия для каждого города в наборе данных.
  • Вычисляет оценку CS
  • Получает вес вклада в рейтинг, передавая оценку города, количество оценок, количество положительных и отрицательных отзывов. На этот раз (параметры T и Q передаются непосредственно из нового метода в классе RatingExtractor.
  • Вычисляет окончательную оценку с помощью метода calculate_final_score (реализованного в предыдущих главах) с использованием оценки CS и рейтингового веса.
  • Получает 5 лучших городов с наивысшим итоговым баллом и возвращается в формате JSON.

Запрос

Мы добавим новые запросы в файл request.py, чтобы с новым методом получать рекомендации по 3 категориям

Сначала давайте добавим метод для получения рекомендаций с новой реализацией из класса RecommenderEngine:

def get_recommendations_include_rating_count_threshold_positive_negative_reviews(keywords):
    return RecommenderEngine.get_recommendations_include_rating_count_threshold_positive_negative_reviews(keywords)

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

# Version 4 requests are below:
top_5_cultural_with_rating_count_threshold_reviews = get_recommendations_include_rating_count_threshold_positive_negative_reviews(culture_keywords)
city_names_for_cultural_rating_count_threshold_reviews = get_top_5_city_names_out_of_json(top_5_cultural_with_rating_count_threshold_reviews)
print(city_names_for_cultural_rating_count_threshold_reviews)
print("#################")
top_5_summer_with_rating_count_threshold_reviews = get_recommendations_include_rating_count_threshold_positive_negative_reviews(beach_n_sun_keywords)
city_names_for_summer_rating_count_threshold_reviews = get_top_5_city_names_out_of_json(top_5_summer_with_rating_count_threshold_reviews)
print(city_names_for_summer_rating_count_threshold_reviews)
print("#################")
top_5_party_with_rating_count_threshold_reviews = get_recommendations_include_rating_count_threshold_positive_negative_reviews(nightlife_keywords)
city_names_for_party_rating_count_threshold_reviews = get_top_5_city_names_out_of_json(top_5_party_with_rating_count_threshold_reviews)
print(city_names_for_party_rating_count_threshold_reviews)
print("#################")

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

[('Athens', 0.2622560540924768), 
('Milan', 0.2040068651858985), 
('Rome', 0.16752794267650856), 
('Stockholm', 0.14984473241175314), 
('Barcelona', 0.14831614523091158)]

Но я настоятельно рекомендую вам изучить результаты для всех категорий, перейти к главе 3 и сравнить результаты здесь, что отличается и почему? Проверьте набор данных, чтобы найти ответы, дайте мне знать, если у вас возникнут вопросы :)

Заключение

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

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

Вы можете скачать финальную версию проекта здесь.

Ваше здоровье!

Дополнительный

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

Пожалуйста, дайте мне знать, если вы заинтересованы в создании пользовательского интерфейса для системы рекомендаций, реализованной здесь через Flutter. Может быть, я тоже смогу делать посты для создания приложения :)

Заботиться!