В первой части мы рассмотрели основы прогнозирования ввода-вывода и установили его зависимости. В этой части мы собираемся создать приложение.
Импорт данных из TMDB
Мы будем импортировать данные, используя Prediction IO SDK, поэтому сначала нужно указать Flight, чтобы они использовались. В начале вашего файла index.php
добавьте следующий код:
<?php session_start(); //start a session require 'vendor/autoload.php'; //autoload dependencies use PredictionIO\PredictionIOClient; //import the prediction IO client
Затем зарегистрируйте клиент Prediction IO в Flight, чтобы мы могли использовать его в нашем приложении:
Flight::register('prediction', 'PredictionIO\PredictionIOClient');
Пока мы здесь, давайте также зарегистрируем класс MongoDB, чтобы мы могли запросить MongoDB позже:
Flight::register('mdb', 'Mongo', array('mongodb://localhost'));
Далее мы сопоставляем фабричный метод с методом Flight и вызываем prediction_client
. Мы будем использовать это позже, чтобы совершать звонки с помощью IO-клиента Prediction.
Flight::map('prediction_client', function(){ $client = Flight::prediction()->factory(array("appkey" => "YOUR_PREDICTION_IO_APP_KEY")); return $client; });
Наконец, мы также сообщаем Полету о Guzzle. Таким образом, нам не нужно инициализировать Guzzle каждый раз, когда нам нужно его использовать. Мы можем просто вызвать Flight::guzzle()->some_guzzle_method()
и покончить с этим.
Flight::register('guzzle', 'GuzzleHttp\Client');
Теперь мы можем начать писать код для импорта данных из TMDB. Сначала объявите маршрут к movies/import
пути movies/import
. Мы получим доступ к этому из браузера позже, чтобы начать импорт.
Flight::route('GET /movies/import', array('Admin', 'import'));
В приведенном выше коде мы указываем контроллер и метод, который Flight будет использовать внутри массива. Идите дальше и создайте каталог controllers
внутри корня папки вашего проекта. Затем создайте файл admin.php
, который будет служить контроллером. Откройте файл и объявите класс с тем же именем, что и имя файла:
<?php class Admin { public static function import() { } }
Внутри метода import
инициализируйте клиент IO Prediction. Затем создайте цикл for
который будет повторяться 100 раз. Внутри цикла мы вызываем API TMDB, чтобы вернуть данные о самых популярных для нас фильмах. Каждый вызов API возвращает 20 фильмов, поэтому, если мы повторяем цикл 100 раз, мы получаем в общей сложности 2000 фильмов.
$client = PredictionIOClient::factory(array("appkey" => "YOUR_PREDICTIONIO_APP_KEY")); $index = 0; for($x = 3; $x <= 100; $x++){ $movies_url = 'https://api.themoviedb.org/3/movie/popular?api_key=YOUR_TMDB_API_KEY&page=' . $x; $movies_response = Flight::guzzle()->get($movies_url); //get most popular movies $movies_body = $movies_response->getBody(); //get response body }
В приведенном выше коде мы используем Guzzle для получения некоторых фильмов из API TMDB для нас. Затем мы конвертируем возвращенные данные в формате JSON в массив, используя json_decode
:
$movies_result = json_decode($movies_body, true); $movies = $movies_result['results'];
Как только это будет сделано, мы можем просмотреть все фильмы и извлечь нужные нам поля. В этом случае нам нужен идентификатор, заголовок и путь к постеру фильма:
if(!empty($movies)){ //loop through all the movies foreach($res as $row){ $id = $row['id']; $title = $row['title']; $poster_path = ''; if(!empty($row['poster_path'])){ $poster_path = $row['poster_path']; } }
Чтобы получить более подробную информацию, нам нужно сделать отдельный звонок для каждого фильма, используя его идентификатор. Затем мы конвертируем возвращенные данные JSON в массив так же, как и ранее:
$moviedetails_url = 'https://api.themoviedb.org/3/movie/' . $id . '?api_key=YOUR_TMDB_API_KEY'; $moviedetails_response = Flight::guzzle()->get($moviedetails_url); $movie_details_body = $moviedetails_response->getBody(); $movie = json_decode($movie_details_body, true);
Вызов ресурса фильма возвращает целую кучу данных о фильме, но для этого приложения мы будем использовать только обзор и дату выпуска:
$overview = $movie['overview']; $release_date = $movie['release_date'];
Теперь, когда у нас есть все необходимые данные, мы можем сохранить их в базе данных, используя метод create_item
из SDK Prediction IO. Этот вызов принимает 2 аргумента: pio_iid
и pio_itypes
. pio_iid
— идентификатор элемента; в этом случае мы просто будем использовать $movie_id
, переменную, которую мы объявили ранее. Мы просто увеличиваем эту переменную для каждой итерации цикла, чтобы у нас был уникальный идентификатор для каждого фильма. Другим обязательным аргументом является pio_itypes
. Здесь мы указываем тип элемента. Вы можете использовать любое описательное имя для этого movie
. Но для этого приложения мы просто установим pio_itypes
в 1. Затем мы установим детали фильма, используя метод set
. Как только это будет сделано, мы просто вызываем метод execute
чтобы добавить фильм в базу данных. Затем мы используем print_r
чтобы распечатать ответ, который мы получаем, чтобы убедиться, что операция прошла успешно.
$command = $client->getCommand('create_item', array('pio_iid' => $movie_id, 'pio_itypes' => 1)); $command->set('tmdb_id', $id); $command->set('title', $title); $command->set('poster_path', $poster_path); $command->set('overview', $overview); $command->set('release_date', $release_date); $client_response = $client->execute($command); print_r($client_response);
Наконец, мы увеличиваем $movie_id
на 1.
$movie_id += 1;
Собирая все вместе, мы имеем следующий код:
$client = PredictionIOClient::factory(array("appkey" => "YOUR_PREDICTIONIO_APP_KEY")); $index = 41; for($x = 3; $x <= 100; $x++){ $movies_url = 'https://api.themoviedb.org/3/movie/popular?api_key=YOUR_TMDB_API_KEY&page=' . $x; $movies_response = Flight::guzzle()->get($movies_url); $movies_body = $movies_response->getBody(); $movies_result = json_decode($movies_body, true); $movies = $movies_result['results']; if(!empty($movies)){ foreach($movies as $row){ $id = $row['id']; $title = $row['title']; $poster_path = ''; if(!empty($row['poster_path'])){ $poster_path = $row['poster_path']; } $moviedetails_url = 'https://api.themoviedb.org/3/movie/' . $id . '?api_key=YOUR_TMDB_API_KEY'; $moviedetails_response = Flight::guzzle()->get($moviedetails_url); $movie_details_body = $moviedetails_response->getBody(); $movie = json_decode($movie_details_body, true); $overview = $movie['overview']; $release_date = $movie['release_date']; $command = $client->getCommand('create_item', array('pio_iid' => $index, 'pio_itypes' => 1)); $command->set('tmdb_id', $id); $command->set('title', $title); $command->set('poster_path', $poster_path); $command->set('overview', $overview); $command->set('release_date', $release_date); $client_response = $client->execute($command); print_r($client_response); echo "<br><br>"; $index++; } } }
Как только это будет сделано, откройте index.php
в корне каталога проекта и вставьте следующее в последнюю строку. Это запустит рамки полета. Как правило, это всегда должно быть в последней строке файла:
Flight::start();
После этого вы можете получить доступ к пути /movies/import
в браузере, чтобы начать импорт некоторых фильмов из TMDB. Это может занять некоторое время, чтобы пойти выпить кофе или посмотреть эпизод вашего любимого шоу.
Выбор случайных фильмов
Теперь, когда у нас есть несколько фильмов, мы готовы показать некоторые случайные для пользователя. Сначала создайте маршрут для страницы индекса:
Flight::route('GET /', array('Home', 'index'));
Это использует контроллер Home
так что home.php
создадим home.php
внутри каталога controllers
с помощью метода index
.
<?php class Home { public static function index() { } }
Внутри метода index
uniqid
уникальный идентификатор с uniqid
метода uniqid
, затем присвойте его user_id
сеанса user_id
. Кроме того, инициализируйте movies_viewed
со значением 0
. Это будет представлять количество фильмов, которые мы показали пользователю до сих пор. Мы увеличим его позже, когда пользователю будут предложены случайные фильмы. Далее мы используем клиент прогнозирования для сохранения пользователя в базе данных. SDK Prediction IO предоставляет нам метод create_user
который будет взаимодействовать с пользовательским API . Метод create_user
требует pio_uid
качестве аргумента. Это почти вся информация, которая нам нужна, поэтому мы просто вызываем метод execute
после добавления идентификатора пользователя. Если вы хотите добавить больше информации о пользователе, вы можете просто использовать метод set для установки пользовательской информации.
$user_id = uniqid(); $_SESSION['user_id'] = $user_id; $_SESSION['movies_viewed'] = 0; $client = Flight::prediction_client(); $command = $client->getCommand('create_user', array('pio_uid' => $user_id)); $client->execute($command);
Как только новый пользователь добавлен в базу данных, мы можем отобразить страницу индекса, используя метод render
предоставленный Flight. Здесь мы отображаем два представления: первое — это фактическая страница, а второе — макет. Сначала нам нужно вызвать render
на реальной странице, потому что макет зависит от устанавливаемой нами переменной content
. В вызове render
для макета мы устанавливаем заголовок страницы и базовый путь для файлов CSS и JS, которые мы связываем на странице:
Flight::render('index', array(), 'content'); Flight::render('layout', array('title' => 'Home', 'base_path' => '/movie_recommender'));
Метод render
ожидает имя представления в качестве первого аргумента. Предполагается, что представления в Flight находятся в каталоге представлений относительно корневого каталога проекта. Таким образом, в приведенном выше примере имя файла, используемого для представления, — index.php
и он будет содержать следующий HTML-код:
<div class="row"> <div id="movie-container" class="col-md-10 col-centered"> </div> </div> <div class="row"> <div id="recommended-movie-container" class="col-md-12 col-centered"> </div> </div> <script id="movie-template" type="text/x-handlebars-template"> <div class="col-md-8"> <img src="http://image.tmdb.org/t/p/w500{{ca_poster_path}}"> </div> <div class="col-md-4"> <h3>{{ca_title}}</h3> <div class="release-date"> {{ca_release_date}} </div> <div class="overview"> {{ca_overview}} </div> <div class="button-container"> <button class="btn btn-success btn-block btn-next" data-id="{{_id}}" data-action="like">Like</button> <button class="btn btn-danger btn-block btn-next" data-id="{{_id}}" data-action="dislike">Dislike</button> <a href="/movie_recommender/movies/recommended" class="show-recommendations">Show Recommendations</a> </div> </div> </script> <span class="label label-success"></span> <script id="recommended-movie-template" type="text/x-handlebars-template"> <div class="col-md-4"> <img src="http://image.tmdb.org/t/p/w500{{ca_poster_path}}" class="rm-image"> <h5>{{ca_title}}</h5> <div class="release-date"> {{ca_release_date}} </div> <div class="overview"> {{ca_overview}} </div> </div> </script>
Как видно из приведенного выше кода, мы в основном используем клиентские шаблоны для рендеринга деталей фильма. Для этого приложения мы используем рули . Каждый последующий фильм будет загружен через AJAX, поэтому мы также собираемся загрузить первый фильм через AJAX после загрузки страницы.
Для макета имя файла будет layout.php
. Файл макета содержит следующее:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title><?= $title; ?></title> <link rel="stylesheet" href="<?= $base_path; ?>/assets/css/bootstrap.min.css"> <link rel="stylesheet" href="<?= $base_path; ?>/assets/css/style.css"> </head> <body> <div id="wrapper"> <div class="navbar navbar-default"> <div class="navbar-header"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-responsive-collapse"> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="<?= $base_path; ?>">Movie Recommender</a> </div> <div class="navbar-collapse collapse navbar-responsive-collapse"> <ul class="nav navbar-nav"> <li><a href="/">Home</a></li> </ul> </div> </div> <div class="container"> <?= $content; ?> </div> </div> <script src="<?= $base_path; ?>/assets/js/jquery.min.js"></script> <script src="<?= $base_path; ?>/assets/js/bootstrap.min.js"></script> <script src="<?= $base_path; ?>/assets/js/handlebars.min.js"></script> <script src="<?= $base_path; ?>/assets/js/main.js"></script> </body> </html>
Из приведенного выше кода вы можете видеть, что мы используем Bootstrap в качестве основы для стиля. У нас также есть базовый стиль для всего приложения, который добавлен в файл style.css
:
.col-centered { float: none; margin: 0 auto; } .button-container { margin-top: 20px; } .show-recommendations { display: none; } #recommended-movies > div { height: 1000px; }
Для сценариев мы используем jQuery , файл JavaScript Bootstrap, Handlebars и основной файл JavaScript для приложения.
Для основного JavaScript мы добавляем следующий код:
var movie_src = $("#movie-template").html(); var movie_template = Handlebars.compile(movie_src); function getRandomMovie(request_data){ request_data = typeof request_data !== 'undefined' ? request_data : {}; $.post('movie/random', request_data, function(response){ var data = JSON.parse(response); var movie_html = movie_template(data); $('#movie-container').html(movie_html); if(data.has_recommended){ $('.show-recommendations').show(); } }); } getRandomMovie(); $('#movie-container').on('click', '.btn-next', function(){ var self = $(this); var id = self.data('id'); var action = self.data('action'); getRandomMovie({'movie_id' : id, 'action' : action}); });
Разбивая его, мы сначала скомпилируем шаблон Handlebars, который хранится в div с идентификатором movie-template
:
var movie_src = $("#movie-template").html(); var movie_template = Handlebars.compile(movie_src);
Затем мы объявляем метод getRandomMovie
. Это принимает request_data
в качестве необязательного параметра. Внутри функции мы используем метод post
jQuery для выдачи POST
запроса к movie/random
пути. Это возвращает случайные данные фильма с сервера в формате JSON. Затем мы преобразуем его в объект, который может использоваться JavaScript с JSON.parse
метода JSON.parse
. Как только это будет сделано, мы просто передадим его в шаблон Handlebars, который мы скомпилировали ранее, а затем обновим содержимое элемента movie-container
. Если в возвращаемых данных есть элемент has_recommended
мы показываем ссылку, которая приведет пользователя на страницу, где отображаются фильмы, рекомендованные программой Prediction IO:
function getRandomMovie(request_data){ request_data = typeof request_data !== 'undefined' ? request_data : {}; $.post('movie/random', request_data, function(response){ var data = JSON.parse(response); var movie_html = movie_template(data); $('#movie-container').html(movie_html); if(data.has_recommended){ $('.show-recommendations').show(); } }); }
Как только скрипт загружен, мы выполняем функцию для загрузки первого случайного фильма:
getRandomMovie();
Затем мы прослушиваем событие click
для кнопки с btn-next
. Если вы помните обзор приложения ранее, у нас есть две кнопки: нравится и не нравится. Эти кнопки имеют btn-next
. Поэтому каждый раз, когда на них нажимают, выполняется код ниже. Он вызывает функцию getRandomMovie
и предоставляет идентификатор фильма и действие. Действие может иметь значение «нравится» или «не нравится»:
$('#movie-container').on('click', '.btn-next', function(){ var self = $(this); var id = self.data('id'); var action = self.data('action'); getRandomMovie({'movie_id' : id, 'action' : action}); });
Возвращаясь к серверной части, теперь мы готовы написать код для получения случайного фильма из базы данных. Сначала объявите новый маршрут, который отвечает на запросы POST
к movie/random
пути:
Flight::route('POST /movie/random', array('Home', 'random'));
В приведенном выше коде мы используем тот же контроллер, который мы использовали ранее для рендеринга домашней страницы приложения. Но на этот раз мы используем random
метод. Так что продолжайте и объявите это в вашем файле controllers/Home.php
:
public static function random() { }
Внутри random
метода мы получаем детали запроса и затем проверяем, был ли установлен сеанс пользователя. Если есть сеанс пользователя, мы получаем фильмы, которые были просмотрены текущим пользователем. Затем мы подключаемся к базе данных MongoDB, используя переменную mdb
которую мы присвоили Flight ранее. Затем мы генерируем случайное число от 1 до 2000. 1 представляет исходный идентификатор фильма, который мы использовали ранее, 2000 — это общее количество импортированных фильмов. Я просто жестко запрограммировал это, поскольку мы уже знаем общее количество фильмов, и мы не будем его увеличивать. После этого мы просто отправляем запрос в MongoDB и itypes
ему, чтобы он нашел все элементы со значением itypes
равным 1
. Затем мы используем случайное число, сгенерированное нами в качестве смещения, и сообщаем ему, чтобы ограничить результат до 1. Выполнение запроса возвращает объект итератора, поэтому нам все еще нужно преобразовать итератор в массив с использованием метода iterator_to_array
. Как только это будет сделано, мы вызываем метод array_values
для преобразования ассоциативного массива в числовой, чтобы мы могли получить array_values
обратившись к первому индексу массива.
$request = Flight::request(); if(!empty($_SESSION['user_id'])){ $movies_viewed = $_SESSION['movies_viewed']; $dbname = 'predictionio_appdata'; $mdb = Flight::mdb(); $db = $mdb->$dbname; $first_movie_id = 1; $last_movie_id = 2000; $skip = mt_rand($first_movie_id, $last_movie_id); //generate a random number that is between the first and last movie id $items = $db->items; //offset using the random number $cursor = $items->find(array('itypes' => '1'))->skip($skip)->limit(1); $data = array_values(iterator_to_array($cursor)); //convert iterator object to an array then convert associative array to numeric $movie = $data[0];
Затем мы проверяем, содержат ли данные запроса movie_id
. Если вы помните из основного файла JavaScript ранее, мы не предоставляли никаких данных для функции getRandomMovie
при первой загрузке страницы. Но когда пользователь начинает взаимодействовать с приложением с помощью кнопки «Нравится» и «Не нравится», мы передаем значение movie_id
и действие. И вот что мы здесь проверяем. Если movie_id
существует, то мы используем клиент IO Prediction, чтобы сохранить это взаимодействие с пользователем в базе данных. Но сначала мы должны извлечь идентификатор фильма, так как Prediction IO добавляет префикс к идентификатору фильма, который мы указали при импорте некоторых фильмов ранее. Идентификаторы фильмов начинаются с префикса приложения, за которым следует подчеркивание. Поэтому мы используем метод substr
для извлечения идентификатора фильма. Мы делаем это, получая позицию подчеркивания и затем добавляя к нему 1.
if(!empty($request->data['movie_id'])){ $params = $request->data; $client = Flight::prediction_client(); $user_id = $_SESSION['user_id']; $movie_id = substr($params['movie_id'], strpos($params['movie_id'], '_') + 1); $action = $params['action'];
Как только это будет сделано, вызовите метод identify
из SDK Prediction IO. Это говорит Prediction IO о назначении конкретного пользователя действиям, которые мы собираемся выполнить. Затем мы используем метод getCommand
для создания команды, которая будет выполнять метод record_action_on_item
в API прогнозирования ввода-вывода . То, что это делает, так же, как это звучит: записывать действия пользователя Допустимые значения для этого включают: как, не нравится, оценить, просмотреть и конверсия. Метод record_action_on_item
требует pio_action
которая является действием пользователя, и pio_iid
которая является идентификатором фильма. После того, как мы их присвоили, все, что нужно, это вызвать метод execute
чтобы зафиксировать действие пользователя.
$client->identify($user_id); $user_action = $client->getCommand('record_action_on_item', array('pio_action' => $action, 'pio_iid' => $movie_id)) $res = $client->execute($user_action);
Затем мы увеличиваем количество просматриваемых фильмов на 1. Затем мы проверяем общее количество просматриваемых фильмов. Если 20 уже просмотрено, мы устанавливаем для элемента has_recommended
значение true
. Если вы помните из основного файла JavaScript ранее, мы проверяем наличие этого элемента. Если он существует, мы показываем ссылку на страницу рекомендуемых фильмов. После этого мы просто обновляем сеанс movies_viewed
чтобы сохранить увеличенные просмотренные фильмы.
$movies_viewed += 1; if($movies_viewed == 20){ $movie['has_recommended'] = true; } $_SESSION['movies_viewed'] = $movies_viewed;
Вне условия проверки существования movie_id
мы просто movie_id
строковое представление JSON данных фильма, используя метод Flight json
. Это почти то же самое, что метод json_encode
, доступный для PHP:
if(!empty($request->data['movie_id'])){ ... } Flight::json($movie);
Собрав все воедино, мы получим следующее:
public static function random() { $request = Flight::request(); if(!empty($_SESSION['user_id'])){ $movies_viewed = $_SESSION['movies_viewed']; $dbname = 'predictionio_appdata'; $mdb = Flight::mdb(); $db = $mdb->$dbname; $skip = mt_rand(1, 2000); $items = $db->items; $cursor = $items->find(array('itypes' => '1'))->skip($skip)->limit(1); $data = array_values(iterator_to_array($cursor)); $movie = $data[0]; if(!empty($request->data['movie_id'])){ $params = $request->data; $client = Flight::prediction_client(); $user_id = $_SESSION['user_id']; $movie_id = substr($params['movie_id'], strpos($params['movie_id'], '_') + 1); $action = $params['action']; $client->identify($user_id); $user_action = $client->getCommand('record_action_on_item', array('pio_action' => $action, 'pio_iid' => $movie_id)); $client->execute($user_action); $movies_viewed += 1; if($movies_viewed == 20){ $movie['has_recommended'] = true; } $_SESSION['movies_viewed'] = $movies_viewed; } Flight::json($movie); } }
Рекомендовать фильмы
Теперь, когда мы закончили с фазой обучения, пришло время приступить к написанию кода для фазы рекомендации. Часть, которая фактически рекомендует соответствующие фильмы пользователю. Обратите внимание, что релевантность результатов, которые возвращает IO Prediction, зависит от настроек, которые мы указали ранее для механизма рекомендации фильмов, и от фактических данных, которые были собраны. Чем больше данных, тем лучше будут результаты. Но мы не можем просить пользователя оценить целую кучу фильмов только для того, чтобы попасть туда. Я считаю, что 20 — это идеальное число.
Создайте новый маршрут, который будет отвечать на запросы GET по пути /movies/recommended
путь:
Flight::route('GET /movies/recommended', array('Home', 'recommended'));
Этот маршрут использует recommended
метод в контроллере Home
.
Внутри метода инициализируйте соединение с базой данных, а затем получите коллекцию элементов. Кроме того, инициализируйте клиент IO Prediction, а затем присвойте пустой массив массиву $recommended_movies
. Здесь мы будем хранить данные для рекомендуемых фильмов позже.
$dbname = 'predictionio_appdata'; $mdb = Flight::mdb(); $db = $mdb->$dbname; $items = $db->items; //get the items collection $client = Flight::prediction_client(); $recommended_movies = array();
Затем создайте оператор try catch
. Внутри блока try
получите идентификатор текущего пользователя из сеанса, а затем используйте метод identify
чтобы назначить текущего пользователя. Затем используйте метод getCommand
для создания команды, которая будет вызывать метод itemrec_get_top_n
в API прогнозирования ввода-вывода. Этот метод возвращает лучшие рекомендуемые фильмы для пользователя, с которым мы его идентифицировали. Этот метод принимает pio_engine
и pio_n
качестве своих параметров. pio_engine
— это имя, которое мы присвоили движку, который мы создали ранее. pio_n
— количество результатов, которые вы хотите вернуть. В этом случае мы просто порекомендуем девять фильмов. Как только это будет сделано, мы просто вызываем метод execute
для выполнения фактического запроса.
Если вызов успешен, он возвращает массив с элементом pio_iids
. Содержит идентификаторы рекомендуемых фильмов. Поскольку мы указали 9 для pio_n
, мы должны получить 9 идентификаторов фильмов. Затем мы используем array_walk
для префикса идентификаторов фильмов с идентификатором приложения. Нам нужно добавить префикс идентификаторов фильмов, потому что Prediction IO возвращает только действительные идентификаторы фильмов. Это не хорошо, потому что то, что на самом деле сохраняется в базе данных в качестве значения для идентификатора фильма, имеет префикс с идентификатором приложения, за которым следует подчеркивание. Вот почему нам нужно добавить префикс перед выполнением запроса к MongoDB. Если вы не знаете, что такое идентификатор приложения, зайдите в интерфейс веб-администратора Prediction IO через браузер, затем выберите приложение, которое вы создали ранее, и нажмите на любой из имеющихся у вас механизмов. Однажды на странице движка URL будет выглядеть примерно так:
http : //localhost:9000/web/?appid=4&engineid=4&engineinfoid=itemrec#engine
Значение appid
в параметрах запроса является идентификатором вашего приложения. Вы можете использовать это в качестве префикса. В этом случае appid
равно 4
.
После того как мы добавили к каждому из элементов массива идентификатор приложения, мы можем запросить MongoDB, чтобы получить данные для каждого из этих идентификаторов фильма. Мы делаем это с помощью оператора $in
. Оператор $in
ожидает массив элементов, которые мы хотим сопоставить. Далее мы конвертируем итератор в массив. Если мы каким-то образом оказываемся под блоком catch
мы просто повторяем, что есть проблема:
try{ $user_id = $_SESSION['user_id']; $client->identify($user_id); $command = $client->getCommand('itemrec_get_top_n', array('pio_engine' => 'movie-recommender', 'pio_n' => 9)); $recommended_movies_raw = $client->execute($command); $movie_iids = $recommended_movies_raw['pio_iids']; array_walk($movie_iids, function(&$movie_iid){ $movie_iid = '4_' . $movie_iid; }); $cursor = $items->find(array('itypes' => '1', '_id' => array('$in' => $movie_iids))); $recommended_movies = array_values(iterator_to_array($cursor)); }catch(Exception $e){ echo "Sorry there's a problem"; }
Если вы оказались внутри блока catch
это означает, что предоставленные нами данные еще не «обучены». Это означает, что IO Prediction еще не обработал число, когда пользователь закончил оценивать фильмы. Есть два решения для этого. Первый — это увеличение количества фильмов, которые должен оценить пользователь. 60 довольно безопасно, так как в минуту 60 секунд. Мы установили расписание тренировок для выполнения каждую минуту, так что это довольно хорошее число, если только пользователь сразу не нажмет случайную кнопку при показе фильма. Второй метод состоит в том, чтобы вручную указать Prediction IO для обучения модели данных. Вы можете сделать это, нажав на вкладку «Алгоритмы» на странице вашего движка. И на используемом алгоритме по умолчанию, нажмите на кнопку «работает». Появится раскрывающийся список, и все, что вам нужно сделать, это нажать на «модель данных поезда сейчас». Это скажет Prediction IO немедленно обучить модель данных.
Наконец, мы сбрасываем значения для movies_viewed
и user_id
в сеансе, а затем визуализируем страницу для рекомендуемых фильмов, передавая данные, которые мы получили из базы данных:
$_SESSION['movies_viewed'] = 0; $_SESSION['user_id'] = ''; Flight::render('recommended', array('recommended_movies' => $recommended_movies), 'body_content');
Вот HTML-код для рекомендуемой страницы фильмов:
<div class="row"> <h1>Recommended Movies</h1> <div id="recommended-movies" class="col-md-12"> <?php foreach($recommended_movies as $rm){ ?> <div class="col-md-6"> <img src="http://image.tmdb.org/t/p/w500<?= $rm['ca_poster_path'] ?>" alt="<?= $rm['ca_title'] ?>"> <h4><?= $rm['ca_title'] ?></h4> <div class="release-date"> <?= $rm['ca_release_date'] ?> </div> <div class="overview"> <?= $rm['ca_overview'] ?> </div> </div> <?php } ?> </div> </div>
То, что мы делаем, — это циклически перебираем массив $recommended_movies
а затем выводим значения для соответствующих полей; а именно название, дата выпуска, обзор и изображение.
Вывод
Это оно! Из этого урока вы узнали, как использовать Prediction IO для обеспечения возможности машинного обучения для вашего приложения. Вы можете проверить этот проект в репозитории Github .
Мы едва поцарапали поверхность этой серии, и с ней можно сделать гораздо больше. Я рекомендую вам ознакомиться с официальными документами для прогнозирования IO, если вы хотите узнать больше.
Если вам известны какие-либо альтернативы Prediction IO или некоторые интересные варианты использования, сообщите нам об этом в комментариях!