Пошаговое руководство по созданию простого Netflix на основе контента показывает механизм рекомендаций, запуск его как API с Flask и развертывание в Heroku.

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



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





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

На повестке дня:

  1. Предварительная обработка данных
  2. Векторизация текста
  3. Система рекомендаций
  4. Flask API
  5. Развертывание на Heroku
  6. Bonus Swag-ger

Давайте начнем!

Начиная

  1. Создайте новую папку с именем «Netflix показывает API рекомендаций».
  2. Откройте новую командную строку (или запрос Anaconda) внутри папки или укажите в каталоге терминала его путь:
cd /d <parent directory path>\Netflix Shows Recommendation API

3. Создайте новую виртуальную среду Python:

virtualenv recommendation_api_env

4. Активируйте рекомендуемый_api_env:

recommendation_api_env\Scripts\activate

5. Для этого проекта требуются пакеты pandas, scikit-learn, flask и gunicorn. Выполните следующую команду, чтобы установить их в пакетном режиме:

pip install pandas sklearn flask gunicorn

6. После завершения установки сохраните список пакетов проекта в текстовый файл с помощью этой команды. Heroku использует этот файл как ссылку на то, какие пакеты устанавливать:

pip freeze > requirements.txt

7. Если вы еще этого не сделали, установите Git:



8. Установите Heroku CLI (если вы знакомы с Heroku и вместо этого планируете подключить его к GitHub, пропустите этот шаг):



9. Создайте Procfile для Heroku, объявив gunicorn в качестве типа процесса:

echo web: gunicorn recommendation_api:app > Procfile

1. Предварительная обработка данных

Набор данных фильмов и телешоу Netflix на Kaggle (также доступен в репозитории GitHub)

Необходимые пакеты для этого проекта:

import pandas as pd
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import pickle

Знакомство с набором данных:

netflix_titles_df = pd.read_csv('netflix_titles.csv')
netflix_titles_df.head()

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

netflix_titles_df.drop(netflix_titles_df.columns[[0,1,5,6,7,9]], axis=1, inplace=True)

Теперь, чтобы проверить оставшиеся столбцы на NaN:

netflix_titles_df.count()
title          6234
director       4265
cast           5664
rating         6224
listed_in      6234
description    6234

Обычно следующим шагом будет удаление всех строк с NaN / отсутствующими значениями. Однако давайте сначала посмотрим, какова доля строк с NaN в любом из столбцов:

null_rows = len(netflix_titles_df[netflix_titles_df.isna().any(axis=1)])
print(f'Rows with NaNs: {null_rows} ({(null_rows/netflix_titles_df.shape[0])*100:.0f}%)')

Строки с NaN: 2329 (37%)

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

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

netflix_titles_df.fillna('', inplace=True)
netflix_titles_df.head()

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

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

Режиссер и актеры: Что может быть интереснее просмотра фильма в любимом жанре пользователя? Тот, который также играет главную роль или снят их любимым актером / режиссером.

Рейтинг: используется для взвешивания похожих шоу, предназначенных для той же возрастной группы старше.

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

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



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

Прежде чем попасть туда, необходима дальнейшая обработка столбцов Director и Cast, в отличие от других столбцов, где каждый отдельный токен / слово сам по себе уникален, именованные сущности (например, имена людей в данном случае) теряют свое значение при разделении (например: «Хью Джекман» - признанный актер, а [«Хью», «Джекман»] просто… Хью и Джекман). Каждое имя само по себе не должно использоваться для обозначения сходства между актерами шоу.

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

netflix_titles_df[['director','cast']] = netflix_titles_df[['director','cast']].applymap(lambda x: ' '.join(x.replace(' ', '').split(',')[:3]))
netflix_titles_df.head()

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

Одна из важных особенностей колонки Title, о которой говорилось ранее, заключается в том, что она помогает находить сиквелы шоу. Мы бы хотели, чтобы сиквелы не только указывались среди аналогичных шоу, но и занимали более высокое место в списке. Этого можно добиться, сделав больший акцент на ключевых словах в заголовках шоу (придав им больший вес).

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

netflix_titles_df['title_dup'] = netflix_titles_df['title']

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

titles_corpus = netflix_titles_df.apply(' '.join, axis=1)

Последними оставшимися шагами являются преобразование строк функций в нижний регистр (чтобы слова обрабатывались одинаково независимо от регистра букв), токенизация корпуса и отбрасывание стоп-слов (the, for, that и т. Д. .). К счастью, sklearn TfidfVectorizer уже позаботится об этом, если заданы правильные параметры:

tfidf_vectorizer_params = TfidfVectorizer(lowercase=True, stop_words='english', ngram_range=(1, 3), max_df = .5)

Почему ngram_range = (1, 3): N-грамма - это последовательность N слов из текста. Если установить минимальное значение 1 и максимальное значение 3, TF-IDF будет захватывать последовательности униграмм, биграмм и триграмм. Пример «Люди едят овощи» приводит к векторам для всего следующего:

Юниграмма 1: «Люди»
Юниграмма 2: «Ешьте»
Юниграмма 3: «овощи»

Биграмма 1: «Люди едят»
Биграмма 2: «едят овощи»

Триграмма 1: «Люди едят овощи»

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

2. Векторизация текста

Векторизация корпуса (или обычно текста) - это процесс преобразования текста в числовое представление для расчета важности токена / слова в корпусе.

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

TF-IDF (термин «частота-обратная частота документов») - это статистическая мера, которая оценивает, насколько слово релевантно документу в коллекции документов. Это достигается путем умножения двух показателей: сколько раз слово встречается в документе, и обратной частоты этого слова в наборе документов.

tfidf_vectorizer = tfidf_vectorizer_params.fit_transform(titles_corpus)

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

pd.DataFrame(tfidf_vectorizer.toarray(), columns=tfidf_vectorizer_params.get_feature_names())

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

pickle.dump(tfidf_vectorizer, open('tfidf_vectorizer.pickle', 'wb'))

Чтобы определить сходство между векторами / шоу, мы измеряем косинус угла между векторами TF-IDF.

Косинусное подобие = (A.B) / (|| A ||. || B ||), где A и B - векторы

Мы получаем косинусное подобие, взяв скалярное произведение векторов TF-IDF:

vects_cos_sim = cosine_similarity(tfidf_vectorizer, tfidf_vectorizer)

Что дает нам эту матрицу попарного косинусного сходства:

pd.DataFrame(data=vects_cos_sim, index=netflix_titles_df['title'], columns=netflix_titles_df['title']).head()

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

Полный код без беспорядка:

3. Механизм рекомендаций

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

  1. Получите индекс запрошенного названия шоу.
  2. Вычислите косинусоидальное сходство всех шоу с этим шоу.
  3. Отсортируйте результаты с шага 2 от наибольшего косинуса подобия к наименьшему и получите 5 лучших.
  4. Возвращает названия шоу и достоверность сходства (косинусное сходство) в формате JSON.

Давайте проверим нашу логику:

def recommended_shows(title):
    
    #Get show index
    title_iloc = netflix_titles_df.index[netflix_titles_df['title'] == title][0]
    
    #Get cosine similarity
    show_cos_sim = cosine_similarity(tfidf_vectorizer[title_iloc], tfidf_vectorizer).flatten()
    
    #Get the top 5 most similar shows
    sim_titles_vects = sorted(list(enumerate(show_cos_sim)), key=lambda x: x[1], reverse=True)[1:6]
    
    #Return result
    response = '\n'.join([f'{netflix_titles_df.iloc[t_vect[0]][0]} --> confidence: {round(t_vect[1],1)}' for t_vect in sim_titles_vects])
    
    return response
print(recommended_shows('The Matrix'))

The Matrix Reloaded → уверенность: 0,4
Matrix Revolutions → уверенность: 0,3
Восход Юпитера → уверенность: 0,1
Терминатор 3: Восстание машин → уверенность: 0,1
Sense8 → уверенность : 0,1

print(recommended_shows('Breaking Bad'))

«Эль Камино: Во все тяжкие» → уверенность: 0,1
Лучше позвоните Саулу → уверенность: 0,1
Дорога на Эль-Камино: за кулисами фильма «Во все тяжкие» → уверенность: 0,1
Плохая кровь → уверенность: 0,1
Озарк → уверенность: 0,1

Кажется, работает как задумано.

Мы сохраним движок в отдельном файле модуля advice_engine.py. Вот как это выглядит после настройки функции для возврата ответа в формате JSON:

4. Flask API

Flask - это легкий микрофреймворк, используемый для быстрой и простой разработки веб-приложений и сервисов с минимальным кодом Python.



При запуске наш сервер Flask API должен:

  1. Загрузите столбец заголовка как фрейм данных индекса.
  2. Загрузить с диска ранее сохраненную матрицу TF-IDF.
  3. Принимать и анализировать запросы POST API, маршрутизированные через конечную точку ‘/ api /’ в формате JSON. Пример приемлемого запроса:

{
«title»: «Матрица»
}



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

5. Ответить на запрос мощностью двигателя.

Сохраните в advice_api.py и выполните эту команду:

python recommendation_api.py

Вы должны увидеть загрузку сервера Flask:

Чтобы протестировать работу API локально, отправьте тестовый запрос на указанный адрес (http://127.0.0.1:5000/api/). Я сделал это с помощью Postman:

Большой! Мы готовы начать развертывание на Heroku.

5. Развертывание на Heroku



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

  1. Войдя в свою учетную запись, перейдите на https://dashboard.heroku.com, нажмите Создать и выберите в меню Создать новое приложение:

2. Дайте приложению имя (например, netflix-advice-api) и нажмите «Создать приложение»:

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

Создайте новый текстовый файл и назовите его точно .gitignore. или выполните эту команду:

touch .gitignore

После создания откройте его в любом текстовом редакторе и вставьте эти инструкции:

#Ignoring virtualenv folder containing large Python packages. Heroku already install the packages in requirements.txt on its end
recommendation_api_env/
#Ignoring pycache folder
__pycache__/
#Ignoring shows_vectorizer as the API will load tfidf_vectorizer.pickle not generate it from scratch
shows_vectorizer.py

Теперь мы готовы продолжить передачу в облако.

3. Вы будете перенаправлены на страницу с четкими инструкциями по настройке приложения в облаке Heroku:

Heroku не займет больше пары минут, чтобы установить пакеты и запустить API.

Чтобы протестировать нашу настройку, мы отправляем запрос на https://netflix-recommendation-api.herokuapp.com/api/:

Запрос POST к Heroku выполнен успешно.

Бонус: API Swagger UI



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

  1. Находясь в каталоге проекта и с включенным virtualenv в разделе advice_api_env, установите flasgger:
pip install flasgger

2. обновите requirements.txt новым пакетом flasgger:

pip freeze > requirements.txt

3. Создайте файл swagger_doc.yml:

Руководство по форматированию Swagger: https://swagger.io/specification

4. Примените следующие изменения к Рекомендации_api.py:

5. Зафиксируйте изменения в Heroku:

$ git add .
$ git commit -am "swagger ui update"
$ git push heroku master

Теперь пользовательский интерфейс доступен на конечной точке ‘/ apidocs /’. Смотрите в прямом эфире на https://netflix-recommendation-api.herokuapp.com/apidocs/

Резюме

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

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