diff --git a/.github/workflows/azureml-cpu-nightly.yml b/.github/workflows/azureml-cpu-nightly.yml index 34869f2c14..0f7d54041b 100644 --- a/.github/workflows/azureml-cpu-nightly.yml +++ b/.github/workflows/azureml-cpu-nightly.yml @@ -14,7 +14,7 @@ on: # │ │ │ │ │ # │ │ │ │ │ schedule: - - cron: '0 0 */2 * *' # basically running every other day at 12AM + - cron: '0 0 */5 * *' # running every 5 days at 12AM # cron works with default branch (main) only: # https://github.community/t/on-schedule-per-branch/17525/2 push: diff --git a/.github/workflows/azureml-gpu-nightly.yml b/.github/workflows/azureml-gpu-nightly.yml index 0e39eb1dfe..df4ab17109 100644 --- a/.github/workflows/azureml-gpu-nightly.yml +++ b/.github/workflows/azureml-gpu-nightly.yml @@ -14,7 +14,7 @@ on: # │ │ │ │ │ # │ │ │ │ │ schedule: - - cron: '0 0 */2 * *' # basically running every other day at 12AM + - cron: '0 0 */5 * *' # running every 5 days at 12AM # cron works with default branch (main) only: # https://github.community/t/on-schedule-per-branch/17525/2 push: diff --git a/.github/workflows/azureml-spark-nightly.yml b/.github/workflows/azureml-spark-nightly.yml index 99675b5993..7069731224 100644 --- a/.github/workflows/azureml-spark-nightly.yml +++ b/.github/workflows/azureml-spark-nightly.yml @@ -14,7 +14,7 @@ on: # │ │ │ │ │ # │ │ │ │ │ schedule: - - cron: '0 0 */2 * *' # basically running every other day at 12AM + - cron: '0 0 */5 * *' # running every 5 days at 12AM # cron works with default branch (main) only: # https://github.community/t/on-schedule-per-branch/17525/2 push: diff --git a/examples/03_evaluate/evaluation.ipynb b/examples/03_evaluate/evaluation.ipynb index 7f8feb6b59..e67b0691c6 100644 --- a/examples/03_evaluate/evaluation.ipynb +++ b/examples/03_evaluate/evaluation.ipynb @@ -58,7 +58,6 @@ "source": [ "# set the environment path to find Recommenders\n", "import sys\n", - "import pandas as pd\n", "import pyspark\n", "from sklearn.preprocessing import minmax_scale\n", @@ -387,7 +386,7 @@ "* **the recommender is to predict ranking instead of explicit rating**. For example, if the consumer of the recommender cares about the ranked recommended items, rating metrics do not apply directly. Usually a relevancy function such as top-k will be applied to generate the ranked list from predicted ratings in order to evaluate the recommender with other metrics. \n", "* **the recommender is to generate recommendation scores that have different scales with the original ratings (e.g., the SAR algorithm)**. In this case, the difference between the generated scores and the original scores (or, ratings) is not valid for measuring accuracy of the model.\n", "\n", - "#### 2.1.2 How-to with the evaluation utilities\n", + "#### 2.1.2 How to work with the evaluation utilities\n", "\n", "A few notes about the interface of the Rating evaluator class:\n", "1. The columns of user, item, and rating (prediction) should be present in the ground-truth DataFrame (prediction DataFrame).\n", @@ -539,7 +538,7 @@ "source": [ "|Metric|Range|Selection criteria|Limitation|Reference|\n", "|------|-------------------------------|---------|----------|---------|\n", - "|RMSE|$> 0$|The smaller the better.|May be biased, and less explainable than MSE|[link](https://en.wikipedia.org/wiki/Root-mean-square_deviation)|\n", + "|RMSE|$> 0$|The smaller the better.|May be biased, and less explainable than MAE|[link](https://en.wikipedia.org/wiki/Root-mean-square_deviation)|\n", "|R2|$\\leq 1$|The closer to $1$ the better.|Depend on variable distributions.|[link](https://en.wikipedia.org/wiki/Coefficient_of_determination)|\n", "|MAE|$\\geq 0$|The smaller the better.|Dependent on variable scale.|[link](https://en.wikipedia.org/wiki/Mean_absolute_error)|\n", "|Explained variance|$\\leq 1$|The closer to $1$ the better.|Depend on variable distributions.|[link](https://en.wikipedia.org/wiki/Explained_variation)|" @@ -556,7 +555,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "\"Beyond-accuray evaluation\" was proposed to evaluate how relevant recommendations are for users. In this case, a recommendation system is a treated as a ranking system. Given a relency definition, recommendation system outputs a list of recommended items to each user, which is ordered by relevance. The evaluation part takes ground-truth data, the actual items that users interact with (e.g., liked, purchased, etc.), and the recommendation data, as inputs, to calculate ranking evaluation metrics. \n", + "\"Beyond-accuray evaluation\" was proposed to evaluate how relevant recommendations are for users. In this case, a recommendation system is a treated as a ranking system. Given relency definition, recommendation system outputs a list of recommended items to each user, which is ordered by relevance. The evaluation part takes ground-truth data, the actual items that users interact with (e.g., liked, purchased, etc.), and the recommendation data, as inputs, to calculate ranking evaluation metrics. \n", "\n", "#### 2.2.1 Use cases\n", "\n", @@ -576,7 +575,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.2.1 Relevancy of recommendation\n", + "#### 2.2.3 Relevancy of recommendation\n", "\n", "Relevancy of recommendation can be measured in different ways:\n", "\n", @@ -641,7 +640,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.2.1 Precision\n", + "#### 2.2.4 Precision\n", "\n", "Precision@k is a metric that evaluates how many items in the recommendation list are relevant (hit) in the ground-truth data. For each user the precision score is normalized by `k` and then the overall precision scores are averaged by the total number of users. \n", "\n", @@ -669,7 +668,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.2.2 Recall\n", + "#### 2.2.5 Recall\n", "\n", "Recall@k is a metric that evaluates how many relevant items in the ground-truth data are in the recommendation list. For each user the recall score is normalized by the total number of ground-truth items and then the overall recall scores are averaged by the total number of users. " ] @@ -695,7 +694,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.2.3 Normalized Discounted Cumulative Gain (NDCG)\n", + "#### 2.2.6 Normalized Discounted Cumulative Gain (NDCG)\n", "\n", "NDCG is a metric that evaluates how well the recommender performs in recommending ranked items to users. Therefore both hit of relevant items and correctness in ranking of these items matter to the NDCG evaluation. The total NDCG score is normalized by the total number of users." ] @@ -721,7 +720,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.2.4 Mean Average Precision (MAP)\n", + "#### 2.2.7 Mean Average Precision (MAP)\n", "\n", "MAP is a metric that evaluates the average precision for each user in the datasets. It also penalizes ranking correctness of the recommended items. The overall MAP score is normalized by the total number of users." ] @@ -747,7 +746,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.2.5 ROC and AUC\n", + "#### 2.2.8 ROC and AUC\n", "\n", "ROC, as well as AUC, is a well known metric that is used for evaluating binary classification problem. It is similar in the case of binary rating typed recommendation algorithm where the \"hit\" accuracy on the relevant items is used for measuring the recommender's performance. \n", "\n", @@ -1891,7 +1890,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "#### 2.2.5 Summary" + "#### 2.3 Summary" ] }, { diff --git a/examples/06_benchmarks/README.md b/examples/06_benchmarks/README.md index 26ac91e475..2f8320a654 100644 --- a/examples/06_benchmarks/README.md +++ b/examples/06_benchmarks/README.md @@ -2,8 +2,6 @@ In this folder we show benchmarks using different algorithms. To facilitate the benchmark computation, we provide a set of wrapper functions that can be found in the file [benchmark_utils.py](benchmark_utils.py). -The machine we used to perform the benchmarks is a Standard NC6s_v2 [Azure DSVM](https://azure.microsoft.com/en-us/services/virtual-machines/data-science-virtual-machines/) (6 vCPUs, 112 GB memory and 1 P100 GPU). Spark ALS is run in local standalone mode. - ## MovieLens [MovieLens](https://grouplens.org/datasets/movielens/) is one of the most common datasets used in the literature in Recommendation Systems. The dataset consists of a collection of users, movies and movie ratings, there are several available sizes: @@ -13,6 +11,4 @@ The machine we used to perform the benchmarks is a Standard NC6s_v2 [Azure DSVM] * MovieLens 10M: 10 million ratings from 72000 users on 10000 movies. * MovieLens 20M: 20 million ratings from 138000 users on 27000 movies -The MovieLens benchmark can be seen at [movielens.ipynb](movielens.ipynb). In this notebook, the MovieLens dataset is split into training / test sets using a stratified splitting method that takes 75% of each user's ratings as training data, and the remaining 25% ratings as test data. For ranking metrics we use `k=10` (top 10 recommended items). The algorithms used in this benchmark are [ALS](../00_quick_start/als_movielens.ipynb), [SVD](../02_model_collaborative_filtering/surprise_svd_deep_dive.ipynb), [SAR](../00_quick_start/sar_movielens.ipynb), [NCF](../00_quick_start/ncf_movielens.ipynb), [BPR](../02_model_collaborative_filtering/cornac_bpr_deep_dive.ipynb), [BiVAE](../02_model_collaborative_filtering/cornac_bivae_deep_dive.ipynb), [LightGCN](../02_model_collaborative_filtering/lightgcn_deep_dive.ipynb) and [FastAI](../00_quick_start/fastai_movielens.ipynb). - - +The MovieLens benchmark can be seen at [movielens.ipynb](movielens.ipynb). This illustrative comparison applies to collaborative filtering algorithms available in this repository such as [Spark ALS](../00_quick_start/als_movielens.ipynb), [SVD](../02_model_collaborative_filtering/surprise_svd_deep_dive.ipynb), [SAR](../00_quick_start/sar_movielens.ipynb), [LightGCN](../02_model_collaborative_filtering/lightgcn_deep_dive.ipynb) and others using the Movielens dataset, using three environments (CPU, GPU and Spark). These algorithms are usable in a variety of recommendation tasks, including product or news recommendations. diff --git a/examples/06_benchmarks/benchmark_utils.py b/examples/06_benchmarks/benchmark_utils.py index b79dbb3b68..561eea4d40 100644 --- a/examples/06_benchmarks/benchmark_utils.py +++ b/examples/06_benchmarks/benchmark_utils.py @@ -1,5 +1,10 @@ -import pandas as pd +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import os import numpy as np +import pandas as pd +from tempfile import TemporaryDirectory from pyspark.ml.recommendation import ALS from pyspark.sql.types import StructType, StructField from pyspark.sql.types import FloatType, IntegerType, LongType @@ -47,6 +52,12 @@ from recommenders.evaluation.python_evaluation import rmse, mae, rsquared, exp_var +# Helpers +tmp_dir = TemporaryDirectory() +TRAIN_FILE = os.path.join(tmp_dir.name, "df_train.csv") +TEST_FILE = os.path.join(tmp_dir.name, "df_test.csv") + + def prepare_training_als(train, test): schema = StructType( ( @@ -57,7 +68,7 @@ def prepare_training_als(train, test): ) ) spark = start_or_get_spark() - return spark.createDataFrame(train, schema) + return spark.createDataFrame(train, schema).cache() def train_als(params, data): @@ -77,7 +88,7 @@ def prepare_metrics_als(train, test): ) ) spark = start_or_get_spark() - return spark.createDataFrame(train, schema), spark.createDataFrame(test, schema) + return spark.createDataFrame(train, schema).cache(), spark.createDataFrame(test, schema).cache() def predict_als(model, test): @@ -223,13 +234,20 @@ def recommend_k_fastai(model, test, train, top_k=DEFAULT_K, remove_seen=True): return topk_scores, t -def prepare_training_ncf(train, test): +def prepare_training_ncf(df_train, df_test): + #df_train.sort_values(["userID"], axis=0, ascending=[True], inplace=True) + #df_test.sort_values(["userID"], axis=0, ascending=[True], inplace=True) + train = df_train.sort_values(["userID"], axis=0, ascending=[True]) + test = df_test.sort_values(["userID"], axis=0, ascending=[True]) + test = test[df_test["userID"].isin(train["userID"].unique())] + test = test[test["itemID"].isin(train["itemID"].unique())] + train.to_csv(TRAIN_FILE, index=False) + test.to_csv(TEST_FILE, index=False) return NCFDataset( - train=train, + train_file=TRAIN_FILE, col_user=DEFAULT_USER_COL, col_item=DEFAULT_ITEM_COL, col_rating=DEFAULT_RATING_COL, - col_timestamp=DEFAULT_TIMESTAMP_COL, seed=SEED, ) @@ -263,6 +281,7 @@ def recommend_k_ncf(model, test, train, top_k=DEFAULT_K, remove_seen=True): topk_scores = merged[merged[DEFAULT_RATING_COL].isnull()].drop( DEFAULT_RATING_COL, axis=1 ) + # Remove temp files return topk_scores, t diff --git a/examples/06_benchmarks/movielens.ipynb b/examples/06_benchmarks/movielens.ipynb index 6abd934bff..28dc5ceb1c 100644 --- a/examples/06_benchmarks/movielens.ipynb +++ b/examples/06_benchmarks/movielens.ipynb @@ -26,7 +26,7 @@ "\n", "* Environment\n", " * The comparison is run on a [Azure Data Science Virtual Machine](https://azure.microsoft.com/en-us/services/virtual-machines/data-science-virtual-machines/). \n", - " * The virtual machine size is Standard NC6 (6 vcpus, 55 GB memory, 1K80 GPU).\n", + " * The virtual machine size is a [Standard_NC6s_v2](https://learn.microsoft.com/es-es/azure/virtual-machines/ncv2-series) with 6 CPUs, 112Gb of RAM, and 1 GPU NVIDIA Tesla P100 with 16Gb of memory.\n", " * It should be noted that the single node DSVM is not supposed to run scalable benchmarking analysis. Either scaling up or out the computing instances is necessary to run the benchmarking in an run-time efficient way without any memory issue.\n", " * **NOTE ABOUT THE DEPENDENCIES TO INSTALL**: This notebook uses CPU, GPU and PySpark algorithms, so make sure you install the `full environment` as detailed in the [SETUP.md](../../SETUP.md). \n", " \n", @@ -71,41 +71,55 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, + "outputs": [], + "source": [ + "import warnings\n", + "warnings.filterwarnings(\"ignore\")\n", + "import logging\n", + "logging.basicConfig(level=logging.ERROR) " + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "System version: 3.6.11 | packaged by conda-forge | (default, Nov 27 2020, 18:57:37) \n", - "[GCC 9.3.0]\n", - "Pandas version: 1.1.5\n", - "PySpark version: 2.4.5\n", + "System version: 3.7.13 (default, Mar 29 2022, 02:18:16) \n", + "[GCC 7.5.0]\n", + "NumPy version: 1.21.6\n", + "Pandas version: 1.3.5\n", + "PySpark version: 3.2.2\n", "Surprise version: 1.1.1\n", - "PyTorch version: 1.4.0\n", - "Fast AI version: 1.0.46\n", - "Cornac version: 1.12.0\n", - "Tensorflow version: 1.15.4\n", - "CUDA version: CUDA Version 10.2.89\n", - "CuDNN version: No CUDNN in this machine\n", - "Number of cores: 48\n" + "PyTorch version: 1.12.1+cu102\n", + "Fast AI version: 1.0.61\n", + "Cornac version: 1.14.2\n", + "TensorFlow version: 2.7.4\n", + "CUDA version: 10.2\n", + "CuDNN version: 7605\n", + "Number of cores: 6\n" ] } ], "source": [ - "import sys\n", - "import os\n", + "import sys\n", "import json\n", "import pandas as pd\n", "import numpy as np\n", "import seaborn as sns\n", "import pyspark\n", + "import tensorflow as tf # NOTE: TF needs to be imported before PyTorch, otherwise we get a weird initialization error\n", + "tf.get_logger().setLevel('ERROR') # only show error messages\n", "import torch\n", "import fastai\n", - "import tensorflow as tf\n", - "tf.get_logger().setLevel('ERROR') # only show error messages\n", "import surprise\n", + "import cornac\n", "\n", + "from recommenders.utils.spark_utils import start_or_get_spark\n", "from recommenders.utils.general_utils import get_number_processors\n", "from recommenders.utils.gpu_utils import get_cuda_version, get_cudnn_version\n", "from recommenders.datasets import movielens\n", @@ -114,33 +128,36 @@ "\n", "from benchmark_utils import * \n", "\n", - "print(\"System version: {}\".format(sys.version))\n", - "print(\"Pandas version: {}\".format(pd.__version__))\n", - "print(\"PySpark version: {}\".format(pyspark.__version__))\n", - "print(\"Surprise version: {}\".format(surprise.__version__))\n", - "print(\"PyTorch version: {}\".format(torch.__version__))\n", - "print(\"Fast AI version: {}\".format(fastai.__version__))\n", - "print(\"Cornac version: {}\".format(cornac.__version__))\n", - "print(\"Tensorflow version: {}\".format(tf.__version__))\n", - "print(\"CUDA version: {}\".format(get_cuda_version()))\n", - "print(\"CuDNN version: {}\".format(get_cudnn_version()))\n", - "n_cores = get_number_processors()\n", - "print(\"Number of cores: {}\".format(n_cores))\n", + "print(f\"System version: {sys.version}\")\n", + "print(f\"NumPy version: {np.__version__}\")\n", + "print(f\"Pandas version: {pd.__version__}\")\n", + "print(f\"PySpark version: {pyspark.__version__}\")\n", + "print(f\"Surprise version: {surprise.__version__}\")\n", + "print(f\"PyTorch version: {torch.__version__}\")\n", + "print(f\"Fast AI version: {fastai.__version__}\")\n", + "print(f\"Cornac version: {cornac.__version__}\")\n", + "print(f\"TensorFlow version: {tf.__version__}\")\n", + "print(f\"CUDA version: {get_cuda_version()}\")\n", + "print(f\"CuDNN version: {get_cudnn_version()}\")\n", + "print(f\"Number of cores: {get_number_processors()}\")\n", "\n", "%load_ext autoreload\n", "%autoreload 2" ] }, { - "cell_type": "markdown", + "cell_type": "code", + "execution_count": null, "metadata": {}, + "outputs": [], "source": [ - "## Parameters" + "spark = start_or_get_spark(\"PySpark\", memory=\"32g\")\n", + "spark.conf.set(\"spark.sql.analyzer.failAmbiguousSelfJoin\", \"false\")" ] }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -150,7 +167,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -160,9 +177,30 @@ "torch.cuda.manual_seed_all(SEED)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Parameters" + ] + }, { "cell_type": "code", - "execution_count": 5, + "execution_count": null, + "metadata": { + "tags": [ + "parameters" + ] + }, + "outputs": [], + "source": [ + "data_sizes = [\"100k\", \"1m\"] # Movielens data size: 100k, 1m, 10m, or 20m\n", + "algorithms = [\"als\", \"svd\", \"sar\", \"ncf\", \"fastai\", \"bpr\", \"bivae\", \"lightgcn\"]" + ] + }, + { + "cell_type": "code", + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -198,13 +236,13 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "als_params = {\n", " \"rank\": 10,\n", - " \"maxIter\": 15,\n", + " \"maxIter\": 20,\n", " \"implicitPrefs\": False,\n", " \"alpha\": 0.1,\n", " \"regParam\": 0.05,\n", @@ -276,10 +314,10 @@ "}\n", "\n", "lightgcn_param = {\n", - " \"yaml_file\": os.path.join(\"..\",\"..\",\"recommenders\", \"recommender\", \"deeprec\", \"config\", \"lightgcn.yaml\"),\n", + " \"yaml_file\": os.path.join(\"..\",\"..\",\"recommenders\", \"models\", \"deeprec\", \"config\", \"lightgcn.yaml\"),\n", " \"n_layers\": 3,\n", " \"batch_size\": 1024,\n", - " \"epochs\": 15,\n", + " \"epochs\": 20,\n", " \"learning_rate\": 0.005,\n", " \"eval_epoch\": 5,\n", " \"top_k\": DEFAULT_K,\n", @@ -299,7 +337,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -317,7 +355,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -329,7 +367,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -347,7 +385,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -360,7 +398,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -378,7 +416,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -403,7 +441,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -437,17 +475,7 @@ }, { "cell_type": "code", - "execution_count": 14, - "metadata": {}, - "outputs": [], - "source": [ - "data_sizes = [\"100k\", \"1m\"] # Movielens data size: 100k, 1m, 10m, or 20m\n", - "algorithms = [\"als\", \"svd\", \"sar\", \"ncf\", \"fastai\", \"bpr\", \"bivae\", \"lightgcn\"]" - ] - }, - { - "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": { "scrolled": true }, @@ -456,7 +484,7 @@ "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 4.81k/4.81k [00:01<00:00, 2.41kKB/s]\n" + "100%|██████████| 4.81k/4.81k [00:00<00:00, 12.5kKB/s]\n" ] }, { @@ -465,65 +493,109 @@ "text": [ "Size of Movielens 100k: (100000, 4)\n", "\n", - "Computing als algorithm on Movielens 100k\n", - "Training time: 6.6739s\n", - "Rating prediction time: 0.0716s\n", - "Ranking prediction time: 0.0910s\n", + "Computing als algorithm on Movielens 100k\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " \r" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training time: 6.8526s\n", + "Rating prediction time: 0.0587s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "22/10/19 08:58:41 WARN Column: Constructing trivially true equals predicate, 'userID#225 = userID#225'. Perhaps you need to use aliases.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ranking prediction time: 0.0782s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + " \r" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\n", "Computing svd algorithm on Movielens 100k\n", - "Training time: 4.2722s\n", - "Rating prediction time: 0.2882s\n", - "Ranking prediction time: 14.9461s\n", + "Training time: 4.0902s\n", + "Rating prediction time: 0.2698s\n", + "Ranking prediction time: 13.8704s\n", "\n", "Computing sar algorithm on Movielens 100k\n", - "Training time: 0.3608s\n", - "Ranking prediction time: 0.0853s\n", + "Training time: 0.3344s\n", + "Ranking prediction time: 0.0836s\n", "\n", "Computing ncf algorithm on Movielens 100k\n", - "Training time: 63.8578s\n", - "Ranking prediction time: 4.8731s\n", + "Training time: 66.8339s\n", + "Ranking prediction time: 3.5393s\n", "\n", "Computing fastai algorithm on Movielens 100k\n", - "Training time: 96.2693s\n", - "Rating prediction time: 0.0533s\n", - "Ranking prediction time: 3.2034s\n", + "Training time: 69.7469s\n", + "Rating prediction time: 0.0338s\n", + "Ranking prediction time: 2.7415s\n", "\n", "Computing bpr algorithm on Movielens 100k\n", - "Training time: 6.6029s\n", - "Ranking prediction time: 1.5408s\n", + "Training time: 5.8205s\n", + "Ranking prediction time: 1.9365s\n", "\n", "Computing bivae algorithm on Movielens 100k\n", - "Training time: 10.4133s\n", - "Ranking prediction time: 1.5687s\n", + "Training time: 11.4762s\n", + "Ranking prediction time: 1.4382s\n", "\n", "Computing lightgcn algorithm on Movielens 100k\n", "Already create adjacency matrix.\n", "Already normalize adjacency matrix.\n", "Using xavier initialization.\n", - "Epoch 1 (train)1.5s: train loss = 0.47708 = (mf)0.47684 + (embed)0.00024\n", - "Epoch 2 (train)0.9s: train loss = 0.28938 = (mf)0.28875 + (embed)0.00063\n", - "Epoch 3 (train)0.9s: train loss = 0.25478 = (mf)0.25398 + (embed)0.00080\n", - "Epoch 4 (train)0.9s: train loss = 0.23985 = (mf)0.23888 + (embed)0.00097\n", - "Epoch 5 (train)0.8s + (eval)0.2s: train loss = 0.23025 = (mf)0.22914 + (embed)0.00110, recall = 0.16051, ndcg = 0.34891, precision = 0.30297, map = 0.09295\n", - "Epoch 6 (train)0.9s: train loss = 0.22287 = (mf)0.22165 + (embed)0.00122\n", - "Epoch 7 (train)0.9s: train loss = 0.21368 = (mf)0.21236 + (embed)0.00132\n", - "Epoch 8 (train)0.9s: train loss = 0.20252 = (mf)0.20107 + (embed)0.00145\n", - "Epoch 9 (train)0.9s: train loss = 0.19008 = (mf)0.18847 + (embed)0.00161\n", - "Epoch 10 (train)0.9s + (eval)0.2s: train loss = 0.18022 = (mf)0.17844 + (embed)0.00178, recall = 0.18071, ndcg = 0.39217, precision = 0.34019, map = 0.10928\n", - "Epoch 11 (train)0.9s: train loss = 0.17680 = (mf)0.17487 + (embed)0.00194\n", - "Epoch 12 (train)1.0s: train loss = 0.17439 = (mf)0.17233 + (embed)0.00206\n", - "Epoch 13 (train)0.9s: train loss = 0.16669 = (mf)0.16450 + (embed)0.00219\n", - "Epoch 14 (train)0.9s: train loss = 0.16579 = (mf)0.16349 + (embed)0.00230\n", - "Epoch 15 (train)0.9s + (eval)0.2s: train loss = 0.16282 = (mf)0.16041 + (embed)0.00241, recall = 0.19053, ndcg = 0.40763, precision = 0.35292, map = 0.11737\n", - "Training time: 14.7697s\n", - "Ranking prediction time: 0.0577s\n" + "Epoch 1 (train)0.9s: train loss = 0.47340 = (mf)0.47316 + (embed)0.00024\n", + "Epoch 2 (train)0.8s: train loss = 0.28803 = (mf)0.28739 + (embed)0.00064\n", + "Epoch 3 (train)0.8s: train loss = 0.25425 = (mf)0.25343 + (embed)0.00082\n", + "Epoch 4 (train)0.8s: train loss = 0.23797 = (mf)0.23699 + (embed)0.00098\n", + "Epoch 5 (train)0.8s + (eval)0.2s: train loss = 0.22717 = (mf)0.22605 + (embed)0.00111, recall = 0.16053, ndcg = 0.34968, precision = 0.30276, map = 0.09269\n", + "Epoch 6 (train)0.8s: train loss = 0.22202 = (mf)0.22081 + (embed)0.00121\n", + "Epoch 7 (train)0.8s: train loss = 0.21388 = (mf)0.21256 + (embed)0.00132\n", + "Epoch 8 (train)0.8s: train loss = 0.20578 = (mf)0.20434 + (embed)0.00144\n", + "Epoch 9 (train)0.8s: train loss = 0.19345 = (mf)0.19186 + (embed)0.00158\n", + "Epoch 10 (train)0.8s + (eval)0.2s: train loss = 0.18439 = (mf)0.18265 + (embed)0.00175, recall = 0.18087, ndcg = 0.39271, precision = 0.34316, map = 0.10908\n", + "Epoch 11 (train)0.8s: train loss = 0.17564 = (mf)0.17374 + (embed)0.00190\n", + "Epoch 12 (train)0.8s: train loss = 0.16834 = (mf)0.16629 + (embed)0.00205\n", + "Epoch 13 (train)0.8s: train loss = 0.17051 = (mf)0.16833 + (embed)0.00218\n", + "Epoch 14 (train)0.8s: train loss = 0.16736 = (mf)0.16508 + (embed)0.00228\n", + "Epoch 15 (train)0.8s + (eval)0.2s: train loss = 0.16324 = (mf)0.16086 + (embed)0.00238, recall = 0.19198, ndcg = 0.40608, precision = 0.35048, map = 0.11731\n", + "Epoch 16 (train)0.8s: train loss = 0.16130 = (mf)0.15882 + (embed)0.00249\n", + "Epoch 17 (train)0.8s: train loss = 0.15924 = (mf)0.15667 + (embed)0.00257\n", + "Epoch 18 (train)0.8s: train loss = 0.15797 = (mf)0.15531 + (embed)0.00266\n", + "Epoch 19 (train)0.8s: train loss = 0.15601 = (mf)0.15325 + (embed)0.00275\n", + "Epoch 20 (train)0.8s + (eval)0.2s: train loss = 0.15112 = (mf)0.14826 + (embed)0.00286, recall = 0.19605, ndcg = 0.41763, precision = 0.36098, map = 0.12163\n", + "Training time: 16.2290s\n", + "Ranking prediction time: 0.0451s\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "100%|██████████| 5.78k/5.78k [00:01<00:00, 2.89kKB/s]\n" + "100%|██████████| 5.78k/5.78k [00:00<00:00, 15.4kKB/s]\n" ] }, { @@ -532,62 +604,110 @@ "text": [ "Size of Movielens 1m: (1000209, 4)\n", "\n", - "Computing als algorithm on Movielens 1m\n", - "Training time: 3.3558s\n", - "Rating prediction time: 0.0262s\n", - "Ranking prediction time: 0.0547s\n", + "Computing als algorithm on Movielens 1m\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "22/10/19 09:03:02 WARN TaskSetManager: Stage 588 contains a task of very large size (2759 KiB). The maximum recommended task size is 1000 KiB.\n", + "22/10/19 09:03:02 WARN TaskSetManager: Stage 589 contains a task of very large size (2759 KiB). The maximum recommended task size is 1000 KiB.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Training time: 7.0365s\n", + "Rating prediction time: 0.0355s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "22/10/19 09:03:19 WARN Column: Constructing trivially true equals predicate, 'userID#2403 = userID#2403'. Perhaps you need to use aliases.\n", + "22/10/19 09:03:19 WARN TaskSetManager: Stage 1045 contains a task of very large size (2759 KiB). The maximum recommended task size is 1000 KiB.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Ranking prediction time: 0.0491s\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "22/10/19 09:03:19 WARN TaskSetManager: Stage 1046 contains a task of very large size (2759 KiB). The maximum recommended task size is 1000 KiB.\n", + "22/10/19 09:03:20 WARN TaskSetManager: Stage 1092 contains a task of very large size (2759 KiB). The maximum recommended task size is 1000 KiB.\n", + " \r" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ "\n", "Computing svd algorithm on Movielens 1m\n", - "Training time: 43.4842s\n", - "Rating prediction time: 3.4605s\n", - "Ranking prediction time: 210.9069s\n", + "Training time: 41.6351s\n", + "Rating prediction time: 2.8386s\n", + "Ranking prediction time: 190.6115s\n", "\n", "Computing sar algorithm on Movielens 1m\n", - "Training time: 3.3966s\n", - "Ranking prediction time: 2.1385s\n", + "Training time: 3.2292s\n", + "Ranking prediction time: 1.9796s\n", "\n", "Computing ncf algorithm on Movielens 1m\n", - "Training time: 818.8540s\n", - "Ranking prediction time: 52.5052s\n", + "Training time: 816.1049s\n", + "Ranking prediction time: 48.9872s\n", "\n", "Computing fastai algorithm on Movielens 1m\n", - "Training time: 800.6882s\n", - "Rating prediction time: 0.4382s\n", - "Ranking prediction time: 54.6923s\n", + "Training time: 663.2788s\n", + "Rating prediction time: 0.3985s\n", + "Ranking prediction time: 47.2290s\n", "\n", "Computing bpr algorithm on Movielens 1m\n", - "Training time: 70.1201s\n", - "Ranking prediction time: 30.3135s\n", + "Training time: 66.0371s\n", + "Ranking prediction time: 27.4882s\n", "\n", "Computing bivae algorithm on Movielens 1m\n", - "Training time: 157.1440s\n", - "Ranking prediction time: 29.6179s\n", + "Training time: 152.0912s\n", + "Ranking prediction time: 27.5727s\n", "\n", "Computing lightgcn algorithm on Movielens 1m\n", "Already create adjacency matrix.\n", "Already normalize adjacency matrix.\n", "Using xavier initialization.\n", - "Epoch 1 (train)33.9s: train loss = 0.34573 = (mf)0.34513 + (embed)0.00060\n", - "Epoch 2 (train)27.2s: train loss = 0.26890 = (mf)0.26748 + (embed)0.00142\n", - "Epoch 3 (train)27.2s: train loss = 0.22710 = (mf)0.22481 + (embed)0.00228\n", - "Epoch 4 (train)27.2s: train loss = 0.20468 = (mf)0.20173 + (embed)0.00295\n", - "Epoch 5 (train)28.2s + (eval)2.3s: train loss = 0.18749 = (mf)0.18387 + (embed)0.00362, recall = 0.12346, ndcg = 0.37514, precision = 0.33876, map = 0.07333\n", - "Epoch 6 (train)28.0s: train loss = 0.17191 = (mf)0.16761 + (embed)0.00430\n", - "Epoch 7 (train)27.3s: train loss = 0.16209 = (mf)0.15716 + (embed)0.00493\n", - "Epoch 8 (train)28.0s: train loss = 0.15376 = (mf)0.14825 + (embed)0.00551\n", - "Epoch 9 (train)27.6s: train loss = 0.14711 = (mf)0.14105 + (embed)0.00606\n", - "Epoch 10 (train)25.7s + (eval)2.0s: train loss = 0.14080 = (mf)0.13424 + (embed)0.00657, recall = 0.13965, ndcg = 0.40881, precision = 0.37071, map = 0.08435\n", - "Epoch 11 (train)25.5s: train loss = 0.13659 = (mf)0.12955 + (embed)0.00704\n", - "Epoch 12 (train)26.1s: train loss = 0.13223 = (mf)0.12474 + (embed)0.00749\n", - "Epoch 13 (train)26.4s: train loss = 0.12833 = (mf)0.12039 + (embed)0.00794\n", - "Epoch 14 (train)25.7s: train loss = 0.12402 = (mf)0.11564 + (embed)0.00838\n", - "Epoch 15 (train)27.1s + (eval)2.0s: train loss = 0.12167 = (mf)0.11285 + (embed)0.00882, recall = 0.14387, ndcg = 0.41690, precision = 0.37853, map = 0.08785\n", - "Training time: 417.1986s\n", - "Ranking prediction time: 0.5851s\n", + "Epoch 1 (train)23.2s: train loss = 0.34771 = (mf)0.34712 + (embed)0.00059\n", + "Epoch 2 (train)22.8s: train loss = 0.27739 = (mf)0.27605 + (embed)0.00134\n", + "Epoch 3 (train)22.8s: train loss = 0.22916 = (mf)0.22690 + (embed)0.00226\n", + "Epoch 4 (train)22.8s: train loss = 0.20559 = (mf)0.20265 + (embed)0.00295\n", + "Epoch 5 (train)22.8s + (eval)1.9s: train loss = 0.18818 = (mf)0.18458 + (embed)0.00360, recall = 0.12101, ndcg = 0.36960, precision = 0.33409, map = 0.07156\n", + "Epoch 6 (train)22.8s: train loss = 0.17528 = (mf)0.17106 + (embed)0.00422\n", + "Epoch 7 (train)22.8s: train loss = 0.16460 = (mf)0.15977 + (embed)0.00482\n", + "Epoch 8 (train)22.8s: train loss = 0.15525 = (mf)0.14984 + (embed)0.00541\n", + "Epoch 9 (train)22.8s: train loss = 0.14808 = (mf)0.14214 + (embed)0.00595\n", + "Epoch 10 (train)22.8s + (eval)1.8s: train loss = 0.14349 = (mf)0.13703 + (embed)0.00647, recall = 0.13872, ndcg = 0.40819, precision = 0.37030, map = 0.08408\n", + "Epoch 11 (train)22.8s: train loss = 0.13819 = (mf)0.13122 + (embed)0.00696\n", + "Epoch 12 (train)22.8s: train loss = 0.13232 = (mf)0.12490 + (embed)0.00742\n", + "Epoch 13 (train)22.8s: train loss = 0.12975 = (mf)0.12188 + (embed)0.00787\n", + "Epoch 14 (train)22.8s: train loss = 0.12524 = (mf)0.11694 + (embed)0.00830\n", + "Epoch 15 (train)22.8s + (eval)1.8s: train loss = 0.12257 = (mf)0.11385 + (embed)0.00872, recall = 0.14381, ndcg = 0.41610, precision = 0.37805, map = 0.08683\n", + "Epoch 16 (train)22.8s: train loss = 0.11986 = (mf)0.11074 + (embed)0.00912\n", + "Epoch 17 (train)22.7s: train loss = 0.11695 = (mf)0.10744 + (embed)0.00952\n", + "Epoch 18 (train)23.0s: train loss = 0.11496 = (mf)0.10506 + (embed)0.00990\n", + "Epoch 19 (train)22.8s: train loss = 0.11245 = (mf)0.10217 + (embed)0.01028\n", + "Epoch 20 (train)22.8s + (eval)1.8s: train loss = 0.11068 = (mf)0.10004 + (embed)0.01063, recall = 0.14773, ndcg = 0.42390, precision = 0.38572, map = 0.08977\n", + "Training time: 463.8591s\n", + "Ranking prediction time: 0.5867s\n", "\n", "Computation finished\n", - "CPU times: user 58min 40s, sys: 8min 2s, total: 1h 6min 43s\n", - "Wall time: 1h 3min 3s\n" + "CPU times: user 55min 50s, sys: 8min 57s, total: 1h 4min 47s\n", + "Wall time: 56min 59s\n" ] } ], @@ -658,7 +778,7 @@ " summary = generate_summary(data_size, algo, DEFAULT_K, time_train, time_rating, ratings, time_ranking, rankings)\n", " df_results.loc[df_results.shape[0] + 1] = summary\n", " \n", - "print(\"\\nComputation finished\")" + "print(\"\\nComputation finished\")\n" ] }, { @@ -670,7 +790,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [ { @@ -716,30 +836,30 @@ "