| --- |
| title: Quick Start - Similar Product Engine Template |
| --- |
| |
| <!-- |
| Licensed to the Apache Software Foundation (ASF) under one or more |
| contributor license agreements. See the NOTICE file distributed with |
| this work for additional information regarding copyright ownership. |
| The ASF licenses this file to You under the Apache License, Version 2.0 |
| (the "License"); you may not use this file except in compliance with |
| the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| --> |
| |
| ## Overview |
| |
| This engine template recommends products that are "similar" to the input product(s). |
| Similarity is not defined by user or item attributes but by users' previous actions. By default, it uses 'view' action such that product A and B are considered similar if most users who view A also view B. The template can be customized to support other action types such as buy, rate, like..etc. |
| |
| This template is ideal for recommending products to customers based on their recent actions. |
| Using the IDs of the recently viewed products of a customer as the *Query*, |
| the engine will predict other products that this customer may also like. |
| |
| This approach works perfectly for customers who are **first-time visitors** or have not signed in. |
| Recommendations are made dynamically in *real-time* based on the most recent product preference you provide in the *Query*. |
| You can, therefore, recommend products to visitors without knowing a long history about them. |
| |
| You can also use this template to build the popular feature of Amazon: **"Customers Who Viewed This Item Also Viewed..."** quickly. |
| Help your customers explore more products that they like, and sell more products. |
| |
| ## Usage |
| |
| ### Event Data Requirements |
| |
| By default, this template takes the following data from Event Server as Training Data: |
| |
| - User *$set* events |
| - Item *$set* events with *categories* properties |
| - Users' *view* item events |
| |
| INFO: This template can easily be customized to consider more user events such as *buy*, *rate* and *like*. |
| You can offer features like "Customers Who Bought This Item Also Bought....". |
| |
| ### Input Query |
| |
| - List of ItemIDs, which are the targeted products |
| - N (number of items to be recommended) |
| - List of white-listed item categories (optional) |
| - List of white-listed ItemIds (optional) |
| - List of black-listed ItemIds (optional) |
| |
| The template also supports black-list and white-list. If a white-list is provided, the engine will include only those products in the recommendation. |
| Likewise, if a black-list is provided, the engine will exclude those products in the recommendation. |
| |
| ### Output PredictedResult |
| |
| - a ranked list of recommended itemIDs |
| |
| ## 1. Install and Run PredictionIO |
| |
| <%= partial 'shared/quickstart/install' %> |
| |
| ## 2. Create a new Engine from an Engine Template |
| |
| <%= partial 'shared/quickstart/create_engine', locals: { engine_name: 'MySimilarProduct', template_name: 'Similar Product Engine Template', template_repo: 'apache/predictionio-template-similar-product' } %> |
| |
| ## 3. Generate an App ID and Access Key |
| |
| <%= partial 'shared/quickstart/create_app' %> |
| |
| ## 4. Collecting Data |
| |
| Next, let's collect some training data for the app of this Engine. By default, |
| the Similar Product Engine Template supports 2 types of entities: **user** and |
| **item**, and event **view**. An item has the **categories** property, which is a list of category names (String). A user can view an item. Respectively, this template requires '$set' user event, '$set' item event, and user-view-item events. |
| |
| INFO: This template can easily be customized to consider more user events such as *buy*, *rate* and *like*. |
| |
| <%= partial 'shared/quickstart/collect_data' %> |
| |
| For example, when a new user with id "u0" is created in your app on time `2014-11-02T09:39:45.618-08:00` (current time will be used if eventTime is not specified), you can send a `$set` event for this user. To send this event, run the following `curl` command: |
| |
| <div class="tabs"> |
| <div data-tab="REST API" data-lang="json"> |
| ``` |
| $ curl -i -X POST http://localhost:7070/events.json?accessKey=$ACCESS_KEY \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "event" : "$set", |
| "entityType" : "user", |
| "entityId" : "u0", |
| "eventTime" : "2014-11-02T09:39:45.618-08:00" |
| }' |
| ``` |
| </div> |
| <div data-tab="Python SDK" data-lang="python"> |
| ```python |
| import predictionio |
| from datetime import datetime |
| |
| client = predictionio.EventClient( |
| access_key=<ACCESS KEY>, |
| url=<URL OF EVENTSERVER>, |
| threads=5, |
| qsize=500 |
| ) |
| |
| # Create a new user |
| |
| client.create_event( |
| event="$set", |
| entity_type="user", |
| entity_id=<USER_ID>, |
| # current time will be used if event_time is not specified |
| event_time=datetime( |
| 2014, 11, 02, 09, 39, 45, 618000, pytz.timezone('US/Pacific') |
| ) |
| ``` |
| </div> |
| <div data-tab="PHP SDK" data-lang="php"> |
| ```php |
| <?php |
| require_once("vendor/autoload.php"); |
| use predictionio\EventClient; |
| |
| $client = new EventClient(<ACCESS KEY>, <URL OF EVENTSERVER>); |
| |
| // Create a new user |
| $client->createEvent(array( |
| 'event' => '$set', |
| 'entityType' => 'user', |
| 'entityId' => <USER ID> |
| )); |
| ?> |
| ``` |
| </div> |
| <div data-tab="Ruby SDK" data-lang="ruby"> |
| ```ruby |
| # Create a client object. |
| client = PredictionIO::EventClient.new(<ACCESS KEY>, <URL OF EVENTSERVER>) |
| |
| # Create a new user |
| client.create_event( |
| '$set', |
| 'user', |
| <USER ID> |
| ) |
| ``` |
| </div> |
| <div data-tab="Java SDK" data-lang="java"> |
| ```java |
| import org.apache.predictionio.Event; |
| import org.apache.predictionio.EventClient; |
| |
| import com.google.common.collect.ImmutableList; |
| |
| EventClient client = new EventClient(<ACCESS KEY>, <URL OF EVENTSERVER>); |
| |
| // Create a new user |
| Event userEvent = new Event() |
| .event("$set") |
| .entityType("user") |
| .entityId(<USER_ID>); |
| client.createEvent(userEvent); |
| ``` |
| </div> |
| </div> |
| |
| When a new item "i0" is created in your app on time `2014-11-02T09:39:45.618-08:00` (current time will be used if eventTime is not specified), you can send a `$set` event for the item. Note that the item is set with categories properties: `"c1"` and `"c2"`. Run the following `curl` command: |
| |
| <div class="tabs"> |
| <div data-tab="REST API" data-lang="json"> |
| ``` |
| $ curl -i -X POST http://localhost:7070/events.json?accessKey=$ACCESS_KEY \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "event" : "$set", |
| "entityType" : "item", |
| "entityId" : "i0", |
| "properties" : { |
| "categories" : ["c1", "c2"] |
| } |
| "eventTime" : "2014-11-02T09:39:45.618-08:00" |
| }' |
| ``` |
| </div> |
| <div data-tab="Python SDK" data-lang="python"> |
| ```python |
| # Create a new item or set existing item's categories |
| |
| client.create_event( |
| event="$set", |
| entity_type="item", |
| entity_id=item_id, |
| properties={ |
| "categories" : ["<CATEGORY_1>", "<CATEGORY_2>"] |
| } |
| ) |
| ``` |
| </div> |
| |
| <div data-tab="PHP SDK" data-lang="php"> |
| |
| ```php |
| <?php |
| |
| // Create a new item or set existing item's categories |
| $client->createEvent(array( |
| 'event' => '$set', |
| 'entityType' => 'item', |
| 'entityId' => <ITEM ID> |
| 'properties' => array('categories' => array('<CATEGORY_1>', '<CATEGORY_2>')) |
| )); |
| |
| ?> |
| ``` |
| </div> |
| |
| <div data-tab="Ruby SDK" data-lang="ruby"> |
| ```ruby |
| # Create a new item or set existing item's categories |
| client.create_event( |
| '$set', |
| 'item', |
| <ITEM ID>, { |
| 'properties' => { 'categories' => ['<CATEGORY_1>', '<CATEGORY_2>'] } |
| } |
| ) |
| |
| ``` |
| </div> |
| <div data-tab="Java SDK" data-lang="java"> |
| ```java |
| // Create a new item or set existing item's categories |
| Event itemEvent = new Event() |
| .event("$set") |
| .entityType("item") |
| .entityId(<ITEM_ID>) |
| .property("categories", ImmutableList.of("<CATEGORY_1>", "<CATEGORY_2>")); |
| client.createEvent(itemEvent) |
| ``` |
| </div> |
| |
| </div> |
| |
| When the user "u0" view item "i0" on time `2014-11-10T12:34:56.123-08:00` (current time will be used if eventTime is not specified), you can send a view event. Run the following `curl` command: |
| |
| <div class="tabs"> |
| <div data-tab="REST API" data-lang="json"> |
| |
| ``` |
| $ curl -i -X POST http://localhost:7070/events.json?accessKey=$ACCESS_KEY \ |
| -H "Content-Type: application/json" \ |
| -d '{ |
| "event" : "view", |
| "entityType" : "user", |
| "entityId" : "u0", |
| "targetEntityType" : "item", |
| "targetEntityId" : "i0", |
| "eventTime" : "2014-11-10T12:34:56.123-08:00" |
| }' |
| ``` |
| </div> |
| <div data-tab="Python SDK" data-lang="python"> |
| ```python |
| # A user views an item |
| |
| client.create_event( |
| event="view", |
| entity_type="user", |
| entity_id=<USER ID>, |
| target_entity_type="item", |
| target_entity_id=<ITEM ID> |
| ) |
| ``` |
| </div> |
| <div data-tab="PHP SDK" data-lang="php"> |
| |
| ```php |
| <?php |
| // A user views an item |
| $client->createEvent(array( |
| 'event' => 'view', |
| 'entityType' => 'user', |
| 'entityId' => <USER ID>, |
| 'targetEntityType' => 'item', |
| 'targetEntityId' => <ITEM ID> |
| )); |
| ?> |
| ``` |
| </div> |
| <div data-tab="Ruby SDK" data-lang="ruby"> |
| ```ruby |
| # A user views an item. |
| client.create_event( |
| 'view', |
| 'user', |
| <USER ID>, { |
| 'targetEntityType' => 'item', |
| 'targetEntityId' => <ITEM ID> |
| } |
| ) |
| ``` |
| </div> |
| <div data-tab="Java SDK" data-lang="java"> |
| |
| ```java |
| // A user views an item |
| Event viewEvent = new Event() |
| .event("view") |
| .entityType("user") |
| .entityId(<USER_ID>) |
| .targetEntityType("item") |
| .targetEntityId(<ITEM_ID>); |
| client.createEvent(viewEvent); |
| |
| ``` |
| </div> |
| </div> |
| |
| <%= partial 'shared/quickstart/query_eventserver' %> |
| |
| ### Import More Sample Data |
| |
| <%= partial 'shared/quickstart/import_sample_data' %> |
| |
| A Python import script `import_eventserver.py` is provided to import sample data. It imports 10 users (with user ID "u1" to "u10") and 50 items (with item ID "i1" to "i50") with some random assigned categories ( with categories "c1" to "c6"). Each user then randomly view 10 items. |
| |
| <%= partial 'shared/quickstart/install_python_sdk' %> |
| |
| Make sure you are under the `MySimilarProduct` directory. Execute the following to import the data: |
| |
| ``` |
| $ cd MySimilarProduct |
| $ python data/import_eventserver.py --access_key $ACCESS_KEY |
| ``` |
| |
| You should see the following output: |
| |
| ``` |
| ... |
| User u10 views item i20 |
| User u10 views item i17 |
| User u10 views item i22 |
| User u10 views item i31 |
| User u10 views item i18 |
| User u10 views item i29 |
| 160 events are imported. |
| ``` |
| |
| <%= partial 'shared/quickstart/query_eventserver_short' %> |
| |
| ## 5. Deploy the Engine as a Service |
| |
| <%= partial 'shared/quickstart/deploy_enginejson', locals: { engine_name: 'MySimilarProduct' } %> |
| |
| <%= partial 'shared/quickstart/deploy', locals: { engine_name: 'MySimilarProduct' } %> |
| |
| ## 6. Use the Engine |
| |
| Now, You can retrieve predicted results. To retrieve 4 items which are similar to item ID "i1". You send this JSON `{ "items": ["i1"], "num": 4 }` to the deployed engine and it will return a JSON of the recommended items. Simply send a query by making a HTTP request or through the `EngineClient` of an SDK. |
| |
| With the deployed engine running, open another terminal and run the following `curl` command or use SDK to send the query: |
| |
| <div class="tabs"> |
| <div data-tab="REST API" data-lang="json"> |
| ``` |
| $ curl -H "Content-Type: application/json" \ |
| -d '{ "items": ["i1"], "num": 4 }' \ |
| http://localhost:8000/queries.json |
| |
| ``` |
| </div> |
| <div data-tab="Python SDK" data-lang="python"> |
| ```python |
| import predictionio |
| engine_client = predictionio.EngineClient(url="http://localhost:8000") |
| print engine_client.send_query({"items": ["i1"], "num": 4}) |
| ``` |
| </div> |
| <div data-tab="PHP SDK" data-lang="php"> |
| ```php |
| <?php |
| require_once("vendor/autoload.php"); |
| use predictionio\EngineClient; |
| |
| $client = new EngineClient('http://localhost:8000'); |
| |
| $response = $client->sendQuery(array('items'=> array('i1'), 'num'=> 4)); |
| print_r($response); |
| |
| ?> |
| ``` |
| </div> |
| <div data-tab="Ruby SDK" data-lang="ruby"> |
| |
| ```ruby |
| # Create client object. |
| client = PredictionIO::EngineClient.new('http://localhost:8000') |
| |
| # Query PredictionIO. |
| response = client.send_query('items' => ['i1'], 'num' => 4) |
| |
| puts response |
| ``` |
| </div> |
| <div data-tab="Java SDK" data-lang="java"> |
| |
| ```java |
| import com.google.common.collect.ImmutableMap; |
| import com.google.common.collect.ImmutableList; |
| import com.google.gson.JsonObject; |
| |
| import org.apache.predictionio.EngineClient; |
| |
| // create client object |
| EngineClient engineClient = new EngineClient("http://localhost:8000"); |
| |
| // query |
| |
| JsonObject response = engineClient.sendQuery(ImmutableMap.<String, Object>of( |
| "items", ImmutableList.of("i1"), |
| "num", 4 |
| )); |
| ``` |
| </div> |
| </div> |
| |
| The following is sample JSON response: |
| |
| ``` |
| { |
| "itemScores":[ |
| {"item":"i43","score":0.7071067811865475}, |
| {"item":"i21","score":0.7071067811865475}, |
| {"item":"i46","score":0.5773502691896258}, |
| {"item":"i8","score":0.5773502691896258} |
| ] |
| } |
| ``` |
| |
| *MySimilarProduct* is now running. |
| |
| <%= partial 'shared/quickstart/production' %> |
| |
| |
| ## Advanced Query |
| |
| ### Recommend items which are similar to multiple items: |
| |
| ``` |
| curl -H "Content-Type: application/json" \ |
| -d '{ "items": ["i1", "i3"], "num": 10}' \ |
| http://localhost:8000/queries.json |
| |
| {"itemScores":[{"item":"i12","score":1.1700499715209998},{"item":"i21","score":1.1153550716504106},{"item":"i43","score":1.1153550716504106},{"item":"i14","score":1.0773502691896257},{"item":"i39","score":1.0773502691896257},{"item":"i26","score":1.0773502691896257},{"item":"i44","score":1.0773502691896257},{"item":"i38","score":0.9553418012614798},{"item":"i36","score":0.9106836025229592},{"item":"i46","score":0.9106836025229592}]} |
| ``` |
| |
| In addition, the Query support the following optional parameters `categories`, `whiteList` and `blackList`. |
| |
| ### Recommend items in selected categories: |
| |
| ``` |
| curl -H "Content-Type: application/json" \ |
| -d '{ |
| "items": ["i1", "i3"], |
| "num": 10, |
| "categories" : ["c4", "c3"] |
| }' \ |
| http://localhost:8000/queries.json |
| |
| {"itemScores":[{"item":"i21","score":1.1153550716504106},{"item":"i14","score":1.0773502691896257},{"item":"i26","score":1.0773502691896257},{"item":"i39","score":1.0773502691896257},{"item":"i44","score":1.0773502691896257},{"item":"i45","score":0.7886751345948129},{"item":"i47","score":0.7618016810571367},{"item":"i9","score":0.7618016810571367},{"item":"i28","score":0.7618016810571367},{"item":"i6","score":0.7618016810571367}]} |
| ``` |
| |
| ### Recommend items in the whiteList: |
| |
| ``` |
| curl -H "Content-Type: application/json" \ |
| -d '{ |
| "items": ["i1", "i3"], |
| "num": 10, |
| "categories" : ["c4", "c3"], |
| "whiteList": ["i21", "i26", "i40"] |
| }' \ |
| http://localhost:8000/queries.json |
| |
| {"itemScores":[{"item":"i21","score":1.1153550716504106},{"item":"i26","score":1.0773502691896257}]} |
| ``` |
| |
| ### Recommend items not in the blackList: |
| |
| ``` |
| curl -H "Content-Type: application/json" \ |
| -d '{ |
| "items": ["i1", "i3"], |
| "num": 10, |
| "categories" : ["c4", "c3"], |
| "blackList": ["i21", "i26", "i40"] |
| }' \ |
| http://localhost:8000/queries.json |
| |
| {"itemScores":[{"item":"i39","score":1.0773502691896257},{"item":"i44","score":1.0773502691896257},{"item":"i14","score":1.0773502691896257},{"item":"i45","score":0.7886751345948129},{"item":"i47","score":0.7618016810571367},{"item":"i6","score":0.7618016810571367},{"item":"i28","score":0.7618016810571367},{"item":"i9","score":0.7618016810571367},{"item":"i29","score":0.6220084679281463},{"item":"i30","score":0.5386751345948129}]} |
| ``` |
| |
| #### [Next: DASE Components Explained](/templates/similarproduct/dase/) |