{{title}}
3 |{{summary}}
4 |{{summary}}
4 |No results found
"); 64 | } 65 | 66 | if(jQuery){ 67 | /* 68 | * We currently only automatically hide bootstrap models. This 69 | * requires jQuery to work. 70 | */ 71 | jQuery('#mkdocs_search_modal a').click(function(){ 72 | jQuery('#mkdocs_search_modal').modal('hide'); 73 | }); 74 | } 75 | 76 | }; 77 | 78 | var search_input = document.getElementById('mkdocs-search-query'); 79 | 80 | var term = getSearchTerm(); 81 | if (term){ 82 | search_input.value = term; 83 | search(); 84 | } 85 | 86 | search_input.addEventListener("keyup", search); 87 | 88 | }); 89 | -------------------------------------------------------------------------------- /docs/js/theme.js: -------------------------------------------------------------------------------- 1 | $( document ).ready(function() { 2 | // Shift nav in mobile when clicking the menu. 3 | $(document).on('click', "[data-toggle='wy-nav-top']", function() { 4 | $("[data-toggle='wy-nav-shift']").toggleClass("shift"); 5 | $("[data-toggle='rst-versions']").toggleClass("shift"); 6 | }); 7 | 8 | // Close menu when you click a link. 9 | $(document).on('click', ".wy-menu-vertical .current ul li a", function() { 10 | $("[data-toggle='wy-nav-shift']").removeClass("shift"); 11 | $("[data-toggle='rst-versions']").toggleClass("shift"); 12 | }); 13 | 14 | $(document).on('click', "[data-toggle='rst-current-version']", function() { 15 | $("[data-toggle='rst-versions']").toggleClass("shift-up"); 16 | }); 17 | 18 | // Make tables responsive 19 | $("table.docutils:not(.field-list)").wrap(""); 20 | 21 | hljs.initHighlightingOnLoad(); 22 | 23 | $('table').addClass('docutils'); 24 | }); 25 | 26 | window.SphinxRtdTheme = (function (jquery) { 27 | var stickyNav = (function () { 28 | var navBar, 29 | win, 30 | stickyNavCssClass = 'stickynav', 31 | applyStickNav = function () { 32 | if (navBar.height() <= win.height()) { 33 | navBar.addClass(stickyNavCssClass); 34 | } else { 35 | navBar.removeClass(stickyNavCssClass); 36 | } 37 | }, 38 | enable = function () { 39 | applyStickNav(); 40 | win.on('resize', applyStickNav); 41 | }, 42 | init = function () { 43 | navBar = jquery('nav.wy-nav-side:first'); 44 | win = jquery(window); 45 | }; 46 | jquery(init); 47 | return { 48 | enable : enable 49 | }; 50 | }()); 51 | return { 52 | StickyNav : stickyNav 53 | }; 54 | }($)); 55 | 56 | // The code below is a copy of @seanmadsen code posted Jan 10, 2017 on issue 803. 57 | // https://github.com/mkdocs/mkdocs/issues/803 58 | // This just incorporates the auto scroll into the theme itself without 59 | // the need for additional custom.js file. 60 | // 61 | $(function() { 62 | $.fn.isFullyWithinViewport = function(){ 63 | var viewport = {}; 64 | viewport.top = $(window).scrollTop(); 65 | viewport.bottom = viewport.top + $(window).height(); 66 | var bounds = {}; 67 | bounds.top = this.offset().top; 68 | bounds.bottom = bounds.top + this.outerHeight(); 69 | return ( ! ( 70 | (bounds.top <= viewport.top) || 71 | (bounds.bottom >= viewport.bottom) 72 | ) ); 73 | }; 74 | if( $('li.toctree-l1.current').length && !$('li.toctree-l1.current').isFullyWithinViewport() ) { 75 | $('.wy-nav-side') 76 | .scrollTop( 77 | $('li.toctree-l1.current').offset().top - 78 | $('.wy-nav-side').offset().top - 79 | 60 80 | ); 81 | } 82 | }); 83 | -------------------------------------------------------------------------------- /examples/2_save.php: -------------------------------------------------------------------------------- 1 | addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight')); 14 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Guardians of the Galaxy')); 15 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Logan')); 16 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Forrest Gump')); 17 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Kid')); 18 | 19 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna')); 20 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Barbara')); 21 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Charlie')); 22 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Dave')); 23 | 24 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0)); 25 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Barbara', 0)); 26 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Charlie', 5)); 27 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Dave', 5)); 28 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Anna', 0)); 29 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Barbara', null)); 30 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Charlie', null)); 31 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Dave', 5)); 32 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Anna', null)); 33 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Barbara', 0)); 34 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Charlie', 4)); 35 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Dave', null)); 36 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Anna', 4)); 37 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Barbara', 5)); 38 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Charlie', 0)); 39 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Dave', 0)); 40 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Anna', 5)); 41 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Barbara', 5)); 42 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Charlie', 0)); 43 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Dave', 0)); 44 | 45 | $model = new Impulse\Recommender\LearningModel($dataset, [ 46 | 'numFeatures' => 2 47 | ]); 48 | 49 | $trainer = new Impulse\Recommender\Trainer($model, [ 50 | 'learningRate' => 0.01, 51 | 'iterations' => 30000, 52 | 'verbose' => true, 53 | 'verboseStep' => 1000 54 | ]); 55 | 56 | $trainer->train(); 57 | 58 | $builder = new Impulse\Recommender\Builder($model); 59 | $builder->save(__DIR__, 'save1'); -------------------------------------------------------------------------------- /docs/examples/2_save.php: -------------------------------------------------------------------------------- 1 | addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight')); 14 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Guardians of the Galaxy')); 15 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Logan')); 16 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Forrest Gump')); 17 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Kid')); 18 | 19 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna')); 20 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Barbara')); 21 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Charlie')); 22 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Dave')); 23 | 24 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0)); 25 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Barbara', 0)); 26 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Charlie', 5)); 27 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Dave', 5)); 28 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Anna', 0)); 29 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Barbara', null)); 30 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Charlie', null)); 31 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Dave', 5)); 32 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Anna', null)); 33 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Barbara', 0)); 34 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Charlie', 4)); 35 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Dave', null)); 36 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Anna', 4)); 37 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Barbara', 5)); 38 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Charlie', 0)); 39 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Dave', 0)); 40 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Anna', 5)); 41 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Barbara', 5)); 42 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Charlie', 0)); 43 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Dave', 0)); 44 | 45 | $model = new Impulse\Recommender\LearningModel($dataset, [ 46 | 'numFeatures' => 2 47 | ]); 48 | 49 | $trainer = new Impulse\Recommender\Trainer($model, [ 50 | 'learningRate' => 0.01, 51 | 'iterations' => 30000, 52 | 'verbose' => true, 53 | 'verboseStep' => 1000 54 | ]); 55 | 56 | $trainer->train(); 57 | 58 | $builder = new Impulse\Recommender\Builder($model); 59 | $builder->save(__DIR__, 'save1'); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Impulse-ML: Recommender, the Recommender Engine 2 | 3 | ### About 4 | Impulse-ML: Recommender is PHP library which can be used to share 5 | personalized content for users on your website. It is written in PHP 6 | and requires no additional dependencies. With OOP API you can achieve good 7 | prediction results and you can quickly apply recommender system in any PHP 8 | application, e.g. in Wordpress, Drupal or any other PHP framework based application. 9 | 10 | ### Machine Learning 11 | Recommender system solves a machine learning problem. Given items (i.e. movies rated by user) 12 | are possible to rate by users (i.e. 0 - 5 star rating). With given 13 | rating data Recommender System can predict: 14 | 15 | - movie ratings, of those movies which are unrated by the user 16 | - find similar movies 17 | - get the prediction for user who don't rate any movie. 18 | 19 | Impulse-ML: Recommender uses **Collaborative** **Filtering** algorithm 20 | so it is not required to provide item features, which can be 21 | understand as real item categories (i.e. comedy or action movie and their values) and it is 22 | not required to provide category features which can be understand as user preferences. 23 | The system learns 24 | by itself with only given items, categories and defined ratings. 25 | 26 | As long as you set Learning Model parameters and Training parameters 27 | more accurate you might end up with pretty good prediction of rating the movie 28 | which is not rated by user yet - assuming that the more ratings you give the more accurate 29 | predictions you will get. 30 | 31 | Impulse-ML: Recommender uses the gradient descent learning algorithm. 32 | 33 | For general details about Recommender Systems you might consider visit 34 | [Wikipedia - Recommender System](https://en.wikipedia.org/wiki/Recommender_system) to get 35 | intuition what is going on under the hood. 36 | 37 | ### Requirements 38 | 39 | - PHP >= 5.4 40 | 41 | ### Table of contents 42 | 43 | - [1. Problem motivation](1_problem_motivation.md) 44 | - [Do I need Impulse-ML: Recommender, the Recommender System?](1_problem_motivation.md#do-i-need-impulse-ml-recommender-the-recommender-system) 45 | - [Problem definition](1_problem_motivation.md#problem-definition) 46 | - [Training and training parameters](1_problem_motivation.md#training-and-training-parameters) 47 | - [2. API - Dataset](2_api_dataset.md) 48 | - [Passing data to Impulse-ML: Recommender Dataset](2_api_dataset.md#passing-data-to-impulse-ml-recommender-dataset) 49 | - [3. API - Learning Model](3_api_learning_model.md) 50 | - [Learning from dataset](3_api_learning_model.md#learning-from-dataset) 51 | - [4. API - Training the Learning Model](4_api_training_the_model.md) 52 | - [Training the model](4_api_training_the_model.md#training-the-model) 53 | - [5. API - Predicting the results](5_api_predicting_the_results.md) 54 | - [Predict rating for user](5_api_predicting_the_results.md#predict-rating-for-user) 55 | - [Find similar items](5_api_predicting_the_results.md#find-similar-items) 56 | - [Predict rate for user which has not rated any movie](5_api_predicting_the_results.md#predict-rate-for-user-which-has-not-rated-any-movie) 57 | - [6. API - Full Example](6_api_full_example.md) 58 | - [7. Examples of training](7_examples_of_training.md) 59 | - [8. API - Saving and restoring trained model](8_api_saving_and_restoring_trained_model.md) 60 | - [Save](8_api_saving_and_restoring_trained_model.md#save) 61 | - [Restore](8_api_saving_and_restoring_trained_model.md#restore) -------------------------------------------------------------------------------- /examples/1_train_and_predict.php: -------------------------------------------------------------------------------- 1 | addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight')); 13 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Guardians of the Galaxy')); 14 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Logan')); 15 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Forrest Gump')); 16 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Kid')); 17 | 18 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna')); 19 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Barbara')); 20 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Charlie')); 21 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Dave')); 22 | 23 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0)); 24 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Barbara', 0)); 25 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Charlie', 5)); 26 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Dave', 5)); 27 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Anna', 0)); 28 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Barbara', null)); 29 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Charlie', null)); 30 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Dave', 5)); 31 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Anna', null)); 32 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Barbara', 0)); 33 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Charlie', 4)); 34 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Dave', null)); 35 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Anna', 4)); 36 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Barbara', 5)); 37 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Charlie', 0)); 38 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Dave', 0)); 39 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Anna', 5)); 40 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Barbara', 5)); 41 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Charlie', 0)); 42 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Dave', 0)); 43 | 44 | $model = new Impulse\Recommender\LearningModel($dataset, [ 45 | 'numFeatures' => 2 46 | ]); 47 | 48 | $trainer = new Impulse\Recommender\Trainer($model, [ 49 | 'learningRate' => 0.01, 50 | 'iterations' => 20000, 51 | 'verbose' => true, 52 | 'verboseStep' => 1000 53 | ]); 54 | 55 | $trainer->train(); 56 | 57 | echo "Prediction for 'Guardians of the Galaxy' for user 'Barbara': {$model->predict('Guardians of the Galaxy', 'Barbara')}\n"; 58 | echo "Prediction for 'Guardians of the Galaxy' for user 'Charlie': {$model->predict('Guardians of the Galaxy', 'Charlie')}\n"; 59 | echo "Prediction for 'Logan' for user 'Anna': {$model->predict('Logan', 'Anna')}\n"; 60 | echo "Prediction for 'Logan' for user 'Dave': {$model->predict('Logan', 'Dave')}\n"; 61 | 62 | echo "Prediction for 'Logan' for user with has not rated any movie: {$model->predict('Logan')}\n"; 63 | 64 | echo "Related movies dump:\n"; 65 | 66 | var_dump($model->findRelated('The Dark Knight', [ 67 | 'limit' => 5 68 | ])); -------------------------------------------------------------------------------- /docs/examples/1_train_and_predict.php: -------------------------------------------------------------------------------- 1 | addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight')); 13 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Guardians of the Galaxy')); 14 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Logan')); 15 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Forrest Gump')); 16 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Kid')); 17 | 18 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna')); 19 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Barbara')); 20 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Charlie')); 21 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Dave')); 22 | 23 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0)); 24 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Barbara', 0)); 25 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Charlie', 5)); 26 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Dave', 5)); 27 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Anna', 0)); 28 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Barbara', null)); 29 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Charlie', null)); 30 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Dave', 5)); 31 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Anna', null)); 32 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Barbara', 0)); 33 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Charlie', 4)); 34 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Dave', null)); 35 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Anna', 4)); 36 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Barbara', 5)); 37 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Charlie', 0)); 38 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Dave', 0)); 39 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Anna', 5)); 40 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Barbara', 5)); 41 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Charlie', 0)); 42 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Dave', 0)); 43 | 44 | $model = new Impulse\Recommender\LearningModel($dataset, [ 45 | 'numFeatures' => 2 46 | ]); 47 | 48 | $trainer = new Impulse\Recommender\Trainer($model, [ 49 | 'learningRate' => 0.01, 50 | 'iterations' => 20000, 51 | 'verbose' => true, 52 | 'verboseStep' => 1000 53 | ]); 54 | 55 | $trainer->train(); 56 | 57 | echo "Prediction for 'Guardians of the Galaxy' for user 'Barbara': {$model->predict('Guardians of the Galaxy', 'Barbara')}\n"; 58 | echo "Prediction for 'Guardians of the Galaxy' for user 'Charlie': {$model->predict('Guardians of the Galaxy', 'Charlie')}\n"; 59 | echo "Prediction for 'Logan' for user 'Anna': {$model->predict('Logan', 'Anna')}\n"; 60 | echo "Prediction for 'Logan' for user 'Dave': {$model->predict('Logan', 'Dave')}\n"; 61 | 62 | echo "Prediction for 'Logan' for user with has not rated any movie: {$model->predict('Logan')}\n"; 63 | 64 | echo "Related movies dump:\n"; 65 | 66 | var_dump($model->findRelated('The Dark Knight', [ 67 | 'limit' => 5 68 | ])); -------------------------------------------------------------------------------- /docs/css/theme_extra.css: -------------------------------------------------------------------------------- 1 | /* 2 | * Sphinx doesn't have support for section dividers like we do in 3 | * MkDocs, this styles the section titles in the nav 4 | * 5 | * https://github.com/mkdocs/mkdocs/issues/175 6 | */ 7 | .wy-menu-vertical span { 8 | line-height: 18px; 9 | padding: 0.4045em 1.618em; 10 | display: block; 11 | position: relative; 12 | font-size: 90%; 13 | color: #838383; 14 | } 15 | 16 | .wy-menu-vertical .subnav a { 17 | padding: 0.4045em 2.427em; 18 | } 19 | 20 | /* 21 | * Long navigations run off the bottom of the screen as the nav 22 | * area doesn't scroll. 23 | * 24 | * https://github.com/mkdocs/mkdocs/pull/202 25 | * 26 | * Builds upon pull 202 https://github.com/mkdocs/mkdocs/pull/202 27 | * to make toc scrollbar end before navigations buttons to not be overlapping. 28 | */ 29 | .wy-nav-side { 30 | height: calc(100% - 45px); 31 | overflow-y: auto; 32 | min-height: 0; 33 | } 34 | 35 | .rst-versions{ 36 | border-top: 0; 37 | height: 45px; 38 | } 39 | 40 | @media screen and (max-width: 768px) { 41 | .wy-nav-side { 42 | height: 100%; 43 | } 44 | } 45 | 46 | /* 47 | * readthedocs theme hides nav items when the window height is 48 | * too small to contain them. 49 | * 50 | * https://github.com/mkdocs/mkdocs/issues/#348 51 | */ 52 | .wy-menu-vertical ul { 53 | margin-bottom: 2em; 54 | } 55 | 56 | /* 57 | * Wrap inline code samples otherwise they shoot of the side and 58 | * can't be read at all. 59 | * 60 | * https://github.com/mkdocs/mkdocs/issues/313 61 | * https://github.com/mkdocs/mkdocs/issues/233 62 | * https://github.com/mkdocs/mkdocs/issues/834 63 | */ 64 | code { 65 | white-space: pre-wrap; 66 | word-wrap: break-word; 67 | padding: 2px 5px; 68 | } 69 | 70 | /** 71 | * Make code blocks display as blocks and give them the appropriate 72 | * font size and padding. 73 | * 74 | * https://github.com/mkdocs/mkdocs/issues/855 75 | * https://github.com/mkdocs/mkdocs/issues/834 76 | * https://github.com/mkdocs/mkdocs/issues/233 77 | */ 78 | pre code { 79 | white-space: pre; 80 | word-wrap: normal; 81 | display: block; 82 | padding: 12px; 83 | font-size: 12px; 84 | } 85 | 86 | /* 87 | * Fix link colors when the link text is inline code. 88 | * 89 | * https://github.com/mkdocs/mkdocs/issues/718 90 | */ 91 | a code { 92 | color: #2980B9; 93 | } 94 | a:hover code { 95 | color: #3091d1; 96 | } 97 | a:visited code { 98 | color: #9B59B6; 99 | } 100 | 101 | /* 102 | * The CSS classes from highlight.js seem to clash with the 103 | * ReadTheDocs theme causing some code to be incorrectly made 104 | * bold and italic. 105 | * 106 | * https://github.com/mkdocs/mkdocs/issues/411 107 | */ 108 | pre .cs, pre .c { 109 | font-weight: inherit; 110 | font-style: inherit; 111 | } 112 | 113 | /* 114 | * Fix some issues with the theme and non-highlighted code 115 | * samples. Without and highlighting styles attached the 116 | * formatting is broken. 117 | * 118 | * https://github.com/mkdocs/mkdocs/issues/319 119 | */ 120 | .no-highlight { 121 | display: block; 122 | padding: 0.5em; 123 | color: #333; 124 | } 125 | 126 | 127 | /* 128 | * Additions specific to the search functionality provided by MkDocs 129 | */ 130 | 131 | .search-results article { 132 | margin-top: 23px; 133 | border-top: 1px solid #E1E4E5; 134 | padding-top: 24px; 135 | } 136 | 137 | .search-results article:first-child { 138 | border-top: none; 139 | } 140 | 141 | form .search-query { 142 | width: 100%; 143 | border-radius: 50px; 144 | padding: 6px 12px; /* csslint allow: box-model */ 145 | border-color: #D1D4D5; 146 | } 147 | 148 | .wy-menu-vertical li ul { 149 | display: inherit; 150 | } 151 | 152 | .wy-menu-vertical li ul.subnav ul.subnav{ 153 | padding-left: 1em; 154 | } 155 | 156 | .wy-menu-vertical .subnav li.current > a { 157 | padding-left: 2.42em; 158 | } 159 | .wy-menu-vertical .subnav li.current > ul li a { 160 | padding-left: 3.23em; 161 | } 162 | 163 | /* 164 | * Improve inline code blocks within admonitions. 165 | * 166 | * https://github.com/mkdocs/mkdocs/issues/656 167 | */ 168 | .admonition code { 169 | color: #404040; 170 | border: 1px solid #c7c9cb; 171 | border: 1px solid rgba(0, 0, 0, 0.2); 172 | background: #f8fbfd; 173 | background: rgba(255, 255, 255, 0.7); 174 | } 175 | 176 | /* 177 | * Account for wide tables which go off the side. 178 | * Override borders to avoid wierdness on narrow tables. 179 | * 180 | * https://github.com/mkdocs/mkdocs/issues/834 181 | * https://github.com/mkdocs/mkdocs/pull/1034 182 | */ 183 | .rst-content .section .docutils { 184 | width: 100%; 185 | overflow: auto; 186 | display: block; 187 | border: none; 188 | } 189 | 190 | td, th { 191 | border: 1px solid #e1e4e5 !important; /* csslint allow: important */ 192 | border-collapse: collapse; 193 | } 194 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |Assuming that we have data stored in Impulse\Recommender\Dataset we are ready to create
152 | a Learning Model.
We can do this by using:
154 |$model = new Impulse\Recommender\LearningModel($dataset, [
155 | 'numFeatures' => 2
156 | ]);
157 |
158 |
159 | "numFeatures" is required parameter. It may strictly correspond to number of categories of database 160 | items or number of defined user preferences. 161 | Notice that you don't need to define how much every item belongs to 162 | given category or how much user belongs to given preference. You just need to know number of them.
163 | 164 |You can get this done by using:
152 |$trainer = new Impulse\Recommender\Trainer($model, [
153 | 'learningRate' => 0.01,
154 | 'iterations' => 20000,
155 | 'verbose' => TRUE, // print debug messages
156 | 'verboseStep' => 1000 // step interval from displaying debug messages
157 | ]);
158 |
159 | $trainer->train();
160 |
161 |
162 | Note that training time may take very long time when your dataset is really large. It can be optimized 163 | more or less by 164 | choosing more accurate "learningRate" and "iterations" parameters.
165 | 166 |You probably don't want to train your model after each one rate given by user, but for sure you might want 153 | to do that job outside your website because the training time could take very large amount of time.
154 |For do this we have implemented saving and restoring your trained Learning Model.
155 |include_once __DIR__ . '/../src/Impulse/Recommender/Builder.php';
157 |
158 | $builder = new Impulse\Recommender\Builder($model);
159 | $builder->save(__DIR__, 'save1');
160 |
161 |
162 | include_once __DIR__ . '/../src/Impulse/Recommender/Builder.php';
164 |
165 | $model = Impulse\Recommender\Builder::load(__DIR__, 'save1');
166 |
167 |
168 | Each of those methods takes 2 parameters which the first one is location of the directory to save the data, 169 | and the second one is name of created directory for the data files.
170 |Check examples/2_save.php and 171 | examples/3_load_and_predict.php for example of implementation.
172 | 173 |There are 3 prediction ways:
155 |$model->predict('Logan', 'Anna'); // float(9.9920072216264E-14)
157 |
158 |
159 | which predicts rate for unrated "Logan" for user "Anna" by returning a number.
160 |Results may vary from desired because of improperly trained or not trained Learning Model.
161 |$model->findRelated('The Dark Knight', [
163 | 'limit' => 1
164 | ])
165 |
166 |
167 | will finds all items in ordered by similarity array and it will returns:
168 |array(1) {
169 | [0]=>
170 | array(2) {
171 | ["similarity"]=>
172 | float(2.2657653531155E-11)
173 | ["model"]=>
174 | array(2) {
175 | ["_id"]=>
176 | string(23) "Guardians of the Galaxy"
177 | ["data"]=>
178 | NULL
179 | }
180 | }
181 | }
182 |
183 |
184 | $model->predict("Forrest Gump"); // int(2)
186 |
187 |
188 | which can be useful when the user has not rated any movie so user has no computed preferences.
189 | 190 |According to our data table from lecture 2_problem_motivation.md consider the following learning parameters 159 | for this dataset:
160 |You might end up with following debug messages:
167 |learningRate = 0.0001, iterations = 1000
169 |
The learningRate is too low and the iterations are too low - we have untrained model with high error.
171 |learningRate = 0.1, iterations = 10000
173 |
The learningRate is too high cause after some step we have got numerical computation error.
175 |learningRate = 0.0001, iterations = 100000
177 |
It is quite good error, but you might consider setting number of iterations to higher value or 179 | increasing learning rate.
180 |learningRate = 0.01, iterations = 100000
182 |
After some steps we are not minimizing the error which is very close to 0 184 | so you might consider decrease number of iterations.
185 |The following examples with too large number of iterations should not have big impact on the time of 187 | training the model according to our small dataset used in previous examples. 188 | You might consider adjust more accurate parameters in larger datasets.
189 | 190 |Each algorithm, not only machine learning algorithm Impulse-ML: Recommender, must have knowledge about 152 | your data. Since the PHP applications use different storage systems Impulse-ML: Recommender has no database 153 | data fetcher - you might consider pass data directly to Dataset class instance.
154 |Consider the following code:
155 |include_once __DIR__ . '/../src/Impulse/Recommender/Dataset.php';
156 |
157 | $dataset = new Impulse\Recommender\Dataset();
158 |
159 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight'));
160 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna'));
161 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0));
162 |
163 |
164 | It is minimum example to set 1 item (The Dark Knight), 1 category (Anna) and set it's rating to value of 0 165 | by user Anna.
166 |The example does not have any sense because we might want to have multiple items, categories as ratings - but 167 | you should have knowledge how to pass data to recommender system.
168 |In this example I used strings as my items, but Impulse\Recommender\Dataset\Item::create
169 | and Impulse\Recommender\Dataset\Category::create methods can also get an integer instead
170 | of string. You might consider pass integers to dataset as long as their values correspond to database
171 | primary keys and you will save a lot of memory than using a strings.
Also, each of those 2 create methods can get second parameter which has no defined and no
173 | required data type. You might
174 | consider pass to it an array with your database model data for future use if database primary keys is not
175 | so much useful.
The Impulse\Recommender\Dataset\Rating::create requires 3 parameters, which the first 2 - the item and
177 | the category should be already added to dataset and the third one should be numeric value.
178 | There is no minimum or
179 | maximum value, but different ranges of all ratings can require a different learning parameters. You might
180 | consider pass NULL if item is not rated but it is not required.
For real life example of creating dataset check examples/1_train_and_predict.php
182 | 183 |Impulse-ML: Recommender is PHP library which can be used to share 158 | personalized content for users on your website. It is written in PHP 159 | and requires no additional dependencies. With OOP API you can achieve good 160 | prediction results and you can quickly apply recommender system in any PHP 161 | application, e.g. in Wordpress, Drupal or any other PHP framework based application.
162 |Recommender system solves a machine learning problem. Given items (i.e. movies rated by user) 164 | are possible to rate by users (i.e. 0 - 5 star rating). With given 165 | rating data Recommender System can predict:
166 |Impulse-ML: Recommender uses Collaborative Filtering algorithm 172 | so it is not required to provide item features, which can be 173 | understand as real item categories (i.e. comedy or action movie and their values) and it is 174 | not required to provide category features which can be understand as user preferences. 175 | The system learns 176 | by itself with only given items, categories and defined ratings.
177 |As long as you set Learning Model parameters and Training parameters 178 | more accurate you might end up with pretty good prediction of rating the movie 179 | which is not rated by user yet - assuming that the more ratings you give the more accurate 180 | predictions you will get.
181 |Impulse-ML: Recommender uses the gradient descent learning algorithm.
182 |For general details about Recommender Systems you might consider visit 183 | Wikipedia - Recommender System to get 184 | intuition what is going on under the hood.
185 |include_once __DIR__ . '/../src/Impulse/Recommender/Dataset.php';
145 | include_once __DIR__ . '/../src/Impulse/Recommender/LearningModel.php';
146 | include_once __DIR__ . '/../src/Impulse/Recommender/Trainer.php';
147 |
148 | $dataset = new Impulse\Recommender\Dataset();
149 |
150 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight'));
151 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Guardians of the Galaxy'));
152 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Logan'));
153 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Forrest Gump'));
154 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Kid'));
155 |
156 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna'));
157 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Barbara'));
158 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Charlie'));
159 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Dave'));
160 |
161 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0));
162 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Barbara', 0));
163 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Charlie', 5));
164 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Dave', 5));
165 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Anna', 0));
166 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Barbara', NULL));
167 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Charlie', NULL));
168 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Dave', 5));
169 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Anna', NULL));
170 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Barbara', 0));
171 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Charlie', 4));
172 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Dave', NULL));
173 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Anna', 4));
174 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Barbara', 5));
175 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Charlie', 0));
176 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Dave', 0));
177 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Anna', 5));
178 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Barbara', 5));
179 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Charlie', 0));
180 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Dave', 0));
181 |
182 | $model = new Impulse\Recommender\LearningModel($dataset, [
183 | 'numFeatures' => 2
184 | ]);
185 |
186 | $trainer = new Impulse\Recommender\Trainer($model, [
187 | 'learningRate' => 0.01,
188 | 'iterations' => 20000,
189 | 'verbose' => TRUE,
190 | 'verboseStep' => 1000
191 | ]);
192 |
193 | $trainer->train();
194 |
195 | echo "Prediction for 'Guardians of the Galaxy' for user 'Barbara': {$model->predict('Guardians of the Galaxy', 'Barbara')}\n";
196 | echo "Prediction for 'Guardians of the Galaxy' for user 'Charlie': {$model->predict('Guardians of the Galaxy', 'Charlie')}\n";
197 | echo "Prediction for 'Logan' for user 'Anna': {$model->predict('Logan', 'Anna')}\n";
198 | echo "Prediction for 'Logan' for user 'Dave': {$model->predict('Logan', 'Dave')}\n";
199 |
200 | echo "Prediction for 'Logan' for user with has not rated any movie: {$model->predict('Logan')}\n";
201 |
202 | echo "Related movies dump:\n";
203 |
204 | var_dump($model->findRelated('The Dark Knight', [
205 | 'limit' => 5
206 | ]));
207 |
208 |
209 | Which may produce output:
210 |Starting train with 20000 steps.
211 | Step 0 with error 45.480538085596
212 | Step 1000 with error 0.14185749105855
213 | Step 2000 with error 0.00012367547481656
214 | Step 3000 with error 3.619659641736E-6
215 | Step 4000 with error 1.2231304078042E-7
216 | Step 5000 with error 5.7888427762046E-9
217 | Step 6000 with error 4.3159398580704E-10
218 | Step 7000 with error 4.2963851024733E-11
219 | Step 8000 with error 4.7458579962238E-12
220 | Step 9000 with error 5.3950928017295E-13
221 | Step 10000 with error 6.1779842297497E-14
222 | Step 11000 with error 7.0872758334659E-15
223 | Step 12000 with error 8.1340228680908E-16
224 | Step 13000 with error 9.3363924793255E-17
225 | Step 14000 with error 1.0716785358187E-17
226 | Step 15000 with error 1.2301363738974E-18
227 | Step 16000 with error 1.4120237226256E-19
228 | Step 17000 with error 1.6208264249521E-20
229 | Step 18000 with error 1.8605120483173E-21
230 | Step 19000 with error 2.1359434707995E-22
231 | Training ended with error 2.4586853842564E-23 after 20000 steps.
232 | Prediction for 'Guardians of the Galaxy' for user 'Barbara': 1.3472778448431E-11
233 | Prediction for 'Guardians of the Galaxy' for user 'Charlie': 4.9999999999974
234 | Prediction for 'Logan' for user 'Anna': 1.3994139180795E-11
235 | Prediction for 'Logan' for user 'Dave': 3.9999999999967
236 | Prediction for 'Logan' for user with has not rated any movie: 2
237 | Related movies dump:
238 | array(4) {
239 | [0]=>
240 | array(2) {
241 | ["similarity"]=>
242 | float(1.1086798146209E-11)
243 | ["model"]=>
244 | array(2) {
245 | ["_id"]=>
246 | string(23) "Guardians of the Galaxy"
247 | ["data"]=>
248 | NULL
249 | }
250 | }
251 | [1]=>
252 | array(2) {
253 | ["similarity"]=>
254 | float(0.17881301819823)
255 | ["model"]=>
256 | array(2) {
257 | ["_id"]=>
258 | string(5) "Logan"
259 | ["data"]=>
260 | NULL
261 | }
262 | }
263 | [2]=>
264 | array(2) {
265 | ["similarity"]=>
266 | float(0.92344428953759)
267 | ["model"]=>
268 | array(2) {
269 | ["_id"]=>
270 | string(12) "Forrest Gump"
271 | ["data"]=>
272 | NULL
273 | }
274 | }
275 | [3]=>
276 | array(2) {
277 | ["similarity"]=>
278 | float(1.7881301818354)
279 | ["model"]=>
280 | array(2) {
281 | ["_id"]=>
282 | string(7) "The Kid"
283 | ["data"]=>
284 | NULL
285 | }
286 | }
287 | }
288 |
289 |
290 | Check examples/1_train_and_predict.php for details.
291 | 292 |If you are a PHP developer who maintains any PHP social application and you 156 | want to predict the "rating" or "preference" that a user would give to an item 157 | the Impulse-ML: Recommender is library that you might consider to use! 158 | In further readings I will show you how to use Impulse-ML: Recommender 159 | and give you a hint on how to choose parameters which makes the predictions 160 | more accurate.
161 |Consider the following data:
163 |+---------------------------------+------+---------+---------+---------+
164 | | Movie \ User | Anna | Barbara | Charlie | Dave |
165 | +---------------------------------+------+---------+---------+---------+
166 | | The Dark Knight | 0 | 0 | 5 | 5 |
167 | +---------------------------------+------+---------+---------+---------+
168 | | Guardians of the Galaxy | 0 | ? | ? | 5 |
169 | +---------------------------------+------+---------+---------+---------+
170 | | Logan | ? | 0 | 4 | ? |
171 | +---------------------------------+------+---------+---------+---------+
172 | | Forrest Gump | 4 | 5 | 0 | 0 |
173 | +---------------------------------+------+---------+---------+---------+
174 | | The Kid | 5 | 5 | 0 | 0 |
175 | +---------------------------------+------+---------+---------+---------+
176 |
177 |
178 | In this particular example we can notice:
179 |Using this data you might want to:
188 |Using Impulse-ML: Recommender you might end up with such predictions:
194 |+---------------------------------+------+---------+---------+---------+
195 | | Movie \ User | Anna | Barbara | Charlie | Dave |
196 | +---------------------------------+------+---------+---------+---------+
197 | | The Dark Knight | - | - | - | - |
198 | +---------------------------------+------+---------+---------+---------+
199 | | Guardians of the Galaxy | - | 0 | 5 | - |
200 | +---------------------------------+------+---------+---------+---------+
201 | | Logan | 0 | - | - | 4 |
202 | +---------------------------------+------+---------+---------+---------+
203 | | Forrest Gump | - | - | - | - |
204 | +---------------------------------+------+---------+---------+---------+
205 | | The Kid | - | - | - | - |
206 | +---------------------------------+------+---------+---------+---------+
207 |
208 |
209 | We might notice:
210 |That's how Collaborative Filtering works.
217 |As each machine learning problem after filling with data in order to get correct prediction the training 219 | (based on the dataset) is required.
220 |There is only one parameter for a Learning Model created from a dataset:
221 |Understand 225 | it like 226 | type or real category of the item. It's value can be set equals number of item types in your 227 | application. You don't need to name them, you have to know number of them.
228 |There are two training parameters:
229 |The learning rate is parameter which describes how much gradient descent 234 | (which minimizes the error) will perform. You might to consider to increase or decrease 235 | this parameter and it has strong 236 | correlation with number of iterations.
237 |The number of iterations is parameter which describes how much steps gradient descent minimize function 238 | will be applied. It's highly correlated with learning rate.
239 |The results of prediction may vary from desired by setting this parameters less accurate.
240 |However, there are some rules of setting these parameters more accurate in order to get 241 | better prediction:
242 |For this particular example i have set:
250 |The key to get well trained model is to choose the right ratio of learning rate and number of iterations.
256 |You might consider try different number of features according to your Application so the dataset also.
257 |Above example was fully implemented in examples/1_train_and_predict.php.
258 | 259 |