博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
使用Prediction.io创建电影推荐应用-实现
阅读量:2506 次
发布时间:2019-05-11

本文共 31487 字,大约阅读时间需要 104 分钟。

In part one, we covered the basics of Prediction IO and installed its dependencies. In this part, we’re going to build the app.

在第一部分中,我们介绍了Prediction IO的基础知识并安装了其依赖项。 在这一部分中,我们将构建应用程序。

从TMDB导入数据 (Importing Data from TMDB)

We will be importing the data using the Prediction IO SDK, so we first need to tell Flight to use it. In the beginning of your index.php file add the following code:

我们将使用Prediction IO SDK导入数据,因此我们首先需要告诉Flight使用它。 在index.php文件的开头,添加以下代码:

Next, register the Prediction IO Client to Flight so that we can use it throughout our app:

接下来,将Prediction IO Client注册为Flight,以便我们可以在整个应用中使用它:

Flight::register('prediction', 'PredictionIO\PredictionIOClient');

While we’re here let’s also register the MongoDB class so that we can query MongoDB later on:

在这里,我们还要注册MongoDB类,以便稍后可以查询MongoDB:

Flight::register('mdb', 'Mongo', array('mongodb://localhost'));

Next, we map the factory method to a Flight method and call it prediction_client. We will be using this later on to make calls with the Prediction IO client.

接下来,我们将工厂方法映射到Flight方法,并将其命名为prediction_client 。 我们稍后将使用它与Prediction IO客户端进行呼叫。

Flight::map('prediction_client', function(){  $client = Flight::prediction()->factory(array("appkey" => "YOUR_PREDICTION_IO_APP_KEY"));  return $client;});

Lastly, we also let Flight know of Guzzle. This way we don’t need to initialize Guzzle every time we need to use it. We can just call Flight::guzzle()->some_guzzle_method() and be done with it.

最后,我们还让Flight知道了Guzzle。 这样,我们不需要每次使用Guzzle时都进行初始化。 我们可以只调用Flight::guzzle()->some_guzzle_method()并完成它。

Flight::register('guzzle', 'GuzzleHttp\Client');

We can now start writing the code for importing data from TMDB. First, declare a route to the movies/import path. We will access this from the browser later on to begin importing.

现在,我们可以开始编写用于从TMDB导入数据的代码。 首先,声明到movies/import路径的路线。 我们稍后将通过浏览器访问它,以开始导入。

Flight::route('GET /movies/import', array('Admin', 'import'));

In the code above, we’re specifying the controller and the method that Flight will use inside an array. Go ahead and create a controllers directory inside the root of your project folder. Then create an admin.php file, which will serve as the controller. Open up the file and declare a class to be the same name as the name of the file:

在上面的代码中,我们指定了Flight将在数组内使用的控制器和方法。 继续,在项目文件夹的根目录下创建一个controllers目录。 然后创建一个admin.php文件,它将用作控制器。 打开文件,并声明一个与文件名相同的类:

Inside the import method, initialize the Prediction IO client. Then create a for loop that would loop 100 times. Inside the loop, we call the TMDB API to return the data on the most popular movies for us. Each API call returns 20 movies so if we loop for 100 times we get a total of 2000 movies.

import方法内部,初始化Prediction IO客户端。 然后创建一个for循环,该循环将循环100次。 在循环内部,我们调用TMDB API为我们返回最流行电影的数据。 每个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          }

In the code above, we’re using Guzzle to fetch some movies from the TMDB API for us. We then convert the returned data which is in JSON format into an array using json_decode:

在上面的代码中,我们正在使用Guzzle从TMDB API中获取一些电影。 然后,我们使用json_decode将返回的JSON格式数据转换为数组:

$movies_result = json_decode($movies_body, true);$movies = $movies_result['results'];

Once that’s done, we can loop through all of the movies and extract the fields that we want. In this case we want the id, title and the path to the movie poster:

完成后,我们可以遍历所有电影并提取所需的字段。 在这种情况下,我们需要ID,标题和电影海报的路径:

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'];    }  }

To get more details we need to make a separate call for each movie by using its id. We then convert the returned JSON data to an array the same way we did earlier:

要获取更多详细信息,我们需要使用其ID对每个电影进行单独调用。 然后,我们以与之前相同的方式将返回的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);

The call to the movie resource returns a whole bunch of data about the movie, but for this app we’re only going to use the overview and release date:

对电影资源的调用返回了有关电影的一堆数据,但是对于这个应用程序,我们将仅使用概述和发行日期:

$overview = $movie['overview'];$release_date = $movie['release_date'];

Now that we have all the data we need, we can save it to the database using the create_item method from the Prediction IO SDK. This call accepts 2 arguments: pio_iid and pio_itypes. pio_iid is the ID of the item; in this case, we’re simply going to use $movie_id, the variable that we declared earlier. We’ll just increment this variable for each iteration of the loop so we have a unique id for each movie. The other required argument is the pio_itypes. This is where we specify the type of the item. You can use any descriptive name for this like movie. But for this app, we’re just going to set the pio_itypes to 1. Next, we set the movie details using the set method. Once that’s done we just call the execute method to add the movie into the database. Then we use print_r to print out the response that we get to make sure the operation was successful.

现在我们有了所需的所有数据,我们可以使用Prediction IO SDK中的create_item方法将其保存到数据库中。 该调用接受2个参数: pio_iidpio_itypespio_iid是商品的ID; 在这种情况下,我们将仅使用$movie_id ,这是我们之前声明的变量。 我们将为循环的每次迭代增加此变量,以便为每部电影都有唯一的ID。 另一个必需的参数是pio_itypes 。 这是我们指定项目类型的地方。 您可以为此movie使用任何描述性名称,例如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);

Lastly, we increment the $movie_id by 1.

最后,我们将$movie_id递增1。

$movie_id += 1;

Putting everything together, we have the following code:

将所有内容放在一起,我们有以下代码:

$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 "
"; $index++; } }}

Once that’s done, open upindex.php at the root of the project directory and put in the following on the last line. This will start the Flight framework. As a convention, this should always be located at the last line of the file:

完成后,在项目目录的根目录下打开index.php并将其放在最后一行。 这将启动Flight框架。 按照惯例,它应始终位于文件的最后一行:

Flight::start();

After that, you can now access the /movies/import path in the browser to begin importing some movies from TMDB. This might take a while to complete so go grab a coffee or watch an episode of your favorite show.

之后,您现在可以在浏览器中访问/movies/import路径,以开始从TMDB导入一些电影。 这可能需要一段时间才能完成,因此可以去喝咖啡或观看您喜欢的节目的一集。

挑选随机电影 (Picking Random Movies)

Now that we have some movies, we’re ready to show some random ones to the user. First, create a route for the index page:

现在我们有了一些电影,我们准备向用户展示一些随机的电影。 首先,为索引页面创建路由:

Flight::route('GET /', array('Home', 'index'));

This makes use of the Home controller so go ahead and create a home.php inside the controllers directory with an index method.

这将利用Home控制器,因此继续使用index方法在controllers目录内创建一个home.php

Inside the index method, generate a unique id using the uniqid method, then assign it to the user_id session item. Also, initialize movies_viewed with a value of 0. This will represent the number of movies that we have shown the user so far. We will increment it later on as random movies get suggested to the user. Next, we use the prediction client to save the user into the database. The Prediction IO SDK provides us with the create_user method which will interact with the . The create_user method needs the pio_uid as its argument. This is pretty much all the information we need so we just call the execute method once we’ve added the user id. If you want to add more user information you can just use the set method to set custom user information.

index方法内部,使用uniqid方法生成唯一ID,然后将其分配给user_id会话项。 另外,将movies_viewed初始化为0 。 这将代表我们到目前为止向用户展示的电影数量。 我们会在以后向用户建议随机电影时增加它。 接下来,我们使用预测客户端将用户保存到数据库中。 Prediction IO SDK为我们提供了create_user方法,该方法将与交互。 create_user方法需要使用pio_uid作为其参数。 这几乎是我们需要的所有信息,因此添加用户ID后,我们只需调用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);

Once the new user is added into the database we can render the index page using the render method provided by Flight. Here we’re rendering two views, the first one is the actual page and the second one is the layout. We need to call render on the actual page first because the layout depends on the content variable that we’re setting. In the render call for the layout, we’re setting the title of the page and the base path for the CSS and JS files that we’re linking in the page:

将新用户添加到数据库后,我们可以使用Flight提供的render方法来渲染索引页。 在这里,我们呈现两个视图,第一个是实际页面,第二个是布局。 我们首先需要在实际页面上调用render ,因为布局取决于我们所设置的content变量。 在布局的render调用中,我们设置页面的标题以及在页面中链接CSS和JS文件的基本路径:

Flight::render('index', array(), 'content');Flight::render('layout', array('title' => 'Home', 'base_path' => '/movie_recommender'));

The render method expects the name of the view as its first argument. Views in Flight are expected to be in the views directory relative to the root directory of the project. So in the example above, the file name used for the view is index.php and it will contain the following HTML:

render方法将视图名称作为其第一个参数。 飞行中的views应该位于相对于项目根目录的views目录中。 因此,在上面的示例中,用于视图的文件名为index.php ,它将包含以下HTML:

As you can see from the above code, we’re mainly using client-side templating to render the details for the movie. For this app, we’re using . Each subsequent movie will be loaded via AJAX so we’re also going to load the first movie via AJAX once the page has loaded.

从上面的代码中可以看到,我们主要是使用客户端模板来呈现电影的细节。 对于此应用程序,我们使用的是 。 随后的每部电影都将通过AJAX加载,因此一旦页面加载完毕,我们还将通过AJAX加载第一部电影。

For the layout, the file name would be layout.php. The layout file contains the following:

对于布局,文件名将为layout.php 。 布局文件包含以下内容:

  
<?= $title; ?>

From the above code you can see that we’re using as a framework for the styling. We also have basic styling for the whole app which is added in the style.css file:

从上面的代码中,您可以看到我们正在使用作为样式框架。 我们还为整个应用程序提供了基本样式,这些样式已添加到style.css文件中:

.col-centered {  float: none;  margin: 0 auto;}.button-container {  margin-top: 20px;}.show-recommendations {  display: none;}#recommended-movies > div {  height: 1000px;}

For the scripts, we use , Bootstrap’s JavaScript file, and the main JavaScript file for the app.

对于脚本,我们使用 ,BootstrapJavaScript文件, 和应用程序的主JavaScript文件。

For the main JavaScript we add the following code:

对于主要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});});

Breaking it down, we first compile the Handlebars template which is stored in the div with the ID of movie-template:

分解它,我们首先编译存储在div中的ID为movie-template的Handlebars movie-template

var movie_src = $("#movie-template").html();var movie_template = Handlebars.compile(movie_src);

We then declare the getRandomMovie method. This takes the request_data as an optional parameter. Inside the function we use jQuery’s post method to issue a POST request to the movie/random path. This returns random movie data from the server in JSON format. We then convert it to an object that can be used by JavaScript using the JSON.parse method. Once that’s done we just supply it to the Handlebars template that we have compiled earlier and then update the contents of movie-container div. If the returned data has the has_recommended item we show the link which will lead the user to the page where the movies recommended by Prediction IO are displayed:

然后,我们声明getRandomMovie方法。 这将request_data作为可选参数。 在函数内部,我们使用jQuery的post方法向movie/random路径发出POST请求。 这将从服务器以JSON格式返回随机电影数据。 然后,我们使用JSON.parse方法将其转换为JavaScript可以使用的对象。 完成后,我们将其提供给我们先前编译的Handlebars模板,然后更新movie-container div的内容。 如果返回的数据包含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();    }  });}

Once the script is loaded we execute the function to load the first random movie:

加载脚本后,我们将执行函数以加载第一个随机电影:

getRandomMovie();

We then listen for the click event on the button with the btn-next class. If you remember the overview of the app earlier we have two buttons: like and dislike. Those buttons have the btn-next class. So every time those are clicked, the code below is executed. What it does is call the getRandomMovie function and supply the movie id and the action. The action can have a value of either like or dislike:

然后,我们使用btn-next类监听按钮上的click事件。 如果您还记得应用程序的概述,那么我们有两个按钮:“喜欢”和“不喜欢”。 这些按钮具有btn-next类。 因此,每次单击它们时,都会执行以下代码。 它的作用是调用getRandomMovie函数,并提供影片ID和动作。 该动作可以具有“喜欢”或“不喜欢”的值:

$('#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});});

Going back to the server side, we’re now ready to write the code for getting a random movie from the database. First, declare a new route that responds to POST requests to the movie/random path:

回到服务器端,我们现在准备编写代码以从数据库中获取随机电影。 首先,声明一个新路由,该新路由响应对movie/random路径的POST请求:

Flight::route('POST /movie/random', array('Home', 'random'));

In the above code, we’re using the the same controller that we used earlier for rendering the home page of the app. But this time we’re using the random method. So go ahead and declare it in your controllers/Home.php file:

在上面的代码中,我们使用的控制器与我们之前渲染应用程序主页所使用的控制器相同。 但是这次我们使用random方法。 因此,继续在您的controllers/Home.php文件中声明它:

public static function random() {}

Inside the random method, we get the details of the request and then check if a user session has been set. If there’s a user session we get the movies that have been viewed by the current user. Next, we connect to the MongoDB database using the mdb variable that we assigned to Flight earlier. We then generate a random number from 1 to 2000. 1 represents the initial movie id that we used earlier, 2000 is the total number of movies that we have imported. I have just hard coded it in there since we already know the total number of movies and we won’t really be increasing it. After that, we just make a query to MongoDB and tell it to find all the items which has an itypes value of 1. Then we use the random number that we generated as an offset and tell it to limit the result to 1. Executing the query returns an iterator object, so we still need to convert the iterator to an array using the iterator_to_array method. Once that’s done, we call the array_values method to convert the associative array to a numeric one so we can get to the data we need by accessing the first index of the array.

random方法内部,我们获取请求的详细信息,然后检查是否已设置用户会话。 如果有用户会话,我们将获取当前用户已观看的电影。 接下来,我们使用之前分配给Flight的mdb变量连接到MongoDB数据库。 然后,我们生成一个从1到2000的随机数。1代表我们之前使用的初始电影ID,2000是我们导入的电影总数。 我已经在其中进行了硬编码,因为我们已经知道了电影的总数,并且我们实际上不会增加​​它。 之后,我们只查询MongoDB并告诉它查找所有itypes值为1 。 然后,我们使用生成的随机数作为偏移量,并告诉它将结果限制为1。执行查询将返回迭代器对象,因此,我们仍然需要使用iterator_to_array方法将迭代器转换为数组。 完成此操作后,我们将调用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];

Next, we check if the request data contains a movie_id. If you remember from the main JavaScript file earlier, we did not supply any data to the getRandomMovie function on the first time the page is loaded. But when the user starts interacting with the app with the like and dislike button we pass on the movie_id and the action. And that’s what we’re checking here. Ifmovie_id exists, then we use the Prediction IO client to save that user interaction in the database. But first we have to extract the movie id as Prediction IO adds a prefix to the movie id that we have supplied when we imported some movies earlier. Movie ids are prefixed with the app id followed by an underscore. So we use the substr method to extract the movie id. We do this by getting the position of the underscore and then adding 1 to it.

接下来,我们检查请求数据是否包含movie_id 。 如果您还记得之前的主JavaScript文件,则在第一次加载页面时,我们没有向getRandomMovie函数提供任何数据。 但是,当用户开始使用“喜欢”和“不喜欢”按钮与应用进行交互时,我们会传递movie_id和操作。 这就是我们在这里检查的内容。 如果movie_id存在,则我们使用Prediction IO客户端将该用户交互保存在数据库中。 但是首先,我们必须提取影片ID,因为Prediction IO会在我们之前导入一些影片时提供的影片ID之前添加前缀。 电影ID前面带有应用ID,后跟一个下划线。 因此,我们使用substr方法提取影片ID。 我们通过获取下划线的位置然后将其加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'];

Once that’s done, call the identify method from the Prediction IO SDK. This tells Prediction IO to assign a specific user to the actions that we’re going to perform. Next, we use the getCommand method to create a command that will execute the record_action_on_item method in the . What this does is just exactly as it sounds: record a user action. Valid values for this include: like, dislike, rate, view, and conversion. The record_action_on_item method requires the pio_action which is the user action and the pio_iid which is the movie id. Once we have assigned those, all that’s needed is to call the execute method to commit the user action.

完成后,从Prediction IO SDK调用identify方法。 这告诉Prediction IO将特定用户分配给我们将要执行的操作。 接下来,我们使用getCommand方法创建一个命令,该命令将在执行record_action_on_item方法。 它的作用完全像听起来那样:记录用户操作。 有效值包括:喜欢,不喜欢,评价,观看和转化。 该record_action_on_item方法需要pio_action这是用户动作和pio_iid这是电影的ID。 分配完这些之后,所需要做的就是调用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);

Next, we increment the movies viewed by 1. Then we check for the current movies viewed total. If 20 have already been viewed, we set the has_recommended item to true. If you remember from the main JavaScript file earlier, we are checking for the existence of this item. If it exists then we show the link to the recommended movies page. After that, we just update the movies_viewed session to store the incremented movies viewed.

接下来,我们将观看的电影增加1。然后检查当前观看的电影总数。 如果已经查看了20个,则将has_recommended项设置为true 。 如果您还记得以前的主JavaScript文件,我们正在检查此项是否存在。 如果存在,那么我们将显示指向推荐电影页面的链接。 在那之后,我们只是更新了movies_viewed会话以存储观看的增量电影。

$movies_viewed += 1;if($movies_viewed == 20){  $movie['has_recommended'] = true;     }    $_SESSION['movies_viewed'] = $movies_viewed;

Outside the condition for checking the existence of the movie_id, we just echo out the JSON string representation of the movie data using Flight’s json method. This is pretty much the same as the json_encode method that’s available for PHP:

在检查movie_id是否存在的条件movie_id ,我们只是使用Flight的json方法回显电影数据的JSON字符串表示形式。 这与可用于PHP的json_encode方法几乎相同:

if(!empty($request->data['movie_id'])){  ...}Flight::json($movie);

Putting everything together we get the following:

将所有内容放在一起,我们得到以下信息:

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);  }}

推荐电影 (Recommending Movies)

Now that we’re done with the learning phase, it’s time to proceed with writing the code for the recommendation phase. The part that actually recommends relevant movies to the user. Note that the relevance of the results that Prediction IO returns depends on the settings that we have supplied to the movie recommendation engine earlier and the actual data that has been collected. The more data, the better the results will be. But we can’t really ask the user to rate a whole bunch of movies just to get there. I believe 20 is an ideal number.

现在我们已经完成了学习阶段,是时候为推荐阶段编写代码了。 实际上向用户推荐相关电影的部分。 请注意,Prediction IO返回的结果的相关性取决于我们之前提供给电影推荐引擎的设置以及已收集的实际数据。 数据越多,结果越好。 但是我们不能真正要求用户仅仅为了达到目标就对整部电影进行评分。 我相信20是一个理想的数字。

Create a new route that will respond to GET requests on the /movies/recommended path:

/movies/recommended路径上创建一个新路由以响应GET请求:

Flight::route('GET /movies/recommended', array('Home', 'recommended'));

This route uses the recommended method in the Home controller. Inside the method, initialize a connection to the database and then get the items collection. Also, initialize the Prediction IO client then assign an empty array to the $recommended_movies array. This is where we will store the data for the recommended movies later on.

此路由使用Home控制器中的recommended方法。 在该方法内部,初始化与数据库的连接,然后获取items集合。 另外,初始化Prediction IO客户端,然后将空数组分配给$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();

Next, create a try catch statement. Inside the try block, get the current user id from the session and then use the identify method to assign the current user. Then, use the getCommand method to create a command that will call the itemrec_get_top_n method in the Prediction IO API. This method returns the top recommended movies for the user that we identified it with. This method accepts the pio_engine and pio_n as its parameters. The pio_engine is the name we assigned to the engine that we created earlier. pio_n is the number of results that you want to return. In this case we’re just going to recommend nine movies. Once that’s done we just call the execute method to make the actual request.

接下来,创建一个try catch语句。 在try块内,从会话中获取当前用户ID,然后使用identify方法分配当前用户。 然后,使用getCommand方法创建一个命令,该命令将在Prediction IO API中调用itemrec_get_top_n方法。 此方法返回与我们一起标识的用户的最佳推荐电影。 此方法接受pio_enginepio_n作为其参数。 pio_engine是我们分配给我们先前创建的引擎的名称。 pio_n是您要返回的结果数。 在这种情况下,我们将只推荐九部电影。 完成后,我们只需调用execute方法发出实际请求即可。

If the call is successful it returns an array with the pio_iids item. This contains the IDs of the recommended movies. Since we specified 9 for the pio_n, we should get 9 movie IDs. We then use array_walk to prefix the movie IDs with the app id. We need to prefix the movie IDs because Prediction IO only returns the actual movie IDs. This is not good because what actually gets saved in the database as the value for the movie ID is prefixed with the app id followed by an underscore. That’s why we need to add the prefix before making a query to MongoDB. If you don’t know what the app ID is, access the Prediction IO web admin interface from your browser, then select the app that you created earlier and click on any of the engines that you have. Once on the engine page the URL will look something like this:

如果调用成功,则返回带有pio_iids项的数组。 其中包含推荐电影的ID。 因为我们为pio_n指定了9,所以我们应该获得9个电影ID。 然后,我们使用array_walk为影片ID加上应用ID前缀。 我们需要给影片ID加上前缀,因为Prediction IO仅返回实际的影片ID。 这不好,因为作为电影ID的值实际存储在数据库中的内容以应用程序ID为前缀,后跟一个下划线。 这就是为什么我们需要在对MongoDB进行查询之前添加前缀。 如果您不知道应用程序ID是什么,请从浏览器访问Prediction IO Web管理员界面,然后选择您先前创建的应用程序,然后单击您拥有的任何引擎。 进入引擎页面后,URL将如下所示:

http://localhost:9000/web/?appid=4&engineid=4&engineinfoid=itemrec#engine

The value of the appid in the query parameters is the id of your app. You can use that as the prefix. In this case the appid is 4.

查询参数中的appid的值是您的应用的ID。 您可以将其用作前缀。 在这个例子中, appid4

Once we have prefixed each of the array items with the app id, we can query MongoDB to get the data for each of those movie ID’s. We do that using the $in operator. The $in operator expects an array of items that we want to match. Next, we convert the iterator to an array. If we somehow find ourselves under the catch block we just echo out that there’s a problem:

在为每个数组项添加了应用程序ID前缀之后,我们可以查询MongoDB以获得每个电影ID的数据。 我们使用$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";}

If you find yourself inside the catch block that means that the data we provided hasn’t been ‘trained’ yet. This means that Prediction IO still hasn’t done the number crunching when the user finished rating the movies. There are two solutions to this. The first is increasing the number of movies that the user has to rate. 60 is pretty safe since there are 60 seconds in a minute. We have set the training schedule to execute every minute so this is a pretty good number, not unless the user immediately clicks a random button when a movie is shown. The second method is to manually tell Prediction IO to train the data model. You can do that by clicking on the ‘Algorithms’ tab on your engine page. And on the default algorithm used, click on the ‘running’ button. A drop-down will show up and all you have to do is to click on the ‘train data model now’. This will tell Prediction IO to train the data model immediately.

如果您发现自己位于catch块中,则意味着我们提供的数据尚未经过“培训”。 这意味着当用户完成对电影的评分时,Prediction IO仍未进行数字处理。 有两种解决方案。 首先是增加用户必须评分的电影数量。 60分钟非常安全,因为一分钟有60秒。 我们将培训计划设置为每分钟执行一次,因此这是一个不错的数字,除非在电影放映时用户立即单击随机按钮,否则这不是一个好数字。 第二种方法是手动告诉Prediction IO训练数据模型。 您可以通过点击引擎页面上的“算法”标签来做到这一点。 然后在使用的默认算法上,单击“运行”按钮。 将显示一个下拉列表,您需要做的就是单击“立即训练数据模型”。 这将告诉Prediction IO立即训练数据模型。

train data model now

Lastly, we reset the values for the movies_viewed and the user_id in the session and then render the page for the recommended movies passing along the data that we got from the database:

最后,我们重置会话中的movies_vieweduser_id的值,然后呈现推荐电影的页面以及从数据库中获取的数据:

$_SESSION['movies_viewed'] = 0;$_SESSION['user_id'] = '';Flight::render('recommended', array('recommended_movies' => $recommended_movies), 'body_content');

Here’s the HTML for the recommended movies page:

这是推荐电影页面HTML:

Recommended Movies

What we’re doing is looping through the $recommended_movies array and then echoing out the values for the relevant fields; namely the title, release date, overview and the image.

我们正在做的是遍历$recommended_movies数组,然后回显相关字段的值。 即标题,发行日期,概述和图像。

结论 (Conclusion)

That’s it! In this tutorial you have learned how to use Prediction IO to provide machine learning capabilities to your app. You can check this project out . We have barely scratched the surface in this series and there’s much more that you can do with it. I recommend that you check out the if you want to learn more.

而已! 在本教程中,您学习了如何使用Prediction IO为您的应用程序提供机器学习功能。 您可以检出此项目。 在本系列文章中,我们几乎没有涉及任何内容,您可以做很多事情。 如果您想了解更多信息,我建议您查看的 。

If you know of any alternatives to Prediction IO or some interesting use cases, let us know in the comments!

如果您知道Prediction IO的替代方案或一些有趣的用例,请在评论中告诉我们!

翻译自:

转载地址:http://xxrgb.baihongyu.com/

你可能感兴趣的文章
利用datapropertyname属性 把datagridview的列绑定到数据源的列
查看>>
Spring MVC过滤器-委派过滤器代理(DelegatingFilterProxy)
查看>>
AspNetCore taghelpers标签的使用
查看>>
带你学习AOP框架之Aspect.Core[1]
查看>>
Socket简单实现数据交互及上传
查看>>
用android studio编写2-4个数的简易四则运算
查看>>
VUE生命周期中的钩子函数及父子组件的执行顺序
查看>>
代理模式
查看>>
不能运行javac的问题
查看>>
chrome developer tool 调试技巧
查看>>
leetcode375 Guess Number Higher or Lower II
查看>>
docker 创建基础镜像
查看>>
移动端-新建空白页面meta各个标签详细解读
查看>>
算法系列之一 快速排序
查看>>
状压DP【p1896】[SCOI2005]互不侵犯
查看>>
gym 101982 B题 Coprime Integers
查看>>
Polynomial Division 数学题
查看>>
JAVA 第四周学习总结
查看>>
JS学习记录(数组)
查看>>
数据可视化六步法
查看>>