Рекомендательная система на основе машинного обучения.

Редактор
01
Построили рекомендательную систему с помощью технологии машинного обучения и сэкономили клиенту сотни часов в год.
02

Рекомендательная система для туристического оператора

У нас есть проект adventure-guide.ru — активные туры по России. Миссия компании — открыть Россию для вас как чужую. Это цитата «Высшая цель путешествия не в том, чтобы увидеть чужую страну, а в том, чтобы увидеть свою страну как чужую» из книги Гилберта Кита Честертона из его эссе Загадка плюща. Пролететь над вулканами на вертолетах, сплавиться по порожистой реке или устроить глэмпинг в Алтае, получая при этом великолепный сервис, вкусную еду и новые знакомства — это все про Adventure-guide.

На сайте около тысячи туров на момент написания статьи. В каждом туре существует блок похожих туров. Он там нужен, чтобы дать клиенту выбор, если просматриваемый тур не подойдет.

До недавнего времени, на сайте, построенном на системе управления 1С-Битрикс, добавление похожих туров происходило вручную, на основе знаний контент-менеджера о турах. Этот процесс требовал обновления и поддержания актуальности свойства "Похожие туры" на более чем тысячи страниц в системе управления.

Рекомендательная система для туристического оператора
03

Задача

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

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

Надеюсь, вы поняли, насколько сложная задача собирать такой алгоритм. Он называется, рекомендательной системой. При кажущейся простоте, это очень и очень сложная задача и не только для туроператора. За похожую задачу (предсказание оценки фильма) компания Netflix в свое время объявила награду в миллион долларов https://ru.wikipedia.org/wiki/Netflix_Prize

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

04

Подготовка датасета

Все начинается с подготовки датасета. Датасет — это таблица с параметрами туров.

Подготовка датасета — это около 80% времени работы дата-сайнтистов. Особенно если данные не структурированы. Но у нас они структурированы и мы их просто берем из базы.

Подготовку датасета, как и всю последующую работу мы будем писать на языке Python.

В качестве параметров мы берем:

  • Цену
  • Продолжительность тура в днях
  • Сложность
  • Месяц проведения тура
  • Тип проживания (турбаза или гостевой дом, гостиница, корабль/каюта, палатка)
  • Раздел тура
  • Регион
  • Вид активности
  • Конкретные турбазы или отели
  • Конкретные корабли
  • Оператор, предоставляющий тур
  • Широта и долгота
  • Количество людей
  • ID похожего тура из уже установленных менеджером

Сталкиваемся с такой интересной проблемой: тур один, а активностей в нем много (и экскурсионным, и сплавом на рафтах, и в горы https://adventure-guide.ru/tours/kamchatskiy-kaleydoskop.html) и регионов тоже может быть много (Адыгея, Кавказ), тогда у нас встает вопрос, как уместить несколько параметров на одну строку в таблице. Вопрос уже решен в библиотеке pandas: нужно разбить все параметры на колонки. То есть если у нас региона два: Адыгея или Кавказ, тогда просто: id_ТУРА ЕСТЬ_В_АДЫГЕЕ ЕСТЬ_НА_КАВКАЗЕ.

Получается таблица с очень большим количеством колонок.

                                                

# Основной датасет
dataset = []

# Колонки
columns = ['id', 'price', 'duration', 'complexity']

# Месяца
for item in range(0, 12):
    columns.append('month_' + str(item+1))

# Места проживания
for item in range(0, 4):
    columns.append('place_' + str(item+1))

# Разделы
cursor.execute(
    'select ID from b_iblock_section where IBLOCK_ID = 1')
for result in cursor.fetchall():
    columns.append('section_' + str(result['ID']))

# Заполняем привязанные свойства из Битрикса
bitrix_fillment_columns = {'region': 3, 'type': 4, 'place_id': 11, 'ship': 9, 'complexity': 15, 'operator': 35}

 
                                            
Подготовка датасета
05

Алгоритм sklearn.metrics.pairwise

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

Удивительно, но в данном случае сработал лучше всего самый простейший алгоритм sklearn.metrics.pairwise.

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

import pandas as pd
from sklearn.metrics.pairwise import cosine_similarity

# load the dataset
df = pd.read_csv('dataset_all.csv')
df.fillna(0, inplace=True)

# select the relevant features for the content-based algorithm
#X = df[['price', 'region', 'style']]
X = df.drop(["same_tour_id","children_terms","group_count"], axis=1) # Features


# apply feature scaling if necessary
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# calculate the cosine similarity matrix
cosine_sim = cosine_similarity(X_scaled)

# define a function to get similar tour IDs
def get_similar_tours(tour_id, cosine_sim, df, top_n=10):
    idx = df.loc[df['id'] == tour_id].index[0]
    sim_scores = list(enumerate(cosine_sim[idx]))
    sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
    sim_scores = sim_scores[1:(top_n+1)]
    tour_indices = [i[0] for i in sim_scores]
    unique_arr = list(set(df['id'].iloc[tour_indices].tolist()))
    unique_arr = [elem for elem in unique_arr if elem != tour_id]
    return unique_arr

# get the similar tour IDs for a given tour ID
similar_tours = get_similar_tours(8227591, cosine_sim, df, top_n=5)
print('Similar tour IDs:', similar_tours)

 
                                            
06

Связываем PHP и Python: Создание микросервиса на FastAPI для поиска похожих туров

Алгоритм мы нашли и похожие туры он показывает. Теперь надо связать сайт и сам алгоритм. Сайт у нас на PHP (Битрикс), а алгоритм написан на Питоне. Как и во мгогих других случаях мы свяжем их с помощью микросервиса на FastAPI. Вообще лучший фрейморк для создания микросервисов для Питона. Он работает как отдельный процесс в Линуксе и просто отдавает список похожих туров по ID тура в JSON.

                                                

@app.get("/same/")
def same(id: int = 0):
"""
Получает id похожих туров
"""

import pandas as pd

# load the dataset
df = pd.read_csv('/var/www/adventure_admin/data/ai/dataset_all.csv')
df.fillna(0, inplace=True)

# select the relevant features for the content-based algorithm
# X = df[['price', 'region', 'style']]
X = df.drop(["same_tour_id", "children_terms", "group_count"], axis=1)  # Features

# apply feature scaling if necessary
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

 
                                            
Связываем PHP и Python: Создание микросервиса на FastAPI для поиска похожих туров
07

На стороне 1С-Битрикс

А на стороне Битрикса при каждом сохранении тура мы поставим событие, которое их у микросервиса запрашивает и записывает в базу.

                                                

# Получаем похожие туры через сервис
$same_tours = file_get_contents ('http://127.0.0.1:7700/same/?id=' . $tour_id);
if (si_is_json($same_tours)) {
	
	$same_tours = json_decode ($same_tours, true);

	if (!empty($same_tours)) {
		$prop = [];
		foreach ($same_tours as $same_id) {
			$prop[] = ['VALUE' => $same_id];
		}
		CModule::IncludeModule('iblock');
		CIBlockElement::SetPropertyValuesEx($tour_id, 1, ['SAME_TOUR_AI_RECOMMENDED' => $prop]);
	}

}

 
                                            
08

Результаты

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

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

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

Результаты
Заключение

Рекомендательные системы являются мощным инструментом автоматизации бизнес-процессов компаний, которые позволяют существенно сэкономить время и ресурсы.