├── .gitignore ├── 1_problem_motivation.md ├── 2_api_dataset.md ├── 3_api_learning_model.md ├── 4_api_training_the_model.md ├── 5_api_predicting_the_results.md ├── 6_api_full_example.md ├── 7_examples_of_training.md ├── 8_api_saving_and_restoring_trained_model.md ├── README.md ├── bb499e6458958e2f0e204d5a7ac6488450ea1e3365254c6bb10f02b14e5a25f4 ├── docs ├── 1_problem_motivation │ └── index.html ├── 2_api_dataset │ └── index.html ├── 3_api_learning_model │ └── index.html ├── 4_api_training_the_model │ └── index.html ├── 5_api_predicting_the_results │ └── index.html ├── 6_api_full_example │ └── index.html ├── 7_examples_of_training │ └── index.html ├── 8_api_saving_and_restoring_trained_model │ └── index.html ├── css │ ├── highlight.css │ ├── theme.css │ └── theme_extra.css ├── examples │ ├── 1_sample_output.txt │ ├── 1_train_and_predict.php │ ├── 2_save.php │ └── 3_load_and_predict.php ├── fonts │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ └── fontawesome-webfont.woff ├── img │ ├── favicon.ico │ ├── training_example_1.png │ ├── training_example_2.png │ ├── training_example_3.png │ └── training_example_4.png ├── index.html ├── js │ ├── highlight.pack.js │ ├── jquery-2.1.1.min.js │ ├── modernizr-2.8.3.min.js │ └── theme.js ├── mkdocs │ ├── js │ │ ├── lunr.min.js │ │ ├── mustache.min.js │ │ ├── require.js │ │ ├── search-results-template.mustache │ │ ├── search.js │ │ └── text.js │ └── search_index.json ├── search.html └── sitemap.xml ├── examples ├── 1_sample_output.txt ├── 1_train_and_predict.php ├── 2_save.php └── 3_load_and_predict.php └── img ├── training_example_1.png ├── training_example_2.png ├── training_example_3.png └── training_example_4.png /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | 3 | -------------------------------------------------------------------------------- /1_problem_motivation.md: -------------------------------------------------------------------------------- 1 | # Problem motivation 2 | 3 | ### Do I need Impulse-ML: Recommender, the Recommender System? 4 | If you are a PHP developer who maintains any PHP social application and you 5 | want to predict the "rating" or "preference" that a user would give to an item 6 | the Impulse-ML: Recommender is library that you might consider to use! 7 | In further readings I will show you how to use Impulse-ML: Recommender 8 | and give you a hint on how to choose parameters which makes the predictions 9 | more accurate. 10 | 11 | ### Problem definition 12 | 13 | Consider the following data: 14 | 15 | ``` 16 | +---------------------------------+------+---------+---------+---------+ 17 | | Movie \ User | Anna | Barbara | Charlie | Dave | 18 | +---------------------------------+------+---------+---------+---------+ 19 | | The Dark Knight | 0 | 0 | 5 | 5 | 20 | +---------------------------------+------+---------+---------+---------+ 21 | | Guardians of the Galaxy | 0 | ? | ? | 5 | 22 | +---------------------------------+------+---------+---------+---------+ 23 | | Logan | ? | 0 | 4 | ? | 24 | +---------------------------------+------+---------+---------+---------+ 25 | | Forrest Gump | 4 | 5 | 0 | 0 | 26 | +---------------------------------+------+---------+---------+---------+ 27 | | The Kid | 5 | 5 | 0 | 0 | 28 | +---------------------------------+------+---------+---------+---------+ 29 | ``` 30 | 31 | In this particular example we can notice: 32 | 33 | - we have 5 items - 5 movies 34 | - we have 4 categories - 4 users 35 | - we can notice 2 types of items: action movie and comedy movie 36 | - it seems that Anna and Barbara hate the action movies but love the comedy movies 37 | - it seems that Charlie and Dave love the action movies but hate the comedy movies 38 | - the table is incomplete because every user has not rated at least one movie 39 | 40 | Using this data you might want to: 41 | 42 | - predict user rating of movie that is unrated by user i.e. to send user the movie which he would like but he does not rated that movie yet 43 | - get movies similar to given movie 44 | - get the prediction of the movie for user that has no rated any movie and use this data 45 | 46 | Using Impulse-ML: Recommender you might end up with such predictions: 47 | 48 | ``` 49 | +---------------------------------+------+---------+---------+---------+ 50 | | Movie \ User | Anna | Barbara | Charlie | Dave | 51 | +---------------------------------+------+---------+---------+---------+ 52 | | The Dark Knight | - | - | - | - | 53 | +---------------------------------+------+---------+---------+---------+ 54 | | Guardians of the Galaxy | - | 0 | 5 | - | 55 | +---------------------------------+------+---------+---------+---------+ 56 | | Logan | 0 | - | - | 4 | 57 | +---------------------------------+------+---------+---------+---------+ 58 | | Forrest Gump | - | - | - | - | 59 | +---------------------------------+------+---------+---------+---------+ 60 | | The Kid | - | - | - | - | 61 | +---------------------------------+------+---------+---------+---------+ 62 | ``` 63 | 64 | We might notice: 65 | 66 | - Anna hates action movies so the prediction of "Logan" will be 0 67 | - Barbara also hates action movies so the prediction of "Guardians of the Galaxy" will be 0 68 | - Charlie loves the action movies so the prediction of "Guardians of the Galaxy" will be 5 69 | - Dave also loves the action movies so prediction of "Logan" will be 4 (not 5 since the maximum rating of this movie is equal 4) 70 | 71 | That's how Collaborative Filtering works. 72 | 73 | ### Training and training parameters 74 | 75 | As each machine learning problem after filling with data in order to get correct prediction the training 76 | (based on the dataset) is required. 77 | 78 | There is only one parameter for a Learning Model created from a dataset: 79 | 80 | - number of features. 81 | 82 | Understand 83 | it like 84 | type or real category of the item. It's value can be set equals number of item types in your 85 | application. You don't need to name them, you have to know number of them. 86 | 87 | There are two training parameters: 88 | 89 | - learning rate 90 | - number of iterations 91 | 92 | The **learning** **rate** is parameter which describes how much gradient descent 93 | (which minimizes the error) will perform. You might to consider to increase or decrease 94 | this parameter and it has strong 95 | correlation with number of iterations. 96 | 97 | The **number** **of** **iterations** is parameter which describes how much steps gradient descent minimize function 98 | will be applied. It's highly correlated with learning rate. 99 | 100 | The results of prediction may vary from desired by setting this parameters less accurate. 101 | 102 | However, there are some rules of setting these parameters more accurate in order to get 103 | better prediction: 104 | 105 | - if you set small learning rate then you might consider increase number of iterations 106 | - if you set large learning rate then you might consider decrease number of iterations 107 | - you might expect very low error - in this example a reasonable error would be less than 0.0001 108 | - setting too high learning rate may cause algorithm get computation error and the predictions become 109 | useless 110 | 111 | For this particular example i have set: 112 | 113 | - learning rate === 0.01 114 | - number of iterations === 20000 115 | - number of features === 2 (since i noticed two types of movies or two user preferences) 116 | 117 | The key to get well trained model is to choose the right ratio of learning rate and number of iterations. 118 | 119 | You might consider try different number of features according to your Application so the dataset also. 120 | 121 | Above example was fully implemented in [examples/1_train_and_predict.php](examples/1_train_and_predict.php). -------------------------------------------------------------------------------- /2_api_dataset.md: -------------------------------------------------------------------------------- 1 | # API - Dataset 2 | 3 | ### Passing data to Impulse-ML: Recommender Dataset 4 | 5 | Each algorithm, not only machine learning algorithm Impulse-ML: Recommender, must have knowledge about 6 | your data. Since the PHP applications use different storage systems Impulse-ML: Recommender has no database 7 | data fetcher - you might consider pass data directly to Dataset class instance. 8 | 9 | Consider the following code: 10 | 11 | ```php 12 | include_once __DIR__ . '/../src/Impulse/Recommender/Dataset.php'; 13 | 14 | $dataset = new Impulse\Recommender\Dataset(); 15 | 16 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight')); 17 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna')); 18 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0)); 19 | ``` 20 | 21 | It is minimum example to set 1 item (The Dark Knight), 1 category (Anna) and set it's rating to value of 0 22 | by user Anna. 23 | 24 | The example does not have any sense because we might want to have multiple items, categories as ratings - but 25 | you should have knowledge how to pass data to recommender system. 26 | 27 | In this example I used strings as my items, but ```Impulse\Recommender\Dataset\Item::create``` 28 | and ```Impulse\Recommender\Dataset\Category::create``` methods can also get an integer instead 29 | of string. You might consider pass integers to dataset as long as their values correspond to database 30 | primary keys and you will save a lot of memory than using a strings. 31 | 32 | Also, each of those 2 ```create``` methods can get second parameter which has no defined and no 33 | required data type. You might 34 | consider pass to it an array with your database model data for future use if database primary keys is not 35 | so much useful. 36 | 37 | The ```Impulse\Recommender\Dataset\Rating::create``` requires 3 parameters, which the first 2 - the item and 38 | the category should be already added to dataset and the third one should be numeric value. 39 | There is no minimum or 40 | maximum value, but different ranges of all ratings can require a different learning parameters. You might 41 | consider pass NULL if item is not rated but it is not required. 42 | 43 | For real life example of creating dataset check [examples/1_train_and_predict.php](examples/1_train_and_predict.php) 44 | 45 | -------------------------------------------------------------------------------- /3_api_learning_model.md: -------------------------------------------------------------------------------- 1 | # API - Learning Model 2 | 3 | ### Learning from dataset 4 | 5 | Assuming that we have data stored in ```Impulse\Recommender\Dataset``` we are ready to create 6 | a Learning Model. 7 | 8 | We can do this by using: 9 | 10 | ```php 11 | $model = new Impulse\Recommender\LearningModel($dataset, [ 12 | 'numFeatures' => 2 13 | ]); 14 | ``` 15 | 16 | "numFeatures" is required parameter. It may strictly correspond to number of categories of database 17 | items or number of defined user preferences. 18 | Notice that you don't need to define how much every item belongs to 19 | given category or how much user belongs to given preference. You just need to know number of them. -------------------------------------------------------------------------------- /4_api_training_the_model.md: -------------------------------------------------------------------------------- 1 | # API - Training the model 2 | 3 | ### Training the model 4 | 5 | You can get this done by using: 6 | 7 | ```php 8 | $trainer = new Impulse\Recommender\Trainer($model, [ 9 | 'learningRate' => 0.01, 10 | 'iterations' => 20000, 11 | 'verbose' => TRUE, // print debug messages 12 | 'verboseStep' => 1000 // step interval from displaying debug messages 13 | ]); 14 | 15 | $trainer->train(); 16 | ``` 17 | 18 | Note that training time may take very long time when your dataset is really large. It can be optimized 19 | more or less by 20 | choosing more accurate "learningRate" and "iterations" parameters. 21 | 22 | -------------------------------------------------------------------------------- /5_api_predicting_the_results.md: -------------------------------------------------------------------------------- 1 | # API - Predicting the results 2 | 3 | There are 3 prediction ways: 4 | 5 | ### Predict rating for user 6 | ```php 7 | $model->predict('Logan', 'Anna'); // float(9.9920072216264E-14) 8 | ``` 9 | 10 | which predicts rate for unrated "Logan" for user "Anna" by returning a number. 11 | 12 | Results may vary from desired because of improperly trained or not trained Learning Model. 13 | 14 | 15 | ### Find similar items 16 | ```php 17 | $model->findRelated('The Dark Knight', [ 18 | 'limit' => 1 19 | ]) 20 | ``` 21 | will finds all items in ordered by similarity array and it will returns: 22 | 23 | ```text 24 | array(1) { 25 | [0]=> 26 | array(2) { 27 | ["similarity"]=> 28 | float(2.2657653531155E-11) 29 | ["model"]=> 30 | array(2) { 31 | ["_id"]=> 32 | string(23) "Guardians of the Galaxy" 33 | ["data"]=> 34 | NULL 35 | } 36 | } 37 | } 38 | ``` 39 | 40 | ### Predict rate for user which has not rated any movie 41 | ```php 42 | $model->predict("Forrest Gump"); // int(2) 43 | ``` 44 | 45 | which can be useful when the user has not rated any movie so user has no computed preferences. 46 | 47 | -------------------------------------------------------------------------------- /6_api_full_example.md: -------------------------------------------------------------------------------- 1 | # API - Full example 2 | 3 | ```php 4 | include_once __DIR__ . '/../src/Impulse/Recommender/Dataset.php'; 5 | include_once __DIR__ . '/../src/Impulse/Recommender/LearningModel.php'; 6 | include_once __DIR__ . '/../src/Impulse/Recommender/Trainer.php'; 7 | 8 | $dataset = new Impulse\Recommender\Dataset(); 9 | 10 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Dark Knight')); 11 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Guardians of the Galaxy')); 12 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Logan')); 13 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('Forrest Gump')); 14 | $dataset->addItem(Impulse\Recommender\Dataset\Item::create('The Kid')); 15 | 16 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Anna')); 17 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Barbara')); 18 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Charlie')); 19 | $dataset->addCategory(Impulse\Recommender\Dataset\Category::create('Dave')); 20 | 21 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Anna', 0)); 22 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Barbara', 0)); 23 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Charlie', 5)); 24 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Dark Knight', 'Dave', 5)); 25 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Anna', 0)); 26 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Barbara', NULL)); 27 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Charlie', NULL)); 28 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Guardians of the Galaxy', 'Dave', 5)); 29 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Anna', NULL)); 30 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Barbara', 0)); 31 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Charlie', 4)); 32 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Logan', 'Dave', NULL)); 33 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Anna', 4)); 34 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Barbara', 5)); 35 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Charlie', 0)); 36 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('Forrest Gump', 'Dave', 0)); 37 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Anna', 5)); 38 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Barbara', 5)); 39 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Charlie', 0)); 40 | $dataset->addRating(Impulse\Recommender\Dataset\Rating::create('The Kid', 'Dave', 0)); 41 | 42 | $model = new Impulse\Recommender\LearningModel($dataset, [ 43 | 'numFeatures' => 2 44 | ]); 45 | 46 | $trainer = new Impulse\Recommender\Trainer($model, [ 47 | 'learningRate' => 0.01, 48 | 'iterations' => 20000, 49 | 'verbose' => TRUE, 50 | 'verboseStep' => 1000 51 | ]); 52 | 53 | $trainer->train(); 54 | 55 | echo "Prediction for 'Guardians of the Galaxy' for user 'Barbara': {$model->predict('Guardians of the Galaxy', 'Barbara')}\n"; 56 | echo "Prediction for 'Guardians of the Galaxy' for user 'Charlie': {$model->predict('Guardians of the Galaxy', 'Charlie')}\n"; 57 | echo "Prediction for 'Logan' for user 'Anna': {$model->predict('Logan', 'Anna')}\n"; 58 | echo "Prediction for 'Logan' for user 'Dave': {$model->predict('Logan', 'Dave')}\n"; 59 | 60 | echo "Prediction for 'Logan' for user with has not rated any movie: {$model->predict('Logan')}\n"; 61 | 62 | echo "Related movies dump:\n"; 63 | 64 | var_dump($model->findRelated('The Dark Knight', [ 65 | 'limit' => 5 66 | ])); 67 | ``` 68 | 69 | Which may produce output: 70 | 71 | ```text 72 | Starting train with 20000 steps. 73 | Step 0 with error 45.480538085596 74 | Step 1000 with error 0.14185749105855 75 | Step 2000 with error 0.00012367547481656 76 | Step 3000 with error 3.619659641736E-6 77 | Step 4000 with error 1.2231304078042E-7 78 | Step 5000 with error 5.7888427762046E-9 79 | Step 6000 with error 4.3159398580704E-10 80 | Step 7000 with error 4.2963851024733E-11 81 | Step 8000 with error 4.7458579962238E-12 82 | Step 9000 with error 5.3950928017295E-13 83 | Step 10000 with error 6.1779842297497E-14 84 | Step 11000 with error 7.0872758334659E-15 85 | Step 12000 with error 8.1340228680908E-16 86 | Step 13000 with error 9.3363924793255E-17 87 | Step 14000 with error 1.0716785358187E-17 88 | Step 15000 with error 1.2301363738974E-18 89 | Step 16000 with error 1.4120237226256E-19 90 | Step 17000 with error 1.6208264249521E-20 91 | Step 18000 with error 1.8605120483173E-21 92 | Step 19000 with error 2.1359434707995E-22 93 | Training ended with error 2.4586853842564E-23 after 20000 steps. 94 | Prediction for 'Guardians of the Galaxy' for user 'Barbara': 1.3472778448431E-11 95 | Prediction for 'Guardians of the Galaxy' for user 'Charlie': 4.9999999999974 96 | Prediction for 'Logan' for user 'Anna': 1.3994139180795E-11 97 | Prediction for 'Logan' for user 'Dave': 3.9999999999967 98 | Prediction for 'Logan' for user with has not rated any movie: 2 99 | Related movies dump: 100 | array(4) { 101 | [0]=> 102 | array(2) { 103 | ["similarity"]=> 104 | float(1.1086798146209E-11) 105 | ["model"]=> 106 | array(2) { 107 | ["_id"]=> 108 | string(23) "Guardians of the Galaxy" 109 | ["data"]=> 110 | NULL 111 | } 112 | } 113 | [1]=> 114 | array(2) { 115 | ["similarity"]=> 116 | float(0.17881301819823) 117 | ["model"]=> 118 | array(2) { 119 | ["_id"]=> 120 | string(5) "Logan" 121 | ["data"]=> 122 | NULL 123 | } 124 | } 125 | [2]=> 126 | array(2) { 127 | ["similarity"]=> 128 | float(0.92344428953759) 129 | ["model"]=> 130 | array(2) { 131 | ["_id"]=> 132 | string(12) "Forrest Gump" 133 | ["data"]=> 134 | NULL 135 | } 136 | } 137 | [3]=> 138 | array(2) { 139 | ["similarity"]=> 140 | float(1.7881301818354) 141 | ["model"]=> 142 | array(2) { 143 | ["_id"]=> 144 | string(7) "The Kid" 145 | ["data"]=> 146 | NULL 147 | } 148 | } 149 | } 150 | ``` 151 | 152 | Check [examples/1_train_and_predict.php](examples/1_train_and_predict.php) for details. -------------------------------------------------------------------------------- /7_examples_of_training.md: -------------------------------------------------------------------------------- 1 | # Examples of training 2 | 3 | According to our data table from lecture 2_problem_motivation.md consider the following learning parameters 4 | for this dataset: 5 | 6 | - 1: learningRate = 0.0001, iterations = 1000 7 | - 2: learningRate = 0.1, iterations = 10000 8 | - 3: learningRate = 0.0001, iterations = 100000 9 | - 4: learningRate = 0.01, iterations = 100000 10 | 11 | You might end up with following debug messages: 12 | 13 | ### Ex. 1 14 | 15 | learningRate = 0.0001, iterations = 1000 16 | 17 | ![Ex. 1](img/training_example_1.png?raw=true) 18 | 19 | 20 | The learningRate is too low and the iterations are too low - we have untrained model with high error. 21 | 22 | ### Ex. 2 23 | 24 | learningRate = 0.1, iterations = 10000 25 | 26 | ![Ex. 2](img/training_example_2.png?raw=true) 27 | 28 | The learningRate is too high cause after some step we have got numerical computation error. 29 | 30 | ### Ex. 3 31 | 32 | learningRate = 0.0001, iterations = 100000 33 | 34 | ![Ex. 3](img/training_example_3.png?raw=true) 35 | 36 | It is quite good error, but you might consider setting number of iterations to higher value or 37 | increasing learning rate. 38 | 39 | ### Ex. 4 40 | 41 | learningRate = 0.01, iterations = 100000 42 | 43 | ![Ex. 4](img/training_example_4.png?raw=true) 44 | 45 | After some steps we are not minimizing the error which is very close to 0 46 | so you might consider decrease number of iterations. 47 | 48 | ### Note 49 | 50 | The following examples with too large number of iterations should not have big impact on the time of 51 | training the model according to our small dataset used in previous examples. 52 | You might consider adjust more accurate parameters in larger datasets. -------------------------------------------------------------------------------- /8_api_saving_and_restoring_trained_model.md: -------------------------------------------------------------------------------- 1 | # API - Saving and restoring trained model 2 | 3 | You probably don't want to train your model after each one rate given by user, but for sure you might want 4 | to do that job outside your website because the training time could take very large amount of time. 5 | 6 | For do this we have implemented saving and restoring your trained Learning Model. 7 | 8 | ### Save 9 | 10 | ```php 11 | include_once __DIR__ . '/../src/Impulse/Recommender/Builder.php'; 12 | 13 | $builder = new Impulse\Recommender\Builder($model); 14 | $builder->save(__DIR__, 'save1'); 15 | ``` 16 | 17 | ### Restore 18 | 19 | ```php 20 | include_once __DIR__ . '/../src/Impulse/Recommender/Builder.php'; 21 | 22 | $model = Impulse\Recommender\Builder::load(__DIR__, 'save1'); 23 | ``` 24 | 25 | Each of those methods takes 2 parameters which the first one is location of the directory to save the data, 26 | and the second one is name of created directory for the data files. 27 | 28 | Check [examples/2_save.php](examples/2_save.php) and 29 | [examples/3_load_and_predict.php](examples/3_load_and_predict.php) for example of implementation. -------------------------------------------------------------------------------- /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) -------------------------------------------------------------------------------- /bb499e6458958e2f0e204d5a7ac6488450ea1e3365254c6bb10f02b14e5a25f4: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /docs/1_problem_motivation/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 1. Problem motivation - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 125 | 126 |
127 | 128 | 129 | 133 | 134 | 135 |
136 |
137 |
138 |
    139 |
  • Docs »
  • 140 | 141 | 142 | 143 |
  • 1. Problem motivation
  • 144 |
  • 145 | 146 |
  • 147 |
148 |
149 |
150 |
151 |
152 | 153 |

Problem motivation

154 |

Do I need Impulse-ML: Recommender, the Recommender System?

155 |

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 |

Problem definition

162 |

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 |
    180 |
  • we have 5 items - 5 movies
  • 181 |
  • we have 4 categories - 4 users
  • 182 |
  • we can notice 2 types of items: action movie and comedy movie
  • 183 |
  • it seems that Anna and Barbara hate the action movies but love the comedy movies
  • 184 |
  • it seems that Charlie and Dave love the action movies but hate the comedy movies
  • 185 |
  • the table is incomplete because every user has not rated at least one movie
  • 186 |
187 |

Using this data you might want to:

188 |
    189 |
  • predict user rating of movie that is unrated by user i.e. to send user the movie which he would like but he does not rated that movie yet
  • 190 |
  • get movies similar to given movie
  • 191 |
  • get the prediction of the movie for user that has no rated any movie and use this data
  • 192 |
193 |

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 |
    211 |
  • Anna hates action movies so the prediction of "Logan" will be 0
  • 212 |
  • Barbara also hates action movies so the prediction of "Guardians of the Galaxy" will be 0
  • 213 |
  • Charlie loves the action movies so the prediction of "Guardians of the Galaxy" will be 5
  • 214 |
  • Dave also loves the action movies so prediction of "Logan" will be 4 (not 5 since the maximum rating of this movie is equal 4)
  • 215 |
216 |

That's how Collaborative Filtering works.

217 |

Training and training parameters

218 |

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 |
    222 |
  • number of features.
  • 223 |
224 |

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 |
    230 |
  • learning rate
  • 231 |
  • number of iterations
  • 232 |
233 |

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 |
    243 |
  • if you set small learning rate then you might consider increase number of iterations
  • 244 |
  • if you set large learning rate then you might consider decrease number of iterations
  • 245 |
  • you might expect very low error - in this example a reasonable error would be less than 0.0001
  • 246 |
  • setting too high learning rate may cause algorithm get computation error and the predictions become 247 | useless
  • 248 |
249 |

For this particular example i have set:

250 |
    251 |
  • learning rate === 0.01
  • 252 |
  • number of iterations === 20000
  • 253 |
  • number of features === 2 (since i noticed two types of movies or two user preferences)
  • 254 |
255 |

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 |
260 |
261 | 284 | 285 |
286 |
287 | 288 |
289 | 290 |
291 | 292 |
293 | 294 | 295 | 296 | « Previous 297 | 298 | 299 | Next » 300 | 301 | 302 |
303 | 304 | 305 | 306 | 307 | -------------------------------------------------------------------------------- /docs/2_api_dataset/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 2. API: Dataset - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 121 | 122 |
123 | 124 | 125 | 129 | 130 | 131 |
132 |
133 |
134 |
    135 |
  • Docs »
  • 136 | 137 | 138 | 139 |
  • 2. API: Dataset
  • 140 |
  • 141 | 142 |
  • 143 |
144 |
145 |
146 |
147 |
148 | 149 |

API - Dataset

150 |

Passing data to Impulse-ML: Recommender Dataset

151 |

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.

172 |

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.

176 |

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.

181 |

For real life example of creating dataset check examples/1_train_and_predict.php

182 | 183 |
184 |
185 | 208 | 209 |
210 |
211 | 212 |
213 | 214 |
215 | 216 |
217 | 218 | 219 | 220 | « Previous 221 | 222 | 223 | Next » 224 | 225 | 226 |
227 | 228 | 229 | 230 | 231 | -------------------------------------------------------------------------------- /docs/3_api_learning_model/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 3. API: Learning Model - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 121 | 122 |
123 | 124 | 125 | 129 | 130 | 131 |
132 |
133 |
134 |
    135 |
  • Docs »
  • 136 | 137 | 138 | 139 |
  • 3. API: Learning Model
  • 140 |
  • 141 | 142 |
  • 143 |
144 |
145 |
146 |
147 |
148 | 149 |

API - Learning Model

150 |

Learning from dataset

151 |

Assuming that we have data stored in Impulse\Recommender\Dataset we are ready to create 152 | a Learning Model.

153 |

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 |
165 |
166 | 189 | 190 |
191 |
192 | 193 |
194 | 195 |
196 | 197 |
198 | 199 | 200 | 201 | « Previous 202 | 203 | 204 | Next » 205 | 206 | 207 |
208 | 209 | 210 | 211 | 212 | -------------------------------------------------------------------------------- /docs/4_api_training_the_model/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 4. API: Training the model - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 121 | 122 |
123 | 124 | 125 | 129 | 130 | 131 |
132 |
133 |
134 |
    135 |
  • Docs »
  • 136 | 137 | 138 | 139 |
  • 4. API: Training the model
  • 140 |
  • 141 | 142 |
  • 143 |
144 |
145 |
146 |
147 |
148 | 149 |

API - Training the model

150 |

Training the model

151 |

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 |
167 |
168 | 191 | 192 |
193 |
194 | 195 |
196 | 197 |
198 | 199 |
200 | 201 | 202 | 203 | « Previous 204 | 205 | 206 | Next » 207 | 208 | 209 |
210 | 211 | 212 | 213 | 214 | -------------------------------------------------------------------------------- /docs/5_api_predicting_the_results/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 5. API: Predicting the results - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 125 | 126 |
127 | 128 | 129 | 133 | 134 | 135 |
136 |
137 |
138 |
    139 |
  • Docs »
  • 140 | 141 | 142 | 143 |
  • 5. API: Predicting the results
  • 144 |
  • 145 | 146 |
  • 147 |
148 |
149 |
150 |
151 |
152 | 153 |

API - Predicting the results

154 |

There are 3 prediction ways:

155 |

Predict rating for user

156 |
$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 |

Find similar items

162 |
$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 |

Predict rate for user which has not rated any movie

185 |
$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 |
191 |
192 | 215 | 216 |
217 |
218 | 219 |
220 | 221 |
222 | 223 |
224 | 225 | 226 | 227 | « Previous 228 | 229 | 230 | Next » 231 | 232 | 233 |
234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /docs/6_api_full_example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 6. API: Full example - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 115 | 116 |
117 | 118 | 119 | 123 | 124 | 125 |
126 |
127 |
128 |
    129 |
  • Docs »
  • 130 | 131 | 132 | 133 |
  • 6. API: Full example
  • 134 |
  • 135 | 136 |
  • 137 |
138 |
139 |
140 |
141 |
142 | 143 |

API - Full example

144 |
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 |
293 |
294 | 317 | 318 |
319 |
320 | 321 |
322 | 323 |
324 | 325 |
326 | 327 | 328 | 329 | « Previous 330 | 331 | 332 | Next » 333 | 334 | 335 |
336 | 337 | 338 | 339 | 340 | -------------------------------------------------------------------------------- /docs/7_examples_of_training/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 7. Examples of training - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 129 | 130 |
131 | 132 | 133 | 137 | 138 | 139 |
140 |
141 |
142 |
    143 |
  • Docs »
  • 144 | 145 | 146 | 147 |
  • 7. Examples of training
  • 148 |
  • 149 | 150 |
  • 151 |
152 |
153 |
154 |
155 |
156 | 157 |

Examples of training

158 |

According to our data table from lecture 2_problem_motivation.md consider the following learning parameters 159 | for this dataset:

160 |
    161 |
  • 1: learningRate = 0.0001, iterations = 1000
  • 162 |
  • 2: learningRate = 0.1, iterations = 10000
  • 163 |
  • 3: learningRate = 0.0001, iterations = 100000
  • 164 |
  • 4: learningRate = 0.01, iterations = 100000
  • 165 |
166 |

You might end up with following debug messages:

167 |

Ex. 1

168 |

learningRate = 0.0001, iterations = 1000

169 |

Ex. 1

170 |

The learningRate is too low and the iterations are too low - we have untrained model with high error.

171 |

Ex. 2

172 |

learningRate = 0.1, iterations = 10000

173 |

Ex. 2

174 |

The learningRate is too high cause after some step we have got numerical computation error.

175 |

Ex. 3

176 |

learningRate = 0.0001, iterations = 100000

177 |

Ex. 3

178 |

It is quite good error, but you might consider setting number of iterations to higher value or 179 | increasing learning rate.

180 |

Ex. 4

181 |

learningRate = 0.01, iterations = 100000

182 |

Ex. 4

183 |

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 |

Note

186 |

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 |
191 |
192 | 215 | 216 |
217 |
218 | 219 |
220 | 221 |
222 | 223 |
224 | 225 | 226 | 227 | « Previous 228 | 229 | 230 | Next » 231 | 232 | 233 |
234 | 235 | 236 | 237 | 238 | -------------------------------------------------------------------------------- /docs/8_api_saving_and_restoring_trained_model/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 8. API: Saving and restoring trained model - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 123 | 124 |
125 | 126 | 127 | 131 | 132 | 133 |
134 |
135 |
136 |
    137 |
  • Docs »
  • 138 | 139 | 140 | 141 |
  • 8. API: Saving and restoring trained model
  • 142 |
  • 143 | 144 |
  • 145 |
146 |
147 |
148 |
149 |
150 | 151 |

API - Saving and restoring trained model

152 |

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 |

Save

156 |
include_once __DIR__ . '/../src/Impulse/Recommender/Builder.php';
157 | 
158 | $builder = new Impulse\Recommender\Builder($model);
159 | $builder->save(__DIR__, 'save1');
160 | 
161 | 162 |

Restore

163 |
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 |
174 |
175 | 196 | 197 |
198 |
199 | 200 |
201 | 202 |
203 | 204 |
205 | 206 | 207 | 208 | « Previous 209 | 210 | 211 | 212 |
213 | 214 | 215 | 216 | 217 | -------------------------------------------------------------------------------- /docs/css/highlight.css: -------------------------------------------------------------------------------- 1 | /* 2 | This is the GitHub theme for highlight.js 3 | 4 | github.com style (c) Vasily Polovnyov 5 | 6 | */ 7 | 8 | .hljs { 9 | display: block; 10 | overflow-x: auto; 11 | color: #333; 12 | -webkit-text-size-adjust: none; 13 | } 14 | 15 | .hljs-comment, 16 | .diff .hljs-header, 17 | .hljs-javadoc { 18 | color: #998; 19 | font-style: italic; 20 | } 21 | 22 | .hljs-keyword, 23 | .css .rule .hljs-keyword, 24 | .hljs-winutils, 25 | .nginx .hljs-title, 26 | .hljs-subst, 27 | .hljs-request, 28 | .hljs-status { 29 | color: #333; 30 | font-weight: bold; 31 | } 32 | 33 | .hljs-number, 34 | .hljs-hexcolor, 35 | .ruby .hljs-constant { 36 | color: #008080; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-tag .hljs-value, 41 | .hljs-phpdoc, 42 | .hljs-dartdoc, 43 | .tex .hljs-formula { 44 | color: #d14; 45 | } 46 | 47 | .hljs-title, 48 | .hljs-id, 49 | .scss .hljs-preprocessor { 50 | color: #900; 51 | font-weight: bold; 52 | } 53 | 54 | .hljs-list .hljs-keyword, 55 | .hljs-subst { 56 | font-weight: normal; 57 | } 58 | 59 | .hljs-class .hljs-title, 60 | .hljs-type, 61 | .vhdl .hljs-literal, 62 | .tex .hljs-command { 63 | color: #458; 64 | font-weight: bold; 65 | } 66 | 67 | .hljs-tag, 68 | .hljs-tag .hljs-title, 69 | .hljs-rule .hljs-property, 70 | .django .hljs-tag .hljs-keyword { 71 | color: #000080; 72 | font-weight: normal; 73 | } 74 | 75 | .hljs-attribute, 76 | .hljs-variable, 77 | .lisp .hljs-body, 78 | .hljs-name { 79 | color: #008080; 80 | } 81 | 82 | .hljs-regexp { 83 | color: #009926; 84 | } 85 | 86 | .hljs-symbol, 87 | .ruby .hljs-symbol .hljs-string, 88 | .lisp .hljs-keyword, 89 | .clojure .hljs-keyword, 90 | .scheme .hljs-keyword, 91 | .tex .hljs-special, 92 | .hljs-prompt { 93 | color: #990073; 94 | } 95 | 96 | .hljs-built_in { 97 | color: #0086b3; 98 | } 99 | 100 | .hljs-preprocessor, 101 | .hljs-pragma, 102 | .hljs-pi, 103 | .hljs-doctype, 104 | .hljs-shebang, 105 | .hljs-cdata { 106 | color: #999; 107 | font-weight: bold; 108 | } 109 | 110 | .hljs-deletion { 111 | background: #fdd; 112 | } 113 | 114 | .hljs-addition { 115 | background: #dfd; 116 | } 117 | 118 | .diff .hljs-change { 119 | background: #0086b3; 120 | } 121 | 122 | .hljs-chunk { 123 | color: #aaa; 124 | } 125 | -------------------------------------------------------------------------------- /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/examples/1_sample_output.txt: -------------------------------------------------------------------------------- 1 | Starting train with 20000 steps. 2 | Step 0 with error 45.480538085596 3 | Step 1000 with error 0.14185749105855 4 | Step 2000 with error 0.00012367547481656 5 | Step 3000 with error 3.619659641736E-6 6 | Step 4000 with error 1.2231304078042E-7 7 | Step 5000 with error 5.7888427762046E-9 8 | Step 6000 with error 4.3159398580704E-10 9 | Step 7000 with error 4.2963851024733E-11 10 | Step 8000 with error 4.7458579962238E-12 11 | Step 9000 with error 5.3950928017295E-13 12 | Step 10000 with error 6.1779842297497E-14 13 | Step 11000 with error 7.0872758334659E-15 14 | Step 12000 with error 8.1340228680908E-16 15 | Step 13000 with error 9.3363924793255E-17 16 | Step 14000 with error 1.0716785358187E-17 17 | Step 15000 with error 1.2301363738974E-18 18 | Step 16000 with error 1.4120237226256E-19 19 | Step 17000 with error 1.6208264249521E-20 20 | Step 18000 with error 1.8605120483173E-21 21 | Step 19000 with error 2.1359434707995E-22 22 | Training ended with error 2.4586853842564E-23 after 20000 steps. 23 | Prediction for 'Guardians of the Galaxy' for user 'Barbara': 1.3472778448431E-11 24 | Prediction for 'Guardians of the Galaxy' for user 'Charlie': 4.9999999999974 25 | Prediction for 'Logan' for user 'Anna': 1.3994139180795E-11 26 | Prediction for 'Logan' for user 'Dave': 3.9999999999967 27 | Prediction for 'Logan' for user with has not rated any movie: 2 28 | Related movies dump: 29 | array(4) { 30 | [0]=> 31 | array(2) { 32 | ["similarity"]=> 33 | float(1.1086798146209E-11) 34 | ["model"]=> 35 | array(2) { 36 | ["_id"]=> 37 | string(23) "Guardians of the Galaxy" 38 | ["data"]=> 39 | NULL 40 | } 41 | } 42 | [1]=> 43 | array(2) { 44 | ["similarity"]=> 45 | float(0.17881301819823) 46 | ["model"]=> 47 | array(2) { 48 | ["_id"]=> 49 | string(5) "Logan" 50 | ["data"]=> 51 | NULL 52 | } 53 | } 54 | [2]=> 55 | array(2) { 56 | ["similarity"]=> 57 | float(0.92344428953759) 58 | ["model"]=> 59 | array(2) { 60 | ["_id"]=> 61 | string(12) "Forrest Gump" 62 | ["data"]=> 63 | NULL 64 | } 65 | } 66 | [3]=> 67 | array(2) { 68 | ["similarity"]=> 69 | float(1.7881301818354) 70 | ["model"]=> 71 | array(2) { 72 | ["_id"]=> 73 | string(7) "The Kid" 74 | ["data"]=> 75 | NULL 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /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/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/3_load_and_predict.php: -------------------------------------------------------------------------------- 1 | predict('Guardians of the Galaxy', 'Barbara')}\n"; 11 | echo "Prediction for 'Guardians of the Galaxy' for user 'Charlie': {$model->predict('Guardians of the Galaxy', 'Charlie')}\n"; 12 | echo "Prediction for 'Logan' for user 'Anna': {$model->predict('Logan', 'Anna')}\n"; 13 | echo "Prediction for 'Logan' for user 'Dave': {$model->predict('Logan', 'Dave')}\n"; 14 | 15 | echo "Prediction for 'Logan' for user with has not rated any movie: {$model->predict('Logan')}\n"; 16 | 17 | echo "Related movies dump:\n"; 18 | 19 | var_dump($model->findRelated('The Dark Knight', [ 20 | 'limit' => 5 21 | ])); -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /docs/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /docs/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/img/favicon.ico -------------------------------------------------------------------------------- /docs/img/training_example_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/img/training_example_1.png -------------------------------------------------------------------------------- /docs/img/training_example_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/img/training_example_2.png -------------------------------------------------------------------------------- /docs/img/training_example_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/img/training_example_3.png -------------------------------------------------------------------------------- /docs/img/training_example_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/docs/img/training_example_4.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Impulse-ML: Recommender - Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 38 | 39 | 40 | 41 | 42 | 43 |
44 | 45 | 46 | 127 | 128 |
129 | 130 | 131 | 135 | 136 | 137 |
138 |
139 |
140 |
    141 |
  • Docs »
  • 142 | 143 | 144 | 145 |
  • Impulse-ML: Recommender
  • 146 |
  • 147 | 148 |
  • 149 |
150 |
151 |
152 |
153 |
154 | 155 |

Impulse-ML: Recommender, the Recommender Engine

156 |

About

157 |

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 |

Machine Learning

163 |

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 |
    167 |
  • movie ratings, of those movies which are unrated by the user
  • 168 |
  • find similar movies
  • 169 |
  • get the prediction for user who don't rate any movie.
  • 170 |
171 |

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 |

Requirements

186 |
    187 |
  • PHP >= 5.4
  • 188 |
189 |

Table of contents

190 | 223 | 224 |
225 |
226 |
227 | 228 | 234 | 235 | 236 |
237 | 238 |
239 | 240 | 241 |

Impulse-ML

242 | 243 |
244 | 245 | Built with MkDocs using a theme provided by Read the Docs. 246 |
247 | 248 |
249 |
250 | 251 |
252 | 253 |
254 | 255 |
256 | 257 | 258 | 259 | 260 | Next » 261 | 262 | 263 |
264 | 265 | 266 | 267 | 268 | 269 | 273 | -------------------------------------------------------------------------------- /docs/js/modernizr-2.8.3.min.js: -------------------------------------------------------------------------------- 1 | window.Modernizr=function(e,t,n){function r(e){b.cssText=e}function o(e,t){return r(S.join(e+";")+(t||""))}function a(e,t){return typeof e===t}function i(e,t){return!!~(""+e).indexOf(t)}function c(e,t){for(var r in e){var o=e[r];if(!i(o,"-")&&b[o]!==n)return"pfx"==t?o:!0}return!1}function s(e,t,r){for(var o in e){var i=t[e[o]];if(i!==n)return r===!1?e[o]:a(i,"function")?i.bind(r||t):i}return!1}function u(e,t,n){var r=e.charAt(0).toUpperCase()+e.slice(1),o=(e+" "+k.join(r+" ")+r).split(" ");return a(t,"string")||a(t,"undefined")?c(o,t):(o=(e+" "+T.join(r+" ")+r).split(" "),s(o,t,n))}function l(){p.input=function(n){for(var r=0,o=n.length;o>r;r++)j[n[r]]=!!(n[r]in E);return j.list&&(j.list=!(!t.createElement("datalist")||!e.HTMLDataListElement)),j}("autocomplete autofocus list placeholder max min multiple pattern required step".split(" ")),p.inputtypes=function(e){for(var r,o,a,i=0,c=e.length;c>i;i++)E.setAttribute("type",o=e[i]),r="text"!==E.type,r&&(E.value=x,E.style.cssText="position:absolute;visibility:hidden;",/^range$/.test(o)&&E.style.WebkitAppearance!==n?(g.appendChild(E),a=t.defaultView,r=a.getComputedStyle&&"textfield"!==a.getComputedStyle(E,null).WebkitAppearance&&0!==E.offsetHeight,g.removeChild(E)):/^(search|tel)$/.test(o)||(r=/^(url|email)$/.test(o)?E.checkValidity&&E.checkValidity()===!1:E.value!=x)),P[e[i]]=!!r;return P}("search tel url email datetime date month week time datetime-local number range color".split(" "))}var d,f,m="2.8.3",p={},h=!0,g=t.documentElement,v="modernizr",y=t.createElement(v),b=y.style,E=t.createElement("input"),x=":)",w={}.toString,S=" -webkit- -moz- -o- -ms- ".split(" "),C="Webkit Moz O ms",k=C.split(" "),T=C.toLowerCase().split(" "),N={svg:"http://www.w3.org/2000/svg"},M={},P={},j={},$=[],D=$.slice,F=function(e,n,r,o){var a,i,c,s,u=t.createElement("div"),l=t.body,d=l||t.createElement("body");if(parseInt(r,10))for(;r--;)c=t.createElement("div"),c.id=o?o[r]:v+(r+1),u.appendChild(c);return a=["­",'"].join(""),u.id=v,(l?u:d).innerHTML+=a,d.appendChild(u),l||(d.style.background="",d.style.overflow="hidden",s=g.style.overflow,g.style.overflow="hidden",g.appendChild(d)),i=n(u,e),l?u.parentNode.removeChild(u):(d.parentNode.removeChild(d),g.style.overflow=s),!!i},z=function(t){var n=e.matchMedia||e.msMatchMedia;if(n)return n(t)&&n(t).matches||!1;var r;return F("@media "+t+" { #"+v+" { position: absolute; } }",function(t){r="absolute"==(e.getComputedStyle?getComputedStyle(t,null):t.currentStyle).position}),r},A=function(){function e(e,o){o=o||t.createElement(r[e]||"div"),e="on"+e;var i=e in o;return i||(o.setAttribute||(o=t.createElement("div")),o.setAttribute&&o.removeAttribute&&(o.setAttribute(e,""),i=a(o[e],"function"),a(o[e],"undefined")||(o[e]=n),o.removeAttribute(e))),o=null,i}var r={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return e}(),L={}.hasOwnProperty;f=a(L,"undefined")||a(L.call,"undefined")?function(e,t){return t in e&&a(e.constructor.prototype[t],"undefined")}:function(e,t){return L.call(e,t)},Function.prototype.bind||(Function.prototype.bind=function(e){var t=this;if("function"!=typeof t)throw new TypeError;var n=D.call(arguments,1),r=function(){if(this instanceof r){var o=function(){};o.prototype=t.prototype;var a=new o,i=t.apply(a,n.concat(D.call(arguments)));return Object(i)===i?i:a}return t.apply(e,n.concat(D.call(arguments)))};return r}),M.flexbox=function(){return u("flexWrap")},M.flexboxlegacy=function(){return u("boxDirection")},M.canvas=function(){var e=t.createElement("canvas");return!(!e.getContext||!e.getContext("2d"))},M.canvastext=function(){return!(!p.canvas||!a(t.createElement("canvas").getContext("2d").fillText,"function"))},M.webgl=function(){return!!e.WebGLRenderingContext},M.touch=function(){var n;return"ontouchstart"in e||e.DocumentTouch&&t instanceof DocumentTouch?n=!0:F(["@media (",S.join("touch-enabled),("),v,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(e){n=9===e.offsetTop}),n},M.geolocation=function(){return"geolocation"in navigator},M.postmessage=function(){return!!e.postMessage},M.websqldatabase=function(){return!!e.openDatabase},M.indexedDB=function(){return!!u("indexedDB",e)},M.hashchange=function(){return A("hashchange",e)&&(t.documentMode===n||t.documentMode>7)},M.history=function(){return!(!e.history||!history.pushState)},M.draganddrop=function(){var e=t.createElement("div");return"draggable"in e||"ondragstart"in e&&"ondrop"in e},M.websockets=function(){return"WebSocket"in e||"MozWebSocket"in e},M.rgba=function(){return r("background-color:rgba(150,255,150,.5)"),i(b.backgroundColor,"rgba")},M.hsla=function(){return r("background-color:hsla(120,40%,100%,.5)"),i(b.backgroundColor,"rgba")||i(b.backgroundColor,"hsla")},M.multiplebgs=function(){return r("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(b.background)},M.backgroundsize=function(){return u("backgroundSize")},M.borderimage=function(){return u("borderImage")},M.borderradius=function(){return u("borderRadius")},M.boxshadow=function(){return u("boxShadow")},M.textshadow=function(){return""===t.createElement("div").style.textShadow},M.opacity=function(){return o("opacity:.55"),/^0.55$/.test(b.opacity)},M.cssanimations=function(){return u("animationName")},M.csscolumns=function(){return u("columnCount")},M.cssgradients=function(){var e="background-image:",t="gradient(linear,left top,right bottom,from(#9f9),to(white));",n="linear-gradient(left top,#9f9, white);";return r((e+"-webkit- ".split(" ").join(t+e)+S.join(n+e)).slice(0,-e.length)),i(b.backgroundImage,"gradient")},M.cssreflections=function(){return u("boxReflect")},M.csstransforms=function(){return!!u("transform")},M.csstransforms3d=function(){var e=!!u("perspective");return e&&"webkitPerspective"in g.style&&F("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(t){e=9===t.offsetLeft&&3===t.offsetHeight}),e},M.csstransitions=function(){return u("transition")},M.fontface=function(){var e;return F('@font-face {font-family:"font";src:url("https://")}',function(n,r){var o=t.getElementById("smodernizr"),a=o.sheet||o.styleSheet,i=a?a.cssRules&&a.cssRules[0]?a.cssRules[0].cssText:a.cssText||"":"";e=/src/i.test(i)&&0===i.indexOf(r.split(" ")[0])}),e},M.generatedcontent=function(){var e;return F(["#",v,"{font:0/0 a}#",v,':after{content:"',x,'";visibility:hidden;font:3px/1 a}'].join(""),function(t){e=t.offsetHeight>=3}),e},M.video=function(){var e=t.createElement("video"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),n.h264=e.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),n.webm=e.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,""))}catch(r){}return n},M.audio=function(){var e=t.createElement("audio"),n=!1;try{(n=!!e.canPlayType)&&(n=new Boolean(n),n.ogg=e.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),n.mp3=e.canPlayType("audio/mpeg;").replace(/^no$/,""),n.wav=e.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),n.m4a=(e.canPlayType("audio/x-m4a;")||e.canPlayType("audio/aac;")).replace(/^no$/,""))}catch(r){}return n},M.localstorage=function(){try{return localStorage.setItem(v,v),localStorage.removeItem(v),!0}catch(e){return!1}},M.sessionstorage=function(){try{return sessionStorage.setItem(v,v),sessionStorage.removeItem(v),!0}catch(e){return!1}},M.webworkers=function(){return!!e.Worker},M.applicationcache=function(){return!!e.applicationCache},M.svg=function(){return!!t.createElementNS&&!!t.createElementNS(N.svg,"svg").createSVGRect},M.inlinesvg=function(){var e=t.createElement("div");return e.innerHTML="",(e.firstChild&&e.firstChild.namespaceURI)==N.svg},M.smil=function(){return!!t.createElementNS&&/SVGAnimate/.test(w.call(t.createElementNS(N.svg,"animate")))},M.svgclippaths=function(){return!!t.createElementNS&&/SVGClipPath/.test(w.call(t.createElementNS(N.svg,"clipPath")))};for(var H in M)f(M,H)&&(d=H.toLowerCase(),p[d]=M[H](),$.push((p[d]?"":"no-")+d));return p.input||l(),p.addTest=function(e,t){if("object"==typeof e)for(var r in e)f(e,r)&&p.addTest(r,e[r]);else{if(e=e.toLowerCase(),p[e]!==n)return p;t="function"==typeof t?t():t,"undefined"!=typeof h&&h&&(g.className+=" "+(t?"":"no-")+e),p[e]=t}return p},r(""),y=E=null,function(e,t){function n(e,t){var n=e.createElement("p"),r=e.getElementsByTagName("head")[0]||e.documentElement;return n.innerHTML="x",r.insertBefore(n.lastChild,r.firstChild)}function r(){var e=y.elements;return"string"==typeof e?e.split(" "):e}function o(e){var t=v[e[h]];return t||(t={},g++,e[h]=g,v[g]=t),t}function a(e,n,r){if(n||(n=t),l)return n.createElement(e);r||(r=o(n));var a;return a=r.cache[e]?r.cache[e].cloneNode():p.test(e)?(r.cache[e]=r.createElem(e)).cloneNode():r.createElem(e),!a.canHaveChildren||m.test(e)||a.tagUrn?a:r.frag.appendChild(a)}function i(e,n){if(e||(e=t),l)return e.createDocumentFragment();n=n||o(e);for(var a=n.frag.cloneNode(),i=0,c=r(),s=c.length;s>i;i++)a.createElement(c[i]);return a}function c(e,t){t.cache||(t.cache={},t.createElem=e.createElement,t.createFrag=e.createDocumentFragment,t.frag=t.createFrag()),e.createElement=function(n){return y.shivMethods?a(n,e,t):t.createElem(n)},e.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+r().join().replace(/[\w\-]+/g,function(e){return t.createElem(e),t.frag.createElement(e),'c("'+e+'")'})+");return n}")(y,t.frag)}function s(e){e||(e=t);var r=o(e);return!y.shivCSS||u||r.hasCSS||(r.hasCSS=!!n(e,"article,aside,dialog,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}mark{background:#FF0;color:#000}template{display:none}")),l||c(e,r),e}var u,l,d="3.7.0",f=e.html5||{},m=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,p=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,h="_html5shiv",g=0,v={};!function(){try{var e=t.createElement("a");e.innerHTML="",u="hidden"in e,l=1==e.childNodes.length||function(){t.createElement("a");var e=t.createDocumentFragment();return"undefined"==typeof e.cloneNode||"undefined"==typeof e.createDocumentFragment||"undefined"==typeof e.createElement}()}catch(n){u=!0,l=!0}}();var y={elements:f.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:d,shivCSS:f.shivCSS!==!1,supportsUnknownElements:l,shivMethods:f.shivMethods!==!1,type:"default",shivDocument:s,createElement:a,createDocumentFragment:i};e.html5=y,s(t)}(this,t),p._version=m,p._prefixes=S,p._domPrefixes=T,p._cssomPrefixes=k,p.mq=z,p.hasEvent=A,p.testProp=function(e){return c([e])},p.testAllProps=u,p.testStyles=F,p.prefixed=function(e,t,n){return t?u(e,t,n):u(e,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(h?" js "+$.join(" "):""),p}(this,this.document); -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /docs/mkdocs/js/lunr.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 0.7.0 3 | * Copyright (C) 2016 Oliver Nightingale 4 | * MIT Licensed 5 | * @license 6 | */ 7 | !function(){var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.7.0",t.utils={},t.utils.warn=function(t){return function(e){t.console&&console.warn&&console.warn(e)}}(this),t.utils.asString=function(t){return void 0===t||null===t?"":t.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var t=Array.prototype.slice.call(arguments),e=t.pop(),n=t;if("function"!=typeof e)throw new TypeError("last argument must be a function");n.forEach(function(t){this.hasHandler(t)||(this.events[t]=[]),this.events[t].push(e)},this)},t.EventEmitter.prototype.removeListener=function(t,e){if(this.hasHandler(t)){var n=this.events[t].indexOf(e);this.events[t].splice(n,1),this.events[t].length||delete this.events[t]}},t.EventEmitter.prototype.emit=function(t){if(this.hasHandler(t)){var e=Array.prototype.slice.call(arguments,1);this.events[t].forEach(function(t){t.apply(void 0,e)})}},t.EventEmitter.prototype.hasHandler=function(t){return t in this.events},t.tokenizer=function(e){return arguments.length&&null!=e&&void 0!=e?Array.isArray(e)?e.map(function(e){return t.utils.asString(e).toLowerCase()}):e.toString().trim().toLowerCase().split(t.tokenizer.seperator):[]},t.tokenizer.seperator=/[\s\-]+/,t.tokenizer.load=function(t){var e=this.registeredFunctions[t];if(!e)throw new Error("Cannot load un-registered function: "+t);return e},t.tokenizer.label="default",t.tokenizer.registeredFunctions={"default":t.tokenizer},t.tokenizer.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing tokenizer: "+n),e.label=n,this.registeredFunctions[n]=e},t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.registeredFunctions[e];if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._stack.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");i+=1,this._stack.splice(i,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._stack.indexOf(e);if(-1==i)throw new Error("Cannot find existingFn");this._stack.splice(i,0,n)},t.Pipeline.prototype.remove=function(t){var e=this._stack.indexOf(t);-1!=e&&this._stack.splice(e,1)},t.Pipeline.prototype.run=function(t){for(var e=[],n=t.length,i=this._stack.length,r=0;n>r;r++){for(var o=t[r],s=0;i>s&&(o=this._stack[s](o,r,t),void 0!==o&&""!==o);s++);void 0!==o&&""!==o&&e.push(o)}return e},t.Pipeline.prototype.reset=function(){this._stack=[]},t.Pipeline.prototype.toJSON=function(){return this._stack.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Vector=function(){this._magnitude=null,this.list=void 0,this.length=0},t.Vector.Node=function(t,e,n){this.idx=t,this.val=e,this.next=n},t.Vector.prototype.insert=function(e,n){this._magnitude=void 0;var i=this.list;if(!i)return this.list=new t.Vector.Node(e,n,i),this.length++;if(en.idx?n=n.next:(i+=e.val*n.val,e=e.next,n=n.next);return i},t.Vector.prototype.similarity=function(t){return this.dot(t)/(this.magnitude()*t.magnitude())},t.SortedSet=function(){this.length=0,this.elements=[]},t.SortedSet.load=function(t){var e=new this;return e.elements=t,e.length=t.length,e},t.SortedSet.prototype.add=function(){var t,e;for(t=0;t1;){if(o===t)return r;t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r]}return o===t?r:-1},t.SortedSet.prototype.locationFor=function(t){for(var e=0,n=this.elements.length,i=n-e,r=e+Math.floor(i/2),o=this.elements[r];i>1;)t>o&&(e=r),o>t&&(n=r),i=n-e,r=e+Math.floor(i/2),o=this.elements[r];return o>t?r:t>o?r+1:void 0},t.SortedSet.prototype.intersect=function(e){for(var n=new t.SortedSet,i=0,r=0,o=this.length,s=e.length,a=this.elements,h=e.elements;;){if(i>o-1||r>s-1)break;a[i]!==h[r]?a[i]h[r]&&r++:(n.add(a[i]),i++,r++)}return n},t.SortedSet.prototype.clone=function(){var e=new t.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},t.SortedSet.prototype.union=function(t){var e,n,i;this.length>=t.length?(e=this,n=t):(e=t,n=this),i=e.clone();for(var r=0,o=n.toArray();rp;p++)c[p]===a&&d++;h+=d/f*l.boost}}this.tokenStore.add(a,{ref:o,tf:h})}n&&this.eventEmitter.emit("add",e,this)},t.Index.prototype.remove=function(t,e){var n=t[this._ref],e=void 0===e?!0:e;if(this.documentStore.has(n)){var i=this.documentStore.get(n);this.documentStore.remove(n),i.forEach(function(t){this.tokenStore.remove(t,n)},this),e&&this.eventEmitter.emit("remove",t,this)}},t.Index.prototype.update=function(t,e){var e=void 0===e?!0:e;this.remove(t,!1),this.add(t,!1),e&&this.eventEmitter.emit("update",t,this)},t.Index.prototype.idf=function(t){var e="@"+t;if(Object.prototype.hasOwnProperty.call(this._idfCache,e))return this._idfCache[e];var n=this.tokenStore.count(t),i=1;return n>0&&(i=1+Math.log(this.documentStore.length/n)),this._idfCache[e]=i},t.Index.prototype.search=function(e){var n=this.pipeline.run(this.tokenizerFn(e)),i=new t.Vector,r=[],o=this._fields.reduce(function(t,e){return t+e.boost},0),s=n.some(function(t){return this.tokenStore.has(t)},this);if(!s)return[];n.forEach(function(e,n,s){var a=1/s.length*this._fields.length*o,h=this,u=this.tokenStore.expand(e).reduce(function(n,r){var o=h.corpusTokens.indexOf(r),s=h.idf(r),u=1,l=new t.SortedSet;if(r!==e){var c=Math.max(3,r.length-e.length);u=1/Math.log(c)}o>-1&&i.insert(o,a*s*u);for(var f=h.tokenStore.get(r),d=Object.keys(f),p=d.length,v=0;p>v;v++)l.add(f[d[v]].ref);return n.union(l)},new t.SortedSet);r.push(u)},this);var a=r.reduce(function(t,e){return t.intersect(e)});return a.map(function(t){return{ref:t,score:i.similarity(this.documentVector(t))}},this).sort(function(t,e){return e.score-t.score})},t.Index.prototype.documentVector=function(e){for(var n=this.documentStore.get(e),i=n.length,r=new t.Vector,o=0;i>o;o++){var s=n.elements[o],a=this.tokenStore.get(s)[e].tf,h=this.idf(s);r.insert(this.corpusTokens.indexOf(s),a*h)}return r},t.Index.prototype.toJSON=function(){return{version:t.version,fields:this._fields,ref:this._ref,tokenizer:this.tokenizerFn.label,documentStore:this.documentStore.toJSON(),tokenStore:this.tokenStore.toJSON(),corpusTokens:this.corpusTokens.toJSON(),pipeline:this.pipeline.toJSON()}},t.Index.prototype.use=function(t){var e=Array.prototype.slice.call(arguments,1);e.unshift(this),t.apply(this,e)},t.Store=function(){this.store={},this.length=0},t.Store.load=function(e){var n=new this;return n.length=e.length,n.store=Object.keys(e.store).reduce(function(n,i){return n[i]=t.SortedSet.load(e.store[i]),n},{}),n},t.Store.prototype.set=function(t,e){this.has(t)||this.length++,this.store[t]=e},t.Store.prototype.get=function(t){return this.store[t]},t.Store.prototype.has=function(t){return t in this.store},t.Store.prototype.remove=function(t){this.has(t)&&(delete this.store[t],this.length--)},t.Store.prototype.toJSON=function(){return{store:this.store,length:this.length}},t.stemmer=function(){var t={ational:"ate",tional:"tion",enci:"ence",anci:"ance",izer:"ize",bli:"ble",alli:"al",entli:"ent",eli:"e",ousli:"ous",ization:"ize",ation:"ate",ator:"ate",alism:"al",iveness:"ive",fulness:"ful",ousness:"ous",aliti:"al",iviti:"ive",biliti:"ble",logi:"log"},e={icate:"ic",ative:"",alize:"al",iciti:"ic",ical:"ic",ful:"",ness:""},n="[^aeiou]",i="[aeiouy]",r=n+"[^aeiouy]*",o=i+"[aeiou]*",s="^("+r+")?"+o+r,a="^("+r+")?"+o+r+"("+o+")?$",h="^("+r+")?"+o+r+o+r,u="^("+r+")?"+i,l=new RegExp(s),c=new RegExp(h),f=new RegExp(a),d=new RegExp(u),p=/^(.+?)(ss|i)es$/,v=/^(.+?)([^s])s$/,g=/^(.+?)eed$/,m=/^(.+?)(ed|ing)$/,y=/.$/,S=/(at|bl|iz)$/,w=new RegExp("([^aeiouylsz])\\1$"),k=new RegExp("^"+r+i+"[^aeiouwxy]$"),x=/^(.+?[^aeiou])y$/,b=/^(.+?)(ational|tional|enci|anci|izer|bli|alli|entli|eli|ousli|ization|ation|ator|alism|iveness|fulness|ousness|aliti|iviti|biliti|logi)$/,E=/^(.+?)(icate|ative|alize|iciti|ical|ful|ness)$/,F=/^(.+?)(al|ance|ence|er|ic|able|ible|ant|ement|ment|ent|ou|ism|ate|iti|ous|ive|ize)$/,_=/^(.+?)(s|t)(ion)$/,z=/^(.+?)e$/,O=/ll$/,P=new RegExp("^"+r+i+"[^aeiouwxy]$"),T=function(n){var i,r,o,s,a,h,u;if(n.length<3)return n;if(o=n.substr(0,1),"y"==o&&(n=o.toUpperCase()+n.substr(1)),s=p,a=v,s.test(n)?n=n.replace(s,"$1$2"):a.test(n)&&(n=n.replace(a,"$1$2")),s=g,a=m,s.test(n)){var T=s.exec(n);s=l,s.test(T[1])&&(s=y,n=n.replace(s,""))}else if(a.test(n)){var T=a.exec(n);i=T[1],a=d,a.test(i)&&(n=i,a=S,h=w,u=k,a.test(n)?n+="e":h.test(n)?(s=y,n=n.replace(s,"")):u.test(n)&&(n+="e"))}if(s=x,s.test(n)){var T=s.exec(n);i=T[1],n=i+"i"}if(s=b,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+t[r])}if(s=E,s.test(n)){var T=s.exec(n);i=T[1],r=T[2],s=l,s.test(i)&&(n=i+e[r])}if(s=F,a=_,s.test(n)){var T=s.exec(n);i=T[1],s=c,s.test(i)&&(n=i)}else if(a.test(n)){var T=a.exec(n);i=T[1]+T[2],a=c,a.test(i)&&(n=i)}if(s=z,s.test(n)){var T=s.exec(n);i=T[1],s=c,a=f,h=P,(s.test(i)||a.test(i)&&!h.test(i))&&(n=i)}return s=O,a=c,s.test(n)&&a.test(n)&&(s=y,n=n.replace(s,"")),"y"==o&&(n=o.toLowerCase()+n.substr(1)),n};return T}(),t.Pipeline.registerFunction(t.stemmer,"stemmer"),t.generateStopWordFilter=function(t){var e=t.reduce(function(t,e){return t[e]=e,t},{});return function(t){return t&&e[t]!==t?t:void 0}},t.stopWordFilter=t.generateStopWordFilter(["a","able","about","across","after","all","almost","also","am","among","an","and","any","are","as","at","be","because","been","but","by","can","cannot","could","dear","did","do","does","either","else","ever","every","for","from","get","got","had","has","have","he","her","hers","him","his","how","however","i","if","in","into","is","it","its","just","least","let","like","likely","may","me","might","most","must","my","neither","no","nor","not","of","off","often","on","only","or","other","our","own","rather","said","say","says","she","should","since","so","some","than","that","the","their","them","then","there","these","they","this","tis","to","too","twas","us","wants","was","we","were","what","when","where","which","while","who","whom","why","will","with","would","yet","you","your"]),t.Pipeline.registerFunction(t.stopWordFilter,"stopWordFilter"),t.trimmer=function(t){return t.replace(/^\W+/,"").replace(/\W+$/,"")},t.Pipeline.registerFunction(t.trimmer,"trimmer"),t.TokenStore=function(){this.root={docs:{}},this.length=0},t.TokenStore.load=function(t){var e=new this;return e.root=t.root,e.length=t.length,e},t.TokenStore.prototype.add=function(t,e,n){var n=n||this.root,i=t.charAt(0),r=t.slice(1);return i in n||(n[i]={docs:{}}),0===r.length?(n[i].docs[e.ref]=e,void(this.length+=1)):this.add(r,e,n[i])},t.TokenStore.prototype.has=function(t){if(!t)return!1;for(var e=this.root,n=0;n":">",'"':""","'":"'","/":"/"};function escapeHtml(string){return String(string).replace(/[&<>"'\/]/g,function(s){return entityMap[s]})}var whiteRe=/\s*/;var spaceRe=/\s+/;var equalsRe=/\s*=/;var curlyRe=/\s*\}/;var tagRe=/#|\^|\/|>|\{|&|=|!/;function parseTemplate(template,tags){if(!template)return[];var sections=[];var tokens=[];var spaces=[];var hasTag=false;var nonSpace=false;function stripSpace(){if(hasTag&&!nonSpace){while(spaces.length)delete tokens[spaces.pop()]}else{spaces=[]}hasTag=false;nonSpace=false}var openingTagRe,closingTagRe,closingCurlyRe;function compileTags(tags){if(typeof tags==="string")tags=tags.split(spaceRe,2);if(!isArray(tags)||tags.length!==2)throw new Error("Invalid tags: "+tags);openingTagRe=new RegExp(escapeRegExp(tags[0])+"\\s*");closingTagRe=new RegExp("\\s*"+escapeRegExp(tags[1]));closingCurlyRe=new RegExp("\\s*"+escapeRegExp("}"+tags[1]))}compileTags(tags||mustache.tags);var scanner=new Scanner(template);var start,type,value,chr,token,openSection;while(!scanner.eos()){start=scanner.pos;value=scanner.scanUntil(openingTagRe);if(value){for(var i=0,valueLength=value.length;i0?sections[sections.length-1][4]:nestedTokens;break;default:collector.push(token)}}return nestedTokens}function Scanner(string){this.string=string;this.tail=string;this.pos=0}Scanner.prototype.eos=function(){return this.tail===""};Scanner.prototype.scan=function(re){var match=this.tail.match(re);if(!match||match.index!==0)return"";var string=match[0];this.tail=this.tail.substring(string.length);this.pos+=string.length;return string};Scanner.prototype.scanUntil=function(re){var index=this.tail.search(re),match;switch(index){case-1:match=this.tail;this.tail="";break;case 0:match="";break;default:match=this.tail.substring(0,index);this.tail=this.tail.substring(index)}this.pos+=match.length;return match};function Context(view,parentContext){this.view=view;this.cache={".":this.view};this.parent=parentContext}Context.prototype.push=function(view){return new Context(view,this)};Context.prototype.lookup=function(name){var cache=this.cache;var value;if(name in cache){value=cache[name]}else{var context=this,names,index,lookupHit=false;while(context){if(name.indexOf(".")>0){value=context.view;names=name.split(".");index=0;while(value!=null&&index")value=this._renderPartial(token,context,partials,originalTemplate);else if(symbol==="&")value=this._unescapedValue(token,context);else if(symbol==="name")value=this._escapedValue(token,context);else if(symbol==="text")value=this._rawValue(token);if(value!==undefined)buffer+=value}return buffer};Writer.prototype._renderSection=function(token,context,partials,originalTemplate){var self=this;var buffer="";var value=context.lookup(token[1]);function subRender(template){return self.render(template,context,partials)}if(!value)return;if(isArray(value)){for(var j=0,valueLength=value.length;jthis.depCount&&!this.defined){if(G(l)){if(this.events.error&&this.map.isDefine||g.onError!==ca)try{f=i.execCb(c,l,b,f)}catch(d){a=d}else f=i.execCb(c,l,b,f);this.map.isDefine&&void 0===f&&((b=this.module)?f=b.exports:this.usingExports&& 19 | (f=this.exports));if(a)return a.requireMap=this.map,a.requireModules=this.map.isDefine?[this.map.id]:null,a.requireType=this.map.isDefine?"define":"require",w(this.error=a)}else f=l;this.exports=f;if(this.map.isDefine&&!this.ignore&&(r[c]=f,g.onResourceLoad))g.onResourceLoad(i,this.map,this.depMaps);y(c);this.defined=!0}this.defining=!1;this.defined&&!this.defineEmitted&&(this.defineEmitted=!0,this.emit("defined",this.exports),this.defineEmitComplete=!0)}}else this.fetch()}},callPlugin:function(){var a= 20 | this.map,b=a.id,d=p(a.prefix);this.depMaps.push(d);q(d,"defined",u(this,function(f){var l,d;d=m(aa,this.map.id);var e=this.map.name,P=this.map.parentMap?this.map.parentMap.name:null,n=i.makeRequire(a.parentMap,{enableBuildCallback:!0});if(this.map.unnormalized){if(f.normalize&&(e=f.normalize(e,function(a){return c(a,P,!0)})||""),f=p(a.prefix+"!"+e,this.map.parentMap),q(f,"defined",u(this,function(a){this.init([],function(){return a},null,{enabled:!0,ignore:!0})})),d=m(h,f.id)){this.depMaps.push(f); 21 | if(this.events.error)d.on("error",u(this,function(a){this.emit("error",a)}));d.enable()}}else d?(this.map.url=i.nameToUrl(d),this.load()):(l=u(this,function(a){this.init([],function(){return a},null,{enabled:!0})}),l.error=u(this,function(a){this.inited=!0;this.error=a;a.requireModules=[b];B(h,function(a){0===a.map.id.indexOf(b+"_unnormalized")&&y(a.map.id)});w(a)}),l.fromText=u(this,function(f,c){var d=a.name,e=p(d),P=M;c&&(f=c);P&&(M=!1);s(e);t(j.config,b)&&(j.config[d]=j.config[b]);try{g.exec(f)}catch(h){return w(C("fromtexteval", 22 | "fromText eval for "+b+" failed: "+h,h,[b]))}P&&(M=!0);this.depMaps.push(e);i.completeLoad(d);n([d],l)}),f.load(a.name,n,l,j))}));i.enable(d,this);this.pluginMaps[d.id]=d},enable:function(){V[this.map.id]=this;this.enabling=this.enabled=!0;v(this.depMaps,u(this,function(a,b){var c,f;if("string"===typeof a){a=p(a,this.map.isDefine?this.map:this.map.parentMap,!1,!this.skipMap);this.depMaps[b]=a;if(c=m(L,a.id)){this.depExports[b]=c(this);return}this.depCount+=1;q(a,"defined",u(this,function(a){this.defineDep(b, 23 | a);this.check()}));this.errback?q(a,"error",u(this,this.errback)):this.events.error&&q(a,"error",u(this,function(a){this.emit("error",a)}))}c=a.id;f=h[c];!t(L,c)&&(f&&!f.enabled)&&i.enable(a,this)}));B(this.pluginMaps,u(this,function(a){var b=m(h,a.id);b&&!b.enabled&&i.enable(a,this)}));this.enabling=!1;this.check()},on:function(a,b){var c=this.events[a];c||(c=this.events[a]=[]);c.push(b)},emit:function(a,b){v(this.events[a],function(a){a(b)});"error"===a&&delete this.events[a]}};i={config:j,contextName:b, 24 | registry:h,defined:r,urlFetched:S,defQueue:A,Module:Z,makeModuleMap:p,nextTick:g.nextTick,onError:w,configure:function(a){a.baseUrl&&"/"!==a.baseUrl.charAt(a.baseUrl.length-1)&&(a.baseUrl+="/");var b=j.shim,c={paths:!0,bundles:!0,config:!0,map:!0};B(a,function(a,b){c[b]?(j[b]||(j[b]={}),U(j[b],a,!0,!0)):j[b]=a});a.bundles&&B(a.bundles,function(a,b){v(a,function(a){a!==b&&(aa[a]=b)})});a.shim&&(B(a.shim,function(a,c){H(a)&&(a={deps:a});if((a.exports||a.init)&&!a.exportsFn)a.exportsFn=i.makeShimExports(a); 25 | b[c]=a}),j.shim=b);a.packages&&v(a.packages,function(a){var b,a="string"===typeof a?{name:a}:a;b=a.name;a.location&&(j.paths[b]=a.location);j.pkgs[b]=a.name+"/"+(a.main||"main").replace(ia,"").replace(Q,"")});B(h,function(a,b){!a.inited&&!a.map.unnormalized&&(a.map=p(b))});if(a.deps||a.callback)i.require(a.deps||[],a.callback)},makeShimExports:function(a){return function(){var b;a.init&&(b=a.init.apply(ba,arguments));return b||a.exports&&da(a.exports)}},makeRequire:function(a,e){function j(c,d,m){var n, 26 | q;e.enableBuildCallback&&(d&&G(d))&&(d.__requireJsBuild=!0);if("string"===typeof c){if(G(d))return w(C("requireargs","Invalid require call"),m);if(a&&t(L,c))return L[c](h[a.id]);if(g.get)return g.get(i,c,a,j);n=p(c,a,!1,!0);n=n.id;return!t(r,n)?w(C("notloaded",'Module name "'+n+'" has not been loaded yet for context: '+b+(a?"":". Use require([])"))):r[n]}J();i.nextTick(function(){J();q=s(p(null,a));q.skipMap=e.skipMap;q.init(c,d,m,{enabled:!0});D()});return j}e=e||{};U(j,{isBrowser:z,toUrl:function(b){var d, 27 | e=b.lastIndexOf("."),k=b.split("/")[0];if(-1!==e&&(!("."===k||".."===k)||1e.attachEvent.toString().indexOf("[native code"))&& 34 | !Y?(M=!0,e.attachEvent("onreadystatechange",b.onScriptLoad)):(e.addEventListener("load",b.onScriptLoad,!1),e.addEventListener("error",b.onScriptError,!1)),e.src=d,J=e,D?y.insertBefore(e,D):y.appendChild(e),J=null,e;if(ea)try{importScripts(d),b.completeLoad(c)}catch(m){b.onError(C("importscripts","importScripts failed for "+c+" at "+d,m,[c]))}};z&&!q.skipDataMain&&T(document.getElementsByTagName("script"),function(b){y||(y=b.parentNode);if(I=b.getAttribute("data-main"))return s=I,q.baseUrl||(E=s.split("/"), 35 | s=E.pop(),O=E.length?E.join("/")+"/":"./",q.baseUrl=O),s=s.replace(Q,""),g.jsExtRegExp.test(s)&&(s=I),q.deps=q.deps?q.deps.concat(s):[s],!0});define=function(b,c,d){var e,g;"string"!==typeof b&&(d=c,c=b,b=null);H(c)||(d=c,c=null);!c&&G(d)&&(c=[],d.length&&(d.toString().replace(ka,"").replace(la,function(b,d){c.push(d)}),c=(1===d.length?["require"]:["require","exports","module"]).concat(c)));if(M){if(!(e=J))N&&"interactive"===N.readyState||T(document.getElementsByTagName("script"),function(b){if("interactive"=== 36 | b.readyState)return N=b}),e=N;e&&(b||(b=e.getAttribute("data-requiremodule")),g=F[e.getAttribute("data-requirecontext")])}(g?g.defQueue:R).push([b,c,d])};define.amd={jQuery:!0};g.exec=function(b){return eval(b)};g(q)}})(this); 37 | -------------------------------------------------------------------------------- /docs/mkdocs/js/search-results-template.mustache: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /docs/mkdocs/js/search.js: -------------------------------------------------------------------------------- 1 | require([ 2 | base_url + '/mkdocs/js/mustache.min.js', 3 | base_url + '/mkdocs/js/lunr.min.js', 4 | 'text!search-results-template.mustache', 5 | 'text!../search_index.json', 6 | ], function (Mustache, lunr, results_template, data) { 7 | "use strict"; 8 | 9 | function getSearchTerm() 10 | { 11 | var sPageURL = window.location.search.substring(1); 12 | var sURLVariables = sPageURL.split('&'); 13 | for (var i = 0; i < sURLVariables.length; i++) 14 | { 15 | var sParameterName = sURLVariables[i].split('='); 16 | if (sParameterName[0] == 'q') 17 | { 18 | return decodeURIComponent(sParameterName[1].replace(/\+/g, '%20')); 19 | } 20 | } 21 | } 22 | 23 | var index = lunr(function () { 24 | this.field('title', {boost: 10}); 25 | this.field('text'); 26 | this.ref('location'); 27 | }); 28 | 29 | data = JSON.parse(data); 30 | var documents = {}; 31 | 32 | for (var i=0; i < data.docs.length; i++){ 33 | var doc = data.docs[i]; 34 | doc.location = base_url + doc.location; 35 | index.add(doc); 36 | documents[doc.location] = doc; 37 | } 38 | 39 | var search = function(){ 40 | 41 | var query = document.getElementById('mkdocs-search-query').value; 42 | var search_results = document.getElementById("mkdocs-search-results"); 43 | while (search_results.firstChild) { 44 | search_results.removeChild(search_results.firstChild); 45 | } 46 | 47 | if(query === ''){ 48 | return; 49 | } 50 | 51 | var results = index.search(query); 52 | 53 | if (results.length > 0){ 54 | for (var i=0; i < results.length; i++){ 55 | var result = results[i]; 56 | doc = documents[result.ref]; 57 | doc.base_url = base_url; 58 | doc.summary = doc.text.substring(0, 200); 59 | var html = Mustache.to_html(results_template, doc); 60 | search_results.insertAdjacentHTML('beforeend', html); 61 | } 62 | } else { 63 | search_results.insertAdjacentHTML('beforeend', "

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/mkdocs/js/text.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @license RequireJS text 2.0.12 Copyright (c) 2010-2014, The Dojo Foundation All Rights Reserved. 3 | * Available via the MIT or new BSD license. 4 | * see: http://github.com/requirejs/text for details 5 | */ 6 | /*jslint regexp: true */ 7 | /*global require, XMLHttpRequest, ActiveXObject, 8 | define, window, process, Packages, 9 | java, location, Components, FileUtils */ 10 | 11 | define(['module'], function (module) { 12 | 'use strict'; 13 | 14 | var text, fs, Cc, Ci, xpcIsWindows, 15 | progIds = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP', 'Msxml2.XMLHTTP.4.0'], 16 | xmlRegExp = /^\s*<\?xml(\s)+version=[\'\"](\d)*.(\d)*[\'\"](\s)*\?>/im, 17 | bodyRegExp = /]*>\s*([\s\S]+)\s*<\/body>/im, 18 | hasLocation = typeof location !== 'undefined' && location.href, 19 | defaultProtocol = hasLocation && location.protocol && location.protocol.replace(/\:/, ''), 20 | defaultHostName = hasLocation && location.hostname, 21 | defaultPort = hasLocation && (location.port || undefined), 22 | buildMap = {}, 23 | masterConfig = (module.config && module.config()) || {}; 24 | 25 | text = { 26 | version: '2.0.12', 27 | 28 | strip: function (content) { 29 | //Strips declarations so that external SVG and XML 30 | //documents can be added to a document without worry. Also, if the string 31 | //is an HTML document, only the part inside the body tag is returned. 32 | if (content) { 33 | content = content.replace(xmlRegExp, ""); 34 | var matches = content.match(bodyRegExp); 35 | if (matches) { 36 | content = matches[1]; 37 | } 38 | } else { 39 | content = ""; 40 | } 41 | return content; 42 | }, 43 | 44 | jsEscape: function (content) { 45 | return content.replace(/(['\\])/g, '\\$1') 46 | .replace(/[\f]/g, "\\f") 47 | .replace(/[\b]/g, "\\b") 48 | .replace(/[\n]/g, "\\n") 49 | .replace(/[\t]/g, "\\t") 50 | .replace(/[\r]/g, "\\r") 51 | .replace(/[\u2028]/g, "\\u2028") 52 | .replace(/[\u2029]/g, "\\u2029"); 53 | }, 54 | 55 | createXhr: masterConfig.createXhr || function () { 56 | //Would love to dump the ActiveX crap in here. Need IE 6 to die first. 57 | var xhr, i, progId; 58 | if (typeof XMLHttpRequest !== "undefined") { 59 | return new XMLHttpRequest(); 60 | } else if (typeof ActiveXObject !== "undefined") { 61 | for (i = 0; i < 3; i += 1) { 62 | progId = progIds[i]; 63 | try { 64 | xhr = new ActiveXObject(progId); 65 | } catch (e) {} 66 | 67 | if (xhr) { 68 | progIds = [progId]; // so faster next time 69 | break; 70 | } 71 | } 72 | } 73 | 74 | return xhr; 75 | }, 76 | 77 | /** 78 | * Parses a resource name into its component parts. Resource names 79 | * look like: module/name.ext!strip, where the !strip part is 80 | * optional. 81 | * @param {String} name the resource name 82 | * @returns {Object} with properties "moduleName", "ext" and "strip" 83 | * where strip is a boolean. 84 | */ 85 | parseName: function (name) { 86 | var modName, ext, temp, 87 | strip = false, 88 | index = name.indexOf("."), 89 | isRelative = name.indexOf('./') === 0 || 90 | name.indexOf('../') === 0; 91 | 92 | if (index !== -1 && (!isRelative || index > 1)) { 93 | modName = name.substring(0, index); 94 | ext = name.substring(index + 1, name.length); 95 | } else { 96 | modName = name; 97 | } 98 | 99 | temp = ext || modName; 100 | index = temp.indexOf("!"); 101 | if (index !== -1) { 102 | //Pull off the strip arg. 103 | strip = temp.substring(index + 1) === "strip"; 104 | temp = temp.substring(0, index); 105 | if (ext) { 106 | ext = temp; 107 | } else { 108 | modName = temp; 109 | } 110 | } 111 | 112 | return { 113 | moduleName: modName, 114 | ext: ext, 115 | strip: strip 116 | }; 117 | }, 118 | 119 | xdRegExp: /^((\w+)\:)?\/\/([^\/\\]+)/, 120 | 121 | /** 122 | * Is an URL on another domain. Only works for browser use, returns 123 | * false in non-browser environments. Only used to know if an 124 | * optimized .js version of a text resource should be loaded 125 | * instead. 126 | * @param {String} url 127 | * @returns Boolean 128 | */ 129 | useXhr: function (url, protocol, hostname, port) { 130 | var uProtocol, uHostName, uPort, 131 | match = text.xdRegExp.exec(url); 132 | if (!match) { 133 | return true; 134 | } 135 | uProtocol = match[2]; 136 | uHostName = match[3]; 137 | 138 | uHostName = uHostName.split(':'); 139 | uPort = uHostName[1]; 140 | uHostName = uHostName[0]; 141 | 142 | return (!uProtocol || uProtocol === protocol) && 143 | (!uHostName || uHostName.toLowerCase() === hostname.toLowerCase()) && 144 | ((!uPort && !uHostName) || uPort === port); 145 | }, 146 | 147 | finishLoad: function (name, strip, content, onLoad) { 148 | content = strip ? text.strip(content) : content; 149 | if (masterConfig.isBuild) { 150 | buildMap[name] = content; 151 | } 152 | onLoad(content); 153 | }, 154 | 155 | load: function (name, req, onLoad, config) { 156 | //Name has format: some.module.filext!strip 157 | //The strip part is optional. 158 | //if strip is present, then that means only get the string contents 159 | //inside a body tag in an HTML string. For XML/SVG content it means 160 | //removing the declarations so the content can be inserted 161 | //into the current doc without problems. 162 | 163 | // Do not bother with the work if a build and text will 164 | // not be inlined. 165 | if (config && config.isBuild && !config.inlineText) { 166 | onLoad(); 167 | return; 168 | } 169 | 170 | masterConfig.isBuild = config && config.isBuild; 171 | 172 | var parsed = text.parseName(name), 173 | nonStripName = parsed.moduleName + 174 | (parsed.ext ? '.' + parsed.ext : ''), 175 | url = req.toUrl(nonStripName), 176 | useXhr = (masterConfig.useXhr) || 177 | text.useXhr; 178 | 179 | // Do not load if it is an empty: url 180 | if (url.indexOf('empty:') === 0) { 181 | onLoad(); 182 | return; 183 | } 184 | 185 | //Load the text. Use XHR if possible and in a browser. 186 | if (!hasLocation || useXhr(url, defaultProtocol, defaultHostName, defaultPort)) { 187 | text.get(url, function (content) { 188 | text.finishLoad(name, parsed.strip, content, onLoad); 189 | }, function (err) { 190 | if (onLoad.error) { 191 | onLoad.error(err); 192 | } 193 | }); 194 | } else { 195 | //Need to fetch the resource across domains. Assume 196 | //the resource has been optimized into a JS module. Fetch 197 | //by the module name + extension, but do not include the 198 | //!strip part to avoid file system issues. 199 | req([nonStripName], function (content) { 200 | text.finishLoad(parsed.moduleName + '.' + parsed.ext, 201 | parsed.strip, content, onLoad); 202 | }); 203 | } 204 | }, 205 | 206 | write: function (pluginName, moduleName, write, config) { 207 | if (buildMap.hasOwnProperty(moduleName)) { 208 | var content = text.jsEscape(buildMap[moduleName]); 209 | write.asModule(pluginName + "!" + moduleName, 210 | "define(function () { return '" + 211 | content + 212 | "';});\n"); 213 | } 214 | }, 215 | 216 | writeFile: function (pluginName, moduleName, req, write, config) { 217 | var parsed = text.parseName(moduleName), 218 | extPart = parsed.ext ? '.' + parsed.ext : '', 219 | nonStripName = parsed.moduleName + extPart, 220 | //Use a '.js' file name so that it indicates it is a 221 | //script that can be loaded across domains. 222 | fileName = req.toUrl(parsed.moduleName + extPart) + '.js'; 223 | 224 | //Leverage own load() method to load plugin value, but only 225 | //write out values that do not have the strip argument, 226 | //to avoid any potential issues with ! in file names. 227 | text.load(nonStripName, req, function (value) { 228 | //Use own write() method to construct full module value. 229 | //But need to create shell that translates writeFile's 230 | //write() to the right interface. 231 | var textWrite = function (contents) { 232 | return write(fileName, contents); 233 | }; 234 | textWrite.asModule = function (moduleName, contents) { 235 | return write.asModule(moduleName, fileName, contents); 236 | }; 237 | 238 | text.write(pluginName, nonStripName, textWrite, config); 239 | }, config); 240 | } 241 | }; 242 | 243 | if (masterConfig.env === 'node' || (!masterConfig.env && 244 | typeof process !== "undefined" && 245 | process.versions && 246 | !!process.versions.node && 247 | !process.versions['node-webkit'])) { 248 | //Using special require.nodeRequire, something added by r.js. 249 | fs = require.nodeRequire('fs'); 250 | 251 | text.get = function (url, callback, errback) { 252 | try { 253 | var file = fs.readFileSync(url, 'utf8'); 254 | //Remove BOM (Byte Mark Order) from utf8 files if it is there. 255 | if (file.indexOf('\uFEFF') === 0) { 256 | file = file.substring(1); 257 | } 258 | callback(file); 259 | } catch (e) { 260 | if (errback) { 261 | errback(e); 262 | } 263 | } 264 | }; 265 | } else if (masterConfig.env === 'xhr' || (!masterConfig.env && 266 | text.createXhr())) { 267 | text.get = function (url, callback, errback, headers) { 268 | var xhr = text.createXhr(), header; 269 | xhr.open('GET', url, true); 270 | 271 | //Allow plugins direct access to xhr headers 272 | if (headers) { 273 | for (header in headers) { 274 | if (headers.hasOwnProperty(header)) { 275 | xhr.setRequestHeader(header.toLowerCase(), headers[header]); 276 | } 277 | } 278 | } 279 | 280 | //Allow overrides specified in config 281 | if (masterConfig.onXhr) { 282 | masterConfig.onXhr(xhr, url); 283 | } 284 | 285 | xhr.onreadystatechange = function (evt) { 286 | var status, err; 287 | //Do not explicitly handle errors, those should be 288 | //visible via console output in the browser. 289 | if (xhr.readyState === 4) { 290 | status = xhr.status || 0; 291 | if (status > 399 && status < 600) { 292 | //An http 4xx or 5xx error. Signal an error. 293 | err = new Error(url + ' HTTP status: ' + status); 294 | err.xhr = xhr; 295 | if (errback) { 296 | errback(err); 297 | } 298 | } else { 299 | callback(xhr.responseText); 300 | } 301 | 302 | if (masterConfig.onXhrComplete) { 303 | masterConfig.onXhrComplete(xhr, url); 304 | } 305 | } 306 | }; 307 | xhr.send(null); 308 | }; 309 | } else if (masterConfig.env === 'rhino' || (!masterConfig.env && 310 | typeof Packages !== 'undefined' && typeof java !== 'undefined')) { 311 | //Why Java, why is this so awkward? 312 | text.get = function (url, callback) { 313 | var stringBuffer, line, 314 | encoding = "utf-8", 315 | file = new java.io.File(url), 316 | lineSeparator = java.lang.System.getProperty("line.separator"), 317 | input = new java.io.BufferedReader(new java.io.InputStreamReader(new java.io.FileInputStream(file), encoding)), 318 | content = ''; 319 | try { 320 | stringBuffer = new java.lang.StringBuffer(); 321 | line = input.readLine(); 322 | 323 | // Byte Order Mark (BOM) - The Unicode Standard, version 3.0, page 324 324 | // http://www.unicode.org/faq/utf_bom.html 325 | 326 | // Note that when we use utf-8, the BOM should appear as "EF BB BF", but it doesn't due to this bug in the JDK: 327 | // http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4508058 328 | if (line && line.length() && line.charAt(0) === 0xfeff) { 329 | // Eat the BOM, since we've already found the encoding on this file, 330 | // and we plan to concatenating this buffer with others; the BOM should 331 | // only appear at the top of a file. 332 | line = line.substring(1); 333 | } 334 | 335 | if (line !== null) { 336 | stringBuffer.append(line); 337 | } 338 | 339 | while ((line = input.readLine()) !== null) { 340 | stringBuffer.append(lineSeparator); 341 | stringBuffer.append(line); 342 | } 343 | //Make sure we return a JavaScript string and not a Java string. 344 | content = String(stringBuffer.toString()); //String 345 | } finally { 346 | input.close(); 347 | } 348 | callback(content); 349 | }; 350 | } else if (masterConfig.env === 'xpconnect' || (!masterConfig.env && 351 | typeof Components !== 'undefined' && Components.classes && 352 | Components.interfaces)) { 353 | //Avert your gaze! 354 | Cc = Components.classes; 355 | Ci = Components.interfaces; 356 | Components.utils['import']('resource://gre/modules/FileUtils.jsm'); 357 | xpcIsWindows = ('@mozilla.org/windows-registry-key;1' in Cc); 358 | 359 | text.get = function (url, callback) { 360 | var inStream, convertStream, fileObj, 361 | readData = {}; 362 | 363 | if (xpcIsWindows) { 364 | url = url.replace(/\//g, '\\'); 365 | } 366 | 367 | fileObj = new FileUtils.File(url); 368 | 369 | //XPCOM, you so crazy 370 | try { 371 | inStream = Cc['@mozilla.org/network/file-input-stream;1'] 372 | .createInstance(Ci.nsIFileInputStream); 373 | inStream.init(fileObj, 1, 0, false); 374 | 375 | convertStream = Cc['@mozilla.org/intl/converter-input-stream;1'] 376 | .createInstance(Ci.nsIConverterInputStream); 377 | convertStream.init(inStream, "utf-8", inStream.available(), 378 | Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); 379 | 380 | convertStream.readString(inStream.available(), readData); 381 | convertStream.close(); 382 | inStream.close(); 383 | callback(readData.value); 384 | } catch (e) { 385 | throw new Error((fileObj && fileObj.path || '') + ': ' + e); 386 | } 387 | }; 388 | } 389 | return text; 390 | }); 391 | -------------------------------------------------------------------------------- /docs/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Impulse-ML: Recommender, the recommender engine 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 34 | 35 | 36 | 37 | 38 | 39 |
40 | 41 | 42 | 105 | 106 |
107 | 108 | 109 | 113 | 114 | 115 |
116 |
117 |
118 |
    119 |
  • Docs »
  • 120 | 121 | 122 |
  • 123 | 124 |
  • 125 |
126 |
127 |
128 |
129 |
130 | 131 | 132 |

Search Results

133 | 134 | 138 | 139 |
140 | Searching... 141 |
142 | 143 | 144 |
145 |
146 |
147 | 148 | 149 |
150 | 151 |
152 | 153 | 154 |

Impulse-ML

155 | 156 |
157 | 158 | Built with MkDocs using a theme provided by Read the Docs. 159 |
160 | 161 |
162 |
163 | 164 |
165 | 166 |
167 | 168 |
169 | 170 | 171 | 172 | 173 | 174 |
175 | 176 | 177 | 178 | 179 | -------------------------------------------------------------------------------- /docs/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | / 7 | 2017-07-20 8 | daily 9 | 10 | 11 | 12 | 13 | 14 | /1_problem_motivation/ 15 | 2017-07-20 16 | daily 17 | 18 | 19 | 20 | 21 | 22 | /2_api_dataset/ 23 | 2017-07-20 24 | daily 25 | 26 | 27 | 28 | 29 | 30 | /3_api_learning_model/ 31 | 2017-07-20 32 | daily 33 | 34 | 35 | 36 | 37 | 38 | /4_api_training_the_model/ 39 | 2017-07-20 40 | daily 41 | 42 | 43 | 44 | 45 | 46 | /5_api_predicting_the_results/ 47 | 2017-07-20 48 | daily 49 | 50 | 51 | 52 | 53 | 54 | /6_api_full_example/ 55 | 2017-07-20 56 | daily 57 | 58 | 59 | 60 | 61 | 62 | /7_examples_of_training/ 63 | 2017-07-20 64 | daily 65 | 66 | 67 | 68 | 69 | 70 | /8_api_saving_and_restoring_trained_model/ 71 | 2017-07-20 72 | daily 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /examples/1_sample_output.txt: -------------------------------------------------------------------------------- 1 | Starting train with 20000 steps. 2 | Step 0 with error 45.480538085596 3 | Step 1000 with error 0.14185749105855 4 | Step 2000 with error 0.00012367547481656 5 | Step 3000 with error 3.619659641736E-6 6 | Step 4000 with error 1.2231304078042E-7 7 | Step 5000 with error 5.7888427762046E-9 8 | Step 6000 with error 4.3159398580704E-10 9 | Step 7000 with error 4.2963851024733E-11 10 | Step 8000 with error 4.7458579962238E-12 11 | Step 9000 with error 5.3950928017295E-13 12 | Step 10000 with error 6.1779842297497E-14 13 | Step 11000 with error 7.0872758334659E-15 14 | Step 12000 with error 8.1340228680908E-16 15 | Step 13000 with error 9.3363924793255E-17 16 | Step 14000 with error 1.0716785358187E-17 17 | Step 15000 with error 1.2301363738974E-18 18 | Step 16000 with error 1.4120237226256E-19 19 | Step 17000 with error 1.6208264249521E-20 20 | Step 18000 with error 1.8605120483173E-21 21 | Step 19000 with error 2.1359434707995E-22 22 | Training ended with error 2.4586853842564E-23 after 20000 steps. 23 | Prediction for 'Guardians of the Galaxy' for user 'Barbara': 1.3472778448431E-11 24 | Prediction for 'Guardians of the Galaxy' for user 'Charlie': 4.9999999999974 25 | Prediction for 'Logan' for user 'Anna': 1.3994139180795E-11 26 | Prediction for 'Logan' for user 'Dave': 3.9999999999967 27 | Prediction for 'Logan' for user with has not rated any movie: 2 28 | Related movies dump: 29 | array(4) { 30 | [0]=> 31 | array(2) { 32 | ["similarity"]=> 33 | float(1.1086798146209E-11) 34 | ["model"]=> 35 | array(2) { 36 | ["_id"]=> 37 | string(23) "Guardians of the Galaxy" 38 | ["data"]=> 39 | NULL 40 | } 41 | } 42 | [1]=> 43 | array(2) { 44 | ["similarity"]=> 45 | float(0.17881301819823) 46 | ["model"]=> 47 | array(2) { 48 | ["_id"]=> 49 | string(5) "Logan" 50 | ["data"]=> 51 | NULL 52 | } 53 | } 54 | [2]=> 55 | array(2) { 56 | ["similarity"]=> 57 | float(0.92344428953759) 58 | ["model"]=> 59 | array(2) { 60 | ["_id"]=> 61 | string(12) "Forrest Gump" 62 | ["data"]=> 63 | NULL 64 | } 65 | } 66 | [3]=> 67 | array(2) { 68 | ["similarity"]=> 69 | float(1.7881301818354) 70 | ["model"]=> 71 | array(2) { 72 | ["_id"]=> 73 | string(7) "The Kid" 74 | ["data"]=> 75 | NULL 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /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 | ])); -------------------------------------------------------------------------------- /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'); -------------------------------------------------------------------------------- /examples/3_load_and_predict.php: -------------------------------------------------------------------------------- 1 | predict('Guardians of the Galaxy', 'Barbara')}\n"; 11 | echo "Prediction for 'Guardians of the Galaxy' for user 'Charlie': {$model->predict('Guardians of the Galaxy', 'Charlie')}\n"; 12 | echo "Prediction for 'Logan' for user 'Anna': {$model->predict('Logan', 'Anna')}\n"; 13 | echo "Prediction for 'Logan' for user 'Dave': {$model->predict('Logan', 'Dave')}\n"; 14 | 15 | echo "Prediction for 'Logan' for user with has not rated any movie: {$model->predict('Logan')}\n"; 16 | 17 | echo "Related movies dump:\n"; 18 | 19 | var_dump($model->findRelated('The Dark Knight', [ 20 | 'limit' => 5 21 | ])); -------------------------------------------------------------------------------- /img/training_example_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/img/training_example_1.png -------------------------------------------------------------------------------- /img/training_example_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/img/training_example_2.png -------------------------------------------------------------------------------- /img/training_example_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/img/training_example_3.png -------------------------------------------------------------------------------- /img/training_example_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/impulse-ml/impulse-ml-recommender-php-documentation/eecc4f68f41ff3022d09437e40fb06686e249ef2/img/training_example_4.png --------------------------------------------------------------------------------