Рекомендательные системы очень востребованы в настоящее время, так как значительно уменьшают время нахождения полезной информации. В отличие от поисковых систем, чтобы получить ответ, рекомендательная система не требует четкого запроса. Пользователю предлагается оценить некоторые объекты из коллекции и на основании его оценок строятся предположения и возвращаются наиболее близкие к ним результаты.
Алгоритмы коллаборативной фильтрации – это модели, которые пытаются предсказать, насколько пользователю понравится тот или иной продукт, получая на вход данные о том, как пользователи оценивали этот и другие продукты в прошлом.
Большинство интернет-продуктов, которые мы используем сегодня, работают на рекомендательных системах. Youtube, Netflix, Amazon, Pinterest, и длинный список других интернет-продуктов, все они полагаются на рекомендательные системы, чтобы фильтровать миллионы контента и давать персональные рекомендации своим пользователям.
В основе создания проекта будет использоваться среда распределенной обработки данных ApacheSpark, главной особенностью которой являются кластерные вычисления в памяти, которые увеличивают скорость обработки приложения, а также библиотека SparkMLlib, которая предоставляет общие алгоритмы машинного обучения. SparkMLlib содержит библиотеку алгоритмов MLlib ALS, которая будет использована в разработке рекомендательной системы фильмов.
В качестве основной среды разработки будет использоваться программное обеспечение IntelliJ IDEA, Плагин Scala(язык программирования),Maven(в качестве системы сборки) и библиотеки ApacheSpark.
Разработка программной реализации состоит из следующих шагов:
- Загрузить пример данных в формате DAT
- Разобрать данные во входном формате для алгоритма ALS.
- Разделить данные на две части: одну для построения модели и одну для тестирования модели.
- Запустить алгоритм ALS, чтобы построить/обучить модель матрицы продукта пользователя.
- Сделать прогнозы с данными обучения и наблюдать за результатами.
- Протестировать модель с тестовыми данными.
Загрузим данные с сайта MovieLens, который содержат 1 000 209 анонимных оценок примерно 3900 фильмов 6040 пользователями MovieLens, которые присоединились к MovieLens в 2000 году.
Все рейтинги содержатся в файле «ratings.dat» и находятся в следующем формате:
USERID :: MovieID :: Рейтинг :: Отметка, здесь:
- USERID — Идентификаторы пользователей
- Диапазон MovieID от 1 до 3952
- Рейтинги составляются по 5-звездочной шкале
- Временная метка представлена в секундах
- У каждого пользователя должно быть не менее 20 оценок
Присвоим ссылку на данные, указанные в качестве входных данных функции main, переменной movieLensHomeDir:valmovieLensHomeDir = args(0)Инициализируем объект SparkSession для создания новой сессии для взаимодействия с базовой функциональностью Spark, которая позволяет программировать Spark с помощью API DataFrame и Dataset.
valsparkSession = SparkSession
.builder()
.appName(«spark-read-csv»)
.master(«local[*]»)
.getOrCreate();
Cоздадим классы case для строк рейтингов и фильмов, которые используются для неизменяемых данных. Метод applycase классов case не требуют объявления экземпляра т.к. case-классы делает это автоматически. Классы case являются val константами.case class Rating(userId: Int, movieId: Int, rating: Float, timestamp: Long)case class Movie(movieId: Int, movieName: String, rating: Float)
Создадим метод parseRating, который принимает строку и разделяет её, где имеется символ «::». Метод создает массив из этих разделенных элементов и возвращает объект Rating.
defparseRating(str: String): Rating = {valfields = str.split(«::») returnRating(fields(0).toInt, fields(1).toInt, fields(2).toFloat, fields(3).toLong)}
Загрузим пакеты, которые содержат неявные методы для преобразования общих объектов Scala в DataFrames: importsparkSession.implicits.
Загружаем личный рейтинг в переменную myRating в виде DataFrame. Для этого обращаемся к нашей сессии sparkSession. Метод read() используется для загрузки Dataset. Метод TextFile загружает текстовый файл «personalRatings.txt» и возвращает Dataset строку. Метод Map() является методом класса RDD и является преобразователем т.е. один тип данных в другой. Метод map() в качестве входного параметра принимает функцию parseRating.
valmyRating: DataFrame = sparkSession.read.textFile(movieLensHomeDir + «personalRatings.txt») .map(parseRating).toDF()
Загружаем рейтинги пользователей в виде DataFrame :
val ratings = sparkSession
.read.textFile(movieLensHomeDir + «ratings.dat»)
.map(parseRating)
.toDF()Разбиваем данные на обучение (50%) и тестирование (50%).
Метод randomSplit() случайно разбивает набор данных:valArray(training, test) = ratingWithMyRats.randomSplit(Array(0.5, 0.5)
Инициализируем новый объект ALS() и поместим его в переменную als. С помощью метода setMaxIter() укажем максимальное количество итераций. Укажем параметр регуляризации равным 0.01 методом setRegParam(). Методом setUserCol() укажем наименование столбца с id пользователями. С помощью методаsetItemCol() укажем наименование столбца с id фильмами, с помощью метода setRatingCol() наименование столбца с id рейтингами:
valals = newALS()
.setMaxIter(3)
.setRegParam(0.01)
.setUserCol(«userId»)
.setItemCol(«movieId»)
.setRatingCol(«rating»).
Произведем обучение модели методом ALS.fit(), используя в качестве входных данные предназначенные для обучения, которые находятся в переменной training: valmodel = als.fit(training)
Рассчитаем прогнозы пользователей, используя тестовый набор данных, находящихся в переменной test. Метод transform() — это алгоритм, который преобразует один DataFrame в другой DataFrame. В нашем случае превращение DataFrame с даннными в DataFrame с предсказаниями: valpredictions = model.transform(test)
Рассчитаем личные прогнозы используя свой рейтинг, который находится в myRating:
valmyPredictions = model.transform(myRating).na.drop
Преобразуем наши рекомендации в формат DataFrame методом map().
valmyMovies = myPredictions.map(r => Movie(r.getInt(1), movies(r.getInt(1)), r.getFloat(2))).toDF
myMovies.show(100)
Произвёдем оценку модели по формуле среднеквадратической ошибки. Инициализируем оценщик для регрессии RegressionEvaluator и присвоим его переменной evaluator. Методом etMetricName устанавливаем метрику типа rmse, которая соответствует формуле среднеквадратической ошибки. Метрика rmseчасто используемая мера различий между значениями (выборка и значения совокупности), прогнозируемыми моделью или оценщиком, и реально наблюдаемыми значениями. Методом setLabelCol() устанавливаем столбец rating в качестве метки и методом setPredictionCol() устанавливаем столбец prediction в качестве прогнозов.
val evaluator = new RegressionEvaluator()
.setMetricName(«rmse»)
.setLabelCol(«rating»)
.setPredictionCol(«prediction»)Получим результаты по формуле RMSE. Метод evaluate() класса RegressionEvaluator измеряет, насколько хорошо подобранная модель справляется с данными по результатам испытаний. В качестве входных параметров функция ожидает данные прогнозов.valrmse = evaluator.evaluate(predictions)Получим личные рекомендации исходя из таблицы личного рейтинга и выведем топ 5 рекомендаций имеющих наивысший предсказанный рейтинг(рис.2)valmyMovies = myPredictions.map(r =>Movie(r.getInt(1), movies(r.getInt(1)), r.getFloat(2))).toDF
import org.apache.spark.sql.functions._
myMovies.orderBy(desc(«rating»)).show(5)
Рисунок 2. Топ 5 рекомендаций
В результате выполнения данной работы была разработана рекомендательная система фильмов, которая предсказывает и рекомендует подбор фильмов для каждого пользователя.
В работе использовалась библиотека MlLib из фреймворка ApacheSpark, который разрабатывался для распределенной обработки больших данных. Основным преимуществом данного инструмента является то, что данные можно кэшировать в оперативной памяти, что позволяет существенно ускорять вычисления в случае итеративных алгоритмов, какими и являются большинство алгоритмов машинного обучения.
Cписок литературы:
- А. Гомзин, А. Коршунов Системы рекомендаций: обзор современных подходов. Препринт. Москва: Труды Института системного программирования РАН. 2012. 20 С.
- P. Melville, V. Sindhwani Recommender systems. Encyclopedia of Machine Learning. 2010.
- Y. Koren Collaborative Filtering with Temporal Dynamics. KDD’09. 2009
- X. Su, T.M. Khoshgoftaar Survey of Collaborative Filtering Techniques. Advances in Artificial Intelligence. 2009.